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; > } >