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).
-- 
: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