On 2016 Oct 11 (Tue) at 00:00:53 +0200 (+0200), Peter Hessler wrote: :Here is an initial implementation of draft-ietf-idr-large-community for :OpenBGPD. I can connect and exchange routes with these attributes :against exabgp. : :Normal communities are two 16bit numbers. With the addition of :32bit ASNs, those will not work if you wish to control one of :them. : :Large Communities are 32bit:32bit:32bit. It seems the convention will be :<control ASN>:<verb>:<noun>, with <verb> and <noun> being locally :defined. : :RFC status: currently accepted by the IDR-WG, is at version -02, the :wire format is set, the attribute codepoint is assigned by IANA, and it :seems that only trivial details need to be addressed. Very likely to be :accepted. : :This was based on a partial implementation from Job Snijders, many :thanks! : :Comments? OK? :
Updated diff: - assert copyright for the non-trivial changes - since the magic ASN matching canaries use valid bits, seperate the filter storage and a wire storage - clean up warnings - fix a few printing issues OK? Index: usr.sbin/bgpctl/bgpctl.8 =================================================================== RCS file: /cvs/openbsd/src/usr.sbin/bgpctl/bgpctl.8,v retrieving revision 1.69 diff -u -p -u -p -r1.69 bgpctl.8 --- usr.sbin/bgpctl/bgpctl.8 25 May 2016 14:15:59 -0000 1.69 +++ usr.sbin/bgpctl/bgpctl.8 5 Sep 2016 13:41:29 -0000 @@ -300,6 +300,9 @@ anywhere in the AS path. .It Cm community Ar community Show all entries with community .Ar community . +.It Cm large-community Ar large-community +Show all entries with large-community +.Ar large-community . .It Cm empty-as Show all entries that are internal routes with no AS's in the AS path. .It Cm memory Index: usr.sbin/bgpctl/bgpctl.c =================================================================== RCS file: /cvs/openbsd/src/usr.sbin/bgpctl/bgpctl.c,v retrieving revision 1.188 diff -u -p -u -p -r1.188 bgpctl.c --- usr.sbin/bgpctl/bgpctl.c 3 Jun 2016 17:36:37 -0000 1.188 +++ usr.sbin/bgpctl/bgpctl.c 13 Oct 2016 15:32:30 -0000 @@ -2,6 +2,8 @@ /* * Copyright (c) 2003 Henning Brauer <henn...@openbsd.org> + * Copyright (c) 2016 Job Snijders <j...@instituut.net> + * Copyright (c) 2016 Peter Hessler <phess...@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -82,6 +84,7 @@ void show_rib_brief(struct ctl_show_ri void show_rib_detail(struct ctl_show_rib *, u_char *, int); void show_attr(void *, u_int16_t); void show_community(u_char *, u_int16_t); +void show_large_community(u_char *, u_int16_t); void show_ext_community(u_char *, u_int16_t); char *fmt_mem(int64_t); int show_rib_memory_msg(struct imsg *); @@ -254,6 +257,13 @@ main(int argc, char *argv[]) sizeof(res->community)); type = IMSG_CTL_SHOW_RIB_COMMUNITY; } + if (res->large_community.as != COMMUNITY_UNSET && + res->large_community.ld1 != COMMUNITY_UNSET && + res->large_community.ld2 != COMMUNITY_UNSET) { + memcpy(&ribreq.large_community, &res->large_community, + sizeof(res->large_community)); + type = IMSG_CTL_SHOW_RIB_LARGECOMMUNITY; + } memcpy(&ribreq.neighbor, &neighbor, sizeof(ribreq.neighbor)); strlcpy(ribreq.rib, res->rib, sizeof(ribreq.rib)); ribreq.aid = res->aid; @@ -275,6 +285,11 @@ main(int argc, char *argv[]) res->community.type != COMMUNITY_UNSET) memcpy(&ribreq.community, &res->community, sizeof(res->community)); + if (res->large_community.as != COMMUNITY_UNSET && + res->large_community.ld1 != COMMUNITY_UNSET && + res->large_community.ld2 != COMMUNITY_UNSET) + memcpy(&ribreq.large_community, &res->large_community, + sizeof(res->large_community)); memcpy(&ribreq.neighbor, &neighbor, sizeof(ribreq.neighbor)); ribreq.aid = res->aid; ribreq.flags = res->flags; @@ -377,6 +392,11 @@ main(int argc, char *argv[]) res->community.type != COMMUNITY_UNSET) memcpy(&ribreq.community, &res->community, sizeof(res->community)); + if (res->large_community.as != COMMUNITY_UNSET && + res->large_community.ld1 != COMMUNITY_UNSET && + res->large_community.ld2 != COMMUNITY_UNSET) + memcpy(&ribreq.large_community, &res->large_community, + sizeof(res->large_community)); memcpy(&ribreq.neighbor, &neighbor, sizeof(ribreq.neighbor)); ribreq.aid = res->aid; ribreq.flags = res->flags; @@ -1424,6 +1444,11 @@ show_attr(void *b, u_int16_t len) show_community(data, alen); printf("\n"); break; + case ATTR_LARGE_COMMUNITIES: + printf(" Large Communities: "); + show_large_community(data, alen); + printf("\n"); + break; case ATTR_AGGREGATOR: memcpy(&as, data, sizeof(as)); memcpy(&id, data + sizeof(as), sizeof(id)); @@ -1493,6 +1518,29 @@ show_community(u_char *data, u_int16_t l printf("%hu:%hu", a, v); if (i + 4 < len) + printf(" "); + } +} + +void +show_large_community(u_char *data, u_int16_t len) +{ + u_int32_t a, l1, l2; + u_int16_t i; + + if (len % 12) + return; + + for (i = 0; i < len; i += 12) { + memcpy(&a, data + i, sizeof(a)); + memcpy(&l1, data + i + 4, sizeof(l1)); + memcpy(&l2, data + i + 8, sizeof(l2)); + a = ntohl(a); + l1 = ntohl(l1); + l2 = ntohl(l2); + printf("%u:%u:%u", a, l1, l2); + + if (i + 12 < len) printf(" "); } } Index: usr.sbin/bgpctl/parser.c =================================================================== RCS file: /cvs/openbsd/src/usr.sbin/bgpctl/parser.c,v retrieving revision 1.73 diff -u -p -u -p -r1.73 parser.c --- usr.sbin/bgpctl/parser.c 11 Oct 2015 19:53:57 -0000 1.73 +++ usr.sbin/bgpctl/parser.c 13 Oct 2016 15:32:35 -0000 @@ -2,6 +2,8 @@ /* * Copyright (c) 2003, 2004 Henning Brauer <henn...@openbsd.org> + * Copyright (c) 2016 Job Snijders <j...@instituut.net> + * Copyright (c) 2016 Peter Hessler <phess...@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -44,6 +46,7 @@ enum token_type { PEERDESC, RIBNAME, COMMUNITY, + LARGE_COMMUNITY, LOCALPREF, MED, NEXTHOP, @@ -90,11 +93,13 @@ static const struct token t_show_mrt_as[ static const struct token t_show_prefix[]; static const struct token t_show_ip[]; static const struct token t_show_community[]; +static const struct token t_show_largecommunity[]; static const struct token t_network[]; static const struct token t_network_show[]; static const struct token t_prefix[]; static const struct token t_set[]; static const struct token t_community[]; +static const struct token t_largecommunity[]; static const struct token t_localpref[]; static const struct token t_med[]; static const struct token t_nexthop[]; @@ -160,6 +165,7 @@ static const struct token t_show_rib[] = { ASTYPE, "peer-as", AS_PEER, t_show_rib_as}, { ASTYPE, "empty-as", AS_EMPTY, t_show_rib}, { KEYWORD, "community", NONE, t_show_community}, + { KEYWORD, "large-community", NONE, t_show_largecommunity}, { FLAG, "best", F_CTL_ACTIVE, t_show_rib}, { FLAG, "selected", F_CTL_ACTIVE, t_show_rib}, { FLAG, "detail", F_CTL_DETAIL, t_show_rib}, @@ -275,6 +281,11 @@ static const struct token t_show_communi { ENDTOKEN, "", NONE, NULL} }; +static const struct token t_show_largecommunity[] = { + { LARGE_COMMUNITY, "", NONE, t_show_rib}, + { ENDTOKEN, "", NONE, NULL} +}; + static const struct token t_network[] = { { KEYWORD, "add", NETWORK_ADD, t_prefix}, { KEYWORD, "delete", NETWORK_REMOVE, t_prefix}, @@ -299,6 +310,7 @@ static const struct token t_network_show static const struct token t_set[] = { { NOTOKEN, "", NONE, NULL}, { KEYWORD, "community", NONE, t_community}, + { KEYWORD, "large-community", NONE, t_largecommunity}, { KEYWORD, "localpref", NONE, t_localpref}, { KEYWORD, "med", NONE, t_med}, { KEYWORD, "metric", NONE, t_med}, @@ -317,6 +329,11 @@ static const struct token t_community[] { ENDTOKEN, "", NONE, NULL} }; +static const struct token t_largecommunity[] = { + { LARGE_COMMUNITY, "", NONE, t_set}, + { ENDTOKEN, "", NONE, NULL} +}; + static const struct token t_localpref[] = { { LOCALPREF, "", NONE, t_set}, { ENDTOKEN, "", NONE, NULL} @@ -391,6 +408,8 @@ int parse_number(const char *, struct enum token_type); int getcommunity(const char *); int parse_community(const char *, struct parse_result *); +int getlargecommunity(const char *); +int parse_largecommunity(const char *, struct parse_result *); int parse_nexthop(const char *, struct parse_result *); int bgpctl_getopt(int *, char **[], int); @@ -403,6 +422,9 @@ parse(int argc, char *argv[]) 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; TAILQ_INIT(&res.set); if ((res.irr_outdir = getcwd(NULL, 0)) == NULL) { fprintf(stderr, "getcwd failed: %s\n", strerror(errno)); @@ -556,6 +578,13 @@ match_token(int *argc, char **argv[], co t = &table[i]; } break; + case LARGE_COMMUNITY: + if (word != NULL && wordlen > 0 && + parse_largecommunity(word, &res)) { + match++; + t = &table[i]; + } + break; case LOCALPREF: case MED: case PREPNBR: @@ -668,6 +697,9 @@ show_valid_args(const struct token table case COMMUNITY: fprintf(stderr, " <community>\n"); break; + case LARGE_COMMUNITY: + fprintf(stderr, " <large-community>\n"); + break; case LOCALPREF: case MED: case PREPNBR: @@ -956,6 +988,57 @@ done: r->community.as = as; r->community.type = type; + + TAILQ_INSERT_TAIL(&r->set, fs, entry); + return (1); +} + +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) +{ + struct filter_set *fs; + char *p = strdup(word); + char *array[3]; + int64_t as, ld1, ld2; + int i = 0; + + while (p != NULL) { + array[i++] = p; + p = strchr(p, ':'); + if (p) + *p++ = 0; + } + + as = getlargecommunity(array[0]); + ld1 = getlargecommunity(array[1]); + ld2 = getlargecommunity(array[2]); + + 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; TAILQ_INSERT_TAIL(&r->set, fs, entry); return (1); Index: usr.sbin/bgpctl/parser.h =================================================================== RCS file: /cvs/openbsd/src/usr.sbin/bgpctl/parser.h,v retrieving revision 1.27 diff -u -p -u -p -r1.27 parser.h --- usr.sbin/bgpctl/parser.h 17 Apr 2015 07:51:09 -0000 1.27 +++ usr.sbin/bgpctl/parser.h 5 Sep 2016 13:41:29 -0000 @@ -63,6 +63,7 @@ struct parse_result { struct filter_as as; struct filter_set_head set; struct filter_community community; + struct filter_largecommunity large_community; char peerdesc[PEER_DESCR_LEN]; char rib[PEER_DESCR_LEN]; char *irr_outdir; Index: usr.sbin/bgpd/bgpd.8 =================================================================== RCS file: /cvs/openbsd/src/usr.sbin/bgpd/bgpd.8,v retrieving revision 1.48 diff -u -p -u -p -r1.48 bgpd.8 --- usr.sbin/bgpd/bgpd.8 14 Aug 2013 06:32:36 -0000 1.48 +++ usr.sbin/bgpd/bgpd.8 2 Oct 2016 13:39:54 -0000 @@ -238,6 +238,17 @@ control socket .Re .Pp .Rs +.%A J. Snijders +.%A J. Heitz +.%A K. Patel +.%A I. Bagdonas +.%A A. Simpson +.%D September 2016 +.%R draft-ietf-idr-large-community +.%T Large BGP Communities Attribute +.Re +.Pp +.Rs .%A A. Heffernan .%D August 1998 .%R RFC 2385 Index: usr.sbin/bgpd/bgpd.conf.5 =================================================================== RCS file: /cvs/openbsd/src/usr.sbin/bgpd/bgpd.conf.5,v retrieving revision 1.147 diff -u -p -u -p -r1.147 bgpd.conf.5 --- usr.sbin/bgpd/bgpd.conf.5 5 Oct 2016 07:38:06 -0000 1.147 +++ usr.sbin/bgpd/bgpd.conf.5 10 Oct 2016 20:05:40 -0000 @@ -1143,6 +1143,39 @@ may be set to which is expanded to the current neighbor remote AS number. .Pp .It Xo +.Ic large-community +.Ar as-number : Ns Ar local : Ns Ar local +.Xc +.It Ic large-community Ar name +This rule applies only to +.Em UPDATES +where the +.Ic Large community +path attribute is present and matches. +Communities are specified as +.Ar as-number : Ns Ar local : Ns Ar local , +where +.Ar as-number +is an AS number and +.Ar local +is a locally significant number between zero and +.Li 4294967295. +Both +.Ar as-number +and +.Ar local +may be set to +.Sq * +to do wildcard matching. +Both +.Ar as-number +and +.Ar local +may be set to +.Ic neighbor-as , +which is expanded to the current neighbor remote AS number. +.Pp +.It Xo .Ic ext-community .Ar subtype Ar as-number : Ns Ar local .Xc @@ -1364,6 +1397,35 @@ Alternately, well-known communities may .Ic NO_EXPORT_SUBCONFED , or .Ic NO_PEER . +For +.Cm delete , +both +.Ar as-number +and +.Ar local +may be set to +.Sq * +to do wildcard matching. +.Pp +.It Xo +.Ic large-community Op Ar delete +.Ar as-number : Ns Ar local : Ns Ar local +.Xc +.It Xo +.Ic large-community Op Ar delete +.Ar name +.Xc +Set or delete the +.Em Large Communities +path attribute. +Communities are specified as +.Ar as-number : Ns Ar local : Ns Ar local , +where +.Ar as-number +is an AS number and +.Ar local +is a locally-significant number between zero and +.Li 4294967295 . For .Cm delete , both Index: usr.sbin/bgpd/bgpd.h =================================================================== RCS file: /cvs/openbsd/src/usr.sbin/bgpd/bgpd.h,v retrieving revision 1.296 diff -u -p -u -p -r1.296 bgpd.h --- usr.sbin/bgpd/bgpd.h 5 Oct 2016 07:38:06 -0000 1.296 +++ usr.sbin/bgpd/bgpd.h 13 Oct 2016 14:41:12 -0000 @@ -377,6 +377,7 @@ enum imsg_type { IMSG_CTL_SHOW_RIB_PREFIX, IMSG_CTL_SHOW_RIB_ATTR, IMSG_CTL_SHOW_RIB_COMMUNITY, + IMSG_CTL_SHOW_RIB_LARGECOMMUNITY, IMSG_CTL_SHOW_NETWORK, IMSG_CTL_SHOW_RIB_MEM, IMSG_CTL_SHOW_TERSE, @@ -648,6 +649,18 @@ struct filter_community { int type; }; +struct filter_largecommunity { + int64_t as; + int64_t ld1; + int64_t ld2; +}; + +struct wire_largecommunity { + uint32_t as; + uint32_t ld1; + uint32_t ld2; +}; + struct filter_extcommunity { u_int16_t flags; u_int8_t type; @@ -675,6 +688,7 @@ struct ctl_show_rib_request { struct bgpd_addr prefix; struct filter_as as; struct filter_community community; + struct filter_largecommunity large_community; u_int32_t peerid; pid_t pid; u_int16_t flags; @@ -793,6 +807,7 @@ struct filter_match { struct filter_as as; struct filter_aslen aslen; struct filter_community community; + struct filter_largecommunity large_community; struct filter_extcommunity ext_community; }; @@ -834,6 +849,8 @@ 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, @@ -852,6 +869,7 @@ struct filter_set { int32_t relative; struct bgpd_addr nexthop; 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/openbsd/src/usr.sbin/bgpd/control.c,v retrieving revision 1.82 diff -u -p -u -p -r1.82 control.c --- usr.sbin/bgpd/control.c 5 Dec 2015 18:28:04 -0000 1.82 +++ usr.sbin/bgpd/control.c 5 Sep 2016 13:41:29 -0000 @@ -244,6 +244,7 @@ control_dispatch_msg(struct pollfd *pfd, case IMSG_CTL_SHOW_RIB_PREFIX: case IMSG_CTL_SHOW_RIB_MEM: case IMSG_CTL_SHOW_RIB_COMMUNITY: + case IMSG_CTL_SHOW_RIB_LARGECOMMUNITY: case IMSG_CTL_SHOW_NETWORK: case IMSG_CTL_SHOW_TERSE: case IMSG_CTL_SHOW_TIMER: @@ -462,6 +463,7 @@ control_dispatch_msg(struct pollfd *pfd, break; case IMSG_CTL_SHOW_RIB_MEM: case IMSG_CTL_SHOW_RIB_COMMUNITY: + 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/openbsd/src/usr.sbin/bgpd/parse.y,v retrieving revision 1.289 diff -u -p -u -p -r1.289 parse.y --- usr.sbin/bgpd/parse.y 5 Oct 2016 07:38:06 -0000 1.289 +++ usr.sbin/bgpd/parse.y 13 Oct 2016 15:31:31 -0000 @@ -5,6 +5,8 @@ * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. * Copyright (c) 2001 Theo de Raadt. All rights reserved. + * Copyright (c) 2016 Job Snijders <j...@instituut.net> + * Copyright (c) 2016 Peter Hessler <phess...@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -138,6 +140,8 @@ struct filter_rule *get_rule(enum action int getcommunity(char *); int parsecommunity(struct filter_community *, char *); +int getlargecommunity(char *); +int parselargecommunity(struct filter_largecommunity *, char *); int parsesubtype(char *); int parseextvalue(char *, u_int32_t *); int parseextcommunity(struct filter_extcommunity *, char *, @@ -185,7 +189,7 @@ typedef struct { %token QUICK %token FROM TO ANY %token CONNECTED STATIC -%token COMMUNITY EXTCOMMUNITY +%token COMMUNITY EXTCOMMUNITY LARGECOMMUNITY %token PREFIX PREFIXLEN SOURCEAS TRANSITAS PEERAS DELETE MAXASLEN MAXASSEQ %token SET LOCALPREF MED METRIC NEXTHOP REJECT BLACKHOLE NOMODIFY SELF %token PREPEND_SELF PREPEND_PEER PFTABLE WEIGHT RTLABEL ORIGIN @@ -1712,10 +1716,12 @@ 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($$)); @@ -1776,6 +1782,18 @@ filter_elm : filter_prefix_h { } 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) { + free($2); + YYERROR; + } + free($2); + } | EXTCOMMUNITY STRING STRING { if (fmopts.m.ext_community.flags & EXT_COMMUNITY_FLAG_VALID) { @@ -2131,6 +2149,31 @@ filter_set_opt : LOCALPREF NUMBER { 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) { + free($3); + free($$); + YYERROR; + } + 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)) { + yyerror("'*' is not allowed in set community"); + free($$); + YYERROR; + } + } | EXTCOMMUNITY delete STRING STRING { if (($$ = calloc(1, sizeof(struct filter_set))) == NULL) fatal(NULL); @@ -2266,6 +2309,7 @@ lookup(char *s) { "inet6", IPV6}, { "ipsec", IPSEC}, { "key", KEY}, + { "large-community", LARGECOMMUNITY}, { "listen", LISTEN}, { "local-address", LOCALADDR}, { "localpref", LOCALPREF}, @@ -2911,6 +2955,59 @@ parsecommunity(struct filter_community * } int +getlargecommunity(char *s) +{ + int val; + const char *errstr; + + if (strcmp(s, "*") == 0) + return (COMMUNITY_ANY); + if (strcmp(s, "neighbor-as") == 0) + return (COMMUNITY_NEIGHBOR_AS); + val = strtonum(s, 0, UINT_MAX, &errstr); + if (errstr) { + yyerror("Large Community %s is %s (max: %u)", + s, errstr, UINT_MAX); + return (COMMUNITY_ERROR); + } + return (val); +} + +int +parselargecommunity(struct filter_largecommunity *c, char *s) +{ + char *p, *q; + int 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 = getlargecommunity(s)) == COMMUNITY_ERROR) + return (-1); + + if ((ld1 = getlargecommunity(p)) == COMMUNITY_ERROR) + return (-1); + + if ((ld2 = getlargecommunity(q)) == COMMUNITY_ERROR) + return (-1); + + c->as = as; + c->ld1 = ld1; + c->ld2 = ld2; + + return (0); +} + +int parsesubtype(char *type) { /* this has to be sorted always */ @@ -3527,6 +3624,10 @@ 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) @@ -3558,6 +3659,18 @@ merge_filterset(struct filter_set_head * return (0); } break; + case ACTION_SET_LARGE_COMMUNITY: + case ACTION_DEL_LARGE_COMMUNITY: + if (s->action.large_community.as < + t->action.large_community.as || + (s->action.large_community.as == + t->action.large_community.as && + s->action.large_community.ld1 < + t->action.large_community.ld2 )) { + TAILQ_INSERT_BEFORE(t, s, entry); + return (0); + } + break; case ACTION_SET_EXT_COMMUNITY: case ACTION_DEL_EXT_COMMUNITY: if (memcmp(&s->action.ext_community, @@ -3634,6 +3747,7 @@ get_rule(enum action_types type) 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/openbsd/src/usr.sbin/bgpd/printconf.c,v retrieving revision 1.98 diff -u -p -u -p -r1.98 printconf.c --- usr.sbin/bgpd/printconf.c 5 Oct 2016 07:38:06 -0000 1.98 +++ usr.sbin/bgpd/printconf.c 13 Oct 2016 15:31:27 -0000 @@ -2,6 +2,8 @@ /* * Copyright (c) 2003, 2004 Henning Brauer <henn...@openbsd.org> + * Copyright (c) 2016 Job Snijders <j...@instituut.net> + * Copyright (c) 2016 Peter Hessler <phess...@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -28,6 +30,7 @@ void print_op(enum comp_ops); void print_community(int, int); +void print_largecommunity(int64_t , int64_t, int64_t); void print_extcommunity(struct filter_extcommunity *); void print_origin(u_int8_t); void print_set(struct filter_set_head *); @@ -102,6 +105,33 @@ print_community(int as, int type) } void +print_largecommunity(int64_t as, int64_t ld1, int64_t ld2) +{ + if (as == COMMUNITY_ANY) + printf("*:"); + else if (as == COMMUNITY_NEIGHBOR_AS) + printf("neighbor-as:"); + else + printf("%llu:", as); + + if (ld1 == COMMUNITY_ANY) + printf("*:"); + else if (ld1 == COMMUNITY_NEIGHBOR_AS) + printf("neighbor-as:"); + else + printf("%llu:", ld1); + + if (ld2 == COMMUNITY_ANY) + printf("* "); + else if (ld2 == COMMUNITY_NEIGHBOR_AS) + printf("neighbor-as "); + else + printf("%llu ", ld2); + +} + + +void print_extcommunity(struct filter_extcommunity *c) { switch (c->type & EXT_COMMUNITY_VALUE) { @@ -202,6 +232,20 @@ print_set(struct filter_set_head *set) 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(" "); + break; case ACTION_PFTABLE: printf("pftable %s ", s->action.pftable); break; @@ -627,6 +671,12 @@ print_rule(struct peer *peer_l, struct f 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/openbsd/src/usr.sbin/bgpd/rde.c,v retrieving revision 1.350 diff -u -p -u -p -r1.350 rde.c --- usr.sbin/bgpd/rde.c 3 Sep 2016 16:22:17 -0000 1.350 +++ usr.sbin/bgpd/rde.c 13 Oct 2016 15:31:23 -0000 @@ -2,6 +2,8 @@ /* * Copyright (c) 2003, 2004 Henning Brauer <henn...@openbsd.org> + * Copyright (c) 2016 Job Snijders <j...@instituut.net> + * Copyright (c) 2016 Peter Hessler <phess...@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -559,6 +561,7 @@ badnet: case IMSG_CTL_SHOW_RIB: case IMSG_CTL_SHOW_RIB_AS: case IMSG_CTL_SHOW_RIB_COMMUNITY: + 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"); @@ -1577,6 +1580,23 @@ bad_flags: ATTR_PARTIAL)) goto bad_flags; goto optattr; + case ATTR_LARGE_COMMUNITIES: + if (attr_len % 12 != 0) { + /* + * mark update as bad and withdraw all routes as per + * draft-ietf-idr-optional-transitive-00.txt + * but only if partial bit is set + */ + if ((flags & ATTR_PARTIAL) == 0) + goto bad_len; + a->flags |= F_ATTR_PARSE_ERR; + log_peer_warnx(&peer->conf, "bad LARGE COMMUNITIES, " + "path invalidated and prefix withdrawn"); + } + if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, + ATTR_PARTIAL)) + goto bad_flags; + goto optattr; case ATTR_EXT_COMMUNITIES: if (attr_len % 8 != 0) { /* @@ -2266,6 +2286,10 @@ rde_dump_filter(struct prefix *p, struct !community_match(p->aspath, req->community.as, req->community.type)) return; + if (req->type == IMSG_CTL_SHOW_RIB_LARGECOMMUNITY && + !community_large_match(p->aspath, req->large_community.as, + req->large_community.ld1, req->large_community.ld2)) + return; if ((req->flags & F_CTL_ACTIVE) && p->rib->active != p) return; rde_dump_rib_as(p, p->aspath, req->pid, req->flags); @@ -2348,6 +2372,7 @@ rde_dump_ctx_new(struct ctl_show_rib_req case IMSG_CTL_SHOW_RIB: case IMSG_CTL_SHOW_RIB_AS: case IMSG_CTL_SHOW_RIB_COMMUNITY: + case IMSG_CTL_SHOW_RIB_LARGECOMMUNITY: ctx->ribctx.ctx_upcall = rde_dump_upcall; break; case IMSG_CTL_SHOW_RIB_PREFIX: Index: usr.sbin/bgpd/rde.h =================================================================== RCS file: /cvs/openbsd/src/usr.sbin/bgpd/rde.h,v retrieving revision 1.149 diff -u -p -u -p -r1.149 rde.h --- usr.sbin/bgpd/rde.h 6 Nov 2015 16:23:26 -0000 1.149 +++ usr.sbin/bgpd/rde.h 13 Oct 2016 14:10:00 -0000 @@ -112,7 +112,8 @@ enum attrtypes { ATTR_MP_UNREACH_NLRI=15, ATTR_EXT_COMMUNITIES=16, ATTR_AS4_PATH=17, - ATTR_AS4_AGGREGATOR=18 + ATTR_AS4_AGGREGATOR=18, + ATTR_LARGE_COMMUNITIES=30, }; /* attribute flags. 4 low order bits reserved */ @@ -367,6 +368,12 @@ int aspath_lenmatch(struct aspath *, e 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 *, Index: usr.sbin/bgpd/rde_attr.c =================================================================== RCS file: /cvs/openbsd/src/usr.sbin/bgpd/rde_attr.c,v retrieving revision 1.95 diff -u -p -u -p -r1.95 rde_attr.c --- usr.sbin/bgpd/rde_attr.c 24 Oct 2015 08:00:42 -0000 1.95 +++ usr.sbin/bgpd/rde_attr.c 13 Oct 2016 15:31:21 -0000 @@ -2,6 +2,8 @@ /* * Copyright (c) 2004 Claudio Jeker <clau...@openbsd.org> + * Copyright (c) 2016 Job Snijders <j...@instituut.net> + * Copyright (c) 2016 Peter Hessler <phess...@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -1351,4 +1353,148 @@ community_ext_matchone(struct filter_ext } return (0); +} + +int +community_large_match(struct rde_aspath *asp, int64_t as, int64_t ld1, + int64_t ld2) +{ + struct wire_largecommunity *bar; + struct attr *a; + u_int8_t *p; + u_int16_t len; + u_int32_t eas, eld1, eld2; + + a = attr_optget(asp, ATTR_LARGE_COMMUNITIES); + if (a == NULL) + /* no communities, no match */ + return (0); + + p = a->data; + for (len = a->len / 12; len > 0; len--) { + bar = (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)) + return (1); + } + return (0); +} + +int +community_large_set(struct rde_aspath *asp, int64_t as, int64_t ld1, + int64_t ld2) +{ + struct wire_largecommunity *bar; + struct attr *attr; + u_int8_t *p = NULL; + unsigned int i, ncommunities = 0; + u_int8_t f = ATTR_OPTIONAL|ATTR_TRANSITIVE; + + attr = attr_optget(asp, ATTR_LARGE_COMMUNITIES); + if (attr != NULL) { + p = attr->data; + ncommunities = attr->len / 12; + } + + /* first check if the community is not already set */ + for (i = 0; i < ncommunities; i++) { + bar = (struct wire_largecommunity *)p; + if (bar->as == as && bar->ld1 == ld1 && bar->ld2 == ld2) + /* already present, nothing todo */ + return (1); + p += 12; + } + + if (ncommunities++ >= USHRT_MAX / 12) + /* overflow */ + return (0); + + 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); + + if (attr != NULL) { + memcpy(p + 12, attr->data, attr->len); + f = attr->flags; + attr_free(asp, attr); + } + + attr_optadd(asp, f, ATTR_LARGE_COMMUNITIES, p, ncommunities * 12); + + free(p); + return (1); +} + +void +community_large_delete(struct rde_aspath *asp, int64_t as, int64_t ld1, + int64_t ld2) +{ + struct wire_largecommunity *bar; + struct attr *attr; + u_int8_t *p, *n; + u_int16_t l = 0, len = 0; + u_int32_t eas, eld1, eld2; + u_int8_t f; + + attr = attr_optget(asp, ATTR_LARGE_COMMUNITIES); + if (attr == NULL) + /* no attr nothing to do */ + return; + + p = attr->data; + for (len = 0; l < attr->len; l += 12) { + bar = (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)) + /* match */ + continue; + len += 12; + } + + if (len == 0) { + attr_free(asp, attr); + return; + } + + if ((n = malloc(len)) == NULL) + fatal("community_delete"); + + p = attr->data; + for (l = 0; l < len && p < attr->data + attr->len; ) { + bar = (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)) + /* match */ + continue; + memcpy(n + l, bar, sizeof(*bar)); + l += 12; + } + + f = attr->flags; + + attr_free(asp, attr); + attr_optadd(asp, f, ATTR_LARGE_COMMUNITIES, n, len); + free(n); } Index: usr.sbin/bgpd/rde_filter.c =================================================================== RCS file: /cvs/openbsd/src/usr.sbin/bgpd/rde_filter.c,v retrieving revision 1.77 diff -u -p -u -p -r1.77 rde_filter.c --- usr.sbin/bgpd/rde_filter.c 3 Jun 2016 17:36:37 -0000 1.77 +++ usr.sbin/bgpd/rde_filter.c 13 Oct 2016 15:31:13 -0000 @@ -2,6 +2,8 @@ /* * Copyright (c) 2004 Claudio Jeker <clau...@openbsd.org> + * Copyright (c) 2016 Job Snijders <j...@instituut.net> + * Copyright (c) 2016 Peter Hessler <phess...@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -35,7 +37,7 @@ rde_apply_set(struct rde_aspath *asp, st { struct filter_set *set; u_char *np; - int as, type; + int as, type, ld1, ld2; u_int32_t prep_as; u_int16_t nl; u_int8_t prepend; @@ -181,6 +183,84 @@ rde_apply_set(struct rde_aspath *asp, st community_delete(asp, as, type); break; + case ACTION_SET_LARGE_COMMUNITY: + switch (set->action.large_community.as) { + case COMMUNITY_ERROR: + fatalx("rde_apply_set bad large community string"); + case COMMUNITY_NEIGHBOR_AS: + as = peer->conf.remote_as; + break; + case COMMUNITY_ANY: + default: + as = set->action.large_community.as; + break; + } + + switch (set->action.large_community.ld1) { + case COMMUNITY_ERROR: + fatalx("rde_apply_set bad large community string"); + case COMMUNITY_NEIGHBOR_AS: + ld1 = peer->conf.remote_as; + break; + case COMMUNITY_ANY: + default: + ld1 = set->action.large_community.ld1; + break; + } + + switch (set->action.large_community.ld2) { + case COMMUNITY_ERROR: + fatalx("rde_apply_set bad large community string"); + case COMMUNITY_NEIGHBOR_AS: + ld2 = peer->conf.remote_as; + break; + case COMMUNITY_ANY: + default: + ld2 = set->action.large_community.ld2; + break; + } + + community_large_set(asp, as, ld1, ld2); + break; + case ACTION_DEL_LARGE_COMMUNITY: + switch (set->action.large_community.as) { + case COMMUNITY_ERROR: + fatalx("rde_apply_set bad large community string"); + case COMMUNITY_NEIGHBOR_AS: + as = peer->conf.remote_as; + break; + case COMMUNITY_ANY: + default: + as = set->action.large_community.as; + break; + } + + switch (set->action.large_community.ld1) { + case COMMUNITY_ERROR: + fatalx("rde_apply_set bad large community string"); + case COMMUNITY_NEIGHBOR_AS: + ld1 = peer->conf.remote_as; + break; + case COMMUNITY_ANY: + default: + ld1 = set->action.large_community.ld1; + break; + } + + switch (set->action.large_community.ld2) { + case COMMUNITY_ERROR: + fatalx("rde_apply_set bad large community string"); + case COMMUNITY_NEIGHBOR_AS: + ld2 = peer->conf.remote_as; + break; + case COMMUNITY_ANY: + default: + ld2 = set->action.large_community.ld2; + break; + } + + community_large_delete(asp, as, ld1, ld2); + break; case ACTION_PFTABLE: /* convert pftable name to an id */ set->action.id = pftable_name2id(set->action.pftable); @@ -222,7 +302,7 @@ rde_filter_match(struct filter_rule *f, struct rde_peer *from) { u_int32_t pas; - int cas, type; + int cas, type, ld1, ld2; if (asp != NULL && f->match.as.type != AS_NONE) { if (f->match.as.flags & AS_FLAG_NEIGHBORAS) @@ -270,6 +350,44 @@ rde_filter_match(struct filter_rule *f, 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_apply_set bad community string"); + case COMMUNITY_NEIGHBOR_AS: + cas = peer->conf.remote_as; + break; + default: + cas = f->match.large_community.as; + break; + } + + switch (f->match.large_community.ld1) { + case COMMUNITY_ERROR: + fatalx("rde_apply_set bad community string"); + case COMMUNITY_NEIGHBOR_AS: + ld1 = peer->conf.remote_as; + break; + default: + ld1 = f->match.large_community.ld1; + break; + } + + switch (f->match.large_community.ld2) { + case COMMUNITY_ERROR: + fatalx("rde_apply_set bad community string"); + case COMMUNITY_NEIGHBOR_AS: + ld2 = peer->conf.remote_as; + break; + default: + ld2 = f->match.large_community.ld2; + break; + } + + if (community_large_match(asp, cas, ld1, ld2) == 0) + return (0); + } if (f->match.prefix.addr.aid != 0) { if (f->match.prefix.addr.aid != prefix->aid) @@ -547,6 +665,14 @@ 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) @@ -630,6 +756,10 @@ 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"); -- What I've done, of course, is total garbage. -- R. Willard, Pure Math 430a