On Tue, Apr 18, 2023 at 11:29:26AM +0200, Claudio Jeker wrote:
> This diff adds the parse.y and config.c bits for flowspec.
> I tried to make flowspec rules as similar to pf rules (even though
> flowspec is more flexible).
>
> Now this diff does nothing in itself but is already large enough to not
> add more to it. In parse.y the individual flowspec components are built up
> in a flowspec context and then at the end converted into a struct flowspec
> object. I wrapped that into a struct flowspec_config so that all the
> parent config bits can be kept together. (For struct network this is
> currently the other way around but I plan to change this at a later
> point).
ok tb
Couple of nits and comments inline. Apart from two copy-paste errors
(s/4/6) nothing important
>
> --
> :wq Claudio
>
> Index: bgpd.h
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v
> retrieving revision 1.471
> diff -u -p -r1.471 bgpd.h
> --- bgpd.h 17 Apr 2023 08:02:21 -0000 1.471
> +++ bgpd.h 18 Apr 2023 09:02:09 -0000
> @@ -231,6 +231,9 @@ SIMPLEQ_HEAD(l3vpn_head, l3vpn);
> struct network;
> TAILQ_HEAD(network_head, network);
>
> +struct flowspec_config;
> +RB_HEAD(flowspec_tree, flowspec_config);
> +
> struct prefixset;
> SIMPLEQ_HEAD(prefixset_head, prefixset);
> struct prefixset_item;
> @@ -271,6 +274,7 @@ struct roa {
> };
>
> RB_HEAD(roa_tree, roa);
> +struct aspa_set;
> RB_HEAD(aspa_tree, aspa_set);
>
> struct set_table;
> @@ -287,6 +291,7 @@ struct bgpd_config {
> struct peer_head peers;
> struct l3vpn_head l3vpns;
> struct network_head networks;
> + struct flowspec_tree flowspecs;
> struct filter_head *filters;
> struct listen_addrs *listen_addrs;
> struct mrt_head *mrt;
> @@ -514,8 +519,23 @@ struct network_config {
> };
>
> struct network {
> - struct network_config net;
> - TAILQ_ENTRY(network) entry;
> + struct network_config net;
> + TAILQ_ENTRY(network) entry;
> +};
> +
> +struct flowspec {
> + uint16_t len;
> + uint8_t aid;
> + uint8_t pad;
> + uint8_t data[1];
> +};
> +#define FLOWSPEC_SIZE (offsetof(struct flowspec, data))
> +
> +struct flowspec_config {
> + RB_ENTRY(flowspec_config) entry;
> + struct filter_set_head attrset;
> + struct flowspec *flow;
> + enum reconf_action reconf_action;
> };
>
> enum rtr_error {
> @@ -1121,6 +1141,10 @@ extern const struct ext_comm_pairs iana_
> #define FLOWSPEC_TYPE_FLOW 13
> #define FLOWSPEC_TYPE_MAX 14
>
> +#define FLOWSPEC_TCP_FLAG_STRING "FSRPAUEW"
> +#define FLOWSPEC_FRAG_STRING4 "DIFL"
> +#define FLOWSPEC_FRAG_STRING6 " IFL"
> +
> struct filter_prefix {
> struct bgpd_addr addr;
> uint8_t op;
> @@ -1366,6 +1390,8 @@ int control_imsg_relay(struct imsg *, st
> struct bgpd_config *new_config(void);
> void copy_config(struct bgpd_config *, struct bgpd_config *);
> void network_free(struct network *);
> +struct flowspec_config *flowspec_alloc(uint8_t, int);
> +void flowspec_free(struct flowspec_config *);
> void free_l3vpns(struct l3vpn_head *);
> void free_config(struct bgpd_config *);
> void free_prefixsets(struct prefixset_head *);
> @@ -1382,6 +1408,7 @@ void expand_networks(struct bgpd_config
> RB_PROTOTYPE(prefixset_tree, prefixset_item, entry, prefixset_cmp);
> RB_PROTOTYPE(roa_tree, roa, entry, roa_cmp);
> RB_PROTOTYPE(aspa_tree, aspa_set, entry, aspa_cmp);
> +RB_PROTOTYPE(flowspec_tree, flowspec_config, entry, flowspec_config_cmp);
>
> /* kroute.c */
> int kr_init(int *, uint8_t);
> Index: config.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/config.c,v
> retrieving revision 1.106
> diff -u -p -r1.106 config.c
> --- config.c 28 Dec 2022 21:30:15 -0000 1.106
> +++ config.c 18 Apr 2023 09:07:42 -0000
> @@ -22,6 +22,7 @@
> #include <errno.h>
> #include <ifaddrs.h>
> #include <netdb.h>
> +#include <stddef.h>
> #include <stdlib.h>
> #include <stdio.h>
> #include <string.h>
> @@ -33,6 +34,7 @@
>
> int host_ip(const char *, struct bgpd_addr *, uint8_t *);
> void free_networks(struct network_head *);
> +void free_flowspecs(struct flowspec_tree *);
>
> struct bgpd_config *
> new_config(void)
> @@ -53,6 +55,7 @@ new_config(void)
> /* init the various list for later */
> RB_INIT(&conf->peers);
> TAILQ_INIT(&conf->networks);
> + RB_INIT(&conf->flowspecs);
> SIMPLEQ_INIT(&conf->l3vpns);
> SIMPLEQ_INIT(&conf->prefixsets);
> SIMPLEQ_INIT(&conf->originsets);
> @@ -105,6 +108,50 @@ free_networks(struct network_head *netwo
> }
> }
>
> +struct flowspec_config *
> +flowspec_alloc(uint8_t aid, int len)
> +{
> + struct flowspec_config *conf;
> + struct flowspec *flow;
> +
> + flow = malloc(FLOWSPEC_SIZE + len);
> + if (flow == NULL)
> + return NULL;
> + memset(flow, 0, FLOWSPEC_SIZE);
I assume not zeroing len bytes is worth this extra dance as opposed
to using calloc().
> +
> + conf = calloc(1, sizeof(*conf));
> + if (conf == NULL) {
> + free(flow);
> + return NULL;
> + }
> +
> + conf->flow = flow;
> + TAILQ_INIT(&conf->attrset);
> + flow->len = len;
> + flow->aid = aid;
> +
> + return conf;
> +}
> +
> +void
> +flowspec_free(struct flowspec_config *f)
> +{
> + filterset_free(&f->attrset);
> + free(f->flow);
> + free(f);
> +}
> +
> +void
> +free_flowspecs(struct flowspec_tree *flowspecs)
> +{
> + struct flowspec_config *f, *nf;
> +
> + RB_FOREACH_SAFE(f, flowspec_tree, flowspecs, nf) {
> + RB_REMOVE(flowspec_tree, flowspecs, f);
> + flowspec_free(f);
> + }
> +}
> +
> void
> free_l3vpns(struct l3vpn_head *l3vpns)
> {
> @@ -213,6 +260,7 @@ free_config(struct bgpd_config *conf)
>
> free_l3vpns(&conf->l3vpns);
> free_networks(&conf->networks);
> + free_flowspecs(&conf->flowspecs);
> filterlist_free(conf->filters);
> free_prefixsets(&conf->prefixsets);
> free_prefixsets(&conf->originsets);
> @@ -251,6 +299,7 @@ merge_config(struct bgpd_config *xconf,
> {
> struct listen_addr *nla, *ola, *next;
> struct peer *p, *np, *nextp;
> + struct flowspec_config *f, *nextf, *xf;
>
> /*
> * merge the freshly parsed conf into the running xconf
> @@ -316,6 +365,26 @@ merge_config(struct bgpd_config *xconf,
> free_networks(&xconf->networks);
> TAILQ_CONCAT(&xconf->networks, &conf->networks, entry);
>
> + /*
> + * merge the flowspec statements, but first mark the old ones
> + * for deletion. Which happens when the flowspec is sent to the RDE.
> + */
This comment is a bit awkward. Maybe split the sentence differently like this:
/*
* Merge the flowspec statements. Mark the old ones for deletion
* which happens when the flowspec is sent to the RDE.
*/
> + RB_FOREACH(f, flowspec_tree, &xconf->flowspecs)
> + f->reconf_action = RECONF_DELETE;
> +
> + RB_FOREACH_SAFE(f, flowspec_tree, &conf->flowspecs, nextf) {
> + RB_REMOVE(flowspec_tree, &conf->flowspecs, f);
> +
> + xf = RB_INSERT(flowspec_tree, &xconf->flowspecs, f);
> + if (xf != NULL) {
> + filterset_free(&xf->attrset);
> + filterset_move(&f->attrset, &xf->attrset);
> + flowspec_free(f);
> + xf->reconf_action = RECONF_KEEP;
> + } else
> + f->reconf_action = RECONF_KEEP;
> + }
> +
> /* switch the l3vpn configs, first remove the old ones */
> free_l3vpns(&xconf->l3vpns);
> SIMPLEQ_CONCAT(&xconf->l3vpns, &conf->l3vpns);
> @@ -668,3 +737,17 @@ aspa_cmp(struct aspa_set *a, struct aspa
> }
>
> RB_GENERATE(aspa_tree, aspa_set, entry, aspa_cmp);
> +
> +static inline int
> +flowspec_config_cmp(struct flowspec_config *a, struct flowspec_config *b)
> +{
> + if (a->flow->aid < b->flow->aid)
> + return -1;
> + if (a->flow->aid > b->flow->aid)
> + return 1;
> +
> + return flowspec_cmp(a->flow->data, a->flow->len,
> + b->flow->data, b->flow->len, a->flow->aid == AID_FLOWSPECv6);
> +}
> +
> +RB_GENERATE(flowspec_tree, flowspec_config, entry, flowspec_config_cmp);
> Index: parse.y
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/parse.y,v
> retrieving revision 1.446
> diff -u -p -r1.446 parse.y
> --- parse.y 5 Apr 2023 08:37:21 -0000 1.446
> +++ parse.y 18 Apr 2023 08:45:51 -0000
> @@ -28,7 +28,10 @@
> #include <sys/stat.h>
> #include <sys/un.h>
> #include <netinet/in.h>
> +#include <netinet/ip.h>
> +#include <netinet/ip_icmp.h>
> #include <netinet/ip_ipsp.h>
> +#include <netinet/icmp6.h>
> #include <arpa/inet.h>
>
> #include <ctype.h>
> @@ -130,6 +133,14 @@ struct aspa_tas_l {
> uint8_t aid;
> };
>
> +struct flowspec_context {
> + uint8_t *components[FLOWSPEC_TYPE_MAX];
> + uint16_t complen[FLOWSPEC_TYPE_MAX];
> + uint8_t aid;
> + uint8_t type;
> + uint8_t addr_type;
> +};
> +
> struct peer *alloc_peer(void);
> struct peer *new_peer(void);
> struct peer *new_group(void);
> @@ -162,7 +173,15 @@ static void add_roa_set(struct prefixse
> static struct rtr_config *get_rtr(struct bgpd_addr *);
> static int insert_rtr(struct rtr_config *);
> static int merge_aspa_set(uint32_t, struct aspa_tas_l *, time_t);
> +static int map_tos(char *, int *);
> static int getservice(char *);
> +static int parse_flags(char *);
> +static struct flowspec_config *flow_to_flowspec(struct
> flowspec_context *);
> +static void flow_free(struct flowspec_context *);
> +static int push_prefix(struct bgpd_addr *, uint8_t);
> +static int push_binop(uint8_t, long long);
> +static int push_unary_numop(enum comp_ops, long long);
> +static int push_binary_numop(enum comp_ops, long long, long long);
>
> static struct bgpd_config *conf;
> static struct network_head *netconf;
> @@ -180,6 +199,7 @@ static struct filter_head *peerfilter_l;
> static struct filter_head *groupfilter_l;
> static struct filter_rule *curpeer_filter[2];
> static struct filter_rule *curgroup_filter[2];
> +static struct flowspec_context *curflow;
> static int noexpires;
>
> typedef struct {
> @@ -215,10 +235,11 @@ typedef struct {
> %}
>
> %token AS ROUTERID HOLDTIME YMIN LISTEN ON FIBUPDATE FIBPRIORITY RTABLE
> -%token NONE UNICAST VPN FLOWSPEC RD EXPORT EXPORTTRGT IMPORTTRGT
> DEFAULTROUTE
> +%token NONE UNICAST VPN RD EXPORT EXPORTTRGT IMPORTTRGT DEFAULTROUTE
> %token RDE RIB EVALUATE IGNORE COMPARE RTR PORT
> %token GROUP NEIGHBOR NETWORK
> %token EBGP IBGP
> +%token FLOWSPEC PROTO FLAGS FRAGMENT TOS LENGTH ICMPTYPE CODE
> %token LOCALAS REMOTEAS DESCR LOCALADDR MULTIHOP PASSIVE MAXPREFIX
> RESTART
> %token ANNOUNCE CAPABILITIES REFRESH AS4BYTE CONNECTRETRY ENHANCED
> ADDPATH
> %token SEND RECV PLUS POLICY ROLE
> @@ -249,7 +270,7 @@ typedef struct {
> %type <v.number> yesno inout restricted expires enforce
> %type <v.number> validity aspa_validity
> %type <v.number> addpathextra addpathmax
> -%type <v.number> port
> +%type <v.number> port proto_item tos length flag icmptype
> %type <v.string> string
> %type <v.addr> address
> %type <v.prefix> prefix addrspec
> @@ -282,6 +303,7 @@ grammar : /* empty */
> | grammar rtr '\n'
> | grammar rib '\n'
> | grammar network '\n'
> + | grammar flowspec '\n'
> | grammar mrtdump '\n'
> | grammar conf_main '\n'
> | grammar l3vpn '\n'
> @@ -1139,6 +1161,118 @@ network : NETWORK prefix filter_set
> {
> }
> ;
>
> +flowspec : FLOWSPEC af {
> + if ((curflow = calloc(1, sizeof(*curflow))) == NULL)
> + fatal("new_flowspec");
> + curflow->aid = $2;
> + } flow_rules filter_set {
> + struct flowspec_config *f;
> +
> + f = flow_to_flowspec(curflow);
> + if (f == NULL) {
> + yyerror("out of memory");
> + free($5);
> + flow_free(curflow);
> + curflow = NULL;
> + YYERROR;
> + }
> + filterset_move($5, &f->attrset);
> + free($5);
> + flow_free(curflow);
> + curflow = NULL;
> +
> + if (RB_INSERT(flowspec_tree, &conf->flowspecs, f) !=
> + NULL) {
> + yyerror("duplicate flowspec definition");
> + flowspec_free(f);
> + YYERROR;
> + }
> + }
> + ;
> +
> +proto : PROTO proto_item
> + | PROTO '{' optnl proto_list optnl '}'
> + ;
> +
> +proto_list : proto_item {
> + curflow->type = FLOWSPEC_TYPE_PROTO;
> + if (push_unary_numop(OP_EQ, $1) == -1)
> + YYERROR;
> + }
> + | proto_list comma proto_item {
> + curflow->type = FLOWSPEC_TYPE_PROTO;
> + if (push_unary_numop(OP_EQ, $3) == -1)
> + YYERROR;
> + }
> + ;
> +
> +proto_item : STRING {
> + struct protoent *p;
> +
> + p = getprotobyname($1);
> + if (p == NULL) {
> + yyerror("unknown protocol %s", $1);
> + free($1);
> + YYERROR;
> + }
> + $$ = p->p_proto;
> + free($1);
> + }
> + | NUMBER {
> + if ($1 < 0 || $1 > 255) {
> + yyerror("protocol outside range");
> + YYERROR;
> + }
> + $$ = $1;
> + }
> + ;
> +
> +from : FROM {
> + curflow->type = FLOWSPEC_TYPE_SRC_PORT;
> + curflow->addr_type = FLOWSPEC_TYPE_SOURCE;
> + } ipportspec
> + ;
> +
> +to : TO {
> + curflow->type = FLOWSPEC_TYPE_DST_PORT;
> + curflow->addr_type = FLOWSPEC_TYPE_DEST;
> + } ipportspec
> + ;
> +
> +ipportspec : ipspec
> + | ipspec PORT portspec
> + | PORT portspec
> + ;
> +
> +ipspec : ANY
> + | prefix {
> + if (push_prefix(&$1.prefix, $1.len) == -1)
> + YYERROR;
> + }
> + ;
> +
> +portspec : port_item
> + | '{' optnl port_list optnl '}'
> + ;
> +
> +port_list : port_item
> + | port_list comma port_item
> + ;
> +
> +port_item : port {
> + if (push_unary_numop(OP_EQ, $1) == -1)
> + YYERROR;
> + }
> + | unaryop port {
> + if (push_unary_numop($1, $2) == -1)
> + YYERROR;
> + }
> + | port binaryop port {
> + if (push_binary_numop($2, $1, $3))
> + YYERROR;
> + }
> + ;
> +
> port : NUMBER {
> if ($1 < 1 || $1 > USHRT_MAX) {
> yyerror("port must be between %u and %u",
> @@ -1157,6 +1291,195 @@ port : NUMBER {
> }
> ;
>
> +flow_rules : /* empty */
> + | flow_rules_l
> + ;
> +
> +flow_rules_l : flowrule
> + | flow_rules_l flowrule
> + ;
> +
> +flowrule : from
> + | to
> + | proto
> + | FLAGS {
> + curflow->type = FLOWSPEC_TYPE_TCP_FLAGS;
> + } flags
> + | icmpspec
> + | TOS tos {
> + curflow->type = FLOWSPEC_TYPE_DSCP;
> + if (push_unary_numop(OP_EQ, $2 >> 2) == -1)
> + YYERROR;
> + }
> + | LENGTH lengthspec {
> + curflow->type = FLOWSPEC_TYPE_PKT_LEN;
> + }
> + | FRAGMENT {
> + curflow->type = FLOWSPEC_TYPE_FRAG;
> + } flags;
> + ;
> +
> +flags : flag '/' flag {
> + if (($1 & $3) != $1) {
> + yyerror("bad flag combination, "
> + "check bit not in mask");
> + YYERROR;
> + }
> + if (push_binop(FLOWSPEC_OP_BIT_MATCH, $1) == -1)
> + YYERROR;
> + /* check if extra mask op is needed */
> + if ($3 & ~$1) {
> + if (push_binop(FLOWSPEC_OP_BIT_NOT |
> + FLOWSPEC_OP_AND, $3 & ~$1) == -1)
> + YYERROR;
> + }
> + }
> + | '/' flag {
> + if (push_binop(FLOWSPEC_OP_BIT_NOT, $2) == -1)
> + YYERROR;
> + }
> + | flag {
> + if (push_binop(0, $1) == -1)
> + YYERROR;
> + }
> + | ANY /* nothing */
> + ;
> +
> +flag : STRING {
> + if (($$ = parse_flags($1)) < 0) {
> + yyerror("bad flags %s", $1);
> + free($1);
> + YYERROR;
> + }
> + free($1);
> + }
> + ;
> +
> +icmpspec : ICMPTYPE icmp_item
> + | ICMPTYPE '{' optnl icmp_list optnl '}'
> + ;
> +
> +icmp_list : icmp_item
> + | icmp_list comma icmp_item
> + ;
> +
> +icmp_item : icmptype {
> + curflow->type = FLOWSPEC_TYPE_ICMP_TYPE;
> + if (push_unary_numop(OP_EQ, $1) == -1)
> + YYERROR;
> + }
> + | icmptype CODE STRING {
> + int code;
> +
> + if ((code = geticmpcodebyname($1, $3, curflow->aid)) ==
> + -1) {
> + yyerror("unknown icmp-code %s", $3);
> + free($3);
> + YYERROR;
> + }
> + free($3);
> +
> + curflow->type = FLOWSPEC_TYPE_ICMP_TYPE;
> + if (push_unary_numop(OP_EQ, $1) == -1)
> + YYERROR;
> + curflow->type = FLOWSPEC_TYPE_ICMP_CODE;
> + if (push_unary_numop(OP_EQ, code) == -1)
> + YYERROR;
> + }
> + | icmptype CODE NUMBER {
> + if ($3 < 0 || $3 > 255) {
> + yyerror("illegal icmp-code %lld", $3);
> + YYERROR;
> + }
> + curflow->type = FLOWSPEC_TYPE_ICMP_TYPE;
> + if (push_unary_numop(OP_EQ, $1) == -1)
> + YYERROR;
> + curflow->type = FLOWSPEC_TYPE_ICMP_CODE;
> + if (push_unary_numop(OP_EQ, $3) == -1)
> + YYERROR;
> + }
> + ;
> +
> +icmptype : STRING {
> + int type;
> +
> + if ((type = geticmptypebyname($1, curflow->aid)) ==
> + -1) {
> + yyerror("unknown icmp-type %s", $1);
> + free($1);
> + YYERROR;
> + }
> + $$ = type;
> + free($1);
> + }
> + | NUMBER {
> + if ($1 < 0 || $1 > 255) {
> + yyerror("illegal icmp-type %lld", $1);
> + YYERROR;
> + }
> + $$ = $1;
> + }
> + ;
> +
> +tos : STRING {
> + int val;
> + char *end;
> +
> + if (map_tos($1, &val))
> + $$ = val;
> + else if ($1[0] == '0' && $1[1] == 'x') {
> + errno = 0;
> + $$ = strtoul($1, &end, 16);
> + if (errno || *end != '\0')
> + $$ = 256;
> + } else
> + $$ = 256;
> + if ($$ < 0 || $$ > 255) {
> + yyerror("illegal tos value %s", $1);
> + free($1);
> + YYERROR;
> + }
> + free($1);
> + }
> + | NUMBER {
> + if ($$ < 0 || $$ > 255) {
> + yyerror("illegal tos value %lld", $1);
> + YYERROR;
> + }
> + $$ = $1;
> + }
> + ;
> +
> +lengthspec : length_item
> + | '{' optnl length_list optnl '}'
> + ;
> +
> +length_list : length_item
> + | length_list comma length_item
> + ;
> +
> +length_item : length {
> + if (push_unary_numop(OP_EQ, $1) == -1)
> + YYERROR;
> + }
> + | unaryop length {
> + if (push_unary_numop($1, $2) == -1)
> + YYERROR;
> + }
> + | length binaryop length {
> + if (push_binary_numop($2, $1, $3) == -1)
> + YYERROR;
> + }
> + ;
> +
> +length : NUMBER {
> + if ($$ < 0 || $$ > USHRT_MAX) {
> + yyerror("illegal ptk length value %lld", $1);
> + YYERROR;
> + }
> + $$ = $1;
> + }
> +
> inout : IN { $$ = 1; }
> | OUT { $$ = 0; }
> ;
> @@ -3206,7 +3529,9 @@ lookup(char *s)
> { "ext-community", EXTCOMMUNITY},
> { "fib-priority", FIBPRIORITY},
> { "fib-update", FIBUPDATE},
> + { "flags", FLAGS},
> { "flowspec", FLOWSPEC},
> + { "fragment", FRAGMENT},
> { "from", FROM},
> { "group", GROUP},
> { "holdtime", HOLDTIME},
> @@ -3265,6 +3590,7 @@ lookup(char *s)
> { "prepend-neighbor", PREPEND_PEER},
> { "prepend-self", PREPEND_SELF},
> { "priority", PRIORITY},
> + { "proto", PROTO},
> { "provider-as", PROVIDERAS},
> { "qualify", QUALIFY},
> { "quick", QUICK},
> @@ -3293,6 +3619,7 @@ lookup(char *s)
> { "static", STATIC},
> { "tcp", TCP},
> { "to", TO},
> + { "tos", TOS},
> { "transit-as", TRANSITAS},
> { "transparent-as", TRANSPARENT},
> { "ttl-security", TTLSECURITY},
> @@ -3781,6 +4108,7 @@ parse_config(char *filename, struct peer
> cur_peers = NULL;
> new_peers = NULL;
> netconf = NULL;
> + curflow = NULL;
>
> if (errors) {
> errors:
> @@ -5159,6 +5487,57 @@ merge_aspa_set(uint32_t as, struct aspa_
> }
>
> static int
> +kw_casecmp(const void *k, const void *e)
> +{
> + return (strcasecmp(k, ((const struct keywords *)e)->k_name));
> +}
> +
> +static int
> +map_tos(char *s, int *val)
> +{
> + /* DiffServ Codepoints and other TOS mappings */
> + const struct keywords toswords[] = {
> + { "af11", IPTOS_DSCP_AF11 },
> + { "af12", IPTOS_DSCP_AF12 },
> + { "af13", IPTOS_DSCP_AF13 },
> + { "af21", IPTOS_DSCP_AF21 },
> + { "af22", IPTOS_DSCP_AF22 },
> + { "af23", IPTOS_DSCP_AF23 },
> + { "af31", IPTOS_DSCP_AF31 },
> + { "af32", IPTOS_DSCP_AF32 },
> + { "af33", IPTOS_DSCP_AF33 },
> + { "af41", IPTOS_DSCP_AF41 },
> + { "af42", IPTOS_DSCP_AF42 },
> + { "af43", IPTOS_DSCP_AF43 },
> + { "critical", IPTOS_PREC_CRITIC_ECP },
> + { "cs0", IPTOS_DSCP_CS0 },
> + { "cs1", IPTOS_DSCP_CS1 },
> + { "cs2", IPTOS_DSCP_CS2 },
> + { "cs3", IPTOS_DSCP_CS3 },
> + { "cs4", IPTOS_DSCP_CS4 },
> + { "cs5", IPTOS_DSCP_CS5 },
> + { "cs6", IPTOS_DSCP_CS6 },
> + { "cs7", IPTOS_DSCP_CS7 },
> + { "ef", IPTOS_DSCP_EF },
> + { "inetcontrol", IPTOS_PREC_INTERNETCONTROL },
> + { "lowdelay", IPTOS_LOWDELAY },
> + { "netcontrol", IPTOS_PREC_NETCONTROL },
> + { "reliability", IPTOS_RELIABILITY },
> + { "throughput", IPTOS_THROUGHPUT }
> + };
> + const struct keywords *p;
> +
> + p = bsearch(s, toswords, sizeof(toswords)/sizeof(toswords[0]),
> + sizeof(toswords[0]), kw_casecmp);
> +
> + if (p) {
> + *val = p->k_val;
> + return (1);
> + }
> + return (0);
> +}
> +
> +static int
> getservice(char *n)
> {
> struct servent *s;
> @@ -5169,4 +5548,452 @@ getservice(char *n)
> if (s == NULL)
> return -1;
> return s->s_port;
> +}
> +
> +static int
> +parse_flags(char *s)
> +{
> + const char *flags = FLOWSPEC_TCP_FLAG_STRING;
> + char *p, *q;
> + uint8_t f = 0;
> +
> + if (curflow->type == FLOWSPEC_TYPE_FRAG) {
> + if (curflow->aid == AID_INET)
> + flags = FLOWSPEC_FRAG_STRING4;
> + else
> + flags = FLOWSPEC_FRAG_STRING4;
s/4/6
flags = FLOWSPEC_FRAG_STRING6;
> + }
> +
> + for (p = s; *p; p++) {
> + if ((q = strchr(flags, *p)) == NULL)
> + return -1;
> + f |= 1 << (q - flags);
> + }
> + return (f ? f : 0xff);
> +}
> +
> +static void
> +component_finish(int type, uint8_t *data, int len)
> +{
> + uint8_t *last;
> + int i = 0;
> +
> + switch (type) {
> + case FLOWSPEC_TYPE_DEST:
> + case FLOWSPEC_TYPE_SOURCE:
> + /* nothing todo */
to do is two words here.
> + return;
> + default:
> + break;
> + }
> +
> + do {
> + last = data + i;
> + i += FLOWSPEC_OP_LEN(*last) + 1;
> + } while (i < len);
> + *last |= FLOWSPEC_OP_EOL;
> +}
> +
> +static struct flowspec_config *
> +flow_to_flowspec(struct flowspec_context *ctx)
> +{
> + struct flowspec_config *f;
> + int i, len = 0;
> + uint8_t aid;
> +
> + switch (ctx->aid) {
> + case AID_INET:
> + aid = AID_FLOWSPECv4;
> + break;
> + case AID_INET6:
> + aid = AID_FLOWSPECv4;
s/4/6
aid = AID_FLOWSPECv6;
> + break;
> + default:
> + return NULL;
> + }
> +
> + for (i = FLOWSPEC_TYPE_MIN; i < FLOWSPEC_TYPE_MAX; i++)
> + if (ctx->components[i] != NULL)
> + len += ctx->complen[i] + 1;
> +
> + f = flowspec_alloc(aid, len);
> + if (f == NULL)
> + return NULL;
> +
> + len = 0;
> + for (i = FLOWSPEC_TYPE_MIN; i < FLOWSPEC_TYPE_MAX; i++)
> + if (ctx->components[i] != NULL) {
> + f->flow->data[len++] = i;
> + component_finish(i, ctx->components[i],
> + ctx->complen[i]);
> + memcpy(f->flow->data + len, ctx->components[i],
> + ctx->complen[i]);
> + len += ctx->complen[i];
> + }
> +
> + return f;
> +}
> +
> +static void
> +flow_free(struct flowspec_context *ctx)
> +{
> + int i;
> +
> + for (i = 0; i < FLOWSPEC_TYPE_MAX; i++)
> + free(ctx->components[i]);
> + free(ctx);
> +}
> +
> +static int
> +push_prefix(struct bgpd_addr *addr, uint8_t len)
> +{
> + void *data;
> + uint8_t *comp;
> + int complen, l = 0;
> +
> + if (curflow->components[curflow->addr_type] != NULL) {
> + yyerror("flowspec address already set");
> + return -1;
> + }
> +
> + if (curflow->aid != addr->aid) {
> + yyerror("wrong address family for flowspec address");
> + return -1;
> + }
> +
> + switch (curflow->aid) {
> + case AID_INET:
> + complen = PREFIX_SIZE(len);
> + data = &addr->v4;
> + break;
> + case AID_INET6:
> + /* IPv6 includes an offset byte */
> + complen = PREFIX_SIZE(len) + 1;
> + data = &addr->v6;
> + break;
> + }
> + comp = malloc(complen);
> + if (comp == NULL) {
> + yyerror("out of memory");
> + return -1;
> + }
> +
> + comp[l++] = len;
> + if (curflow->aid == AID_INET6)
> + comp[l++] = 0;
> + memcpy(comp + l, data, PREFIX_SIZE(len) - 1);
> +
> + curflow->complen[curflow->addr_type] = complen;
> + curflow->components[curflow->addr_type] = comp;
> +
> + return 0;
> +}
> +
> +static int
> +push_binop(uint8_t binop, long long val)
> +{
> + uint8_t *comp;
> + int complen;
> + uint8_t u8;
> +
> + if (val < 0 || val > 0xff) {
> + yyerror("unsupported value for flowspec bin_op");
> + return -1;
> + }
> + u8 = val;
> +
> + complen = curflow->complen[curflow->type];
> + comp = realloc(curflow->components[curflow->type],
> + complen + 2);
> + if (comp == NULL) {
> + yyerror("out of memory");
> + return -1;
> + }
> +
> + comp[complen++] = binop;
> + comp[complen++] = u8;
> + curflow->complen[curflow->type] = complen;
> + curflow->components[curflow->type] = comp;
> +
> + return 0;
> +}
> +
> +static uint8_t
> +component_numop(enum comp_ops op, int and, int len)
> +{
> + uint8_t flag = 0;
> +
> + switch (op) {
> + case OP_EQ:
> + flag |= FLOWSPEC_OP_NUM_EQ;
> + break;
> + case OP_NE:
> + flag |= FLOWSPEC_OP_NUM_NOT;
> + break;
> + case OP_LE:
> + flag |= FLOWSPEC_OP_NUM_LE;
> + break;
> + case OP_LT:
> + flag |= FLOWSPEC_OP_NUM_LT;
> + break;
> + case OP_GE:
> + flag |= FLOWSPEC_OP_NUM_GE;
> + break;
> + case OP_GT:
> + flag |= FLOWSPEC_OP_NUM_GT;
> + break;
> + default:
> + fatalx("unsupported op");
> + }
> +
> + switch (len) {
> + case 2:
> + flag |= 1 << FLOWSPEC_OP_LEN_SHIFT;
> + break;
> + case 4:
> + flag |= 2 << FLOWSPEC_OP_LEN_SHIFT;
> + break;
> + case 8:
> + flag |= 3 << FLOWSPEC_OP_LEN_SHIFT;
> + break;
> + }
> +
> + if (and)
> + flag |= FLOWSPEC_OP_AND;
> +
> + return flag;
> +}
> +
> +static int
> +push_numop(enum comp_ops op, int and, long long val)
> +{
> + uint8_t *comp;
> + void *data;
> + uint32_t u32;
> + uint16_t u16;
> + uint8_t u8;
> + int len, complen;
> +
> + if (val < 0 || val > 0xffffffff) {
> + yyerror("unsupported value for flowspec num_op");
> + return -1;
> + } else if (val <= 255) {
> + len = 1;
> + u8 = val;
> + data = &u8;
> + } else if (val <= 0xffff) {
> + len = 2;
> + u16 = htons(val);
> + data = &u16;
> + } else {
> + len = 4;
> + u32 = htonl(val);
> + data = &u32;
> + }
> +
> + complen = curflow->complen[curflow->type];
> + comp = realloc(curflow->components[curflow->type],
> + complen + len + 1);
> + if (comp == NULL) {
> + yyerror("out of memory");
> + return -1;
> + }
> +
> + comp[complen++] = component_numop(op, and, len);
> + memcpy(comp + complen, data, len);
> + complen += len;
> + curflow->complen[curflow->type] = complen;
> + curflow->components[curflow->type] = comp;
> +
> + return 0;
> +}
> +
> +static int
> +push_unary_numop(enum comp_ops op, long long val)
> +{
> + return push_numop(op, 0, val);
> +}
> +
> +static int
> +push_binary_numop(enum comp_ops op, long long min, long long max)
> +{
> + switch (op) {
> + case OP_RANGE:
> + if (push_numop(OP_GE, 0, min) == -1)
> + return -1;
> + return push_numop(OP_LE, 1, max);
> + case OP_XRANGE:
> + if (push_numop(OP_LT, 0, min) == -1)
> + return -1;
> + return push_numop(OP_GT, 0, max);
> + default:
> + yyerror("unsupported binary flowspec num_op");
> + return -1;
> + }
> +}
> +
> +struct icmptypeent {
> + const char *name;
> + u_int8_t type;
> +};
> +
> +struct icmpcodeent {
> + const char *name;
> + u_int8_t type;
> + u_int8_t code;
> +};
> +
> +static const struct icmptypeent icmp_type[] = {
> + { "echoreq", ICMP_ECHO },
> + { "echorep", ICMP_ECHOREPLY },
> + { "unreach", ICMP_UNREACH },
> + { "squench", ICMP_SOURCEQUENCH },
> + { "redir", ICMP_REDIRECT },
> + { "althost", ICMP_ALTHOSTADDR },
> + { "routeradv", ICMP_ROUTERADVERT },
> + { "routersol", ICMP_ROUTERSOLICIT },
> + { "timex", ICMP_TIMXCEED },
> + { "paramprob", ICMP_PARAMPROB },
> + { "timereq", ICMP_TSTAMP },
> + { "timerep", ICMP_TSTAMPREPLY },
> + { "inforeq", ICMP_IREQ },
> + { "inforep", ICMP_IREQREPLY },
> + { "maskreq", ICMP_MASKREQ },
> + { "maskrep", ICMP_MASKREPLY },
> + { "trace", ICMP_TRACEROUTE },
> + { "dataconv", ICMP_DATACONVERR },
> + { "mobredir", ICMP_MOBILE_REDIRECT },
> + { "ipv6-where", ICMP_IPV6_WHEREAREYOU },
> + { "ipv6-here", ICMP_IPV6_IAMHERE },
> + { "mobregreq", ICMP_MOBILE_REGREQUEST },
> + { "mobregrep", ICMP_MOBILE_REGREPLY },
> + { "skip", ICMP_SKIP },
> + { "photuris", ICMP_PHOTURIS }
> +};
> +
> +static const struct icmptypeent icmp6_type[] = {
> + { "unreach", ICMP6_DST_UNREACH },
> + { "toobig", ICMP6_PACKET_TOO_BIG },
> + { "timex", ICMP6_TIME_EXCEEDED },
> + { "paramprob", ICMP6_PARAM_PROB },
> + { "echoreq", ICMP6_ECHO_REQUEST },
> + { "echorep", ICMP6_ECHO_REPLY },
> + { "groupqry", ICMP6_MEMBERSHIP_QUERY },
> + { "listqry", MLD_LISTENER_QUERY },
> + { "grouprep", ICMP6_MEMBERSHIP_REPORT },
> + { "listenrep", MLD_LISTENER_REPORT },
> + { "groupterm", ICMP6_MEMBERSHIP_REDUCTION },
> + { "listendone", MLD_LISTENER_DONE },
> + { "routersol", ND_ROUTER_SOLICIT },
> + { "routeradv", ND_ROUTER_ADVERT },
> + { "neighbrsol", ND_NEIGHBOR_SOLICIT },
> + { "neighbradv", ND_NEIGHBOR_ADVERT },
> + { "redir", ND_REDIRECT },
> + { "routrrenum", ICMP6_ROUTER_RENUMBERING },
> + { "wrureq", ICMP6_WRUREQUEST },
> + { "wrurep", ICMP6_WRUREPLY },
> + { "fqdnreq", ICMP6_FQDN_QUERY },
> + { "fqdnrep", ICMP6_FQDN_REPLY },
> + { "niqry", ICMP6_NI_QUERY },
> + { "nirep", ICMP6_NI_REPLY },
> + { "mtraceresp", MLD_MTRACE_RESP },
> + { "mtrace", MLD_MTRACE },
> + { "listenrepv2", MLDV2_LISTENER_REPORT },
> +};
> +
> +static const struct icmpcodeent icmp_code[] = {
> + { "net-unr", ICMP_UNREACH, ICMP_UNREACH_NET },
> + { "host-unr", ICMP_UNREACH, ICMP_UNREACH_HOST },
> + { "proto-unr", ICMP_UNREACH, ICMP_UNREACH_PROTOCOL },
> + { "port-unr", ICMP_UNREACH, ICMP_UNREACH_PORT },
> + { "needfrag", ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG },
> + { "srcfail", ICMP_UNREACH, ICMP_UNREACH_SRCFAIL },
> + { "net-unk", ICMP_UNREACH, ICMP_UNREACH_NET_UNKNOWN },
> + { "host-unk", ICMP_UNREACH, ICMP_UNREACH_HOST_UNKNOWN },
> + { "isolate", ICMP_UNREACH, ICMP_UNREACH_ISOLATED },
> + { "net-prohib", ICMP_UNREACH, ICMP_UNREACH_NET_PROHIB },
> + { "host-prohib", ICMP_UNREACH, ICMP_UNREACH_HOST_PROHIB },
> + { "net-tos", ICMP_UNREACH, ICMP_UNREACH_TOSNET },
> + { "host-tos", ICMP_UNREACH, ICMP_UNREACH_TOSHOST },
> + { "filter-prohib", ICMP_UNREACH, ICMP_UNREACH_FILTER_PROHIB },
> + { "host-preced", ICMP_UNREACH, ICMP_UNREACH_HOST_PRECEDENCE },
> + { "cutoff-preced", ICMP_UNREACH, ICMP_UNREACH_PRECEDENCE_CUTOFF
> },
> + { "redir-net", ICMP_REDIRECT, ICMP_REDIRECT_NET },
> + { "redir-host", ICMP_REDIRECT, ICMP_REDIRECT_HOST },
> + { "redir-tos-net", ICMP_REDIRECT, ICMP_REDIRECT_TOSNET },
> + { "redir-tos-host", ICMP_REDIRECT, ICMP_REDIRECT_TOSHOST },
> + { "normal-adv", ICMP_ROUTERADVERT, ICMP_ROUTERADVERT_NORMAL },
> + { "common-adv", ICMP_ROUTERADVERT,
> ICMP_ROUTERADVERT_NOROUTE_COMMON },
> + { "transit", ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS },
> + { "reassemb", ICMP_TIMXCEED, ICMP_TIMXCEED_REASS },
> + { "badhead", ICMP_PARAMPROB, ICMP_PARAMPROB_ERRATPTR },
> + { "optmiss", ICMP_PARAMPROB, ICMP_PARAMPROB_OPTABSENT },
> + { "badlen", ICMP_PARAMPROB, ICMP_PARAMPROB_LENGTH },
> + { "unknown-ind", ICMP_PHOTURIS, ICMP_PHOTURIS_UNKNOWN_INDEX },
> + { "auth-fail", ICMP_PHOTURIS, ICMP_PHOTURIS_AUTH_FAILED },
> + { "decrypt-fail", ICMP_PHOTURIS, ICMP_PHOTURIS_DECRYPT_FAILED }
> +};
> +
> +static const struct icmpcodeent icmp6_code[] = {
> + { "admin-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADMIN },
> + { "noroute-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE },
> + { "beyond-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_BEYONDSCOPE },
> + { "addr-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR },
> + { "port-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOPORT },
> + { "transit", ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT },
> + { "reassemb", ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_REASSEMBLY },
> + { "badhead", ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER },
> + { "nxthdr", ICMP6_PARAM_PROB, ICMP6_PARAMPROB_NEXTHEADER },
> + { "redironlink", ND_REDIRECT, ND_REDIRECT_ONLINK },
> + { "redirrouter", ND_REDIRECT, ND_REDIRECT_ROUTER }
> +};
> +
> +static int
> +geticmptypebyname(char *w, uint8_t aid)
> +{
> + unsigned int i;
> +
> + switch (aid) {
> + case AID_INET:
> + for (i=0; i < (sizeof (icmp_type) / sizeof(icmp_type[0]));
No space after sizeof, I'd drop the extra parens. Might also be worth it
to add a local version of nitems() to this file.
> + i++) {
> + if (!strcmp(w, icmp_type[i].name))
> + return (icmp_type[i].type);
> + }
> + break;
> + case AID_INET6:
> + for (i=0; i < (sizeof (icmp6_type) / sizeof(icmp6_type[0]));
ditto
> + i++) {
> + if (!strcmp(w, icmp6_type[i].name))
> + return (icmp6_type[i].type);
> + }
> + break;
> + }
> + return -1;
> +}
> +
> +static int
> +geticmpcodebyname(u_long type, char *w, uint8_t aid)
> +{
> + unsigned int i;
> +
> + switch (aid) {
> + case AID_INET:
> + for (i=0; i < (sizeof (icmp_code) / sizeof(icmp_code[0]));
> + i++) {
and here
> + if (type == icmp_code[i].type &&
> + !strcmp(w, icmp_code[i].name))
> + return (icmp_code[i].code);
> + }
> + break;
> + case AID_INET6:
> + for (i=0; i < (sizeof (icmp6_code) / sizeof(icmp6_code[0]));
> + i++) {
...
> + if (type == icmp6_code[i].type &&
> + !strcmp(w, icmp6_code[i].name))
> + return (icmp6_code[i].code);
> + }
> + break;
> + }
> + return -1;
> }
>