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

Reply via email to