This is a large diff that changes the way communities are stored in filters and filter_sets. Both standard communities and large communities now share the same data structure for lookups and at the same time the filters are extended to allow more then one community to match per rule (currently the maximum is 3). I did leave extended communities outside for now since this diff is already big enough but they will follow in a second step.
So now filters like deny to any large-community 196618:0:666 large-community 196618:0:3 deny to any community 13030:1016 community 13030:50000 will work and match only if both communities are matched. As a side effect the bgpctl show rib code is changed and there is no longer a limitation that only one of the filter is allowed to be used. In other words 'bgpctl show rib as 13030 community 1303:1036' will work now. Apart from that there should be no other visible change. Please test and report back -- :wq Claudio PS: diff is against /usr/src since both bgpd and bgpctl need to be patched Index: usr.sbin/bgpctl/bgpctl.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpctl/bgpctl.c,v retrieving revision 1.223 diff -u -p -r1.223 bgpctl.c --- usr.sbin/bgpctl/bgpctl.c 1 Nov 2018 10:09:52 -0000 1.223 +++ usr.sbin/bgpctl/bgpctl.c 13 Nov 2018 13:43:04 -0000 @@ -173,14 +173,7 @@ main(int argc, char *argv[]) ribreq.prefix = res->addr; ribreq.prefixlen = res->prefixlen; } - if (res->community.as != COMMUNITY_UNSET && - res->community.type != COMMUNITY_UNSET) - ribreq.community = res->community; - if (res->large_community.as != COMMUNITY_UNSET && - res->large_community.ld1 != COMMUNITY_UNSET && - res->large_community.ld2 != COMMUNITY_UNSET) - ribreq.large_community = res->large_community; - /* XXX extended communities missing? */ + /* XXX currently no communities support */ ribreq.neighbor = neighbor; ribreq.aid = res->aid; ribreq.flags = res->flags; @@ -277,30 +270,17 @@ main(int argc, char *argv[]) case SHOW_RIB: bzero(&ribreq, sizeof(ribreq)); type = IMSG_CTL_SHOW_RIB; - if (res->as.type != AS_UNDEF) { - ribreq.as = res->as; - type = IMSG_CTL_SHOW_RIB_AS; - } if (res->addr.aid) { ribreq.prefix = res->addr; ribreq.prefixlen = res->prefixlen; type = IMSG_CTL_SHOW_RIB_PREFIX; } - if (res->community.as != COMMUNITY_UNSET && - res->community.type != COMMUNITY_UNSET) { + if (res->as.type != AS_UNDEF) + ribreq.as = res->as; + if (res->community.type != COMMUNITY_TYPE_NONE) ribreq.community = res->community; - type = IMSG_CTL_SHOW_RIB_COMMUNITY; - } - if (res->extcommunity.flags == EXT_COMMUNITY_FLAG_VALID) { + if (res->extcommunity.flags == EXT_COMMUNITY_FLAG_VALID) ribreq.extcommunity = res->extcommunity; - type = IMSG_CTL_SHOW_RIB_EXTCOMMUNITY; - } - if (res->large_community.as != COMMUNITY_UNSET && - res->large_community.ld1 != COMMUNITY_UNSET && - res->large_community.ld2 != COMMUNITY_UNSET) { - ribreq.large_community = res->large_community; - type = IMSG_CTL_SHOW_RIB_LARGECOMMUNITY; - } ribreq.neighbor = neighbor; strlcpy(ribreq.rib, res->rib, sizeof(ribreq.rib)); ribreq.aid = res->aid; @@ -399,14 +379,7 @@ main(int argc, char *argv[]) ribreq.prefix = res->addr; ribreq.prefixlen = res->prefixlen; } - if (res->community.as != COMMUNITY_UNSET && - res->community.type != COMMUNITY_UNSET) - ribreq.community = res->community; - if (res->large_community.as != COMMUNITY_UNSET && - res->large_community.ld1 != COMMUNITY_UNSET && - res->large_community.ld2 != COMMUNITY_UNSET) - ribreq.large_community = res->large_community; - /* XXX ext communities missing? */ + /* XXX currently no community support */ ribreq.neighbor = neighbor; ribreq.aid = res->aid; ribreq.flags = res->flags; Index: usr.sbin/bgpctl/parser.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpctl/parser.c,v retrieving revision 1.86 diff -u -p -r1.86 parser.c --- usr.sbin/bgpctl/parser.c 3 Oct 2018 11:36:39 -0000 1.86 +++ usr.sbin/bgpctl/parser.c 13 Nov 2018 09:13:35 -0000 @@ -478,12 +478,10 @@ int parse_addr(const char *, struct b int parse_asnum(const char *, size_t, u_int32_t *); int parse_number(const char *, struct parse_result *, enum token_type); -int getcommunity(const char *); int parse_community(const char *, struct parse_result *); int parsesubtype(const char *, u_int8_t *, u_int8_t *); int parseextvalue(const char *, u_int32_t *); u_int parseextcommunity(const char *, struct parse_result *); -u_int getlargecommunity(const char *); int parse_largecommunity(const char *, struct parse_result *); int parse_nexthop(const char *, struct parse_result *); @@ -496,11 +494,6 @@ parse(int argc, char *argv[]) const struct token *match; bzero(&res, sizeof(res)); - res.community.as = COMMUNITY_UNSET; - res.community.type = COMMUNITY_UNSET; - res.large_community.as = COMMUNITY_UNSET; - res.large_community.ld1 = COMMUNITY_UNSET; - res.large_community.ld2 = COMMUNITY_UNSET; res.rtableid = getrtable(); TAILQ_INIT(&res.set); if ((res.irr_outdir = getcwd(NULL, 0)) == NULL) { @@ -1019,19 +1012,26 @@ parse_number(const char *word, struct pa return (1); } -int -getcommunity(const char *s) +static u_int32_t +getcommunity(const char *s, int large, u_int8_t *flag) { + int64_t max = USHRT_MAX; const char *errstr; - u_int16_t uval; + u_int32_t uval; + + if (strcmp(s, "*") == 0) { + *flag = COMMUNITY_ANY; + return (0); + } - if (strcmp(s, "*") == 0) - return (COMMUNITY_ANY); + if (large) + max = UINT_MAX; - uval = strtonum(s, 0, USHRT_MAX, &errstr); + uval = strtonum(s, 0, max, &errstr); if (errstr) errx(1, "Community is %s: %s", errstr, s); + *flag = 0; return (uval); } @@ -1040,7 +1040,8 @@ parse_community(const char *word, struct { struct filter_set *fs; char *p; - int as, type; + u_int32_t as, type; + u_int8_t asflag, tflag; /* Well-known communities */ if (strcasecmp(word, "GRACEFUL_SHUTDOWN") == 0) { @@ -1075,33 +1076,20 @@ parse_community(const char *word, struct } *p++ = 0; - as = getcommunity(word); - type = getcommunity(p); + as = getcommunity(word, 0, &asflag); + type = getcommunity(p, 0, &tflag); done: - if (as == 0) { - fprintf(stderr, "Invalid community\n"); - return (0); - } - if (as == COMMUNITY_WELLKNOWN) - switch (type) { - case COMMUNITY_GRACEFUL_SHUTDOWN: - case COMMUNITY_NO_EXPORT: - case COMMUNITY_NO_ADVERTISE: - case COMMUNITY_NO_EXPSUBCONFED: - case COMMUNITY_BLACKHOLE: - /* valid */ - break; - } - if ((fs = calloc(1, sizeof(struct filter_set))) == NULL) err(1, NULL); fs->type = ACTION_SET_COMMUNITY; - fs->action.community.as = as; - fs->action.community.type = type; + fs->action.community.type = COMMUNITY_TYPE_BASIC; + fs->action.community.data1 = as; + fs->action.community.data2 = type; + fs->action.community.dflag1 = asflag; + fs->action.community.dflag2 = tflag; - r->community.as = as; - r->community.type = type; + r->community = fs->action.community; TAILQ_INSERT_TAIL(&r->set, fs, entry); return (1); @@ -1285,22 +1273,6 @@ parseextcommunity(const char *word, stru return (0); } -u_int -getlargecommunity(const char *s) -{ - const char *errstr; - u_int32_t uval; - - if (strcmp(s, "*") == 0) - return (COMMUNITY_ANY); - - uval = strtonum(s, 0, UINT_MAX, &errstr); - if (errstr) - errx(1, "Large Community is %s: %s", errstr, s); - - return (uval); -} - int parse_largecommunity(const char *word, struct parse_result *r) { @@ -1308,7 +1280,8 @@ parse_largecommunity(const char *word, s char *p, *po = strdup(word); char *array[3] = { NULL, NULL, NULL }; char *val; - int64_t as, ld1, ld2; + u_int32_t as, ld1, ld2; + u_int8_t asflag, ld1flag, ld2flag; int i = 0; p = po; @@ -1320,22 +1293,24 @@ parse_largecommunity(const char *word, s if ((p != NULL) || !(array[0] && array[1] && array[2])) errx(1, "Invalid Large-Community syntax"); - as = getlargecommunity(array[0]); - ld1 = getlargecommunity(array[1]); - ld2 = getlargecommunity(array[2]); + as = getcommunity(array[0], 1, &asflag); + ld1 = getcommunity(array[1], 1, &ld1flag); + ld2 = getcommunity(array[2], 1, &ld2flag); free(po); if ((fs = calloc(1, sizeof(struct filter_set))) == NULL) err(1, NULL); - fs->type = ACTION_SET_LARGE_COMMUNITY; - fs->action.large_community.as = as; - fs->action.large_community.ld1 = ld1; - fs->action.large_community.ld2 = ld2; - - r->large_community.as = as; - r->large_community.ld1 = ld1; - r->large_community.ld2 = ld2; + fs->type = ACTION_SET_COMMUNITY; + fs->action.community.type = COMMUNITY_TYPE_LARGE; + fs->action.community.data1 = as; + fs->action.community.data2 = ld1; + fs->action.community.data3 = ld2; + fs->action.community.dflag1 = asflag; + fs->action.community.dflag2 = ld1flag; + fs->action.community.dflag3 = ld2flag; + + r->community = fs->action.community; TAILQ_INSERT_TAIL(&r->set, fs, entry); return (1); Index: usr.sbin/bgpctl/parser.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpctl/parser.h,v retrieving revision 1.31 diff -u -p -r1.31 parser.h --- usr.sbin/bgpctl/parser.h 1 Oct 2018 23:09:53 -0000 1.31 +++ usr.sbin/bgpctl/parser.h 12 Nov 2018 14:49:31 -0000 @@ -64,7 +64,6 @@ struct parse_result { struct filter_set_head set; struct filter_community community; struct filter_extcommunity extcommunity; - struct filter_largecommunity large_community; char peerdesc[PEER_DESCR_LEN]; char rib[PEER_DESCR_LEN]; char shutcomm[SHUT_COMM_LEN]; Index: usr.sbin/bgpd/bgpd.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v retrieving revision 1.353 diff -u -p -r1.353 bgpd.h --- usr.sbin/bgpd/bgpd.h 10 Nov 2018 11:19:01 -0000 1.353 +++ usr.sbin/bgpd/bgpd.h 13 Nov 2018 09:18:58 -0000 @@ -50,6 +50,7 @@ #define READ_BUF_SIZE 65535 #define RT_BUF_SIZE 16384 #define MAX_RTSOCK_BUF (2 * 1024 * 1024) +#define MAX_COMM_MATCH 3 #define BGPD_OPT_VERBOSE 0x0001 #define BGPD_OPT_VERBOSE2 0x0002 @@ -427,12 +428,8 @@ enum imsg_type { IMSG_CTL_SHOW_NEXTHOP, IMSG_CTL_SHOW_INTERFACE, IMSG_CTL_SHOW_RIB, - IMSG_CTL_SHOW_RIB_AS, IMSG_CTL_SHOW_RIB_PREFIX, IMSG_CTL_SHOW_RIB_ATTR, - IMSG_CTL_SHOW_RIB_COMMUNITY, - IMSG_CTL_SHOW_RIB_EXTCOMMUNITY, - IMSG_CTL_SHOW_RIB_LARGECOMMUNITY, IMSG_CTL_SHOW_NETWORK, IMSG_CTL_SHOW_RIB_MEM, IMSG_CTL_SHOW_RIB_HASH, @@ -741,14 +738,13 @@ struct filter_ovs { }; struct filter_community { - int as; - int type; -}; - -struct filter_largecommunity { - int64_t as; - int64_t ld1; - int64_t ld2; + u_int8_t type; + u_int8_t dflag1; /* one of set, any, local-as, neighbor-as */ + u_int8_t dflag2; + u_int8_t dflag3; + u_int32_t data1; + u_int32_t data2; + u_int32_t data3; }; struct filter_extcommunity { @@ -779,7 +775,6 @@ struct ctl_show_rib_request { struct filter_as as; struct filter_community community; struct filter_extcommunity extcommunity; - struct filter_largecommunity large_community; u_int32_t peerid; u_int32_t flags; u_int8_t validation_state; @@ -829,11 +824,16 @@ struct filter_peers { }; /* special community type */ -#define COMMUNITY_ERROR -1 -#define COMMUNITY_ANY -2 -#define COMMUNITY_NEIGHBOR_AS -3 -#define COMMUNITY_LOCAL_AS -4 -#define COMMUNITY_UNSET -5 +#define COMMUNITY_TYPE_NONE 0 +#define COMMUNITY_TYPE_BASIC 1 +#define COMMUNITY_TYPE_EXT 2 +#define COMMUNITY_TYPE_LARGE 3 + +#define COMMUNITY_ANY 1 +#define COMMUNITY_NEIGHBOR_AS 2 +#define COMMUNITY_LOCAL_AS 3 + +/* wellknown community definitions */ #define COMMUNITY_WELLKNOWN 0xffff #define COMMUNITY_GRACEFUL_SHUTDOWN 0x0000 /* RFC 8326 */ #define COMMUNITY_BLACKHOLE 0x029A /* RFC 7999 */ @@ -928,8 +928,7 @@ struct filter_match { struct filter_nexthop nexthop; struct filter_as as; struct filter_aslen aslen; - struct filter_community community; - struct filter_largecommunity large_community; + struct filter_community community[MAX_COMM_MATCH]; struct filter_extcommunity ext_community; struct filter_prefixset prefixset; struct filter_originset originset; @@ -974,8 +973,6 @@ enum action_types { ACTION_SET_NEXTHOP_SELF, ACTION_SET_COMMUNITY, ACTION_DEL_COMMUNITY, - ACTION_DEL_LARGE_COMMUNITY, - ACTION_SET_LARGE_COMMUNITY, ACTION_SET_EXT_COMMUNITY, ACTION_DEL_EXT_COMMUNITY, ACTION_PFTABLE, @@ -996,7 +993,6 @@ struct filter_set { struct bgpd_addr nexthop; struct nexthop *nh; struct filter_community community; - struct filter_largecommunity large_community; struct filter_extcommunity ext_community; char pftable[PFTABLE_LEN]; char rtlabel[RTLABEL_LEN]; Index: usr.sbin/bgpd/control.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/control.c,v retrieving revision 1.90 diff -u -p -r1.90 control.c --- usr.sbin/bgpd/control.c 11 Aug 2017 16:02:53 -0000 1.90 +++ usr.sbin/bgpd/control.c 13 Nov 2018 09:22:56 -0000 @@ -248,12 +248,8 @@ control_dispatch_msg(struct pollfd *pfd, case IMSG_CTL_SHOW_NEXTHOP: case IMSG_CTL_SHOW_INTERFACE: case IMSG_CTL_SHOW_RIB: - case IMSG_CTL_SHOW_RIB_AS: case IMSG_CTL_SHOW_RIB_PREFIX: case IMSG_CTL_SHOW_RIB_MEM: - case IMSG_CTL_SHOW_RIB_COMMUNITY: - case IMSG_CTL_SHOW_RIB_EXTCOMMUNITY: - case IMSG_CTL_SHOW_RIB_LARGECOMMUNITY: case IMSG_CTL_SHOW_NETWORK: case IMSG_CTL_SHOW_TERSE: case IMSG_CTL_SHOW_TIMER: @@ -421,7 +417,6 @@ control_dispatch_msg(struct pollfd *pfd, IMSG_HEADER_SIZE); break; case IMSG_CTL_SHOW_RIB: - case IMSG_CTL_SHOW_RIB_AS: case IMSG_CTL_SHOW_RIB_PREFIX: if (imsg.hdr.len == IMSG_HEADER_SIZE + sizeof(struct ctl_show_rib_request)) { @@ -471,9 +466,6 @@ control_dispatch_msg(struct pollfd *pfd, "wrong length"); break; case IMSG_CTL_SHOW_RIB_MEM: - case IMSG_CTL_SHOW_RIB_COMMUNITY: - case IMSG_CTL_SHOW_RIB_EXTCOMMUNITY: - case IMSG_CTL_SHOW_RIB_LARGECOMMUNITY: case IMSG_CTL_SHOW_NETWORK: c->ibuf.pid = imsg.hdr.pid; imsg_ctl_rde(imsg.hdr.type, imsg.hdr.pid, Index: usr.sbin/bgpd/parse.y =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/parse.y,v retrieving revision 1.362 diff -u -p -r1.362 parse.y --- usr.sbin/bgpd/parse.y 1 Nov 2018 00:18:44 -0000 1.362 +++ usr.sbin/bgpd/parse.y 12 Nov 2018 13:20:59 -0000 @@ -153,9 +153,7 @@ int merge_filterset(struct filter_set_ void merge_filter_lists(struct filter_head *, struct filter_head *); struct filter_rule *get_rule(enum action_types); -int64_t getcommunity(char *, int); -int parsecommunity(struct filter_community *, char *); -int parselargecommunity(struct filter_largecommunity *, char *); +int parsecommunity(struct filter_community *, int, char *); int parsesubtype(char *, int *, int *); int parseextvalue(char *, u_int32_t *); int parseextcommunity(struct filter_extcommunity *, char *, @@ -232,7 +230,7 @@ typedef struct { %type <v.addr> address %type <v.prefix> prefix addrspec %type <v.prefixset_item> prefixset_item -%type <v.u8> action quick direction delete +%type <v.u8> action quick direction delete community %type <v.filter_rib> filter_rib_h filter_rib_l filter_rib %type <v.filter_peers> filter_peer filter_peer_l filter_peer_h %type <v.filter_match> filter_match filter_elm filter_match_h @@ -2085,13 +2083,9 @@ filter_as : as4number_any { filter_match_h : /* empty */ { bzero(&$$, sizeof($$)); - $$.m.community.as = COMMUNITY_UNSET; - $$.m.large_community.as = COMMUNITY_UNSET; } | { bzero(&fmopts, sizeof(fmopts)); - fmopts.m.community.as = COMMUNITY_UNSET; - fmopts.m.large_community.as = COMMUNITY_UNSET; } filter_match { memcpy(&$$, &fmopts, sizeof($$)); @@ -2146,25 +2140,20 @@ filter_elm : filter_prefix_h { fmopts.m.aslen.type = ASLEN_SEQ; fmopts.m.aslen.aslen = $2; } - | COMMUNITY STRING { - if (fmopts.m.community.as != COMMUNITY_UNSET) { - yyerror("\"community\" already specified"); - free($2); - YYERROR; + | community STRING { + int i; + for (i = 0; i < MAX_COMM_MATCH; i++) { + if (fmopts.m.community[i].type == + COMMUNITY_TYPE_NONE) + break; } - if (parsecommunity(&fmopts.m.community, $2) == -1) { + if (i >= MAX_COMM_MATCH) { + yyerror("too many \"community\" filters " + "specified"); free($2); YYERROR; } - free($2); - } - | LARGECOMMUNITY STRING { - if (fmopts.m.large_community.as != COMMUNITY_UNSET) { - yyerror("\"large-community\" already specified"); - free($2); - YYERROR; - } - if (parselargecommunity(&fmopts.m.large_community, $2) == -1) { + if (parsecommunity(&fmopts.m.community[i], $1, $2) == -1) { free($2); YYERROR; } @@ -2405,6 +2394,10 @@ filter_set_l : filter_set_l comma filter } ; +community : COMMUNITY { $$ = COMMUNITY_TYPE_BASIC; } + | LARGECOMMUNITY { $$ = COMMUNITY_TYPE_LARGE; } + ; + delete : /* empty */ { $$ = 0; } | DELETE { $$ = 1; } ; @@ -2637,7 +2630,7 @@ filter_set_opt : LOCALPREF NUMBER { } free($2); } - | COMMUNITY delete STRING { + | community delete STRING { if (($$ = calloc(1, sizeof(struct filter_set))) == NULL) fatal(NULL); if ($2) @@ -2645,30 +2638,8 @@ filter_set_opt : LOCALPREF NUMBER { else $$->type = ACTION_SET_COMMUNITY; - if (parsecommunity(&$$->action.community, $3) == -1) { - free($3); - free($$); - YYERROR; - } - free($3); - /* Don't allow setting of any match */ - if (!$2 && ($$->action.community.as == COMMUNITY_ANY || - $$->action.community.type == COMMUNITY_ANY)) { - yyerror("'*' is not allowed in set community"); - free($$); - YYERROR; - } - } - | LARGECOMMUNITY delete STRING { - if (($$ = calloc(1, sizeof(struct filter_set))) == NULL) - fatal(NULL); - if ($2) - $$->type = ACTION_DEL_LARGE_COMMUNITY; - else - $$->type = ACTION_SET_LARGE_COMMUNITY; - - if (parselargecommunity(&$$->action.large_community, - $3) == -1) { + if (parsecommunity(&$$->action.community, $1, $3) == + -1) { free($3); free($$); YYERROR; @@ -2676,9 +2647,9 @@ filter_set_opt : LOCALPREF NUMBER { free($3); /* Don't allow setting of any match */ if (!$2 && - ($$->action.large_community.as == COMMUNITY_ANY || - $$->action.large_community.ld1 == COMMUNITY_ANY || - $$->action.large_community.ld2 == COMMUNITY_ANY)) { + ($$->action.community.dflag1 == COMMUNITY_ANY || + $$->action.community.dflag2 == COMMUNITY_ANY || + $$->action.community.dflag3 == COMMUNITY_ANY)) { yyerror("'*' is not allowed in set community"); free($$); YYERROR; @@ -3470,60 +3441,105 @@ symget(const char *nam) return (NULL); } -int64_t -getcommunity(char *s, int large) +static int +getcommunity(char *s, int large, u_int32_t *val, u_int8_t *flag) { int64_t max = USHRT_MAX; - u_int val; const char *errstr; - if (strcmp(s, "*") == 0) - return (COMMUNITY_ANY); - if (strcmp(s, "neighbor-as") == 0) - return (COMMUNITY_NEIGHBOR_AS); - if (strcmp(s, "local-as") == 0) - return (COMMUNITY_LOCAL_AS); + *flag = 0; + *val = 0; + if (strcmp(s, "*") == 0) { + *flag = COMMUNITY_ANY; + return 0; + } else if (strcmp(s, "neighbor-as") == 0) { + *flag = COMMUNITY_NEIGHBOR_AS; + return 0; + } else if (strcmp(s, "local-as") == 0) { + *flag = COMMUNITY_LOCAL_AS; + return 0; + } if (large) max = UINT_MAX; - val = strtonum(s, 0, max, &errstr); + *val = strtonum(s, 0, max, &errstr); if (errstr) { yyerror("Community %s is %s (max: %llu)", s, errstr, max); - return (COMMUNITY_ERROR); + return -1; } - return (val); + return 0; } +static void +setcommunity(struct filter_community *c, u_int32_t as, u_int32_t data, + u_int8_t asflag, u_int8_t dataflag) +{ + memset(c, 0, sizeof(*c)); + c->type = COMMUNITY_TYPE_BASIC; + c->dflag1 = asflag; + c->dflag2 = dataflag; + c->data1 = as; + c->data2 = data; +} + +static int +parselargecommunity(struct filter_community *c, char *s) +{ + char *p, *q; + + if ((p = strchr(s, ':')) == NULL) { + yyerror("Bad community syntax"); + return (-1); + } + *p++ = 0; + + if ((q = strchr(p, ':')) == NULL) { + yyerror("Bad community syntax"); + return (-1); + } + *q++ = 0; + + if (getcommunity(s, 1, &c->data1, &c->dflag1) == -1 || + getcommunity(p, 1, &c->data2, &c->dflag2) == -1 || + getcommunity(q, 1, &c->data3, &c->dflag3) == -1) + return (-1); + c->type = COMMUNITY_TYPE_LARGE; + return (0); +} int -parsecommunity(struct filter_community *c, char *s) +parsecommunity(struct filter_community *c, int type, char *s) { char *p; - int i, as; + u_int32_t as, data; + u_int8_t asflag, dataflag; + + if (type == COMMUNITY_TYPE_LARGE) + return parselargecommunity(c, s); /* Well-known communities */ if (strcasecmp(s, "GRACEFUL_SHUTDOWN") == 0) { - c->as = COMMUNITY_WELLKNOWN; - c->type = COMMUNITY_GRACEFUL_SHUTDOWN; + setcommunity(c, COMMUNITY_WELLKNOWN, + COMMUNITY_GRACEFUL_SHUTDOWN, 0, 0); return (0); } else if (strcasecmp(s, "NO_EXPORT") == 0) { - c->as = COMMUNITY_WELLKNOWN; - c->type = COMMUNITY_NO_EXPORT; + setcommunity(c, COMMUNITY_WELLKNOWN, + COMMUNITY_NO_EXPORT, 0, 0); return (0); } else if (strcasecmp(s, "NO_ADVERTISE") == 0) { - c->as = COMMUNITY_WELLKNOWN; - c->type = COMMUNITY_NO_ADVERTISE; + setcommunity(c, COMMUNITY_WELLKNOWN, + COMMUNITY_NO_ADVERTISE, 0, 0); return (0); } else if (strcasecmp(s, "NO_EXPORT_SUBCONFED") == 0) { - c->as = COMMUNITY_WELLKNOWN; - c->type = COMMUNITY_NO_EXPSUBCONFED; + setcommunity(c, COMMUNITY_WELLKNOWN, + COMMUNITY_NO_EXPSUBCONFED, 0, 0); return (0); } else if (strcasecmp(s, "NO_PEER") == 0) { - c->as = COMMUNITY_WELLKNOWN; - c->type = COMMUNITY_NO_PEER; + setcommunity(c, COMMUNITY_WELLKNOWN, + COMMUNITY_NO_PEER, 0, 0); return (0); } else if (strcasecmp(s, "BLACKHOLE") == 0) { - c->as = COMMUNITY_WELLKNOWN; - c->type = COMMUNITY_BLACKHOLE; + setcommunity(c, COMMUNITY_WELLKNOWN, + COMMUNITY_BLACKHOLE, 0, 0); return (0); } @@ -3533,49 +3549,10 @@ parsecommunity(struct filter_community * } *p++ = 0; - if ((i = getcommunity(s, 0)) == COMMUNITY_ERROR) + if (getcommunity(s, 0, &as, &asflag) == -1 || + getcommunity(p, 0, &data, &dataflag) == -1) return (-1); - as = i; - - if ((i = getcommunity(p, 0)) == COMMUNITY_ERROR) - return (-1); - c->as = as; - c->type = i; - - return (0); -} - -int -parselargecommunity(struct filter_largecommunity *c, char *s) -{ - char *p, *q; - int64_t as, ld1, ld2; - - if ((p = strchr(s, ':')) == NULL) { - yyerror("Bad community syntax"); - return (-1); - } - *p++ = 0; - - if ((q = strchr(p, ':')) == NULL) { - yyerror("Bad community syntax"); - return (-1); - } - *q++ = 0; - - if ((as = getcommunity(s, 1)) == COMMUNITY_ERROR) - return (-1); - - if ((ld1 = getcommunity(p, 1)) == COMMUNITY_ERROR) - return (-1); - - if ((ld2 = getcommunity(q, 1)) == COMMUNITY_ERROR) - return (-1); - - c->as = as; - c->ld1 = ld1; - c->ld2 = ld2; - + setcommunity(c, as, data, asflag, dataflag); return (0); } @@ -4218,10 +4195,6 @@ merge_filterset(struct filter_set_head * yyerror("community is already set"); else if (s->type == ACTION_DEL_COMMUNITY) yyerror("community will already be deleted"); - else if (s->type == ACTION_SET_LARGE_COMMUNITY) - yyerror("large-community is already set"); - else if (s->type == ACTION_DEL_LARGE_COMMUNITY) - yyerror("large-community will already be deleted"); else if (s->type == ACTION_SET_EXT_COMMUNITY) yyerror("ext-community is already set"); else if (s->type == ACTION_DEL_EXT_COMMUNITY) @@ -4243,21 +4216,9 @@ merge_filterset(struct filter_set_head * switch (s->type) { case ACTION_SET_COMMUNITY: case ACTION_DEL_COMMUNITY: - if (s->action.community.as < - t->action.community.as || - (s->action.community.as == - t->action.community.as && - s->action.community.type < - t->action.community.type)) { - TAILQ_INSERT_BEFORE(t, s, entry); - return (0); - } - break; - case ACTION_SET_LARGE_COMMUNITY: - case ACTION_DEL_LARGE_COMMUNITY: - if (memcmp(&s->action.large_community, - &t->action.large_community, - sizeof(s->action.large_community)) < 0) { + if (memcmp(&s->action.community, + &t->action.community, + sizeof(s->action.community)) < 0) { TAILQ_INSERT_BEFORE(t, s, entry); return (0); } @@ -4321,8 +4282,6 @@ get_rule(enum action_types type) r->quick = 0; r->dir = out ? DIR_OUT : DIR_IN; r->action = ACTION_NONE; - r->match.community.as = COMMUNITY_UNSET; - r->match.large_community.as = COMMUNITY_UNSET; TAILQ_INIT(&r->set); if (curpeer == curgroup) { /* group */ Index: usr.sbin/bgpd/printconf.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/printconf.c,v retrieving revision 1.123 diff -u -p -r1.123 printconf.c --- usr.sbin/bgpd/printconf.c 29 Sep 2018 08:11:11 -0000 1.123 +++ usr.sbin/bgpd/printconf.c 12 Nov 2018 13:26:07 -0000 @@ -30,7 +30,8 @@ #include "log.h" void print_prefix(struct filter_prefix *p); -void print_community(int, int); +const char *community_type(struct filter_community *c); +void print_community(struct filter_community *c); void print_largecommunity(int64_t, int64_t, int64_t); void print_extcommunity(struct filter_extcommunity *); void print_origin(u_int8_t); @@ -104,61 +105,100 @@ print_prefix(struct filter_prefix *p) } } -void -print_community(int as, int type) +const char * +community_type(struct filter_community *c) { - if (as == COMMUNITY_ANY) - printf("*:"); - else if (as == COMMUNITY_NEIGHBOR_AS) - printf("neighbor-as:"); - else if (as == COMMUNITY_LOCAL_AS) - printf("local-as:"); - else - printf("%u:", (unsigned int)as); - - if (type == COMMUNITY_ANY) - printf("* "); - else if (type == COMMUNITY_NEIGHBOR_AS) - printf("neighbor-as "); - else if (type == COMMUNITY_LOCAL_AS) - printf("local-as"); - else - printf("%d ", type); + switch (c->type) { + case COMMUNITY_TYPE_BASIC: + return "community"; + case COMMUNITY_TYPE_LARGE: + return "large-community"; + default: + return "???"; + } } void -print_largecommunity(int64_t as, int64_t ld1, int64_t ld2) +print_community(struct filter_community *c) { - if (as == COMMUNITY_ANY) - printf("*:"); - else if (as == COMMUNITY_NEIGHBOR_AS) - printf("neighbor-as:"); - else if (as == COMMUNITY_LOCAL_AS) - printf("local-as:"); - else - printf("%lld:", as); - - if (ld1 == COMMUNITY_ANY) - printf("*:"); - else if (ld1 == COMMUNITY_NEIGHBOR_AS) - printf("neighbor-as:"); - else if (ld1 == COMMUNITY_LOCAL_AS) - printf("local-as:"); - else - printf("%lld:", ld1); - - if (ld2 == COMMUNITY_ANY) - printf("* "); - else if (ld2 == COMMUNITY_NEIGHBOR_AS) - printf("neighbor-as "); - else if (ld2 == COMMUNITY_LOCAL_AS) - printf("local-as "); - else - printf("%lld ", ld2); - + switch (c->type) { + case COMMUNITY_TYPE_BASIC: + switch (c->dflag1) { + case COMMUNITY_ANY: + printf("*:"); + break; + case COMMUNITY_NEIGHBOR_AS: + printf("neighbor-as:"); + break; + case COMMUNITY_LOCAL_AS: + printf("local-as:"); + break; + default: + printf("%u:", c->data1); + break; + } + switch (c->dflag2) { + case COMMUNITY_ANY: + printf("* "); + break; + case COMMUNITY_NEIGHBOR_AS: + printf("neighbor-as "); + break; + case COMMUNITY_LOCAL_AS: + printf("local-as "); + break; + default: + printf("%u ", c->data2); + break; + } + break; + case COMMUNITY_TYPE_LARGE: + switch (c->dflag1) { + case COMMUNITY_ANY: + printf("*:"); + break; + case COMMUNITY_NEIGHBOR_AS: + printf("neighbor-as:"); + break; + case COMMUNITY_LOCAL_AS: + printf("local-as:"); + break; + default: + printf("%u:", c->data1); + break; + } + switch (c->dflag2) { + case COMMUNITY_ANY: + printf("*:"); + break; + case COMMUNITY_NEIGHBOR_AS: + printf("neighbor-as:"); + break; + case COMMUNITY_LOCAL_AS: + printf("local-as:"); + break; + default: + printf("%u:", c->data2); + break; + } + switch (c->dflag3) { + case COMMUNITY_ANY: + printf("* "); + break; + case COMMUNITY_NEIGHBOR_AS: + printf("neighbor-as "); + break; + case COMMUNITY_LOCAL_AS: + printf("local-as "); + break; + default: + printf("%u ", c->data3); + break; + } + break; + } } - void print_extcommunity(struct filter_extcommunity *c) { @@ -263,30 +303,13 @@ print_set(struct filter_set_head *set) printf("prepend-neighbor %u ", s->action.prepend); break; case ACTION_DEL_COMMUNITY: - printf("community delete "); - print_community(s->action.community.as, - s->action.community.type); - printf(" "); + printf("%s delete ", + community_type(&s->action.community)); + print_community(&s->action.community); break; case ACTION_SET_COMMUNITY: - printf("community "); - print_community(s->action.community.as, - s->action.community.type); - printf(" "); - break; - case ACTION_DEL_LARGE_COMMUNITY: - printf("large-community delete "); - print_largecommunity(s->action.large_community.as, - s->action.large_community.ld1, - s->action.large_community.ld2); - printf(" "); - break; - case ACTION_SET_LARGE_COMMUNITY: - printf("large-community "); - print_largecommunity(s->action.large_community.as, - s->action.large_community.ld1, - s->action.large_community.ld2); - printf(" "); + printf("%s ", community_type(&s->action.community)); + print_community(&s->action.community); break; case ACTION_PFTABLE: printf("pftable %s ", s->action.pftable); @@ -715,7 +738,8 @@ void print_as(struct filter_rule *r) void print_rule(struct peer *peer_l, struct filter_rule *r) { - struct peer *p; + struct peer *p; + int i; if (r->action == ACTION_ALLOW) printf("allow "); @@ -817,20 +841,16 @@ print_rule(struct peer *peer_l, struct f "max-as-len" : "max-as-seq", r->match.aslen.aslen); } - if (r->match.community.as != COMMUNITY_UNSET) { - printf("community "); - print_community(r->match.community.as, - r->match.community.type); + for (i = 0; i < MAX_COMM_MATCH; i++) { + struct filter_community *c = &r->match.community[i]; + if (c->type != COMMUNITY_TYPE_NONE) { + printf("%s ", community_type(c)); + print_community(c); + } } if (r->match.ext_community.flags & EXT_COMMUNITY_FLAG_VALID) { printf("ext-community "); print_extcommunity(&r->match.ext_community); - } - if (r->match.large_community.as != COMMUNITY_UNSET) { - printf("large-community "); - print_largecommunity(r->match.large_community.as, - r->match.large_community.ld1, - r->match.large_community.ld2); } print_set(&r->set); Index: usr.sbin/bgpd/rde.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v retrieving revision 1.449 diff -u -p -r1.449 rde.c --- usr.sbin/bgpd/rde.c 10 Nov 2018 11:19:01 -0000 1.449 +++ usr.sbin/bgpd/rde.c 13 Nov 2018 09:38:08 -0000 @@ -583,10 +583,6 @@ badnetdel: break; case IMSG_CTL_SHOW_NETWORK: case IMSG_CTL_SHOW_RIB: - case IMSG_CTL_SHOW_RIB_AS: - case IMSG_CTL_SHOW_RIB_COMMUNITY: - case IMSG_CTL_SHOW_RIB_EXTCOMMUNITY: - case IMSG_CTL_SHOW_RIB_LARGECOMMUNITY: case IMSG_CTL_SHOW_RIB_PREFIX: if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(req)) { log_warnx("rde_dispatch: wrong imsg len"); @@ -2224,21 +2220,24 @@ rde_dump_filter(struct prefix *p, struct if ((req->flags & F_CTL_INVALID) && (asp->flags & F_ATTR_PARSE_ERR) == 0) return; - if (req->type == IMSG_CTL_SHOW_RIB_AS && - !aspath_match(asp->aspath->data, asp->aspath->len, - &req->as, 0)) + if (req->as.type != AS_UNDEF && !aspath_match(asp->aspath->data, + asp->aspath->len, &req->as, 0)) return; - if (req->type == IMSG_CTL_SHOW_RIB_COMMUNITY && - !community_match(asp, req->community.as, - req->community.type)) - return; - if (req->type == IMSG_CTL_SHOW_RIB_EXTCOMMUNITY && + switch (req->community.type) { + case COMMUNITY_TYPE_NONE: + break; + case COMMUNITY_TYPE_BASIC: + if (!community_match(asp, &req->community, NULL)) + return; + break; + case COMMUNITY_TYPE_LARGE: + if (!community_large_match(asp, &req->community, NULL)) + return; + break; + } + if (req->extcommunity.flags == EXT_COMMUNITY_FLAG_VALID && !community_ext_match(asp, &req->extcommunity, 0)) return; - if (req->type == IMSG_CTL_SHOW_RIB_LARGECOMMUNITY && - !community_large_match(asp, req->large_community.as, - req->large_community.ld1, req->large_community.ld2)) - return; if (!ovs_match(p, req->flags)) return; rde_dump_rib_as(p, asp, req->pid, req->flags); @@ -2334,10 +2333,6 @@ rde_dump_ctx_new(struct ctl_show_rib_req goto nomem; break; case IMSG_CTL_SHOW_RIB: - case IMSG_CTL_SHOW_RIB_AS: - case IMSG_CTL_SHOW_RIB_COMMUNITY: - case IMSG_CTL_SHOW_RIB_EXTCOMMUNITY: - case IMSG_CTL_SHOW_RIB_LARGECOMMUNITY: if (rib_dump_new(rid, ctx->req.aid, CTL_MSG_HIGH_MARK, ctx, rde_dump_upcall, rde_dump_done, rde_dump_throttled) == -1) goto nomem; Index: usr.sbin/bgpd/rde.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v retrieving revision 1.202 diff -u -p -r1.202 rde.h --- usr.sbin/bgpd/rde.h 4 Nov 2018 12:34:54 -0000 1.202 +++ usr.sbin/bgpd/rde.h 12 Nov 2018 13:39:09 -0000 @@ -361,25 +361,28 @@ int aspath_loopfree(struct aspath *, u int aspath_compare(struct aspath *, struct aspath *); u_char *aspath_prepend(struct aspath *, u_int32_t, int, u_int16_t *); int aspath_lenmatch(struct aspath *, enum aslen_spec, u_int); -int community_match(struct rde_aspath *, int, int); -int community_set(struct rde_aspath *, int, int); -void community_delete(struct rde_aspath *, int, int); -int community_large_match(struct rde_aspath *, int64_t, int64_t, - int64_t); -int community_large_set(struct rde_aspath *, int64_t, int64_t, - int64_t); -void community_large_delete(struct rde_aspath *, int64_t, - int64_t, int64_t); -int community_ext_match(struct rde_aspath *, - struct filter_extcommunity *, u_int16_t); -int community_ext_set(struct rde_aspath *, - struct filter_extcommunity *, u_int16_t); -void community_ext_delete(struct rde_aspath *, - struct filter_extcommunity *, u_int16_t); -int community_ext_conv(struct filter_extcommunity *, u_int16_t, - u_int64_t *); -u_char *community_ext_delete_non_trans(u_char *, u_int16_t, - u_int16_t *); + +int community_match(struct rde_aspath *, struct filter_community *, + struct rde_peer *); +int community_set(struct rde_aspath *, struct filter_community *, + struct rde_peer *); +void community_delete(struct rde_aspath *, struct filter_community *, + struct rde_peer *); +int community_large_match(struct rde_aspath *, struct filter_community *, + struct rde_peer *); +int community_large_set(struct rde_aspath *, struct filter_community *, + struct rde_peer *); +void community_large_delete(struct rde_aspath *, struct filter_community *, + struct rde_peer *); +int community_ext_match(struct rde_aspath *, + struct filter_extcommunity *, u_int16_t); +int community_ext_set(struct rde_aspath *, + struct filter_extcommunity *, u_int16_t); +void community_ext_delete(struct rde_aspath *, + struct filter_extcommunity *, u_int16_t); +int community_ext_conv(struct filter_extcommunity *, u_int16_t, + u_int64_t *); +u_char *community_ext_delete_non_trans(u_char *, u_int16_t, u_int16_t *); /* rde_decide.c */ void prefix_evaluate(struct prefix *, struct rib_entry *); Index: usr.sbin/bgpd/rde_attr.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde_attr.c,v retrieving revision 1.112 diff -u -p -r1.112 rde_attr.c --- usr.sbin/bgpd/rde_attr.c 10 Oct 2018 06:21:47 -0000 1.112 +++ usr.sbin/bgpd/rde_attr.c 13 Nov 2018 13:51:59 -0000 @@ -995,18 +995,65 @@ aspath_lenmatch(struct aspath *a, enum a int community_ext_matchone(struct filter_extcommunity *, u_int16_t, u_int64_t); +static int +community_extract(struct filter_community *fc, struct rde_peer *peer, + int field, int large, u_int32_t *value) +{ + u_int32_t data; + u_int8_t flag; + switch (field) { + case 1: + flag = fc->dflag1; + data = fc->data1; + break; + case 2: + flag = fc->dflag2; + data = fc->data2; + break; + case 3: + flag = fc->dflag3; + data = fc->data3; + break; + default: + fatalx("%s: unknown field %d", __func__, field); + } + + switch (flag) { + case COMMUNITY_NEIGHBOR_AS: + if (peer == NULL) + return -1; + *value = peer->conf.remote_as; + case COMMUNITY_LOCAL_AS: + if (peer == NULL) + return -1; + *value = peer->conf.local_as; + default: + *value = data; + } + if (!large && *value > USHRT_MAX) + return -1; + return 0; +} + int -community_match(struct rde_aspath *asp, int as, int type) +community_match(struct rde_aspath *asp, struct filter_community *fc, + struct rde_peer *peer) { struct attr *a; u_int8_t *p; - u_int16_t eas, etype, len; + u_int32_t as, type, eas, etype; + u_int16_t len; a = attr_optget(asp, ATTR_COMMUNITIES); if (a == NULL) /* no communities, no match */ return (0); + if (community_extract(fc, peer, 1, 0, &as) == -1 || + community_extract(fc, peer, 2, 0, &type) == -1) + /* can't match community */ + return (0); + p = a->data; for (len = a->len / 4; len > 0; len--) { eas = *p++; @@ -1015,21 +1062,29 @@ community_match(struct rde_aspath *asp, etype = *p++; etype <<= 8; etype |= *p++; - if ((as == COMMUNITY_ANY || (u_int16_t)as == eas) && - (type == COMMUNITY_ANY || (u_int16_t)type == etype)) + if ((fc->dflag1 == COMMUNITY_ANY || as == eas) && + (fc->dflag2 == COMMUNITY_ANY || type == etype)) return (1); } return (0); } int -community_set(struct rde_aspath *asp, int as, int type) +community_set(struct rde_aspath *asp, struct filter_community *fc, + struct rde_peer *peer) { struct attr *attr; u_int8_t *p = NULL; unsigned int i, ncommunities = 0; + u_int32_t as, type; u_int8_t f = ATTR_OPTIONAL|ATTR_TRANSITIVE; + if (fc->dflag1 == COMMUNITY_ANY || fc->dflag2 == COMMUNITY_ANY || + community_extract(fc, peer, 1, 0, &as) == -1 || + community_extract(fc, peer, 2, 0, &type) == -1) + /* bad community */ + return (0); + attr = attr_optget(asp, ATTR_COMMUNITIES); if (attr != NULL) { p = attr->data; @@ -1070,12 +1125,13 @@ community_set(struct rde_aspath *asp, in } void -community_delete(struct rde_aspath *asp, int as, int type) +community_delete(struct rde_aspath *asp, struct filter_community *fc, + struct rde_peer *peer) { struct attr *attr; u_int8_t *p, *n; u_int16_t l, len = 0; - u_int16_t eas, etype; + u_int32_t as, type, eas, etype; u_int8_t f; attr = attr_optget(asp, ATTR_COMMUNITIES); @@ -1083,6 +1139,11 @@ community_delete(struct rde_aspath *asp, /* no attr nothing to do */ return; + if (community_extract(fc, peer, 1, 0, &as) == -1 || + community_extract(fc, peer, 2, 0, &type) == -1) + /* bad community, nothing to do */ + return; + p = attr->data; for (l = 0; l < attr->len; l += 4) { eas = *p++; @@ -1092,8 +1153,8 @@ community_delete(struct rde_aspath *asp, etype <<= 8; etype |= *p++; - if ((as == COMMUNITY_ANY || (u_int16_t)as == eas) && - (type == COMMUNITY_ANY || (u_int16_t)type == etype)) + if ((fc->dflag1 == COMMUNITY_ANY || as == eas) && + (fc->dflag2 == COMMUNITY_ANY || type == etype)) /* match */ continue; len += 4; @@ -1116,8 +1177,8 @@ community_delete(struct rde_aspath *asp, etype <<= 8; etype |= *p++; - if ((as == COMMUNITY_ANY || (u_int16_t)as == eas) && - (type == COMMUNITY_ANY || (u_int16_t)type == etype)) + if ((fc->dflag1 == COMMUNITY_ANY || as == eas) && + (fc->dflag2 == COMMUNITY_ANY || type == etype)) /* match */ continue; n[l++] = eas >> 8; @@ -1382,46 +1443,66 @@ struct wire_largecommunity { }; int -community_large_match(struct rde_aspath *asp, int64_t as, int64_t ld1, - int64_t ld2) +community_large_match(struct rde_aspath *asp, struct filter_community *fc, + struct rde_peer *peer) { - struct wire_largecommunity *bar; + struct wire_largecommunity *wlc; struct attr *a; u_int8_t *p; u_int16_t len; - u_int32_t eas, eld1, eld2; + u_int32_t as, ld1, ld2; a = attr_optget(asp, ATTR_LARGE_COMMUNITIES); if (a == NULL) /* no communities, no match */ return (0); + if (community_extract(fc, peer, 1, 1, &as) == -1 || + community_extract(fc, peer, 2, 1, &ld1) == -1 || + community_extract(fc, peer, 3, 1, &ld2) == -1) + /* can't match community */ + return (0); + + as = htonl(as); + ld1 = htonl(ld1); + ld2 = htonl(ld2); + p = a->data; for (len = a->len / 12; len > 0; len--) { - bar = (struct wire_largecommunity *)p; + wlc = (struct wire_largecommunity *)p; p += 12; - eas = betoh32(bar->as); - eld1 = betoh32(bar->ld1); - eld2 = betoh32(bar->ld2); - - if ((as == COMMUNITY_ANY || as == eas) && - (ld1 == COMMUNITY_ANY || ld1 == eld1) && - (ld2 == COMMUNITY_ANY || ld2 == eld2)) + + if ((fc->dflag1 == COMMUNITY_ANY || as == wlc->as) && + (fc->dflag2 == COMMUNITY_ANY || ld1 == wlc->ld1) && + (fc->dflag3 == COMMUNITY_ANY || ld2 == wlc->ld2)) return (1); } return (0); } int -community_large_set(struct rde_aspath *asp, int64_t as, int64_t ld1, - int64_t ld2) +community_large_set(struct rde_aspath *asp, struct filter_community *fc, + struct rde_peer *peer) { - struct wire_largecommunity *bar; + struct wire_largecommunity *wlc; struct attr *attr; u_int8_t *p = NULL; unsigned int i, ncommunities = 0; + u_int32_t as, ld1, ld2; u_int8_t f = ATTR_OPTIONAL|ATTR_TRANSITIVE; + if (fc->dflag1 == COMMUNITY_ANY || fc->dflag2 == COMMUNITY_ANY || + fc->dflag3 == COMMUNITY_ANY || + community_extract(fc, peer, 1, 1, &as) == -1 || + community_extract(fc, peer, 2, 1, &ld1) == -1 || + community_extract(fc, peer, 3, 1, &ld2) == -1) + /* can't match community */ + return (0); + + as = htonl(as); + ld1 = htonl(ld1); + ld2 = htonl(ld2); + attr = attr_optget(asp, ATTR_LARGE_COMMUNITIES); if (attr != NULL) { p = attr->data; @@ -1430,9 +1511,8 @@ community_large_set(struct rde_aspath *a /* first check if the community is not already set */ for (i = 0; i < ncommunities; i++) { - bar = (struct wire_largecommunity *)p; - if (bar->as == htobe32(as) && bar->ld1 == htobe32(ld1) && - bar->ld2 == htobe32(ld2)) + wlc = (struct wire_largecommunity *)p; + if (wlc->as == as && wlc->ld1 == ld1 && wlc->ld2 == ld2) /* already present, nothing todo */ return (1); p += 12; @@ -1445,10 +1525,10 @@ community_large_set(struct rde_aspath *a if ((p = reallocarray(NULL, ncommunities, 12)) == NULL) fatal("community_set"); - bar = (struct wire_largecommunity *)p; - bar->as = htobe32(as); - bar->ld1 = htobe32(ld1); - bar->ld2 = htobe32(ld2); + wlc = (struct wire_largecommunity *)p; + wlc->as = as; + wlc->ld1 = ld1; + wlc->ld2 = ld2; if (attr != NULL) { memcpy(p + 12, attr->data, attr->len); @@ -1463,14 +1543,14 @@ community_large_set(struct rde_aspath *a } void -community_large_delete(struct rde_aspath *asp, int64_t as, int64_t ld1, - int64_t ld2) +community_large_delete(struct rde_aspath *asp, struct filter_community *fc, + struct rde_peer *peer) { - struct wire_largecommunity *bar; + struct wire_largecommunity *wlc; struct attr *attr; u_int8_t *p, *n; u_int16_t l = 0, len = 0; - u_int32_t eas, eld1, eld2; + u_int32_t as, ld1, ld2; u_int8_t f; attr = attr_optget(asp, ATTR_LARGE_COMMUNITIES); @@ -1478,17 +1558,24 @@ community_large_delete(struct rde_aspath /* no attr nothing to do */ return; + if (community_extract(fc, peer, 1, 1, &as) == -1 || + community_extract(fc, peer, 2, 1, &ld1) == -1 || + community_extract(fc, peer, 3, 1, &ld2) == -1) + /* can't match community */ + return; + + as = htonl(as); + ld1 = htonl(ld1); + ld2 = htonl(ld2); + p = attr->data; for (len = 0; l < attr->len; l += 12) { - bar = (struct wire_largecommunity *)p; + wlc = (struct wire_largecommunity *)p; p += 12; - eas = betoh32(bar->as); - eld1 = betoh32(bar->ld1); - eld2 = betoh32(bar->ld2); - - if ((as == COMMUNITY_ANY || as == eas) && - (ld1 == COMMUNITY_ANY || ld1 == eld1) && - (ld2 == COMMUNITY_ANY || ld2 == eld2)) + + if ((fc->dflag1 == COMMUNITY_ANY || as == wlc->as) && + (fc->dflag2 == COMMUNITY_ANY || ld1 == wlc->ld1) && + (fc->dflag3 == COMMUNITY_ANY || ld2 == wlc->ld2)) /* match */ continue; len += 12; @@ -1504,18 +1591,15 @@ community_large_delete(struct rde_aspath p = attr->data; for (l = 0; l < len && p < attr->data + attr->len; ) { - bar = (struct wire_largecommunity *)p; + wlc = (struct wire_largecommunity *)p; p += 12; - eas = betoh32(bar->as); - eld1 = betoh32(bar->ld1); - eld2 = betoh32(bar->ld2); - - if ((as == COMMUNITY_ANY || as == eas) && - (ld1 == COMMUNITY_ANY || ld1 == eld1) && - (ld2 == COMMUNITY_ANY || ld2 == eld2)) + + if ((fc->dflag1 == COMMUNITY_ANY || as == wlc->as) && + (fc->dflag2 == COMMUNITY_ANY || ld1 == wlc->ld1) && + (fc->dflag3 == COMMUNITY_ANY || ld2 == wlc->ld2)) /* match */ continue; - memcpy(n + l, bar, sizeof(*bar)); + memcpy(n + l, wlc, sizeof(*wlc)); l += 12; } @@ -1525,7 +1609,6 @@ community_large_delete(struct rde_aspath attr_optadd(asp, f, ATTR_LARGE_COMMUNITIES, n, len); free(n); } - u_char * community_ext_delete_non_trans(u_char *data, u_int16_t len, u_int16_t *newlen) Index: usr.sbin/bgpd/rde_filter.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde_filter.c,v retrieving revision 1.112 diff -u -p -r1.112 rde_filter.c --- usr.sbin/bgpd/rde_filter.c 29 Sep 2018 08:11:11 -0000 1.112 +++ usr.sbin/bgpd/rde_filter.c 12 Nov 2018 13:38:30 -0000 @@ -40,8 +40,6 @@ rde_apply_set(struct filter_set_head *sh { struct filter_set *set; u_char *np; - int as, type; - int64_t las, ld1, ld2; u_int32_t prep_as; u_int16_t nl; u_int8_t prepend; @@ -142,172 +140,28 @@ rde_apply_set(struct filter_set_head *sh &state->nexthop, &state->nhflags); break; case ACTION_SET_COMMUNITY: - switch (set->action.community.as) { - case COMMUNITY_ERROR: - case COMMUNITY_ANY: - fatalx("%s: bad community string", __func__); - case COMMUNITY_NEIGHBOR_AS: - as = peer->conf.remote_as; - break; - case COMMUNITY_LOCAL_AS: - as = peer->conf.local_as; - break; - default: - as = set->action.community.as; - break; - } - switch (set->action.community.type) { - case COMMUNITY_ERROR: - case COMMUNITY_ANY: - fatalx("%s: bad community string", __func__); - case COMMUNITY_NEIGHBOR_AS: - type = peer->conf.remote_as; - break; - case COMMUNITY_LOCAL_AS: - type = peer->conf.local_as; - break; - default: - type = set->action.community.type; + case COMMUNITY_TYPE_BASIC: + community_set(&state->aspath, + &set->action.community, peer); + break; + case COMMUNITY_TYPE_LARGE: + community_large_set(&state->aspath, + &set->action.community, peer); break; } - - community_set(&state->aspath, as, type); break; case ACTION_DEL_COMMUNITY: - switch (set->action.community.as) { - case COMMUNITY_ERROR: - fatalx("%s: bad community string", __func__); - case COMMUNITY_NEIGHBOR_AS: - as = peer->conf.remote_as; - break; - case COMMUNITY_LOCAL_AS: - as = peer->conf.local_as; - break; - case COMMUNITY_ANY: - default: - as = set->action.community.as; - break; - } - switch (set->action.community.type) { - case COMMUNITY_ERROR: - fatalx("%s: bad community string", __func__); - case COMMUNITY_NEIGHBOR_AS: - type = peer->conf.remote_as; - break; - case COMMUNITY_LOCAL_AS: - type = peer->conf.local_as; - break; - case COMMUNITY_ANY: - default: - type = set->action.community.type; + case COMMUNITY_TYPE_BASIC: + community_delete(&state->aspath, + &set->action.community, peer); + break; + case COMMUNITY_TYPE_LARGE: + community_large_delete(&state->aspath, + &set->action.community, peer); break; } - - community_delete(&state->aspath, as, type); - break; - case ACTION_SET_LARGE_COMMUNITY: - switch (set->action.large_community.as) { - case COMMUNITY_ERROR: - fatalx("%s: bad large community string", - __func__); - case COMMUNITY_NEIGHBOR_AS: - las = peer->conf.remote_as; - break; - case COMMUNITY_LOCAL_AS: - las = peer->conf.local_as; - break; - case COMMUNITY_ANY: - default: - las = set->action.large_community.as; - break; - } - - switch (set->action.large_community.ld1) { - case COMMUNITY_ERROR: - fatalx("%s: bad large community string", - __func__); - case COMMUNITY_NEIGHBOR_AS: - ld1 = peer->conf.remote_as; - break; - case COMMUNITY_LOCAL_AS: - ld1 = peer->conf.local_as; - break; - case COMMUNITY_ANY: - default: - ld1 = set->action.large_community.ld1; - break; - } - - switch (set->action.large_community.ld2) { - case COMMUNITY_ERROR: - fatalx("%s: bad large community string", - __func__); - case COMMUNITY_NEIGHBOR_AS: - ld2 = peer->conf.remote_as; - break; - case COMMUNITY_LOCAL_AS: - ld2 = peer->conf.local_as; - break; - case COMMUNITY_ANY: - default: - ld2 = set->action.large_community.ld2; - break; - } - - community_large_set(&state->aspath, las, ld1, ld2); - break; - case ACTION_DEL_LARGE_COMMUNITY: - switch (set->action.large_community.as) { - case COMMUNITY_ERROR: - fatalx("%s: bad large community string", - __func__); - case COMMUNITY_NEIGHBOR_AS: - las = peer->conf.remote_as; - break; - case COMMUNITY_LOCAL_AS: - las = peer->conf.local_as; - break; - case COMMUNITY_ANY: - default: - las = set->action.large_community.as; - break; - } - - switch (set->action.large_community.ld1) { - case COMMUNITY_ERROR: - fatalx("%s: bad large community string", - __func__); - case COMMUNITY_NEIGHBOR_AS: - ld1 = peer->conf.remote_as; - break; - case COMMUNITY_LOCAL_AS: - ld1 = peer->conf.local_as; - break; - case COMMUNITY_ANY: - default: - ld1 = set->action.large_community.ld1; - break; - } - - switch (set->action.large_community.ld2) { - case COMMUNITY_ERROR: - fatalx("%s: bad large community string", - __func__); - case COMMUNITY_NEIGHBOR_AS: - ld2 = peer->conf.remote_as; - break; - case COMMUNITY_LOCAL_AS: - ld2 = peer->conf.local_as; - break; - case COMMUNITY_ANY: - default: - ld2 = set->action.large_community.ld2; - break; - } - - community_large_delete(&state->aspath, las, ld1, ld2); break; case ACTION_PFTABLE: /* convert pftable name to an id */ @@ -348,9 +202,8 @@ int rde_filter_match(struct filter_rule *f, struct rde_peer *peer, struct filterstate *state, struct prefix *p) { - int cas, type; - int64_t las, ld1, ld2; - struct rde_aspath *asp = NULL; + struct rde_aspath *asp = NULL; + int i; if (state != NULL) asp = &state->aspath; @@ -376,89 +229,28 @@ rde_filter_match(struct filter_rule *f, f->match.aslen.aslen) == 0) return (0); - if (asp != NULL && f->match.community.as != COMMUNITY_UNSET) { - switch (f->match.community.as) { - case COMMUNITY_ERROR: - fatalx("rde_filter_match bad community string"); - case COMMUNITY_NEIGHBOR_AS: - cas = peer->conf.remote_as; - break; - case COMMUNITY_LOCAL_AS: - cas = peer->conf.local_as; - break; - default: - cas = f->match.community.as; - break; - } - - switch (f->match.community.type) { - case COMMUNITY_ERROR: - fatalx("rde_filter_match bad community string"); - case COMMUNITY_NEIGHBOR_AS: - type = peer->conf.remote_as; - break; - case COMMUNITY_LOCAL_AS: - type = peer->conf.local_as; + for (i = 0; asp != NULL && i < MAX_COMM_MATCH; i++) { + switch (f->match.community[i].type) { + case COMMUNITY_TYPE_NONE: + i = MAX_COMM_MATCH; + break; + case COMMUNITY_TYPE_BASIC: + if (community_match(asp, &f->match.community[i], + peer) == 0) + return (0); break; - default: - type = f->match.community.type; + case COMMUNITY_TYPE_LARGE: + if (community_large_match(asp, &f->match.community[i], + peer) == 0) + return (0); break; } - - if (community_match(asp, cas, type) == 0) - return (0); } if (asp != NULL && (f->match.ext_community.flags & EXT_COMMUNITY_FLAG_VALID)) if (community_ext_match(asp, &f->match.ext_community, peer->conf.remote_as) == 0) return (0); - if (asp != NULL && f->match.large_community.as != COMMUNITY_UNSET) { - switch (f->match.large_community.as) { - case COMMUNITY_ERROR: - fatalx("rde_filter_match bad community string"); - case COMMUNITY_NEIGHBOR_AS: - las = peer->conf.remote_as; - break; - case COMMUNITY_LOCAL_AS: - las = peer->conf.local_as; - break; - default: - las = f->match.large_community.as; - break; - } - - switch (f->match.large_community.ld1) { - case COMMUNITY_ERROR: - fatalx("rde_filter_match bad community string"); - case COMMUNITY_NEIGHBOR_AS: - ld1 = peer->conf.remote_as; - break; - case COMMUNITY_LOCAL_AS: - ld1 = peer->conf.local_as; - break; - default: - ld1 = f->match.large_community.ld1; - break; - } - - switch (f->match.large_community.ld2) { - case COMMUNITY_ERROR: - fatalx("rde_filter_match bad community string"); - case COMMUNITY_NEIGHBOR_AS: - ld2 = peer->conf.remote_as; - break; - case COMMUNITY_LOCAL_AS: - ld2 = peer->conf.local_as; - break; - default: - ld2 = f->match.large_community.ld2; - break; - } - - if (community_large_match(asp, las, ld1, ld2) == 0) - return (0); - } if (state != NULL && f->match.nexthop.flags != 0) { struct bgpd_addr *nexthop, *cmpaddr; @@ -744,11 +536,8 @@ filterset_cmp(struct filter_set *a, stru if (a->type == ACTION_SET_COMMUNITY || a->type == ACTION_DEL_COMMUNITY) { /* a->type == b->type */ - /* compare community */ - if (a->action.community.as - b->action.community.as != 0) - return (a->action.community.as - - b->action.community.as); - return (a->action.community.type - b->action.community.type); + return (memcmp(&a->action.community, &b->action.community, + sizeof(a->action.community))); } if (a->type == ACTION_SET_EXT_COMMUNITY || @@ -757,21 +546,6 @@ filterset_cmp(struct filter_set *a, stru &b->action.ext_community, sizeof(a->action.ext_community))); } - if (a->type == ACTION_SET_LARGE_COMMUNITY || - a->type == ACTION_DEL_LARGE_COMMUNITY) { /* a->type == b->type */ - /* compare community */ - if (a->action.large_community.as - - b->action.large_community.as != 0) - return (a->action.large_community.as - - b->action.large_community.as); - if (a->action.large_community.ld1 - - b->action.large_community.ld1 != 0) - return (a->action.large_community.ld1 - - b->action.large_community.ld1); - return (a->action.large_community.ld2 - - b->action.large_community.ld2); - } - if (a->type == ACTION_SET_NEXTHOP && b->type == ACTION_SET_NEXTHOP) { /* * This is the only interesting case, all others are considered @@ -845,14 +619,6 @@ filterset_equal(struct filter_set_head * sizeof(a->action.community)) == 0) continue; break; - case ACTION_DEL_LARGE_COMMUNITY: - case ACTION_SET_LARGE_COMMUNITY: - if (a->type == b->type && - memcmp(&a->action.large_community, - &b->action.large_community, - sizeof(a->action.large_community)) == 0) - continue; - break; case ACTION_PFTABLE: case ACTION_PFTABLE_ID: if (b->type == ACTION_PFTABLE) @@ -936,10 +702,6 @@ filterset_name(enum action_types type) return ("community"); case ACTION_DEL_COMMUNITY: return ("community delete"); - case ACTION_SET_LARGE_COMMUNITY: - return ("large-community"); - case ACTION_DEL_LARGE_COMMUNITY: - return ("large-community delete"); case ACTION_PFTABLE: case ACTION_PFTABLE_ID: return ("pftable"); Index: usr.sbin/bgpd/rde_update.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde_update.c,v retrieving revision 1.103 diff -u -p -r1.103 rde_update.c --- usr.sbin/bgpd/rde_update.c 4 Nov 2018 12:34:54 -0000 1.103 +++ usr.sbin/bgpd/rde_update.c 7 Nov 2018 17:45:29 -0000 @@ -66,6 +66,22 @@ RB_GENERATE(uptree_attr, update_attr, en SIPHASH_KEY uptree_key; +static struct filter_community comm_no_advertise = { + .type = COMMUNITY_TYPE_BASIC, + .data1 = COMMUNITY_WELLKNOWN, + .data2 = COMMUNITY_NO_ADVERTISE +}; +static struct filter_community comm_no_export = { + .type = COMMUNITY_TYPE_BASIC, + .data1 = COMMUNITY_WELLKNOWN, + .data2 = COMMUNITY_NO_EXPORT +}; +static struct filter_community comm_no_expsubconfed = { + .type = COMMUNITY_TYPE_BASIC, + .data1 = COMMUNITY_WELLKNOWN, + .data2 = COMMUNITY_NO_EXPSUBCONFED +}; + void up_init(struct rde_peer *peer) { @@ -329,14 +345,14 @@ up_test_update(struct rde_peer *peer, st } /* well known communities */ - if (community_match(asp, COMMUNITY_WELLKNOWN, COMMUNITY_NO_ADVERTISE)) - return (0); - if (peer->conf.ebgp && community_match(asp, COMMUNITY_WELLKNOWN, - COMMUNITY_NO_EXPORT)) - return (0); - if (peer->conf.ebgp && community_match(asp, COMMUNITY_WELLKNOWN, - COMMUNITY_NO_EXPSUBCONFED)) + if (community_match(asp, &comm_no_advertise, NULL)) return (0); + if (peer->conf.ebgp) { + if (community_match(asp, &comm_no_export, NULL)) + return (0); + if (community_match(asp, &comm_no_expsubconfed, NULL)) + return (0); + } /* * Don't send messages back to originator