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

Reply via email to