On Thu, Feb 07, 2019 at 11:41:39AM +0100, Claudio Jeker wrote:
> At a2k19 David (dlg@) and I sat down and looked at BGP L3 MPLS VPN
> support. Now David wants to use this in production and realized that
> in his case it would be great to have more than one mpe(4) interface per
> rdomain. This way it is possible to write good firewall rules using
> interface names or groups.
>
> The definition of VPNs in bgpd was never super elegant. The 'depend on
> mpeX' config was a bit redundant and so after some discussions we decided
> to rework this part.
>
> L3 MPLS VPN are now configured like this:
>
> vpn "staff" on mpe1 {
> rd $ASN:1
> import-target rt $ASN:100
> export-target rt $ASN:101
> network 0/0
> }
>
> vpn "users" on mpe2 {
> rd $ASN:2
> import-target rt $ASN:200
> export-target rt $ASN:201
> network 0/0
> }
>
> Now in this example mpe1 and mpe2 can be in the same rdomain. The rdomain
> is now selected based on the rdomain of the mpe(4) interface. There are
> probably still some gotchas around this which can be tackled in a 2nd
> round.
>
> This diff does a lot of shuffling of code and especially affect network
> statements (since those are now per VPN and no longer per rdomain).
> The rewrite of the network code fixed also some other behaviour bugs which
> do not only affect VPN setups. In short conficts between 'network A.B.C.D/N'
> and 'network static' are now properly handled (with 'network A.B.C.D/N'
> having preference).
>
> Since this is a major change please test (or suffer later).
Reads good, works fine.
OK denis@
While testing, I noticed that import-target/export-target are not updated on
reload (problem not introduced by this diff though)
> --
> :wq Claudio
>
> Index: usr.sbin/bgpctl/bgpctl.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpctl/bgpctl.c,v
> retrieving revision 1.228
> diff -u -p -r1.228 bgpctl.c
> --- usr.sbin/bgpctl/bgpctl.c 20 Jan 2019 23:30:15 -0000 1.228
> +++ usr.sbin/bgpctl/bgpctl.c 7 Feb 2019 10:11:19 -0000
> @@ -346,7 +346,7 @@ main(int argc, char *argv[])
> bzero(&net, sizeof(net));
> net.prefix = res->addr;
> net.prefixlen = res->prefixlen;
> - net.rtableid = tableid;
> + net.rd = res->rd;
> /* attribute sets are not supported */
> if (res->action == NETWORK_ADD) {
> imsg_compose(ibuf, IMSG_NETWORK_ADD, 0, 0, -1,
> @@ -927,7 +927,7 @@ show_fib_head(void)
> printf("flags: "
> "* = valid, B = BGP, C = Connected, S = Static, D = Dynamic\n");
> printf(" "
> - "N = BGP Nexthop reachable via this route R = redistributed\n");
> + "N = BGP Nexthop reachable via this route\n");
> printf(" r = reject route, b = blackhole route\n\n");
> printf("flags prio destination gateway\n");
> }
> @@ -969,11 +969,6 @@ show_fib_flags(u_int16_t flags)
> else
> printf(" ");
>
> - if (flags & F_REDISTRIBUTED)
> - printf("R");
> - else
> - printf(" ");
> -
> if (flags & F_REJECT && flags & F_BLACKHOLE)
> printf("f");
> else if (flags & F_REJECT)
> @@ -1983,7 +1978,7 @@ network_bulk(struct parse_result *res)
> errx(1, "bad prefix: %s", b);
> net.prefix = h;
> net.prefixlen = len;
> - net.rtableid = tableid;
> + net.rd = res->rd;
>
> if (res->action == NETWORK_BULK_ADD) {
> imsg_compose(ibuf, IMSG_NETWORK_ADD,
> @@ -2128,7 +2123,7 @@ network_mrt_dump(struct mrt_rib *mr, str
> net.prefix = ctl.prefix;
> net.prefixlen = ctl.prefixlen;
> net.type = NETWORK_MRTCLONE;
> - /* XXX rtableid */
> + /* XXX rd can't be set and will be 0 */
>
> imsg_compose(ibuf, IMSG_NETWORK_ADD, 0, 0, -1,
> &net, sizeof(net));
> Index: usr.sbin/bgpctl/parser.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpctl/parser.c,v
> retrieving revision 1.89
> diff -u -p -r1.89 parser.c
> --- usr.sbin/bgpctl/parser.c 20 Jan 2019 23:30:15 -0000 1.89
> +++ usr.sbin/bgpctl/parser.c 7 Feb 2019 10:11:19 -0000
> @@ -58,6 +58,7 @@ enum token_type {
> PREPNBR,
> PREPSELF,
> WEIGHT,
> + RD,
> FAMILY,
> GETOPT,
> RTABLE,
> @@ -374,6 +375,11 @@ static const struct token t_network_show
> { ENDTOKEN, "", NONE, NULL}
> };
>
> +static const struct token t_rd[] = {
> + { RD, "", NONE, t_set},
> + { ENDTOKEN, "", NONE, NULL}
> +};
> +
> static const struct token t_set[] = {
> { NOTOKEN, "", NONE, NULL},
> { KEYWORD, "community", NONE, t_community},
> @@ -386,6 +392,7 @@ static const struct token t_set[] = {
> { KEYWORD, "pftable", NONE, t_pftable},
> { KEYWORD, "prepend-neighbor", NONE, t_prepnbr},
> { KEYWORD, "prepend-self", NONE, t_prepself},
> + { KEYWORD, "rd", NONE, t_rd},
> { KEYWORD, "weight", NONE, t_weight},
> { KEYWORD, "add", NETWORK_BULK_ADD, NULL},
> { KEYWORD, "delete", NETWORK_BULK_REMOVE, NULL},
> @@ -493,18 +500,14 @@ static struct parse_result res;
> const struct token *match_token(int *argc, char **argv[],
> const struct token []);
> void show_valid_args(const struct token []);
> -int parse_addr(const char *, struct bgpd_addr *);
> -int parse_asnum(const char *, size_t, u_int32_t *);
> -int parse_number(const char *, struct parse_result *,
> - enum token_type);
> -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 *);
> -int parse_largecommunity(const char *,
> - struct parse_result *);
> -int parse_nexthop(const char *, struct parse_result *);
> -int bgpctl_getopt(int *, char **[], int);
> +
> +int parse_addr(const char *, struct bgpd_addr *);
> +int parse_asnum(const char *, size_t, u_int32_t *);
> +int parse_number(const char *, struct parse_result *, enum token_type);
> +void parsecommunity(struct filter_community *c, int type, char *s);
> +int parseextcommunity(struct filter_community *c, const char *t, char *s);
> +int parse_nexthop(const char *, struct parse_result *);
> +int bgpctl_getopt(int *, char **[], int);
>
> struct parse_result *
> parse(int argc, char *argv[])
> @@ -675,8 +678,25 @@ match_token(int *argc, char **argv[], co
> }
> break;
> case COMMUNITY:
> - if (word != NULL && wordlen > 0 &&
> - parse_community(word, &res)) {
> + case LARGE_COMMUNITY:
> + if (word != NULL && wordlen > 0) {
> + int type = COMMUNITY_TYPE_BASIC;
> + char *p = strdup(word);
> +
> + if (p == NULL)
> + err(1, NULL);
> + if (table[i].type == LARGE_COMMUNITY)
> + type = COMMUNITY_TYPE_LARGE;
> + parsecommunity(&res.community,
> + COMMUNITY_TYPE_BASIC, p);
> + free(p);
> +
> + if ((fs = calloc(1, sizeof(*fs))) == NULL)
> + err(1, NULL);
> + fs->type = ACTION_SET_COMMUNITY;
> + fs->action.community = res.community;
> + TAILQ_INSERT_TAIL(&res.set, fs, entry);
> +
> match++;
> t = &table[i];
> }
> @@ -684,24 +704,62 @@ match_token(int *argc, char **argv[], co
> case EXTCOM_SUBTYPE:
> if (word != NULL && strncmp(word, table[i].keyword,
> wordlen) == 0) {
> - if (parsesubtype(word, &res.community.c.e.type,
> - &res.community.c.e.subtype) == 0)
> - errx(1, "Bad ext-community unknown "
> - "type");
> + res.ext_comm_subtype = table[i].keyword;
> match++;
> t = &table[i];
> }
> break;
> case EXTCOMMUNITY:
> - if (word != NULL && wordlen > 0 &&
> - parseextcommunity(word, &res)) {
> + if (word != NULL && wordlen > 0) {
> + char *p = strdup(word);
> +
> + if (p == NULL)
> + err(1, NULL);
> + parseextcommunity(&res.community,
> + res.ext_comm_subtype, p);
> + free(p);
> +
> + if ((fs = calloc(1, sizeof(*fs))) == NULL)
> + err(1, NULL);
> + fs->type = ACTION_SET_COMMUNITY;
> + fs->action.community = res.community;
> + TAILQ_INSERT_TAIL(&res.set, fs, entry);
> +
> match++;
> t = &table[i];
> }
> break;
> - case LARGE_COMMUNITY:
> - if (word != NULL && wordlen > 0 &&
> - parse_largecommunity(word, &res)) {
> + case RD:
> + if (word != NULL && wordlen > 0) {
> + char *p = strdup(word);
> + struct filter_community ext;
> + u_int64_t rd;
> +
> + if (p == NULL)
> + err(1, NULL);
> + parseextcommunity(&ext, "rt", p);
> + free(p);
> +
> + switch (ext.c.e.type) {
> + case EXT_COMMUNITY_TRANS_TWO_AS:
> + rd = (0ULL << 48);
> + rd |= (u_int64_t)ext.c.e.data1 << 32;
> + rd |= ext.c.e.data2 & 0xffffffff;
> + break;
> + case EXT_COMMUNITY_TRANS_IPV4:
> + rd = (1ULL << 48);
> + rd |= (u_int64_t)ext.c.e.data1 << 16;
> + rd |= ext.c.e.data2 & 0xffff;
> + break;
> + case EXT_COMMUNITY_TRANS_FOUR_AS:
> + rd = (2ULL << 48);
> + rd |= (u_int64_t)ext.c.e.data1 << 16;
> + rd |= ext.c.e.data2 & 0xffff;
> + break;
> + default:
> + errx(1, "bad encoding of rd");
> + }
> + res.rd = htobe64(rd);
> match++;
> t = &table[i];
> }
> @@ -823,11 +881,14 @@ show_valid_args(const struct token table
> case COMMUNITY:
> fprintf(stderr, " <community>\n");
> break;
> + case LARGE_COMMUNITY:
> + fprintf(stderr, " <large-community>\n");
> + break;
> case EXTCOMMUNITY:
> fprintf(stderr, " <extended-community>\n");
> break;
> - case LARGE_COMMUNITY:
> - fprintf(stderr, " <large-community>\n");
> + case RD:
> + fprintf(stderr, " <route-distinguisher>\n");
> break;
> case LOCALPREF:
> case MED:
> @@ -1035,95 +1096,118 @@ parse_number(const char *word, struct pa
> return (1);
> }
>
> -static u_int32_t
> -getcommunity(const char *s, int large, u_int8_t *flag)
> +static void
> +getcommunity(char *s, int large, u_int32_t *val, u_int8_t *flag)
> {
> int64_t max = USHRT_MAX;
> const char *errstr;
> - u_int32_t uval;
>
> + *flag = 0;
> + *val = 0;
> if (strcmp(s, "*") == 0) {
> *flag = COMMUNITY_ANY;
> - return (0);
> + return;
> + } else if (strcmp(s, "neighbor-as") == 0) {
> + *flag = COMMUNITY_NEIGHBOR_AS;
> + return;
> + } else if (strcmp(s, "local-as") == 0) {
> + *flag = COMMUNITY_LOCAL_AS;
> + return;
> }
> -
> if (large)
> max = UINT_MAX;
> -
> - uval = strtonum(s, 0, max, &errstr);
> + *val = strtonum(s, 0, max, &errstr);
> if (errstr)
> - errx(1, "Community is %s: %s", errstr, s);
> -
> - *flag = 0;
> - return (uval);
> + errx(1, "Community %s is %s (max: %llu)", s, errstr, max);
> }
>
> -int
> -parse_community(const char *word, struct parse_result *r)
> +static void
> +setcommunity(struct filter_community *c, u_int32_t as, u_int32_t data,
> + u_int8_t asflag, u_int8_t dataflag)
> {
> - struct filter_set *fs;
> - char *p;
> - u_int32_t as, type;
> - u_int8_t asflag, tflag;
> + memset(c, 0, sizeof(*c));
> + c->type = COMMUNITY_TYPE_BASIC;
> + c->dflag1 = asflag;
> + c->dflag2 = dataflag;
> + c->c.b.data1 = as;
> + c->c.b.data2 = data;
> +}
>
> - /* Well-known communities */
> - if (strcasecmp(word, "GRACEFUL_SHUTDOWN") == 0) {
> - as = COMMUNITY_WELLKNOWN;
> - type = COMMUNITY_GRACEFUL_SHUTDOWN;
> - goto done;
> - } else if (strcasecmp(word, "NO_EXPORT") == 0) {
> - as = COMMUNITY_WELLKNOWN;
> - type = COMMUNITY_NO_EXPORT;
> - goto done;
> - } else if (strcasecmp(word, "NO_ADVERTISE") == 0) {
> - as = COMMUNITY_WELLKNOWN;
> - type = COMMUNITY_NO_ADVERTISE;
> - goto done;
> - } else if (strcasecmp(word, "NO_EXPORT_SUBCONFED") == 0) {
> - as = COMMUNITY_WELLKNOWN;
> - type = COMMUNITY_NO_EXPSUBCONFED;
> - goto done;
> - } else if (strcasecmp(word, "NO_PEER") == 0) {
> - as = COMMUNITY_WELLKNOWN;
> - type = COMMUNITY_NO_PEER;
> - goto done;
> - } else if (strcasecmp(word, "BLACKHOLE") == 0) {
> - as = COMMUNITY_WELLKNOWN;
> - type = COMMUNITY_BLACKHOLE;
> - goto done;
> - }
> +static void
> +parselargecommunity(struct filter_community *c, char *s)
> +{
> + char *p, *q;
>
> - if ((p = strchr(word, ':')) == NULL) {
> - fprintf(stderr, "Bad community syntax\n");
> - return (0);
> - }
> + if ((p = strchr(s, ':')) == NULL)
> + errx(1, "Bad community syntax");
> *p++ = 0;
>
> - as = getcommunity(word, 0, &asflag);
> - type = getcommunity(p, 0, &tflag);
> + if ((q = strchr(p, ':')) == NULL)
> + errx(1, "Bad community syntax");
> + *q++ = 0;
> +
> + getcommunity(s, 1, &c->c.l.data1, &c->dflag1);
> + getcommunity(p, 1, &c->c.l.data2, &c->dflag2);
> + getcommunity(q, 1, &c->c.l.data3, &c->dflag3);
>
> -done:
> - if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
> - err(1, NULL);
> - fs->type = ACTION_SET_COMMUNITY;
> - fs->action.community.type = COMMUNITY_TYPE_BASIC;
> - fs->action.community.c.b.data1 = as;
> - fs->action.community.c.b.data2 = type;
> - fs->action.community.dflag1 = asflag;
> - fs->action.community.dflag2 = tflag;
> + c->type = COMMUNITY_TYPE_LARGE;
> +}
>
> - r->community = fs->action.community;
> +void
> +parsecommunity(struct filter_community *c, int type, char *s)
> +{
> + char *p;
> + u_int32_t as, data;
> + u_int8_t asflag, dataflag;
> +
> + if (type == COMMUNITY_TYPE_LARGE) {
> + parselargecommunity(c, s);
> + return;
> + }
>
> - TAILQ_INSERT_TAIL(&r->set, fs, entry);
> - return (1);
> + /* Well-known communities */
> + if (strcasecmp(s, "GRACEFUL_SHUTDOWN") == 0) {
> + setcommunity(c, COMMUNITY_WELLKNOWN,
> + COMMUNITY_GRACEFUL_SHUTDOWN, 0, 0);
> + return;
> + } else if (strcasecmp(s, "NO_EXPORT") == 0) {
> + setcommunity(c, COMMUNITY_WELLKNOWN,
> + COMMUNITY_NO_EXPORT, 0, 0);
> + return;
> + } else if (strcasecmp(s, "NO_ADVERTISE") == 0) {
> + setcommunity(c, COMMUNITY_WELLKNOWN,
> + COMMUNITY_NO_ADVERTISE, 0, 0);
> + return;
> + } else if (strcasecmp(s, "NO_EXPORT_SUBCONFED") == 0) {
> + setcommunity(c, COMMUNITY_WELLKNOWN,
> + COMMUNITY_NO_EXPSUBCONFED, 0, 0);
> + return;
> + } else if (strcasecmp(s, "NO_PEER") == 0) {
> + setcommunity(c, COMMUNITY_WELLKNOWN,
> + COMMUNITY_NO_PEER, 0, 0);
> + return;
> + } else if (strcasecmp(s, "BLACKHOLE") == 0) {
> + setcommunity(c, COMMUNITY_WELLKNOWN,
> + COMMUNITY_BLACKHOLE, 0, 0);
> + return;
> + }
> +
> + if ((p = strchr(s, ':')) == NULL)
> + errx(1, "Bad community syntax");
> + *p++ = 0;
> +
> + getcommunity(s, 0, &as, &asflag);
> + getcommunity(p, 0, &data, &dataflag);
> + setcommunity(c, as, data, asflag, dataflag);
> }
>
> -int
> -parsesubtype(const char *name, u_int8_t *type, u_int8_t *subtype)
> +static int
> +parsesubtype(const char *name, int *type, int *subtype)
> {
> const struct ext_comm_pairs *cp;
> int found = 0;
>
> +printf("%s: looking for %s\n", __func__, name);
> for (cp = iana_ext_comms; cp->subname != NULL; cp++) {
> if (strcmp(name, cp->subname) == 0) {
> if (found == 0) {
> @@ -1138,79 +1222,89 @@ parsesubtype(const char *name, u_int8_t
> return (found);
> }
>
> -int
> -parseextvalue(const char *s, u_int32_t *v)
> +static int
> +parseextvalue(int type, char *s, u_int32_t *v)
> {
> - const char *errstr;
> + const char *errstr;
> char *p;
> struct in_addr ip;
> - u_int32_t uvalh = 0, uval;
> + u_int32_t uvalh, uval;
>
> - if ((p = strchr(s, '.')) == NULL) {
> + if (type != -1) {
> + /* nothing */
> + } else if ((p = strchr(s, '.')) == NULL) {
> /* AS_PLAIN number (4 or 2 byte) */
> - uval = strtonum(s, 0, UINT_MAX, &errstr);
> - if (errstr) {
> - fprintf(stderr, "Bad ext-community: %s is %s\n", s,
> - errstr);
> - return (-1);
> - }
> - *v = uval;
> - if (uval <= USHRT_MAX)
> - return (EXT_COMMUNITY_TRANS_TWO_AS);
> + strtonum(s, 0, USHRT_MAX, &errstr);
> + if (errstr == NULL)
> + type = EXT_COMMUNITY_TRANS_TWO_AS;
> else
> - return (EXT_COMMUNITY_TRANS_FOUR_AS);
> + type = EXT_COMMUNITY_TRANS_FOUR_AS;
> } else if (strchr(p + 1, '.') == NULL) {
> /* AS_DOT number (4-byte) */
> + type = EXT_COMMUNITY_TRANS_FOUR_AS;
> + } else {
> + /* more than one dot -> IP address */
> + type = EXT_COMMUNITY_TRANS_IPV4;
> + }
> +
> + switch (type) {
> + case EXT_COMMUNITY_TRANS_TWO_AS:
> + uval = strtonum(s, 0, USHRT_MAX, &errstr);
> + if (errstr)
> + errx(1, "Bad ext-community %s is %s", s, errstr);
> + *v = uval;
> + break;
> + case EXT_COMMUNITY_TRANS_FOUR_AS:
> + if ((p = strchr(s, '.')) == NULL) {
> + uval = strtonum(s, 0, UINT_MAX, &errstr);
> + if (errstr)
> + errx(1, "Bad ext-community %s is %s", s,
> + errstr);
> + *v = uval;
> + break;
> + }
> *p++ = '\0';
> uvalh = strtonum(s, 0, USHRT_MAX, &errstr);
> - if (errstr) {
> - fprintf(stderr, "Bad ext-community: %s is %s\n", s,
> - errstr);
> - return (-1);
> - }
> + if (errstr)
> + errx(1, "Bad ext-community %s is %s", s, errstr);
> uval = strtonum(p, 0, USHRT_MAX, &errstr);
> - if (errstr) {
> - fprintf(stderr, "Bad ext-community: %s is %s\n", p,
> - errstr);
> - return (-1);
> - }
> + if (errstr)
> + errx(1, "Bad ext-community %s is %s", p, errstr);
> *v = uval | (uvalh << 16);
> - return (EXT_COMMUNITY_TRANS_FOUR_AS);
> - } else {
> - /* more than one dot -> IP address */
> - if (inet_aton(s, &ip) == 0) {
> - fprintf(stderr, "Bad ext-community: %s not parseable\n",
> - s);
> - return (-1);
> - }
> + break;
> + case EXT_COMMUNITY_TRANS_IPV4:
> + if (inet_aton(s, &ip) == 0)
> + errx(1, "Bad ext-community %s not parseable", s);
> *v = ntohl(ip.s_addr);
> - return (EXT_COMMUNITY_TRANS_IPV4);
> + break;
> + default:
> + errx(1, "%s: unexpected type %d", __func__, type);
> }
> - return (-1);
> + return (type);
> }
>
> -u_int
> -parseextcommunity(const char *word, struct parse_result *r)
> +int
> +parseextcommunity(struct filter_community *c, const char *t, char *s)
> {
> - struct filter_set *fs;
> - const struct ext_comm_pairs *cp;
> - const char *errstr;
> - u_int64_t ullval;
> - u_int32_t uval;
> - char *p, *ep;
> - int type;
> + const struct ext_comm_pairs *cp;
> + const char *errstr;
> + u_int64_t ullval;
> + u_int32_t uval;
> + char *p, *ep;
> + int type, subtype;
>
> - type = r->community.c.e.type;
> + if (parsesubtype(t, &type, &subtype) == 0)
> + errx(1, "Bad ext-community unknown type");
>
> switch (type) {
> - case 0xff:
> - if ((p = strchr(word, ':')) == NULL) {
> - fprintf(stderr, "Bad ext-community: %s\n", word);
> - return (0);
> - }
> + case EXT_COMMUNITY_TRANS_TWO_AS:
> + case EXT_COMMUNITY_TRANS_FOUR_AS:
> + case EXT_COMMUNITY_TRANS_IPV4:
> + case -1:
> + if ((p = strchr(s, ':')) == NULL)
> + errx(1, "Bad ext-community %s", s);
> *p++ = '\0';
> - if ((type = parseextvalue(word, &uval)) == -1)
> - return (0);
> + type = parseextvalue(type, s, &uval);
> switch (type) {
> case EXT_COMMUNITY_TRANS_TWO_AS:
> ullval = strtonum(p, 0, UINT_MAX, &errstr);
> @@ -1220,118 +1314,46 @@ parseextcommunity(const char *word, stru
> ullval = strtonum(p, 0, USHRT_MAX, &errstr);
> break;
> default:
> - fprintf(stderr, "parseextcommunity: unexpected "
> - "result\n");
> - return (0);
> - }
> - if (errstr) {
> - fprintf(stderr, "Bad ext-community: %s is %s\n", p,
> - errstr);
> - return (0);
> - }
> - switch (type) {
> - case EXT_COMMUNITY_TRANS_TWO_AS:
> - r->community.c.e.data1 = uval;
> - r->community.c.e.data2 = ullval;
> - break;
> - case EXT_COMMUNITY_TRANS_IPV4:
> - case EXT_COMMUNITY_TRANS_FOUR_AS:
> - r->community.c.e.data1 = uval;
> - r->community.c.e.data2 = ullval;
> - break;
> + errx(1, "parseextcommunity: unexpected result");
> }
> + if (errstr)
> + errx(1, "Bad ext-community %s is %s", p, errstr);
> + c->c.e.data1 = uval;
> + c->c.e.data2 = ullval;
> break;
> case EXT_COMMUNITY_TRANS_OPAQUE:
> case EXT_COMMUNITY_TRANS_EVPN:
> errno = 0;
> - ullval = strtoull(word, &ep, 0);
> - if (word[0] == '\0' || *ep != '\0') {
> - fprintf(stderr, "Bad ext-community: bad value\n");
> - return (0);
> - }
> - if (errno == ERANGE && ullval > EXT_COMMUNITY_OPAQUE_MAX) {
> - fprintf(stderr, "Bad ext-community: too big\n");
> - return (0);
> - }
> - r->community.c.e.data2 = ullval;
> + ullval = strtoull(s, &ep, 0);
> + if (s[0] == '\0' || *ep != '\0')
> + errx(1, "Bad ext-community bad value");
> + if (errno == ERANGE && ullval > EXT_COMMUNITY_OPAQUE_MAX)
> + errx(1, "Bad ext-community value too big");
> + c->c.e.data2 = ullval;
> break;
> case EXT_COMMUNITY_NON_TRANS_OPAQUE:
> - if (strcmp(word, "valid") == 0)
> - r->community.c.e.data2 = EXT_COMMUNITY_OVS_VALID;
> - else if (strcmp(word, "invalid") == 0)
> - r->community.c.e.data2 = EXT_COMMUNITY_OVS_INVALID;
> - else if (strcmp(word, "not-found") == 0)
> - r->community.c.e.data2 = EXT_COMMUNITY_OVS_NOTFOUND;
> - else {
> - fprintf(stderr, "Bad ext-community value: %s\n", word);
> - return (0);
> - }
> + if (strcmp(s, "valid") == 0)
> + c->c.e.data2 = EXT_COMMUNITY_OVS_VALID;
> + else if (strcmp(s, "invalid") == 0)
> + c->c.e.data2 = EXT_COMMUNITY_OVS_INVALID;
> + else if (strcmp(s, "not-found") == 0)
> + c->c.e.data2 = EXT_COMMUNITY_OVS_NOTFOUND;
> + else
> + errx(1, "Bad ext-community %s", s);
> break;
> }
> - r->community.c.e.type = type;
> + c->c.e.type = type;
> + c->c.e.subtype = subtype;
>
> /* verify type/subtype combo */
> for (cp = iana_ext_comms; cp->subname != NULL; cp++) {
> - if (cp->type == r->community.c.e.type &&
> - cp->subtype == r->community.c.e.subtype) {
> - r->community.type = COMMUNITY_TYPE_EXT;;
> - if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
> - err(1, NULL);
> -
> - fs->type = ACTION_SET_COMMUNITY;
> - memcpy(&fs->action.community, &r->community,
> - sizeof(struct filter_community));
> -
> - TAILQ_INSERT_TAIL(&r->set, fs, entry);
> - return (1);
> + if (cp->type == type && cp->subtype == subtype) {
> + c->type = COMMUNITY_TYPE_EXT;
> + return (0);
> }
> }
>
> - fprintf(stderr, "Bad ext-community: bad format for type\n");
> - return (0);
> -}
> -
> -int
> -parse_largecommunity(const char *word, struct parse_result *r)
> -{
> - struct filter_set *fs;
> - char *p, *po = strdup(word);
> - char *array[3] = { NULL, NULL, NULL };
> - char *val;
> - u_int32_t as, ld1, ld2;
> - u_int8_t asflag, ld1flag, ld2flag;
> - int i = 0;
> -
> - p = po;
> - while ((p != NULL) && (i < 3)) {
> - val = strsep(&p, ":");
> - array[i++] = val;
> - }
> -
> - if ((p != NULL) || !(array[0] && array[1] && array[2]))
> - errx(1, "Invalid Large-Community syntax");
> -
> - 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_COMMUNITY;
> - fs->action.community.type = COMMUNITY_TYPE_LARGE;
> - fs->action.community.c.l.data1 = as;
> - fs->action.community.c.l.data2 = ld1;
> - fs->action.community.c.l.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);
> + errx(1, "Bad ext-community bad format for type");
> }
>
> int
> Index: usr.sbin/bgpctl/parser.h
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpctl/parser.h,v
> retrieving revision 1.34
> diff -u -p -r1.34 parser.h
> --- usr.sbin/bgpctl/parser.h 20 Jan 2019 23:30:15 -0000 1.34
> +++ usr.sbin/bgpctl/parser.h 7 Feb 2019 10:11:19 -0000
> @@ -67,6 +67,8 @@ struct parse_result {
> char rib[PEER_DESCR_LEN];
> char shutcomm[SHUT_COMM_LEN];
> char *irr_outdir;
> + const char *ext_comm_subtype;
> + u_int64_t rd;
> int flags;
> int is_group;
> u_int8_t validation_state;
> Index: usr.sbin/bgpd/bgpd.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/bgpd.c,v
> retrieving revision 1.207
> diff -u -p -r1.207 bgpd.c
> --- usr.sbin/bgpd/bgpd.c 20 Jan 2019 06:13:40 -0000 1.207
> +++ usr.sbin/bgpd/bgpd.c 7 Feb 2019 10:11:19 -0000
> @@ -173,7 +173,7 @@ main(int argc, char *argv[])
>
> if (cmd_opts & BGPD_OPT_VERBOSE)
> print_config(conf, &ribnames, &conf->networks, peer_l,
> - conf->filters, conf->mrt, &conf->rdomains);
> + conf->filters, conf->mrt, &conf->l3vpns);
> else
> fprintf(stderr, "configuration OK\n");
> exit(0);
> @@ -438,7 +438,7 @@ reconfigure(char *conffile, struct bgpd_
> struct filter_rule *r;
> struct listen_addr *la;
> struct rde_rib *rr;
> - struct rdomain *rd;
> + struct l3vpn *vpn;
> struct as_set *aset;
> struct prefixset *ps;
> struct prefixset_item *psi, *npsi;
> @@ -512,8 +512,7 @@ reconfigure(char *conffile, struct bgpd_
> }
>
> /* networks go via kroute to the RDE */
> - if (kr_net_reload(conf->default_tableid, &conf->networks))
> - return (-1);
> + kr_net_reload(conf->default_tableid, 0, &conf->networks);
>
> /* prefixsets for filters in the RDE */
> while ((ps = SIMPLEQ_FIRST(&conf->prefixsets)) != NULL) {
> @@ -627,43 +626,42 @@ reconfigure(char *conffile, struct bgpd_
> free(r);
> }
>
> - while ((rd = SIMPLEQ_FIRST(&conf->rdomains)) != NULL) {
> - SIMPLEQ_REMOVE_HEAD(&conf->rdomains, entry);
> - if (ktable_update(rd->rtableid, rd->descr, rd->flags,
> + while ((vpn = SIMPLEQ_FIRST(&conf->l3vpns)) != NULL) {
> + SIMPLEQ_REMOVE_HEAD(&conf->l3vpns, entry);
> + if (ktable_update(vpn->rtableid, vpn->descr, vpn->flags,
> conf->fib_priority) == -1) {
> log_warnx("failed to load rdomain %d",
> - rd->rtableid);
> + vpn->rtableid);
> return (-1);
> }
> /* networks go via kroute to the RDE */
> - if (kr_net_reload(rd->rtableid, &rd->net_l))
> - return (-1);
> + kr_net_reload(vpn->rtableid, vpn->rd, &vpn->net_l);
>
> - if (imsg_compose(ibuf_rde, IMSG_RECONF_RDOMAIN, 0, 0, -1,
> - rd, sizeof(*rd)) == -1)
> + if (imsg_compose(ibuf_rde, IMSG_RECONF_VPN, 0, 0, -1,
> + vpn, sizeof(*vpn)) == -1)
> return (-1);
>
> /* export targets */
> - if (imsg_compose(ibuf_rde, IMSG_RECONF_RDOMAIN_EXPORT, 0, 0,
> + if (imsg_compose(ibuf_rde, IMSG_RECONF_VPN_EXPORT, 0, 0,
> -1, NULL, 0) == -1)
> return (-1);
> - if (send_filterset(ibuf_rde, &rd->export) == -1)
> + if (send_filterset(ibuf_rde, &vpn->export) == -1)
> return (-1);
> - filterset_free(&rd->export);
> + filterset_free(&vpn->export);
>
> /* import targets */
> - if (imsg_compose(ibuf_rde, IMSG_RECONF_RDOMAIN_IMPORT, 0, 0,
> + if (imsg_compose(ibuf_rde, IMSG_RECONF_VPN_IMPORT, 0, 0,
> -1, NULL, 0) == -1)
> return (-1);
> - if (send_filterset(ibuf_rde, &rd->import) == -1)
> + if (send_filterset(ibuf_rde, &vpn->import) == -1)
> return (-1);
> - filterset_free(&rd->import);
> + filterset_free(&vpn->import);
>
> - if (imsg_compose(ibuf_rde, IMSG_RECONF_RDOMAIN_DONE, 0, 0,
> + if (imsg_compose(ibuf_rde, IMSG_RECONF_VPN_DONE, 0, 0,
> -1, NULL, 0) == -1)
> return (-1);
>
> - free(rd);
> + free(vpn);
> }
>
> /* send a drain message to know when all messages where processed */
> Index: usr.sbin/bgpd/bgpd.conf.5
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/bgpd.conf.5,v
> retrieving revision 1.183
> diff -u -p -r1.183 bgpd.conf.5
> --- usr.sbin/bgpd/bgpd.conf.5 4 Feb 2019 20:32:23 -0000 1.183
> +++ usr.sbin/bgpd/bgpd.conf.5 7 Feb 2019 10:11:19 -0000
> @@ -558,37 +558,56 @@ See also the
> section.
> .Sh MPLS VPN CONFIGURATION
> .Xr bgpd 8
> -supports the setup and distribution of Virtual Private Networks.
> -It is possible to import and export prefixes between routing domains.
> -Each routing domain is specified by an
> -.Ic rdomain
> -section, which allows properties to be set specifically for that rdomain:
> +supports the setup and distribution of MPLS Virtual Private Networks.
> +A router can be configured to participate in a VPN by specifying a
> +.Ic vpn
> +section with a description for the VPN and an
> +.Xr mpe 4
> +interface.
> +.Pp
> +The vpn configuraion section allows properties to be set specifically
> +for that VPN:
> .Bd -literal -offset indent
> -rdomain 1 {
> - descr "a rdomain"
> - rd 65002:1
> +vpn "description" on mpe1 {
> + rd 65002:1
> import-target rt 65002:42
> export-target rt 65002:42
> network 192.168.1/24
> - depend on mpe0
> }
> .Ed
> .Pp
> -There are several routing domain properties:
> -.Pp
> -.Bl -tag -width Ds -compact
> -.It Ic depend on Ar interface
> -Routes added to the rdomain will use this interface as the outgoing
> interface.
> -Normally this will be an MPLS Provider Edge,
> -.Xr mpe 4 ,
> -interface that is part of the rdomain.
> -Local networks will be announced with the MPLS label specified on the
> interface.
> -.Pp
> -.It Ic descr Ar description
> -Add a description.
> The description is used when logging but has no further meaning to
> .Xr bgpd 8 .
> .Pp
> +The
> +.Xr mpe 4
> +interface will be used as the outgoing interface for routes to
> +the VPN, and local networks will be annouced with the MPLS label
> +specified on the interface.
> +The interface can provide VPN connectivity for another rdomain by
> +being configured in that rdomain.
> +The required rdomain must be configured on the interface before
> +.Xr bgpd 8
> +uses it.
> +Multiple VPNs may be connected to a single rdomain, including the rdomain
> that
> +.Xr bgpd 8
> +is running in.
> +.Pp
> +An example
> +.Xr hostname.if 8
> +configuration for an
> +.Xr mpe 4
> +interface providing connectivity to rdomain 1:
> +.Bd -literal -offset indent
> +rdomain 1
> +mplslabel 2000
> +inet 192.198.0.1 255.255.255.255
> +up
> +.Ed
> +.Pp
> +There are several VPN properties:
> +.Pp
> +.Bl -tag -width Ds -compact
> .It Ic export-target Ar subtype Ar as-number : Ns Ar local
> .It Ic export-target Ar subtype Ar IP : Ns Ar local
> Specify an extended community which will be attached to announced networks.
> Index: usr.sbin/bgpd/bgpd.h
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v
> retrieving revision 1.364
> diff -u -p -r1.364 bgpd.h
> --- usr.sbin/bgpd/bgpd.h 4 Feb 2019 18:53:10 -0000 1.364
> +++ usr.sbin/bgpd/bgpd.h 7 Feb 2019 10:11:19 -0000
> @@ -81,7 +81,6 @@
> #define F_BLACKHOLE 0x0100
> #define F_LONGER 0x0200
> #define F_MPLS 0x0400
> -#define F_REDISTRIBUTED 0x0800
> #define F_CTL_DETAIL 0x1000 /* only used by bgpctl */
> #define F_CTL_ADJ_IN 0x2000
> #define F_CTL_ADJ_OUT 0x4000
> @@ -231,8 +230,8 @@ struct listen_addr {
> TAILQ_HEAD(listen_addrs, listen_addr);
> TAILQ_HEAD(filter_set_head, filter_set);
>
> -struct rdomain;
> -SIMPLEQ_HEAD(rdomain_head, rdomain);
> +struct l3vpn;
> +SIMPLEQ_HEAD(l3vpn_head, l3vpn);
>
> struct network;
> TAILQ_HEAD(network_head, network);
> @@ -267,7 +266,7 @@ struct filter_rule;
> TAILQ_HEAD(filter_head, filter_rule);
>
> struct bgpd_config {
> - struct rdomain_head rdomains;
> + struct l3vpn_head l3vpns;
> struct network_head networks;
> struct filter_head *filters;
> struct listen_addrs *listen_addrs;
> @@ -411,7 +410,7 @@ struct network_config {
> struct filter_set_head attrset;
> struct rde_aspath *asp;
> char psname[SET_NAME_LEN];
> - u_int rtableid;
> + u_int64_t rd;
> u_int16_t rtlabel;
> enum network_type type;
> u_int8_t prefixlen;
> @@ -467,10 +466,10 @@ enum imsg_type {
> IMSG_RECONF_FILTER,
> IMSG_RECONF_LISTENER,
> IMSG_RECONF_CTRL,
> - IMSG_RECONF_RDOMAIN,
> - IMSG_RECONF_RDOMAIN_EXPORT,
> - IMSG_RECONF_RDOMAIN_IMPORT,
> - IMSG_RECONF_RDOMAIN_DONE,
> + IMSG_RECONF_VPN,
> + IMSG_RECONF_VPN_EXPORT,
> + IMSG_RECONF_VPN_IMPORT,
> + IMSG_RECONF_VPN_DONE,
> IMSG_RECONF_PREFIX_SET,
> IMSG_RECONF_PREFIX_SET_ITEM,
> IMSG_RECONF_AS_SET,
> @@ -567,15 +566,18 @@ enum suberr_cease {
> struct kroute_node;
> struct kroute6_node;
> struct knexthop_node;
> +struct kredist_node;
> RB_HEAD(kroute_tree, kroute_node);
> RB_HEAD(kroute6_tree, kroute6_node);
> RB_HEAD(knexthop_tree, knexthop_node);
> +RB_HEAD(kredist_tree, kredist_node);
>
> struct ktable {
> char descr[PEER_DESCR_LEN];
> struct kroute_tree krt;
> struct kroute6_tree krt6;
> struct knexthop_tree knt;
> + struct kredist_tree kredist;
> struct network_head krn;
> u_int rtableid;
> u_int nhtableid; /* rdomain id for nexthop lookup */
> @@ -1024,8 +1026,8 @@ struct as_set {
> int dirty;
> };
>
> -struct rdomain {
> - SIMPLEQ_ENTRY(rdomain) entry;
> +struct l3vpn {
> + SIMPLEQ_ENTRY(l3vpn) entry;
> char descr[PEER_DESCR_LEN];
> char ifmpe[IFNAMSIZ];
> struct filter_set_head import;
> @@ -1176,7 +1178,7 @@ void kr_nexthop_delete(u_int32_t, stru
> struct bgpd_config *);
> void kr_show_route(struct imsg *);
> void kr_ifinfo(char *);
> -int kr_net_reload(u_int, struct network_head *);
> +void kr_net_reload(u_int, u_int64_t, struct network_head *);
> int kr_reload(void);
> struct in6_addr *prefixlen2mask6(u_int8_t prefixlen);
>
> Index: usr.sbin/bgpd/config.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/config.c,v
> retrieving revision 1.79
> diff -u -p -r1.79 config.c
> --- usr.sbin/bgpd/config.c 27 Dec 2018 20:23:24 -0000 1.79
> +++ usr.sbin/bgpd/config.c 7 Feb 2019 10:11:19 -0000
> @@ -39,7 +39,7 @@
> u_int32_t get_bgpid(void);
> int host_ip(const char *, struct bgpd_addr *, u_int8_t *);
> void free_networks(struct network_head *);
> -void free_rdomains(struct rdomain_head *);
> +void free_l3vpns(struct l3vpn_head *);
>
> struct bgpd_config *
> new_config(void)
> @@ -75,7 +75,7 @@ new_config(void)
>
> /* init the various list for later */
> TAILQ_INIT(&conf->networks);
> - SIMPLEQ_INIT(&conf->rdomains);
> + SIMPLEQ_INIT(&conf->l3vpns);
> SIMPLEQ_INIT(&conf->prefixsets);
> SIMPLEQ_INIT(&conf->originsets);
> RB_INIT(&conf->roa);
> @@ -101,16 +101,16 @@ free_networks(struct network_head *netwo
> }
>
> void
> -free_rdomains(struct rdomain_head *rdomains)
> +free_l3vpns(struct l3vpn_head *l3vpns)
> {
> - struct rdomain *rd;
> + struct l3vpn *vpn;
>
> - while ((rd = SIMPLEQ_FIRST(rdomains)) != NULL) {
> - SIMPLEQ_REMOVE_HEAD(rdomains, entry);
> - filterset_free(&rd->export);
> - filterset_free(&rd->import);
> - free_networks(&rd->net_l);
> - free(rd);
> + while ((vpn = SIMPLEQ_FIRST(l3vpns)) != NULL) {
> + SIMPLEQ_REMOVE_HEAD(l3vpns, entry);
> + filterset_free(&vpn->export);
> + filterset_free(&vpn->import);
> + free_networks(&vpn->net_l);
> + free(vpn);
> }
> }
>
> @@ -145,7 +145,7 @@ free_config(struct bgpd_config *conf)
> struct listen_addr *la;
> struct mrt *m;
>
> - free_rdomains(&conf->rdomains);
> + free_l3vpns(&conf->l3vpns);
> free_networks(&conf->networks);
> filterlist_free(conf->filters);
> free_prefixsets(&conf->prefixsets);
> @@ -250,9 +250,9 @@ merge_config(struct bgpd_config *xconf,
> TAILQ_INSERT_TAIL(&xconf->networks, n, entry);
> }
>
> - /* switch the rdomain configs, first remove the old ones */
> - free_rdomains(&xconf->rdomains);
> - SIMPLEQ_CONCAT(&xconf->rdomains, &conf->rdomains);
> + /* switch the l3vpn configs, first remove the old ones */
> + free_l3vpns(&xconf->l3vpns);
> + SIMPLEQ_CONCAT(&xconf->l3vpns, &conf->l3vpns);
>
> /*
> * merge new listeners:
> @@ -469,27 +469,42 @@ prepare_listeners(struct bgpd_config *co
> }
>
> int
> -get_mpe_label(struct rdomain *r)
> +get_mpe_config(const char *name, u_int *rdomain, u_int *label)
> {
> struct ifreq ifr;
> struct shim_hdr shim;
> int s;
>
> + *label = 0;
> + *rdomain = 0;
> +
> s = socket(AF_INET, SOCK_DGRAM, 0);
> if (s == -1)
> return (-1);
>
> bzero(&shim, sizeof(shim));
> bzero(&ifr, sizeof(ifr));
> - strlcpy(ifr.ifr_name, r->ifmpe, sizeof(ifr.ifr_name));
> + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
> ifr.ifr_data = (caddr_t)&shim;
>
> if (ioctl(s, SIOCGETLABEL, (caddr_t)&ifr) == -1) {
> close(s);
> return (-1);
> }
> +
> + ifr.ifr_data = NULL;
> + if (ioctl(s, SIOCGIFRDOMAIN, (caddr_t)&ifr) == -1) {
> + close(s);
> + return (-1);
> + }
> +
> close(s);
> - r->label = shim.shim_label;
> +
> + *rdomain = ifr.ifr_rdomainid;
> + *label = shim.shim_label;
> +
> + log_debug("%s: rdomain %u label %u", __func__, *rdomain, *label);
> +
> return (0);
> }
>
> Index: usr.sbin/bgpd/kroute.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/kroute.c,v
> retrieving revision 1.229
> diff -u -p -r1.229 kroute.c
> --- usr.sbin/bgpd/kroute.c 18 Jan 2019 23:30:45 -0000 1.229
> +++ usr.sbin/bgpd/kroute.c 7 Feb 2019 10:11:19 -0000
> @@ -65,6 +65,14 @@ struct knexthop_node {
> void *kroute;
> };
>
> +struct kredist_node {
> + RB_ENTRY(kredist_node) entry;
> + struct bgpd_addr prefix;
> + u_int64_t rd;
> + u_int8_t prefixlen;
> + u_int8_t dynamic;
> +};
> +
> struct kif_kr {
> LIST_ENTRY(kif_kr) entry;
> struct kroute_node *kr;
> @@ -99,16 +107,16 @@ int kr6_delete(struct ktable *, struct k
> int krVPN4_delete(struct ktable *, struct kroute_full *, u_int8_t);
> int krVPN6_delete(struct ktable *, struct kroute_full *, u_int8_t);
> void kr_net_delete(struct network *);
> -struct network *kr_net_match(struct ktable *, struct kroute *);
> -struct network *kr_net_match6(struct ktable *, struct kroute6 *);
> +int kr_net_match(struct ktable *, struct network_config *, u_int16_t);
> struct network *kr_net_find(struct ktable *, struct network *);
> -int kr_redistribute(int, struct ktable *, struct kroute *);
> -int kr_redistribute6(int, struct ktable *, struct kroute6 *);
> +void kr_redistribute(int, struct ktable *, struct kroute *);
> +void kr_redistribute6(int, struct ktable *, struct kroute6 *);
> struct kroute_full *kr_tofull(struct kroute *);
> struct kroute_full *kr6_tofull(struct kroute6 *);
> int kroute_compare(struct kroute_node *, struct kroute_node *);
> int kroute6_compare(struct kroute6_node *, struct kroute6_node *);
> int knexthop_compare(struct knexthop_node *, struct knexthop_node *);
> +int kredist_compare(struct kredist_node *, struct kredist_node *);
> int kif_compare(struct kif_node *, struct kif_node *);
> void kr_fib_update_prio(u_int, u_int8_t);
>
> @@ -185,6 +193,9 @@ RB_GENERATE(kroute6_tree, kroute6_node,
> RB_PROTOTYPE(knexthop_tree, knexthop_node, entry, knexthop_compare)
> RB_GENERATE(knexthop_tree, knexthop_node, entry, knexthop_compare)
>
> +RB_PROTOTYPE(kredist_tree, kredist_node, entry, kredist_compare)
> +RB_GENERATE(kredist_tree, kredist_node, entry, kredist_compare)
> +
> RB_HEAD(kif_tree, kif_node) kit;
> RB_PROTOTYPE(kif_tree, kif_node, entry, kif_compare)
> RB_GENERATE(kif_tree, kif_node, entry, kif_compare)
> @@ -399,35 +410,6 @@ ktable_update(u_int rtableid, char *name
> return (0);
> }
>
> -void
> -ktable_preload(void)
> -{
> - struct ktable *kt;
> - u_int i;
> -
> - for (i = 0; i < krt_size; i++) {
> - if ((kt = ktable_get(i)) == NULL)
> - continue;
> - kt->state = RECONF_DELETE;
> - }
> -}
> -
> -void
> -ktable_postload(u_int8_t fib_prio)
> -{
> - struct ktable *kt;
> - u_int i;
> -
> - for (i = krt_size; i > 0; i--) {
> - if ((kt = ktable_get(i - 1)) == NULL)
> - continue;
> - if (kt->state == RECONF_DELETE)
> - ktable_free(i - 1, fib_prio);
> - else if (kt->state == RECONF_REINIT)
> - kt->fib_sync = kt->fib_conf;
> - }
> -}
> -
> int
> ktable_exists(u_int rtableid, u_int *rdomid)
> {
> @@ -1199,93 +1181,105 @@ kr_net_delete(struct network *n)
> free(n);
> }
>
> -struct network *
> -kr_net_match(struct ktable *kt, struct kroute *kr)
> +static int
> +kr_net_redist_add(struct ktable *kt, struct network_config *net,
> + struct filter_set_head *attr, int dynamic)
> +{
> + struct kredist_node *r, *xr;
> +
> + if ((r = calloc(1, sizeof(*r))) == NULL)
> + fatal("%s", __func__);
> + r->prefix = net->prefix;
> + r->prefixlen = net->prefixlen;
> + r->rd = net->rd;
> + r->dynamic = dynamic;
> +
> + xr = RB_INSERT(kredist_tree, &kt->kredist, r);
> + if (xr != NULL && dynamic != xr->dynamic) {
> + if (dynamic) {
> + /*
> + * ignore update, a non-dynamic announcement
> + * is already present.
> + */
> + free(r);
> + return 0;
> + }
> + /* non-dynamic announcments are preferred */
> + xr->dynamic = dynamic;
> + }
> +
> + if (send_network(IMSG_NETWORK_ADD, net, attr) == -1)
> + log_warnx("%s: faild to send network update", __func__);
> + return 1;
> +}
> +
> +static void
> +kr_net_redist_del(struct ktable *kt, struct network_config *net, int dynamic)
> {
> - struct network *xn;
> + struct kredist_node *r, node;
>
> - TAILQ_FOREACH(xn, &kt->krn, entry) {
> - if (xn->net.prefix.aid != AID_INET)
> - continue;
> - switch (xn->net.type) {
> - case NETWORK_DEFAULT:
> - if (xn->net.prefixlen == kr->prefixlen &&
> - xn->net.prefix.v4.s_addr == kr->prefix.s_addr)
> - /* static match already redistributed */
> - return (NULL);
> - break;
> - case NETWORK_STATIC:
> - if (kr->flags & F_STATIC)
> - return (xn);
> - break;
> - case NETWORK_CONNECTED:
> - if (kr->flags & F_CONNECTED)
> - return (xn);
> - break;
> - case NETWORK_RTLABEL:
> - if (kr->labelid == xn->net.rtlabel)
> - return (xn);
> - break;
> - case NETWORK_MRTCLONE:
> - /* can not happen */
> - break;
> - case NETWORK_PRIORITY:
> - if (kr->priority == xn->net.priority)
> - return (xn);
> - break;
> - case NETWORK_PREFIXSET:
> - /* must not happen */
> - log_warnx("%s: found a NETWORK_PREFIXSET, "
> - "please send a bug report", __func__);
> - break;
> - }
> + bzero(&node, sizeof(node));
> + node.prefix = net->prefix;
> + node.prefixlen = net->prefixlen;
> + node.rd = net->rd;
> +
> + r = RB_FIND(kredist_tree, &kt->kredist, &node);
> + if (r == NULL || dynamic != r->dynamic)
> + return;
> +
> + if (RB_REMOVE(kredist_tree, &kt->kredist, r) == NULL) {
> + log_warnx("%s: failed to remove network %s/%u", __func__,
> + log_addr(&node.prefix), node.prefixlen);
> + return;
> }
> - return (NULL);
> + free(r);
> +
> + if (send_network(IMSG_NETWORK_REMOVE, net, NULL) == -1)
> + log_warnx("%s: faild to send network update", __func__);
> }
>
> -struct network *
> -kr_net_match6(struct ktable *kt, struct kroute6 *kr6)
> +int
> +kr_net_match(struct ktable *kt, struct network_config *net, u_int16_t flags)
> {
> struct network *xn;
> + int matched = 0;
>
> TAILQ_FOREACH(xn, &kt->krn, entry) {
> - if (xn->net.prefix.aid != AID_INET6)
> + if (xn->net.prefix.aid != net->prefix.aid)
> continue;
> switch (xn->net.type) {
> case NETWORK_DEFAULT:
> - if (xn->net.prefixlen == kr6->prefixlen &&
> - memcmp(&xn->net.prefix.v6, &kr6->prefix,
> - sizeof(struct in6_addr)) == 0)
> - /* static match already redistributed */
> - return (NULL);
> - break;
> + /* static match already redistributed */
> + continue;
> case NETWORK_STATIC:
> - if (kr6->flags & F_STATIC)
> - return (xn);
> - break;
> + if (flags & F_STATIC)
> + break;
> + continue;
> case NETWORK_CONNECTED:
> - if (kr6->flags & F_CONNECTED)
> - return (xn);
> - break;
> + if (flags & F_CONNECTED)
> + break;
> + continue;
> case NETWORK_RTLABEL:
> - if (kr6->labelid == xn->net.rtlabel)
> - return (xn);
> - break;
> - case NETWORK_MRTCLONE:
> - /* can not happen */
> - break;
> + if (net->rtlabel == xn->net.rtlabel)
> + break;
> + continue;
> case NETWORK_PRIORITY:
> - if (kr6->priority == xn->net.priority)
> - return (xn);
> - break;
> + if (net->priority == xn->net.priority)
> + break;
> + continue;
> + case NETWORK_MRTCLONE:
> case NETWORK_PREFIXSET:
> /* must not happen */
> log_warnx("%s: found a NETWORK_PREFIXSET, "
> "please send a bug report", __func__);
> - break;
> + continue;
> }
> +
> + net->rd = xn->net.rd;
> + if (kr_net_redist_add(kt, net, &xn->net.attrset, 1))
> + matched = 1;
> }
> - return (NULL);
> + return matched;
> }
>
> struct network *
> @@ -1296,7 +1290,7 @@ kr_net_find(struct ktable *kt, struct ne
> TAILQ_FOREACH(xn, &kt->krn, entry) {
> if (n->net.type != xn->net.type ||
> n->net.prefixlen != xn->net.prefixlen ||
> - n->net.rtableid != xn->net.rtableid)
> + n->net.rd != xn->net.rd)
> continue;
> if (memcmp(&n->net.prefix, &xn->net.prefix,
> sizeof(n->net.prefix)) == 0)
> @@ -1305,26 +1299,21 @@ kr_net_find(struct ktable *kt, struct ne
> return (NULL);
> }
>
> -int
> -kr_net_reload(u_int rtableid, struct network_head *nh)
> +void
> +kr_net_reload(u_int rtableid, u_int64_t rd, struct network_head *nh)
> {
> struct network *n, *xn;
> struct ktable *kt;
>
> - if ((kt = ktable_get(rtableid)) == NULL) {
> - log_warnx("%s: non-existent rtableid %d", __func__, rtableid);
> - return (-1);
> - }
> -
> - TAILQ_FOREACH(n, &kt->krn, entry)
> - n->net.old = 1;
> + if ((kt = ktable_get(rtableid)) == NULL)
> + fatalx("%s: non-existent rtableid %d", __func__, rtableid);
>
> while ((n = TAILQ_FIRST(nh)) != NULL) {
> log_debug("%s: processing %s/%u", __func__,
> log_addr(&n->net.prefix), n->net.prefixlen);
> TAILQ_REMOVE(nh, n, entry);
> n->net.old = 0;
> - n->net.rtableid = rtableid;
> + n->net.rd = rd;
> xn = kr_net_find(kt, n);
> if (xn) {
> xn->net.old = 0;
> @@ -1334,44 +1323,33 @@ kr_net_reload(u_int rtableid, struct net
> } else
> TAILQ_INSERT_TAIL(&kt->krn, n, entry);
> }
> -
> - for (n = TAILQ_FIRST(&kt->krn); n != NULL; n = xn) {
> - xn = TAILQ_NEXT(n, entry);
> - if (n->net.old) {
> - if (n->net.type == NETWORK_DEFAULT)
> - if (send_network(IMSG_NETWORK_REMOVE, &n->net,
> - NULL))
> - return (-1);
> - TAILQ_REMOVE(&kt->krn, n, entry);
> - kr_net_delete(n);
> - }
> - }
> -
> - return (0);
> }
>
> -int
> +void
> kr_redistribute(int type, struct ktable *kt, struct kroute *kr)
> {
> - struct network *match;
> struct network_config net;
> u_int32_t a;
>
> + bzero(&net, sizeof(net));
> + net.prefix.aid = AID_INET;
> + net.prefix.v4.s_addr = kr->prefix.s_addr;
> + net.prefixlen = kr->prefixlen;
> + net.rtlabel = kr->labelid;
> + net.priority = kr->priority;
> +
> /* shortcut for removals */
> if (type == IMSG_NETWORK_REMOVE) {
> - if (!(kr->flags & F_REDISTRIBUTED))
> - return (0); /* no match, don't redistribute */
> - kr->flags &= ~F_REDISTRIBUTED;
> - match = NULL;
> - goto sendit;
> + kr_net_redist_del(kt, &net, 1);
> + return;
> }
>
> if (!(kr->flags & F_KERNEL))
> - return (0);
> + return;
>
> /* Dynamic routes are not redistributable. */
> if (kr->flags & F_DYNAMIC)
> - return (0);
> + return;
>
> /*
> * We consider the loopback net, multicast and experimental addresses
> @@ -1380,61 +1358,48 @@ kr_redistribute(int type, struct ktable
> a = ntohl(kr->prefix.s_addr);
> if (IN_MULTICAST(a) || IN_BADCLASS(a) ||
> (a >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)
> - return (0);
> + return;
>
> /* Consider networks with nexthop loopback as not redistributable. */
> if (kr->nexthop.s_addr == htonl(INADDR_LOOPBACK))
> - return (0);
> + return;
>
> /*
> * never allow 0.0.0.0/0 the default route can only be redistributed
> * with announce default.
> */
> if (kr->prefix.s_addr == INADDR_ANY && kr->prefixlen == 0)
> - return (0);
> -
> - match = kr_net_match(kt, kr);
> - if (match == NULL) {
> - if (!(kr->flags & F_REDISTRIBUTED))
> - return (0); /* no match, don't redistribute */
> - /* route no longer matches but is redistributed, so remove */
> - kr->flags &= ~F_REDISTRIBUTED;
> - type = IMSG_NETWORK_REMOVE;
> - } else
> - kr->flags |= F_REDISTRIBUTED;
> -
> -sendit:
> - bzero(&net, sizeof(net));
> - net.prefix.aid = AID_INET;
> - net.prefix.v4.s_addr = kr->prefix.s_addr;
> - net.prefixlen = kr->prefixlen;
> - net.rtlabel = kr->labelid;
> - net.rtableid = kt->rtableid;
> + return;
>
> - return (send_network(type, &net, match ? &match->net.attrset : NULL));
> + if (kr_net_match(kt, &net, kr->flags) == 0)
> + /* no longer matches, if still present remove it */
> + kr_net_redist_del(kt, &net, 1);
> }
>
> -int
> +void
> kr_redistribute6(int type, struct ktable *kt, struct kroute6 *kr6)
> {
> - struct network *match;
> struct network_config net;
>
> + bzero(&net, sizeof(net));
> + net.prefix.aid = AID_INET6;
> + memcpy(&net.prefix.v6, &kr6->prefix, sizeof(struct in6_addr));
> + net.prefixlen = kr6->prefixlen;
> + net.rtlabel = kr6->labelid;
> + net.priority = kr6->priority;
> +
> /* shortcut for removals */
> if (type == IMSG_NETWORK_REMOVE) {
> - if (!(kr6->flags & F_REDISTRIBUTED))
> - return (0); /* no match, don't redistribute */
> - kr6->flags &= ~F_REDISTRIBUTED;
> - match = NULL;
> - goto sendit;
> + kr_net_redist_del(kt, &net, 1);
> + return;
> }
>
> if (!(kr6->flags & F_KERNEL))
> - return (0);
> + return;
>
> /* Dynamic routes are not redistributable. */
> if (kr6->flags & F_DYNAMIC)
> - return (0);
> + return;
>
> /*
> * We consider unspecified, loopback, multicast, link- and site-local,
> @@ -1447,13 +1412,13 @@ kr_redistribute6(int type, struct ktable
> IN6_IS_ADDR_SITELOCAL(&kr6->prefix) ||
> IN6_IS_ADDR_V4MAPPED(&kr6->prefix) ||
> IN6_IS_ADDR_V4COMPAT(&kr6->prefix))
> - return (0);
> + return;
>
> /*
> * Consider networks with nexthop loopback as not redistributable.
> */
> if (IN6_IS_ADDR_LOOPBACK(&kr6->nexthop))
> - return (0);
> + return;
>
> /*
> * never allow ::/0 the default route can only be redistributed
> @@ -1461,26 +1426,57 @@ kr_redistribute6(int type, struct ktable
> */
> if (kr6->prefixlen == 0 &&
> memcmp(&kr6->prefix, &in6addr_any, sizeof(struct in6_addr)) == 0)
> - return (0);
> + return;
>
> - match = kr_net_match6(kt, kr6);
> - if (match == NULL) {
> - if (!(kr6->flags & F_REDISTRIBUTED))
> - return (0); /* no match, don't redistribute */
> - /* route no longer matches but is redistributed, so remove */
> - kr6->flags &= ~F_REDISTRIBUTED;
> - type = IMSG_NETWORK_REMOVE;
> - } else
> - kr6->flags |= F_REDISTRIBUTED;
> -sendit:
> - bzero(&net, sizeof(net));
> - net.prefix.aid = AID_INET6;
> - memcpy(&net.prefix.v6, &kr6->prefix, sizeof(struct in6_addr));
> - net.prefixlen = kr6->prefixlen;
> - net.rtlabel = kr6->labelid;
> - net.rtableid = kt->rtableid;
> + if (kr_net_match(kt, &net, kr6->flags) == 0)
> + /* no longer matches, if still present remove it */
> + kr_net_redist_del(kt, &net, 1);
> +}
> +
> +void
> +ktable_preload(void)
> +{
> + struct ktable *kt;
> + struct network *n;
> + u_int i;
> +
> + for (i = 0; i < krt_size; i++) {
> + if ((kt = ktable_get(i)) == NULL)
> + continue;
> + kt->state = RECONF_DELETE;
> +
> + /* mark all networks as old */
> + TAILQ_FOREACH(n, &kt->krn, entry)
> + n->net.old = 1;
> + }
> +}
> +
> +void
> +ktable_postload(u_int8_t fib_prio)
> +{
> + struct ktable *kt;
> + struct network *n, *xn;
> + u_int i;
> +
> + for (i = krt_size; i > 0; i--) {
> + if ((kt = ktable_get(i - 1)) == NULL)
> + continue;
> + if (kt->state == RECONF_DELETE) {
> + ktable_free(i - 1, fib_prio);
> + continue;
> + } else if (kt->state == RECONF_REINIT)
> + kt->fib_sync = kt->fib_conf;
>
> - return (send_network(type, &net, match ? &match->net.attrset : NULL));
> + /* cleanup old networks */
> + TAILQ_FOREACH_SAFE(n, &kt->krn, entry, xn) {
> + if (n->net.old) {
> + TAILQ_REMOVE(&kt->krn, n, entry);
> + if (n->net.type == NETWORK_DEFAULT)
> + kr_net_redist_del(kt, &n->net, 0);
> + kr_net_delete(n);
> + }
> + }
> + }
> }
>
> int
> @@ -1503,9 +1499,8 @@ kr_reload(void)
>
> TAILQ_FOREACH(n, &kt->krn, entry)
> if (n->net.type == NETWORK_DEFAULT) {
> - if (send_network(IMSG_NETWORK_ADD, &n->net,
> - &n->net.attrset))
> - return (-1);
> + kr_net_redist_add(kt, &n->net,
> + &n->net.attrset, 0);
> } else
> hasdyn = 1;
>
> @@ -1640,13 +1635,53 @@ knexthop_compare(struct knexthop_node *a
> }
> break;
> default:
> - fatalx("knexthop_compare: unknown AF");
> + fatalx("%s: unknown AF", __func__);
> }
>
> return (0);
> }
>
> int
> +kredist_compare(struct kredist_node *a, struct kredist_node *b)
> +{
> + int i;
> +
> + if (a->prefix.aid != b->prefix.aid)
> + return (b->prefix.aid - a->prefix.aid);
> +
> + if (a->prefixlen < b->prefixlen)
> + return (-1);
> + if (a->prefixlen > b->prefixlen)
> + return (1);
> +
> + switch (a->prefix.aid) {
> + case AID_INET:
> + if (ntohl(a->prefix.v4.s_addr) < ntohl(b->prefix.v4.s_addr))
> + return (-1);
> + if (ntohl(a->prefix.v4.s_addr) > ntohl(b->prefix.v4.s_addr))
> + return (1);
> + break;
> + case AID_INET6:
> + for (i = 0; i < 16; i++) {
> + if (a->prefix.v6.s6_addr[i] < b->prefix.v6.s6_addr[i])
> + return (-1);
> + if (a->prefix.v6.s6_addr[i] > b->prefix.v6.s6_addr[i])
> + return (1);
> + }
> + break;
> + default:
> + fatalx("%s: unknown AF", __func__);
> + }
> +
> + if (a->rd < b->rd)
> + return (-1);
> + if (a->rd > b->rd)
> + return (1);
> +
> + return (0);
> +}
> +
> +int
> kif_compare(struct kif_node *a, struct kif_node *b)
> {
> return (b->k.ifindex - a->k.ifindex);
> @@ -3506,21 +3541,14 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
> changed = 1;
> kr->r.flags = flags;
>
> - if (rtlabel_changed) {
> - if (oflags & F_REDISTRIBUTED) {
> - kr->r.flags |= F_REDISTRIBUTED;
> - kr_redistribute(
> - IMSG_NETWORK_REMOVE, kt,
> - &kr->r);
> - }
> + if (rtlabel_changed)
> kr_redistribute(IMSG_NETWORK_ADD,
> kt, &kr->r);
> - }
>
> if ((oflags & F_CONNECTED) &&
> !(flags & F_CONNECTED)) {
> kif_kr_remove(kr);
> - kr_redistribute(IMSG_NETWORK_REMOVE,
> + kr_redistribute(IMSG_NETWORK_ADD,
> kt, &kr->r);
> }
> if ((flags & F_CONNECTED) &&
> @@ -3619,21 +3647,14 @@ add4:
> changed = 1;
> kr6->r.flags = flags;
>
> - if (rtlabel_changed) {
> - if (oflags & F_REDISTRIBUTED) {
> - kr6->r.flags |= F_REDISTRIBUTED;
> - kr_redistribute6(
> - IMSG_NETWORK_REMOVE, kt,
> - &kr6->r);
> - }
> + if (rtlabel_changed)
> kr_redistribute6(IMSG_NETWORK_ADD,
> kt, &kr6->r);
> - }
>
> if ((oflags & F_CONNECTED) &&
> !(flags & F_CONNECTED)) {
> kif_kr6_remove(kr6);
> - kr_redistribute6(IMSG_NETWORK_REMOVE,
> + kr_redistribute6(IMSG_NETWORK_ADD,
> kt, &kr6->r);
> }
> if ((flags & F_CONNECTED) &&
> Index: usr.sbin/bgpd/parse.y
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/parse.y,v
> retrieving revision 1.369
> diff -u -p -r1.369 parse.y
> --- usr.sbin/bgpd/parse.y 4 Feb 2019 18:53:10 -0000 1.369
> +++ usr.sbin/bgpd/parse.y 7 Feb 2019 10:11:19 -0000
> @@ -91,7 +91,7 @@ static struct network_head *netconf;
> static struct peer *peer_l, *peer_l_old;
> static struct peer *curpeer;
> static struct peer *curgroup;
> -static struct rdomain *currdom;
> +static struct l3vpn *curvpn;
> static struct prefixset *curpset, *curoset;
> static struct prefixset_tree *curpsitree;
> static struct filter_head *filter_l;
> @@ -154,7 +154,6 @@ void optimize_filters(struct filter_he
> struct filter_rule *get_rule(enum action_types);
>
> int parsecommunity(struct filter_community *, int, char *);
> -int parsesubtype(char *, int *, int *);
> int parseextcommunity(struct filter_community *, char *,
> char *);
> static int new_as_set(char *);
> @@ -195,7 +194,7 @@ typedef struct {
> %}
>
> %token AS ROUTERID HOLDTIME YMIN LISTEN ON FIBUPDATE FIBPRIORITY RTABLE
> -%token RDOMAIN RD EXPORT EXPORTTRGT IMPORTTRGT
> +%token NONE UNICAST VPN RD EXPORT EXPORTTRGT IMPORTTRGT
> %token RDE RIB EVALUATE IGNORE COMPARE
> %token GROUP NEIGHBOR NETWORK
> %token EBGP IBGP
> @@ -223,7 +222,7 @@ typedef struct {
> %token <v.string> STRING
> %token <v.number> NUMBER
> %type <v.number> asnumber as4number as4number_any
> optnumber
> -%type <v.number> espah family restart origincode nettype
> +%type <v.number> espah family safi restart origincode
> nettype
> %type <v.number> yesno inout restricted validity
> %type <v.string> string
> %type <v.addr> address
> @@ -253,7 +252,7 @@ grammar : /* empty */
> | grammar roa_set '\n'
> | grammar origin_set '\n'
> | grammar conf_main '\n'
> - | grammar rdomain '\n'
> + | grammar l3vpn '\n'
> | grammar neighbor '\n'
> | grammar group '\n'
> | grammar filterrule '\n'
> @@ -1044,39 +1043,57 @@ optnumber : /* empty */ { $$ = 0; }
> | NUMBER
> ;
>
> -rdomain : RDOMAIN NUMBER {
> - if ($2 > RT_TABLEID_MAX) {
> - yyerror("rtable %llu too big: max %u", $2,
> - RT_TABLEID_MAX);
> - YYERROR;
> +l3vpn : VPN STRING ON STRING {
> + u_int rdomain, label;
> +
> + if (get_mpe_config($4, &rdomain, &label) == -1) {
> + yyerror("troubles getting config of %s", $4);
> + if ((cmd_opts & BGPD_OPT_NOACTION) == 0) {
> + free($4);
> + free($2);
> + YYERROR;
> + }
> }
> - if ((cmd_opts & BGPD_OPT_NOACTION) == 0 &&
> - ktable_exists($2, NULL) != 1) {
> - yyerror("rdomain %lld does not exist", $2);
> +
> + if (!(curvpn = calloc(1, sizeof(struct l3vpn))))
> + fatal(NULL);
> + strlcpy(curvpn->ifmpe, $4, IFNAMSIZ);
> +
> + if (strlcpy(curvpn->descr, $2,
> + sizeof(curvpn->descr)) >=
> + sizeof(curvpn->descr)) {
> + yyerror("descr \"%s\" too long: max %zu",
> + $2, sizeof(curvpn->descr) - 1);
> + free($2);
> + free($4);
> + free(curvpn);
> + curvpn = NULL;
> YYERROR;
> }
> - if (!(currdom = calloc(1, sizeof(struct rdomain))))
> - fatal(NULL);
> - currdom->rtableid = $2;
> - TAILQ_INIT(&currdom->import);
> - TAILQ_INIT(&currdom->export);
> - TAILQ_INIT(&currdom->net_l);
> - netconf = &currdom->net_l;
> - } '{' rdomainopts_l '}' {
> + free($2);
> + free($4);
> +
> + TAILQ_INIT(&curvpn->import);
> + TAILQ_INIT(&curvpn->export);
> + TAILQ_INIT(&curvpn->net_l);
> + curvpn->label = label;
> + curvpn->rtableid = rdomain;
> + netconf = &curvpn->net_l;
> + } '{' l3vpnopts_l '}' {
> /* insert into list */
> - SIMPLEQ_INSERT_TAIL(&conf->rdomains, currdom, entry);
> - currdom = NULL;
> + SIMPLEQ_INSERT_TAIL(&conf->l3vpns, curvpn, entry);
> + curvpn = NULL;
> netconf = &conf->networks;
> }
> ;
>
> -rdomainopts_l : /* empty */
> - | rdomainopts_l '\n'
> - | rdomainopts_l rdomainopts '\n'
> - | rdomainopts_l error '\n'
> +l3vpnopts_l : /* empty */
> + | l3vpnopts_l '\n'
> + | l3vpnopts_l l3vpnopts '\n'
> + | l3vpnopts_l error '\n'
> ;
>
> -rdomainopts : RD STRING {
> +l3vpnopts : RD STRING {
> struct filter_community ext;
> u_int64_t rd;
>
> @@ -1108,7 +1125,7 @@ rdomainopts : RD STRING {
> yyerror("bad encoding of rd");
> YYERROR;
> }
> - currdom->rd = htobe64(rd);
> + curvpn->rd = htobe64(rd);
> }
> | EXPORTTRGT STRING STRING {
> struct filter_set *set;
> @@ -1126,7 +1143,7 @@ rdomainopts : RD STRING {
> }
> free($3);
> free($2);
> - TAILQ_INSERT_TAIL(&currdom->export, set, entry);
> + TAILQ_INSERT_TAIL(&curvpn->export, set, entry);
> }
> | IMPORTTRGT STRING STRING {
> struct filter_set *set;
> @@ -1144,44 +1161,15 @@ rdomainopts : RD STRING {
> }
> free($3);
> free($2);
> - TAILQ_INSERT_TAIL(&currdom->import, set, entry);
> - }
> - | DESCR string {
> - if (strlcpy(currdom->descr, $2,
> - sizeof(currdom->descr)) >=
> - sizeof(currdom->descr)) {
> - yyerror("descr \"%s\" too long: max %zu",
> - $2, sizeof(currdom->descr) - 1);
> - free($2);
> - YYERROR;
> - }
> - free($2);
> + TAILQ_INSERT_TAIL(&curvpn->import, set, entry);
> }
> | FIBUPDATE yesno {
> if ($2 == 0)
> - currdom->flags |= F_RIB_NOFIBSYNC;
> + curvpn->flags |= F_RIB_NOFIBSYNC;
> else
> - currdom->flags &= ~F_RIB_NOFIBSYNC;
> + curvpn->flags &= ~F_RIB_NOFIBSYNC;
> }
> | network
> - | DEPEND ON STRING {
> - /* XXX this is a hack */
> - if ((cmd_opts & BGPD_OPT_NOACTION) == 0 &&
> - if_nametoindex($3) == 0) {
> - yyerror("interface %s does not exist", $3);
> - free($3);
> - YYERROR;
> - }
> - strlcpy(currdom->ifmpe, $3, IFNAMSIZ);
> - free($3);
> - /* XXX this is in the wrong place */
> - if ((cmd_opts & BGPD_OPT_NOACTION) == 0 &&
> - get_mpe_label(currdom)) {
> - yyerror("failed to get mpls label from %s",
> - currdom->ifmpe);
> - YYERROR;
> - }
> - }
> ;
>
> neighbor : { curpeer = new_peer(); }
> @@ -1350,30 +1338,23 @@ peeropts : REMOTEAS as4number {
> }
> curpeer->conf.min_holdtime = $3;
> }
> - | ANNOUNCE family STRING {
> + | ANNOUNCE family safi {
> u_int8_t aid, safi;
> - int8_t val = 1;
> + u_int16_t afi;
>
> - if (!strcmp($3, "none")) {
> - safi = SAFI_UNICAST;
> - val = 0;
> - } else if (!strcmp($3, "unicast")) {
> - safi = SAFI_UNICAST;
> - } else if (!strcmp($3, "vpn")) {
> - safi = SAFI_MPLSVPN;
> + if ($3 == SAFI_NONE) {
> + for (aid = 0; aid < AID_MAX; aid++) {
> + if (aid2afi(aid, &afi, &safi) == -1)
> + continue;
> + curpeer->conf.capabilities.mp[aid] = 0;
> + }
> } else {
> - yyerror("unknown/unsupported SAFI \"%s\"",
> - $3);
> - free($3);
> - YYERROR;
> - }
> - free($3);
> -
> - if (afi2aid($2, safi, &aid) == -1) {
> - yyerror("unknown AFI/SAFI pair");
> - YYERROR;
> + if (afi2aid($2, $3, &aid) == -1) {
> + yyerror("unknown AFI/SAFI pair");
> + YYERROR;
> + }
> + curpeer->conf.capabilities.mp[aid] = 1;
> }
> - curpeer->conf.capabilities.mp[aid] = val;
> }
> | ANNOUNCE CAPABILITIES yesno {
> curpeer->conf.announce_capa = $3;
> @@ -1717,6 +1698,11 @@ family : IPV4 { $$ = AFI_IPv4; }
> | IPV6 { $$ = AFI_IPv6; }
> ;
>
> +safi : NONE { $$ = SAFI_NONE; }
> + | UNICAST { $$ = SAFI_UNICAST; }
> + | VPN { $$ = SAFI_MPLSVPN; }
> + ;
> +
> nettype : STATIC { $$ = 1; },
> | CONNECTED { $$ = 0; }
> ;
> @@ -2880,6 +2866,7 @@ lookup(char *s)
> { "network", NETWORK},
> { "nexthop", NEXTHOP},
> { "no-modify", NOMODIFY},
> + { "none", NONE},
> { "on", ON},
> { "or-longer", LONGER},
> { "origin", ORIGIN},
> @@ -2900,7 +2887,6 @@ lookup(char *s)
> { "quick", QUICK},
> { "rd", RD},
> { "rde", RDE},
> - { "rdomain", RDOMAIN},
> { "refresh", REFRESH },
> { "reject", REJECT},
> { "remote-as", REMOTEAS},
> @@ -2924,7 +2910,9 @@ lookup(char *s)
> { "transit-as", TRANSITAS},
> { "transparent-as", TRANSPARENT},
> { "ttl-security", TTLSECURITY},
> + { "unicast", UNICAST},
> { "via", VIA},
> + { "vpn", VPN},
> { "weight", WEIGHT}
> };
> const struct keywords *p;
> @@ -3586,7 +3574,7 @@ parsecommunity(struct filter_community *
> return (0);
> }
>
> -int
> +static int
> parsesubtype(char *name, int *type, int *subtype)
> {
> const struct ext_comm_pairs *cp;
> Index: usr.sbin/bgpd/printconf.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/printconf.c,v
> retrieving revision 1.127
> diff -u -p -r1.127 printconf.c
> --- usr.sbin/bgpd/printconf.c 4 Feb 2019 18:53:10 -0000 1.127
> +++ usr.sbin/bgpd/printconf.c 7 Feb 2019 10:11:19 -0000
> @@ -35,8 +35,8 @@ void print_community(struct filter_com
> void print_origin(u_int8_t);
> void print_set(struct filter_set_head *);
> void print_mainconf(struct bgpd_config *);
> -void print_rdomain_targets(struct filter_set_head *, const char *);
> -void print_rdomain(struct rdomain *);
> +void print_l3vpn_targets(struct filter_set_head *, const char *);
> +void print_l3vpn(struct l3vpn *);
> const char *print_af(u_int8_t);
> void print_network(struct network_config *, const char *);
> void print_as_sets(struct as_set_head *);
> @@ -378,7 +378,7 @@ print_mainconf(struct bgpd_config *conf)
> }
>
> void
> -print_rdomain_targets(struct filter_set_head *set, const char *tgt)
> +print_l3vpn_targets(struct filter_set_head *set, const char *tgt)
> {
> struct filter_set *s;
> TAILQ_FOREACH(s, set, entry) {
> @@ -389,27 +389,24 @@ print_rdomain_targets(struct filter_set_
> }
>
> void
> -print_rdomain(struct rdomain *r)
> +print_l3vpn(struct l3vpn *vpn)
> {
> struct network *n;
>
> - printf("rdomain %u {\n", r->rtableid);
> - if (*r->descr)
> - printf("\tdescr \"%s\"\n", r->descr);
> - if (r->flags & F_RIB_NOFIBSYNC)
> + printf("vpn \"%s\" on %s {\n", vpn->descr, vpn->ifmpe);
> + printf("\n\t%s\n", log_rd(vpn->rd));
> +
> + print_l3vpn_targets(&vpn->export, "export-target");
> + print_l3vpn_targets(&vpn->import, "import-target");
> +
> + if (vpn->flags & F_RIB_NOFIBSYNC)
> printf("\tfib-update no\n");
> else
> printf("\tfib-update yes\n");
> - printf("\tdepend on %s\n", r->ifmpe);
>
> - TAILQ_FOREACH(n, &r->net_l, entry)
> + TAILQ_FOREACH(n, &vpn->net_l, entry)
> print_network(&n->net, "\t");
>
> - printf("\n\t%s\n", log_rd(r->rd));
> -
> - print_rdomain_targets(&r->export, "export-target");
> - print_rdomain_targets(&r->import, "import-target");
> -
> printf("}\n");
> }
>
> @@ -958,12 +955,12 @@ void
> print_config(struct bgpd_config *conf, struct rib_names *rib_l,
> struct network_head *net_l, struct peer *peer_l,
> struct filter_head *rules_l, struct mrt_head *mrt_l,
> - struct rdomain_head *rdom_l)
> + struct l3vpn_head *vpns_l)
> {
> struct filter_rule *r;
> struct network *n;
> struct rde_rib *rr;
> - struct rdomain *rd;
> + struct l3vpn *vpn;
>
> print_mainconf(conf);
> print_roa(&conf->roa);
> @@ -972,10 +969,10 @@ print_config(struct bgpd_config *conf, s
> print_originsets(&conf->originsets);
> TAILQ_FOREACH(n, net_l, entry)
> print_network(&n->net, "");
> - if (!SIMPLEQ_EMPTY(rdom_l))
> + if (!SIMPLEQ_EMPTY(vpns_l))
> printf("\n");
> - SIMPLEQ_FOREACH(rd, rdom_l, entry)
> - print_rdomain(rd);
> + SIMPLEQ_FOREACH(vpn, vpns_l, entry)
> + print_l3vpn(vpn);
> printf("\n");
> SIMPLEQ_FOREACH(rr, rib_l, entry) {
> if (rr->flags & F_RIB_NOEVALUATE)
> Index: usr.sbin/bgpd/rde.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v
> retrieving revision 1.461
> diff -u -p -r1.461 rde.c
> --- usr.sbin/bgpd/rde.c 21 Jan 2019 02:07:56 -0000 1.461
> +++ usr.sbin/bgpd/rde.c 7 Feb 2019 10:11:19 -0000
> @@ -75,7 +75,7 @@ void rde_dump_ctx_throttle(pid_t, int)
> void rde_dump_ctx_terminate(pid_t);
> void rde_dump_mrt_new(struct mrt *, pid_t, int);
>
> -int rde_rdomain_import(struct rde_aspath *, struct rdomain *);
> +int rde_l3vpn_import(struct rde_aspath *, struct l3vpn *);
> void rde_reload_done(void);
> static void rde_softreconfig_in_done(void *, u_int8_t);
> static void rde_softreconfig_out_done(void *, u_int8_t);
> @@ -124,7 +124,7 @@ struct rde_prefixset_head originsets_old
> struct rde_prefixset roa_old;
> struct as_set_head *as_sets_tmp, *as_sets_old;
> struct filter_head *out_rules, *out_rules_tmp;
> -struct rdomain_head *rdomains_l, *newdomains;
> +struct l3vpn_head *l3vpns_l, *newdomains;
> struct imsgbuf *ibuf_se;
> struct imsgbuf *ibuf_se_ctl;
> struct imsgbuf *ibuf_main;
> @@ -227,10 +227,10 @@ rde_main(int debug, int verbose)
> fatal(NULL);
> TAILQ_INIT(out_rules);
>
> - rdomains_l = calloc(1, sizeof(struct rdomain_head));
> - if (rdomains_l == NULL)
> + l3vpns_l = calloc(1, sizeof(struct l3vpn_head));
> + if (l3vpns_l == NULL)
> fatal(NULL);
> - SIMPLEQ_INIT(rdomains_l);
> + SIMPLEQ_INIT(l3vpns_l);
>
> if ((conf = calloc(1, sizeof(struct bgpd_config))) == NULL)
> fatal(NULL);
> @@ -675,7 +675,7 @@ rde_dispatch_imsg_parent(struct imsgbuf
> static struct rde_prefixset *last_prefixset;
> static struct as_set *last_as_set;
> static struct set_table *last_set;
> - static struct rdomain *rd;
> + static struct l3vpn *vpn;
> struct imsg imsg;
> struct mrt xmrt;
> struct rde_rib rn;
> @@ -764,7 +764,7 @@ rde_dispatch_imsg_parent(struct imsgbuf
> if (out_rules_tmp == NULL)
> fatal(NULL);
> TAILQ_INIT(out_rules_tmp);
> - newdomains = calloc(1, sizeof(struct rdomain_head));
> + newdomains = calloc(1, sizeof(struct l3vpn_head));
> if (newdomains == NULL)
> fatal(NULL);
> SIMPLEQ_INIT(newdomains);
> @@ -948,34 +948,34 @@ rde_dispatch_imsg_parent(struct imsgbuf
> set_prep(last_as_set->set);
> last_as_set = NULL;
> break;
> - case IMSG_RECONF_RDOMAIN:
> + case IMSG_RECONF_VPN:
> if (imsg.hdr.len - IMSG_HEADER_SIZE !=
> - sizeof(struct rdomain))
> - fatalx("IMSG_RECONF_RDOMAIN bad len");
> - if ((rd = malloc(sizeof(struct rdomain))) == NULL)
> + sizeof(struct l3vpn))
> + fatalx("IMSG_RECONF_VPN bad len");
> + if ((vpn = malloc(sizeof(struct l3vpn))) == NULL)
> fatal(NULL);
> - memcpy(rd, imsg.data, sizeof(struct rdomain));
> - TAILQ_INIT(&rd->import);
> - TAILQ_INIT(&rd->export);
> - SIMPLEQ_INSERT_TAIL(newdomains, rd, entry);
> + memcpy(vpn, imsg.data, sizeof(struct l3vpn));
> + TAILQ_INIT(&vpn->import);
> + TAILQ_INIT(&vpn->export);
> + SIMPLEQ_INSERT_TAIL(newdomains, vpn, entry);
> break;
> - case IMSG_RECONF_RDOMAIN_EXPORT:
> - if (rd == NULL) {
> + case IMSG_RECONF_VPN_EXPORT:
> + if (vpn == NULL) {
> log_warnx("rde_dispatch_imsg_parent: "
> - "IMSG_RECONF_RDOMAIN_EXPORT unexpected");
> + "IMSG_RECONF_VPN_EXPORT unexpected");
> break;
> }
> - parent_set = &rd->export;
> + parent_set = &vpn->export;
> break;
> - case IMSG_RECONF_RDOMAIN_IMPORT:
> - if (rd == NULL) {
> + case IMSG_RECONF_VPN_IMPORT:
> + if (vpn == NULL) {
> log_warnx("rde_dispatch_imsg_parent: "
> - "IMSG_RECONF_RDOMAIN_IMPORT unexpected");
> + "IMSG_RECONF_VPN_IMPORT unexpected");
> break;
> }
> - parent_set = &rd->import;
> + parent_set = &vpn->import;
> break;
> - case IMSG_RECONF_RDOMAIN_DONE:
> + case IMSG_RECONF_VPN_DONE:
> parent_set = NULL;
> break;
> case IMSG_RECONF_DRAIN:
> @@ -2530,7 +2530,7 @@ rde_dump_mrt_new(struct mrt *mrt, pid_t
> * kroute specific functions
> */
> int
> -rde_rdomain_import(struct rde_aspath *asp, struct rdomain *rd)
> +rde_l3vpn_import(struct rde_aspath *asp, struct l3vpn *rd)
> {
> struct filter_set *s;
>
> @@ -2548,7 +2548,7 @@ rde_send_kroute(struct rib *rib, struct
> struct bgpd_addr addr;
> struct prefix *p;
> struct rde_aspath *asp;
> - struct rdomain *rd;
> + struct l3vpn *vpn;
> enum imsg_type type;
>
> /*
> @@ -2588,8 +2588,8 @@ rde_send_kroute(struct rib *rib, struct
> /* not Loc-RIB, no update for VPNs */
> break;
>
> - SIMPLEQ_FOREACH(rd, rdomains_l, entry) {
> - if (!rde_rdomain_import(asp, rd))
> + SIMPLEQ_FOREACH(vpn, l3vpns_l, entry) {
> + if (!rde_l3vpn_import(asp, vpn))
> continue;
> /* must send exit_nexthop so that correct MPLS tunnel
> * is chosen
> @@ -2599,8 +2599,8 @@ rde_send_kroute(struct rib *rib, struct
> &prefix_nexthop(p)->exit_nexthop,
> sizeof(kr.nexthop));
> /* XXX not ideal but this will change */
> - kr.ifindex = if_nametoindex(rd->ifmpe);
> - if (imsg_compose(ibuf_main, type, rd->rtableid, 0, -1,
> + kr.ifindex = if_nametoindex(vpn->ifmpe);
> + if (imsg_compose(ibuf_main, type, vpn->rtableid, 0, -1,
> &kr, sizeof(kr)) == -1)
> fatal("%s %d imsg_compose error", __func__,
> __LINE__);
> @@ -2881,7 +2881,7 @@ rde_send_nexthop(struct bgpd_addr *next,
> void
> rde_reload_done(void)
> {
> - struct rdomain *rd;
> + struct l3vpn *vpn;
> struct rde_peer *peer;
> struct filter_head *fh;
> u_int16_t rid;
> @@ -2923,15 +2923,15 @@ rde_reload_done(void)
> peerself->conf.remote_masklen = 32;
> peerself->short_as = conf->short_as;
>
> - /* apply new set of rdomain, sync will be done later */
> - while ((rd = SIMPLEQ_FIRST(rdomains_l)) != NULL) {
> - SIMPLEQ_REMOVE_HEAD(rdomains_l, entry);
> - filterset_free(&rd->import);
> - filterset_free(&rd->export);
> - free(rd);
> + /* apply new set of l3vpn, sync will be done later */
> + while ((vpn = SIMPLEQ_FIRST(l3vpns_l)) != NULL) {
> + SIMPLEQ_REMOVE_HEAD(l3vpns_l, entry);
> + filterset_free(&vpn->import);
> + filterset_free(&vpn->export);
> + free(vpn);
> }
> - free(rdomains_l);
> - rdomains_l = newdomains;
> + free(l3vpns_l);
> + l3vpns_l = newdomains;
> /* XXX WHERE IS THE SYNC ??? */
>
> /* check if roa changed */
> @@ -3687,7 +3687,7 @@ void
> network_add(struct network_config *nc, int flagstatic)
> {
> struct filterstate state;
> - struct rdomain *rd;
> + struct l3vpn *vpn;
> struct rde_aspath *asp;
> struct filter_set_head *vpnset = NULL;
> in_addr_t prefix4;
> @@ -3695,44 +3695,44 @@ network_add(struct network_config *nc, i
> u_int8_t vstate;
> u_int16_t i;
>
> - if (nc->rtableid != conf->default_tableid) {
> - SIMPLEQ_FOREACH(rd, rdomains_l, entry) {
> - if (rd->rtableid != nc->rtableid)
> + if (nc->rd != 0) {
> + SIMPLEQ_FOREACH(vpn, l3vpns_l, entry) {
> + if (vpn->rd != nc->rd)
> continue;
> switch (nc->prefix.aid) {
> case AID_INET:
> prefix4 = nc->prefix.v4.s_addr;
> bzero(&nc->prefix, sizeof(nc->prefix));
> nc->prefix.aid = AID_VPN_IPv4;
> - nc->prefix.vpn4.rd = rd->rd;
> + nc->prefix.vpn4.rd = vpn->rd;
> nc->prefix.vpn4.addr.s_addr = prefix4;
> nc->prefix.vpn4.labellen = 3;
> nc->prefix.vpn4.labelstack[0] =
> - (rd->label >> 12) & 0xff;
> + (vpn->label >> 12) & 0xff;
> nc->prefix.vpn4.labelstack[1] =
> - (rd->label >> 4) & 0xff;
> + (vpn->label >> 4) & 0xff;
> nc->prefix.vpn4.labelstack[2] =
> - (rd->label << 4) & 0xf0;
> + (vpn->label << 4) & 0xf0;
> nc->prefix.vpn4.labelstack[2] |= BGP_MPLS_BOS;
> - vpnset = &rd->export;
> + vpnset = &vpn->export;
> break;
> case AID_INET6:
> memcpy(&prefix6, &nc->prefix.v6.s6_addr,
> sizeof(struct in6_addr));
> memset(&nc->prefix, 0, sizeof(nc->prefix));
> nc->prefix.aid = AID_VPN_IPv6;
> - nc->prefix.vpn6.rd = rd->rd;
> + nc->prefix.vpn6.rd = vpn->rd;
> memcpy(&nc->prefix.vpn6.addr.s6_addr, &prefix6,
> sizeof(struct in6_addr));
> nc->prefix.vpn6.labellen = 3;
> nc->prefix.vpn6.labelstack[0] =
> - (rd->label >> 12) & 0xff;
> + (vpn->label >> 12) & 0xff;
> nc->prefix.vpn6.labelstack[1] =
> - (rd->label >> 4) & 0xff;
> + (vpn->label >> 4) & 0xff;
> nc->prefix.vpn6.labelstack[2] =
> - (rd->label << 4) & 0xf0;
> + (vpn->label << 4) & 0xf0;
> nc->prefix.vpn6.labelstack[2] |= BGP_MPLS_BOS;
> - vpnset = &rd->export;
> + vpnset = &vpn->export;
> break;
> default:
> log_warnx("unable to VPNize prefix");
> @@ -3741,10 +3741,11 @@ network_add(struct network_config *nc, i
> }
> break;
> }
> - if (rd == NULL) {
> + if (vpn == NULL) {
> log_warnx("network_add: "
> - "prefix %s/%u in non-existing rdomain %u",
> - log_addr(&nc->prefix), nc->prefixlen, nc->rtableid);
> + "prefix %s/%u in non-existing l3vpn %s",
> + log_addr(&nc->prefix), nc->prefixlen,
> + log_rd(nc->rd));
> return;
> }
> }
> @@ -3789,29 +3790,29 @@ network_add(struct network_config *nc, i
> void
> network_delete(struct network_config *nc)
> {
> - struct rdomain *rd;
> + struct l3vpn *vpn;
> in_addr_t prefix4;
> struct in6_addr prefix6;
> u_int32_t i;
>
> - if (nc->rtableid) {
> - SIMPLEQ_FOREACH(rd, rdomains_l, entry) {
> - if (rd->rtableid != nc->rtableid)
> + if (nc->rd) {
> + SIMPLEQ_FOREACH(vpn, l3vpns_l, entry) {
> + if (vpn->rd != nc->rd)
> continue;
> switch (nc->prefix.aid) {
> case AID_INET:
> prefix4 = nc->prefix.v4.s_addr;
> bzero(&nc->prefix, sizeof(nc->prefix));
> nc->prefix.aid = AID_VPN_IPv4;
> - nc->prefix.vpn4.rd = rd->rd;
> + nc->prefix.vpn4.rd = vpn->rd;
> nc->prefix.vpn4.addr.s_addr = prefix4;
> nc->prefix.vpn4.labellen = 3;
> nc->prefix.vpn4.labelstack[0] =
> - (rd->label >> 12) & 0xff;
> + (vpn->label >> 12) & 0xff;
> nc->prefix.vpn4.labelstack[1] =
> - (rd->label >> 4) & 0xff;
> + (vpn->label >> 4) & 0xff;
> nc->prefix.vpn4.labelstack[2] =
> - (rd->label << 4) & 0xf0;
> + (vpn->label << 4) & 0xf0;
> nc->prefix.vpn4.labelstack[2] |= BGP_MPLS_BOS;
> break;
> case AID_INET6:
> @@ -3819,16 +3820,16 @@ network_delete(struct network_config *nc
> sizeof(struct in6_addr));
> memset(&nc->prefix, 0, sizeof(nc->prefix));
> nc->prefix.aid = AID_VPN_IPv6;
> - nc->prefix.vpn6.rd = rd->rd;
> + nc->prefix.vpn6.rd = vpn->rd;
> memcpy(&nc->prefix.vpn6.addr.s6_addr, &prefix6,
> sizeof(struct in6_addr));
> nc->prefix.vpn6.labellen = 3;
> nc->prefix.vpn6.labelstack[0] =
> - (rd->label >> 12) & 0xff;
> + (vpn->label >> 12) & 0xff;
> nc->prefix.vpn6.labelstack[1] =
> - (rd->label >> 4) & 0xff;
> + (vpn->label >> 4) & 0xff;
> nc->prefix.vpn6.labelstack[2] =
> - (rd->label << 4) & 0xf0;
> + (vpn->label << 4) & 0xf0;
> nc->prefix.vpn6.labelstack[2] |= BGP_MPLS_BOS;
> break;
> default:
> Index: usr.sbin/bgpd/session.h
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/session.h,v
> retrieving revision 1.128
> diff -u -p -r1.128 session.h
> --- usr.sbin/bgpd/session.h 20 Jan 2019 23:27:48 -0000 1.128
> +++ usr.sbin/bgpd/session.h 7 Feb 2019 10:11:19 -0000
> @@ -248,7 +248,7 @@ int carp_demote_set(char *, int);
> int merge_config(struct bgpd_config *, struct bgpd_config *,
> struct peer *);
> int prepare_listeners(struct bgpd_config *);
> -int get_mpe_label(struct rdomain *);
> +int get_mpe_config(const char *, u_int *, u_int *);
>
> /* control.c */
> int control_check(char *);
> @@ -285,7 +285,7 @@ int pfkey_init(struct bgpd_sysdep *);
> /* printconf.c */
> void print_config(struct bgpd_config *, struct rib_names *,
> struct network_head *, struct peer *, struct filter_head *,
> - struct mrt_head *, struct rdomain_head *);
> + struct mrt_head *, struct l3vpn_head *);
>
> /* rde.c */
> void rde_main(int, int);
>