Here is a serie of diffs to enable MPLSv6, MPLS transport over IPv6.

Second diff : add support for IPv6 MPLS routes exchange with bgpd(8).

(***********************************)
pe1# cat /etc/hostname.mpe0                                                    
rdomain 2
mplslabel 42
inet6 2001:db8::2/128
up
(***********************************)
pe1# cat /etc/hostname.vio0                                                    
rdomain 2
inet6 2001:db8::2 126
up
(***********************************)
pe1# cat /etc/hostname.vio1                                                    
mpls
inet6 2001:db8:1::2 126
up
(***********************************)
pe1# cat /etc/hostname.lo0                                                     
inet6 2001:db8:fffe::1 128
up
(***********************************)
pe1# cat /etc/bgpd.conf                                                        
router-id 10.0.0.2
AS 65530

rdomain 2 {
  descr "CUSTOMER1"
  rd 65530:2
  import-target rt 65530:2
  export-target rt 65530:2
  depend on mpe0
  network inet connected
  network inet6 connected
}
group "ibgp" {
  announce IPv4 vpn
  announce IPv4 unicast
  announce IPv6 vpn
  announce IPv6 unicast
  remote-as 65530
  neighbor 10.255.254.2 {
    local-address 10.255.254.1
    descr PE2v4
    down
  }
  neighbor 2001:db8:fffe::2 {
    local-address 2001:db8:fffe::1
    descr PE2v6
  }
}
allow from ibgp
allow to ibgp
(***********************************)
pe1# bgpctl sh rib                                                              
                                                      
flags: * = Valid, > = Selected, I = via IBGP, A = Announced,
       S = Stale, E = Error
origin validation state: N = not-found, V = valid, ! = invalid
origin: i = IGP, e = EGP, ? = Incomplete

flags ovs destination          gateway          lpref   med aspath origin       
                                                      
AI*>    N rd 65530:2 2001:db8::/126 rd 0:0 ::          100     0 i
I*>     N rd 65530:2 2001:db8:ffff::/126 2001:db8:fffe::2    100     0 i        
  
(***********************************)
pe2# tcpdump -n -i vio1 mpls
tcpdump: listening on vio1, link-type EN10MB
08:13:01.870005 MPLS(label 42, exp 0, ttl 62) 2001:db8::1 > 2001:db8:ffff::1: 
icmp6: echo request                                     
08:13:01.870882 MPLS(label 26, exp 0, ttl 63) MPLS(label 42, exp 0, ttl 63) 
2001:db8:ffff::1 > 2001:db8::1: icmp6: echo reply         
08:13:02.362564 MPLS(label 42, exp 0, ttl 62) 2001:db8::1 > 2001:db8:ffff::1: 
icmp6: echo request                                     
08:13:02.363173 MPLS(label 26, exp 0, ttl 63) MPLS(label 42, exp 0, ttl 63) 
2001:db8:ffff::1 > 2001:db8::1: icmp6: echo reply         
08:13:02.865183 MPLS(label 42, exp 0, ttl 62) 2001:db8::1 > 2001:db8:ffff::1: 
icmp6: echo request
(***********************************)

We can only exchange MPLS routes with the same address family as the
transport AF.

Unfortunately I don't have gear to test interoperability. It seems there is very
few support that.  Has anyone access to such hardware ?

Index: bgpd/bgpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v
retrieving revision 1.357
diff -u -p -r1.357 bgpd.h
--- bgpd/bgpd.h 11 Dec 2018 09:02:14 -0000      1.357
+++ bgpd/bgpd.h 18 Dec 2018 11:04:07 -0000
@@ -154,7 +154,8 @@ extern const struct aid aid_vals[];
 #define        AID_INET        1
 #define        AID_INET6       2
 #define        AID_VPN_IPv4    3
-#define        AID_MAX         4
+#define        AID_VPN_IPv6    4
+#define        AID_MAX         5
 #define        AID_MIN         1       /* skip AID_UNSPEC since that is a 
dummy */
 
 #define AID_VALS       {                                       \
@@ -162,14 +163,16 @@ extern const struct aid aid_vals[];
        { AFI_UNSPEC, AF_UNSPEC, SAFI_NONE, "unspec"},          \
        { AFI_IPv4, AF_INET, SAFI_UNICAST, "IPv4 unicast" },    \
        { AFI_IPv6, AF_INET6, SAFI_UNICAST, "IPv6 unicast" },   \
-       { AFI_IPv4, AF_INET, SAFI_MPLSVPN, "IPv4 vpn" }         \
+       { AFI_IPv4, AF_INET, SAFI_MPLSVPN, "IPv4 vpn" },        \
+       { AFI_IPv6, AF_INET6, SAFI_MPLSVPN, "IPv6 vpn" }        \
 }
 
 #define AID_PTSIZE     {                               \
        0,                                              \
        sizeof(struct pt_entry4),                       \
        sizeof(struct pt_entry6),                       \
-       sizeof(struct pt_entry_vpn4)                    \
+       sizeof(struct pt_entry_vpn4),                   \
+       sizeof(struct pt_entry_vpn6)                    \
 }
 
 struct vpn4_addr {
@@ -181,6 +184,15 @@ struct vpn4_addr {
        u_int8_t        pad2;
 };
 
+struct vpn6_addr {
+       u_int64_t       rd;
+       struct in6_addr addr;
+       u_int8_t        labelstack[21]; /* max that makes sense */
+       u_int8_t        labellen;
+       u_int8_t        pad1;
+       u_int8_t        pad2;
+};
+
 #define BGP_MPLS_BOS   0x01
 
 struct bgpd_addr {
@@ -188,6 +200,7 @@ struct bgpd_addr {
                struct in_addr          v4;
                struct in6_addr         v6;
                struct vpn4_addr        vpn4;
+               struct vpn6_addr        vpn6;
                /* maximum size for a prefix is 256 bits */
                u_int8_t                addr8[32];
                u_int16_t               addr16[16];
@@ -198,6 +211,7 @@ struct bgpd_addr {
 #define        v4      ba.v4
 #define        v6      ba.v6
 #define        vpn4    ba.vpn4
+#define        vpn6    ba.vpn6
 #define        addr8   ba.addr8
 #define        addr16  ba.addr16
 #define        addr32  ba.addr32
@@ -597,6 +611,7 @@ struct kroute {
 struct kroute6 {
        struct in6_addr prefix;
        struct in6_addr nexthop;
+       u_int32_t       mplslabel;
        u_int16_t       flags;
        u_int16_t       labelid;
        u_short         ifindex;
@@ -1270,6 +1285,8 @@ int                nlri_get_prefix(u_char *, u_int16_
 int             nlri_get_prefix6(u_char *, u_int16_t, struct bgpd_addr *,
                     u_int8_t *);
 int             nlri_get_vpn4(u_char *, u_int16_t, struct bgpd_addr *,
+                    u_int8_t *, int);
+int             nlri_get_vpn6(u_char *, u_int16_t, struct bgpd_addr *,
                     u_int8_t *, int);
 int             prefix_compare(const struct bgpd_addr *,
                    const struct bgpd_addr *, int);
Index: bgpd/kroute.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/kroute.c,v
retrieving revision 1.226
diff -u -p -r1.226 kroute.c
--- bgpd/kroute.c       6 Dec 2018 13:04:40 -0000       1.226
+++ bgpd/kroute.c       18 Dec 2018 11:04:07 -0000
@@ -93,9 +93,11 @@ struct ktable        *ktable_get(u_int);
 int    kr4_change(struct ktable *, struct kroute_full *, u_int8_t);
 int    kr6_change(struct ktable *, struct kroute_full *, u_int8_t);
 int    krVPN4_change(struct ktable *, struct kroute_full *, u_int8_t);
+int    krVPN6_change(struct ktable *, struct kroute_full *, u_int8_t);
 int    kr4_delete(struct ktable *, struct kroute_full *, u_int8_t);
 int    kr6_delete(struct ktable *, struct kroute_full *, u_int8_t);
 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 *);
@@ -475,6 +477,8 @@ kr_change(u_int rtableid, struct kroute_
                return (kr6_change(kt, kl, fib_prio));
        case AID_VPN_IPv4:
                return (krVPN4_change(kt, kl, fib_prio));
+       case AID_VPN_IPv6:
+               return (krVPN6_change(kt, kl, fib_prio));
        }
        log_warnx("%s: not handled AID", __func__);
        return (-1);
@@ -669,6 +673,81 @@ krVPN4_change(struct ktable *kt, struct 
 }
 
 int
+krVPN6_change(struct ktable *kt, struct kroute_full *kl, u_int8_t fib_prio)
+{
+       struct kroute6_node     *kr6;
+       struct in6_addr          lo6 = IN6ADDR_LOOPBACK_INIT;
+       int                      action = RTM_ADD;
+       u_int32_t                mplslabel = 0;
+       u_int16_t                labelid;
+
+       if ((kr6 = kroute6_find(kt, &kl->prefix.vpn6.addr, kl->prefixlen,
+           fib_prio)) != NULL)
+               action = RTM_CHANGE;
+
+       /* nexthop to loopback -> ignore silently */
+       if (IN6_IS_ADDR_LOOPBACK(&kl->nexthop.v6))
+               return (0);
+
+       /* only single MPLS label are supported for now */
+       if (kl->prefix.vpn6.labellen != 3) {
+               log_warnx("%s: %s/%u has not a single label", __func__,
+                   log_addr(&kl->prefix), kl->prefixlen);
+               return (0);
+       }
+       mplslabel = (kl->prefix.vpn6.labelstack[0] << 24) |
+           (kl->prefix.vpn6.labelstack[1] << 16) |
+           (kl->prefix.vpn6.labelstack[2] << 8);
+       mplslabel = htonl(mplslabel);
+
+       /* for blackhole and reject routes nexthop needs to be ::1 */
+       if (kl->flags & (F_BLACKHOLE|F_REJECT))
+               bcopy(&lo6, &kl->nexthop.v6, sizeof(kl->nexthop.v6));
+
+       labelid = rtlabel_name2id(kl->label);
+
+       if (action == RTM_ADD) {
+               if ((kr6 = calloc(1, sizeof(struct kroute6_node))) == NULL) {
+                       log_warn("%s", __func__);
+                       return (-1);
+               }
+               memcpy(&kr6->r.prefix, &kl->prefix.vpn6.addr,
+                   sizeof(struct in6_addr));
+               kr6->r.prefixlen = kl->prefixlen;
+               memcpy(&kr6->r.nexthop, &kl->nexthop.v6,
+                   sizeof(struct in6_addr));
+               kr6->r.flags = kl->flags | F_BGPD_INSERTED | F_MPLS;
+               kr6->r.priority = fib_prio;
+               kr6->r.labelid = labelid;
+               kr6->r.mplslabel = mplslabel;
+
+               if (kroute6_insert(kt, kr6) == -1) {
+                       free(kr6);
+                       return (-1);
+               }
+       } else {
+               kr6->r.mplslabel = mplslabel;
+               memcpy(&kr6->r.nexthop, &kl->nexthop.v6,
+                   sizeof(struct in6_addr));
+               rtlabel_unref(kr6->r.labelid);
+               kr6->r.labelid = labelid;
+               if (kl->flags & F_BLACKHOLE)
+                       kr6->r.flags |= F_BLACKHOLE;
+               else
+                       kr6->r.flags &= ~F_BLACKHOLE;
+               if (kl->flags & F_REJECT)
+                       kr6->r.flags |= F_REJECT;
+               else
+                       kr6->r.flags &= ~F_REJECT;
+       }
+
+       if (send_rt6msg(kr_state.fd, action, kt, &kr6->r, fib_prio) == -1)
+               return (-1);
+
+       return (0);
+}
+
+int
 kr_delete(u_int rtableid, struct kroute_full *kl, u_int8_t fib_prio)
 {
        struct ktable           *kt;
@@ -684,6 +763,8 @@ kr_delete(u_int rtableid, struct kroute_
                return (kr6_delete(kt, kl, fib_prio));
        case AID_VPN_IPv4:
                return (krVPN4_delete(kt, kl, fib_prio));
+       case AID_VPN_IPv6:
+               return (krVPN6_delete(kt, kl, fib_prio));
        }
        log_warnx("%s: not handled AID", __func__);
        return (-1);
@@ -758,6 +839,29 @@ krVPN4_delete(struct ktable *kt, struct 
        return (0);
 }
 
+int
+krVPN6_delete(struct ktable *kt, struct kroute_full *kl, u_int8_t fib_prio)
+{
+       struct kroute6_node     *kr6;
+
+       if ((kr6 = kroute6_find(kt, &kl->prefix.vpn6.addr, kl->prefixlen,
+           fib_prio)) == NULL)
+               return (0);
+
+       if (!(kr6->r.flags & F_BGPD_INSERTED))
+               return (0);
+
+       if (send_rt6msg(kr_state.fd, RTM_DELETE, kt, &kr6->r, fib_prio) == -1)
+               return (-1);
+
+       rtlabel_unref(kr6->r.labelid);
+
+       if (kroute6_remove(kt, kr6) == -1)
+               return (-1);
+
+       return (0);
+}
+
 void
 kr_shutdown(u_int8_t fib_prio, u_int rdomain)
 {
@@ -2723,13 +2827,18 @@ int
 send_rt6msg(int fd, int action, struct ktable *kt, struct kroute6 *kroute,
     u_int8_t fib_prio)
 {
-       struct iovec            iov[5];
+       struct iovec            iov[7];
        struct rt_msghdr        hdr;
        struct pad {
                struct sockaddr_in6     addr;
                char                    pad[sizeof(long)];
        } prefix, nexthop, mask;
+       struct {
+               struct sockaddr_dl      dl;
+               char                    pad[sizeof(long)];
+       }                       ifp;
        struct sockaddr_rtlabel label;
+       struct sockaddr_mpls    mpls;
        int                     iovcnt = 0;
 
        if (!kt->fib_sync)
@@ -2792,6 +2901,34 @@ send_rt6msg(int fd, int action, struct k
        /* adjust iovec */
        iov[iovcnt].iov_base = &mask;
        iov[iovcnt++].iov_len = ROUNDUP(sizeof(struct sockaddr_in6));
+
+       if (kt->ifindex) {
+               memset(&ifp, 0, sizeof(ifp));
+               ifp.dl.sdl_len = sizeof(struct sockaddr_dl);
+               ifp.dl.sdl_family = AF_LINK;
+               ifp.dl.sdl_index = kt->ifindex;
+               hdr.rtm_addrs |= RTA_IFP;
+               hdr.rtm_msglen += ROUNDUP(sizeof(struct sockaddr_dl));
+               iov[iovcnt].iov_base = &ifp;
+               iov[iovcnt++].iov_len = ROUNDUP(sizeof(struct sockaddr_dl));
+       }
+
+       if (kroute->flags & F_MPLS) {
+               memset(&mpls, 0, sizeof(mpls));
+               mpls.smpls_len = sizeof(mpls);
+               mpls.smpls_family = AF_MPLS;
+               mpls.smpls_label = kroute->mplslabel;
+               /* adjust header */
+               hdr.rtm_flags |= RTF_MPLS;
+               hdr.rtm_mpls = MPLS_OP_PUSH;
+               hdr.rtm_addrs |= RTA_SRC;
+               hdr.rtm_msglen += ROUNDUP(sizeof(struct sockaddr_mpls));
+               /* clear gateway flag since this is for mpe(4) */
+               hdr.rtm_flags &= ~RTF_GATEWAY;
+               /* adjust iovec */
+               iov[iovcnt].iov_base = &mpls;
+               iov[iovcnt++].iov_len = ROUNDUP(sizeof(struct sockaddr_mpls));
+       }
 
        if (kroute->labelid) {
                bzero(&label, sizeof(label));
Index: bgpd/mrt.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/mrt.c,v
retrieving revision 1.87
diff -u -p -r1.87 mrt.c
--- bgpd/mrt.c  24 Oct 2018 08:26:37 -0000      1.87
+++ bgpd/mrt.c  18 Dec 2018 11:04:07 -0000
@@ -222,6 +222,15 @@ mrt_attr_dump(struct ibuf *buf, struct r
                        DUMP_NLONG(nhbuf, 0);
                        DUMP_NLONG(nhbuf, nexthop->v4.s_addr);
                        break;
+               case AID_VPN_IPv6:
+                       DUMP_BYTE(nhbuf, sizeof(u_int64_t) +
+                           sizeof(struct in6_addr));
+                       DUMP_NLONG(nhbuf, 0);   /* set RD to 0 */
+                       DUMP_NLONG(nhbuf, 0);
+                       if (ibuf_add(nhbuf, &nexthop->v6,
+                           sizeof(struct in6_addr)) == -1) {
+                       }
+                       break;
                }
                if (!v2)
                        DUMP_BYTE(nhbuf, 0);
Index: bgpd/parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/parse.y,v
retrieving revision 1.365
diff -u -p -r1.365 parse.y
--- bgpd/parse.y        6 Dec 2018 12:38:01 -0000       1.365
+++ bgpd/parse.y        18 Dec 2018 11:04:07 -0000
@@ -3952,6 +3952,7 @@ merge_prefixspec(struct filter_prefix *p
                max_len = 32;
                break;
        case AID_INET6:
+       case AID_VPN_IPv6:
                max_len = 128;
                break;
        }
Index: bgpd/printconf.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/printconf.c,v
retrieving revision 1.124
diff -u -p -r1.124 printconf.c
--- bgpd/printconf.c    28 Nov 2018 08:32:27 -0000      1.124
+++ bgpd/printconf.c    18 Dec 2018 11:04:07 -0000
@@ -69,6 +69,7 @@ print_prefix(struct filter_prefix *p)
                max_len = 32;
                break;
        case AID_INET6:
+       case AID_VPN_IPv6:
                max_len = 128;
                break;
        case AID_UNSPEC:
Index: bgpd/rde.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v
retrieving revision 1.452
diff -u -p -r1.452 rde.c
--- bgpd/rde.c  11 Dec 2018 09:02:14 -0000      1.452
+++ bgpd/rde.c  18 Dec 2018 11:04:07 -0000
@@ -1236,6 +1236,23 @@ rde_update_dispatch(struct imsg *imsg)
                                rde_update_withdraw(peer, &prefix, prefixlen);
                        }
                        break;
+               case AID_VPN_IPv6:
+                       while (mplen > 0) {
+                               if ((pos = nlri_get_vpn6(mpp, mplen,
+                                   &prefix, &prefixlen, 1)) == -1) {
+                                       log_peer_warnx(&peer->conf,
+                                           "bad VPNv6 withdraw prefix");
+                                       rde_update_err(peer, ERR_UPDATE,
+                                           ERR_UPD_OPTATTR, mpa.unreach,
+                                           mpa.unreach_len);
+                                       goto done;
+                               }
+                               mpp += pos;
+                               mplen -= pos;
+
+                               rde_update_withdraw(peer, &prefix, prefixlen);
+                       }
+                       break;
                default:
                        /* silently ignore unsupported multiprotocol AF */
                        break;
@@ -1359,6 +1376,25 @@ rde_update_dispatch(struct imsg *imsg)
                                        goto done;
                        }
                        break;
+               case AID_VPN_IPv6:
+                       while (mplen > 0) {
+                               if ((pos = nlri_get_vpn6(mpp, mplen,
+                                   &prefix, &prefixlen, 0)) == -1) {
+                                       log_peer_warnx(&peer->conf,
+                                           "bad VPNv6 nlri prefix");
+                                       rde_update_err(peer, ERR_UPDATE,
+                                           ERR_UPD_OPTATTR,
+                                           mpa.reach, mpa.reach_len);
+                                       goto done;
+                               }
+                               mpp += pos;
+                               mplen -= pos;
+
+                               if (rde_update_update(peer, &state, &prefix,
+                                   prefixlen) == -1)
+                                       goto done;
+                       }
+                       break;
                default:
                        /* silently ignore unsupported multiprotocol AF */
                        break;
@@ -1900,6 +1936,16 @@ rde_get_mp_nexthop(u_char *data, u_int16
                }
                memcpy(&nexthop.v6.s6_addr, data, 16);
                break;
+       case AID_VPN_IPv6:
+               if (nhlen != 24) {
+                       log_warnx("bad multiprotocol nexthop, bad size %d",
+                           nhlen);
+                       return (-1);
+               }
+               memcpy(&nexthop.v6, data + sizeof(u_int64_t),
+                   sizeof(nexthop.v6));
+               nexthop.aid = AID_INET6;
+               break;
        case AID_VPN_IPv4:
                /*
                 * Neither RFC4364 nor RFC3107 specify the format of the
@@ -2346,6 +2392,7 @@ rde_dump_ctx_new(struct ctl_show_rib_req
                        hostplen = 32;
                        break;
                case AID_INET6:
+               case AID_VPN_IPv6:
                        hostplen = 128;
                        break;
                default:
@@ -2484,6 +2531,7 @@ rde_send_kroute(struct rib *rib, struct 
 
        switch (addr.aid) {
        case AID_VPN_IPv4:
+       case AID_VPN_IPv6:
                if (!(rib->flags & F_RIB_LOCAL))
                        /* not Loc-RIB, no update for VPNs */
                        break;
@@ -3610,6 +3658,7 @@ network_add(struct network_config *nc, i
        struct rde_aspath       *asp;
        struct filter_set_head  *vpnset = NULL;
        in_addr_t                prefix4;
+       struct in6_addr          prefix6;
        u_int8_t                 vstate;
        u_int16_t                i;
 
@@ -3634,6 +3683,24 @@ network_add(struct network_config *nc, i
                                nc->prefix.vpn4.labelstack[2] |= BGP_MPLS_BOS;
                                vpnset = &rd->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;
+                               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;
+                               nc->prefix.vpn6.labelstack[1] =
+                                   (rd->label >> 4) & 0xff;
+                               nc->prefix.vpn6.labelstack[2] =
+                                   (rd->label << 4) & 0xf0;
+                               nc->prefix.vpn6.labelstack[2] |= BGP_MPLS_BOS;
+                               vpnset = &rd->export;
+                               break;
                        default:
                                log_warnx("unable to VPNize prefix");
                                filterset_free(&nc->attrset);
@@ -3691,6 +3758,7 @@ network_delete(struct network_config *nc
 {
        struct rdomain  *rd;
        in_addr_t        prefix4;
+       struct in6_addr  prefix6;
        u_int32_t        flags = F_PREFIX_ANNOUNCED;
        u_int32_t        i;
 
@@ -3716,6 +3784,23 @@ network_delete(struct network_config *nc
                                nc->prefix.vpn4.labelstack[2] =
                                    (rd->label << 4) & 0xf0;
                                nc->prefix.vpn4.labelstack[2] |= BGP_MPLS_BOS;
+                               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;
+                               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;
+                               nc->prefix.vpn6.labelstack[1] =
+                                   (rd->label >> 4) & 0xff;
+                               nc->prefix.vpn6.labelstack[2] =
+                                   (rd->label << 4) & 0xf0;
+                               nc->prefix.vpn6.labelstack[2] |= BGP_MPLS_BOS;
                                break;
                        default:
                                log_warnx("unable to VPNize prefix");
Index: bgpd/rde.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v
retrieving revision 1.205
diff -u -p -r1.205 rde.h
--- bgpd/rde.h  17 Dec 2018 11:24:30 -0000      1.205
+++ bgpd/rde.h  18 Dec 2018 11:04:07 -0000
@@ -282,6 +282,19 @@ struct pt_entry_vpn4 {
        u_int8_t                         pad2;
 };
 
+struct pt_entry_vpn6 {
+       RB_ENTRY(pt_entry)               pt_e;
+       u_int8_t                         aid;
+       u_int8_t                         prefixlen;
+       u_int16_t                        refcnt;
+       struct in6_addr                  prefix6;
+       u_int64_t                        rd;
+       u_int8_t                         labelstack[21];
+       u_int8_t                         labellen;
+       u_int8_t                         pad1;
+       u_int8_t                         pad2;
+};
+
 struct prefix {
        LIST_ENTRY(prefix)               rib_l, nexthop_l;
        struct rib_entry                *re;
Index: bgpd/rde_prefix.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_prefix.c,v
retrieving revision 1.34
diff -u -p -r1.34 rde_prefix.c
--- bgpd/rde_prefix.c   4 Sep 2018 12:00:29 -0000       1.34
+++ bgpd/rde_prefix.c   18 Dec 2018 11:04:07 -0000
@@ -90,6 +90,16 @@ pt_getaddr(struct pt_entry *pte, struct 
                    ((struct pt_entry_vpn4 *)pte)->labelstack,
                    addr->vpn4.labellen);
                break;
+       case AID_VPN_IPv6:
+               memcpy(&addr->vpn6.addr,
+                   &((struct pt_entry_vpn6 *)pte)->prefix6,
+                   sizeof(addr->vpn6.addr));
+               addr->vpn6.rd = ((struct pt_entry_vpn6 *)pte)->rd;
+               addr->vpn6.labellen = ((struct pt_entry_vpn6 *)pte)->labellen;
+               memcpy(addr->vpn6.labelstack,
+                   ((struct pt_entry_vpn6 *)pte)->labelstack,
+                   addr->vpn6.labellen);
+               break;
        default:
                fatalx("pt_getaddr: unknown af");
        }
@@ -101,6 +111,7 @@ pt_fill(struct bgpd_addr *prefix, int pr
        static struct pt_entry4         pte4;
        static struct pt_entry6         pte6;
        static struct pt_entry_vpn4     pte_vpn4;
+       static struct pt_entry_vpn6     pte_vpn6;
 
        switch (prefix->aid) {
        case AID_INET:
@@ -132,6 +143,19 @@ pt_fill(struct bgpd_addr *prefix, int pr
                memcpy(pte_vpn4.labelstack, prefix->vpn4.labelstack,
                    prefix->vpn4.labellen);
                return ((struct pt_entry *)&pte_vpn4);
+       case AID_VPN_IPv6:
+               memset(&pte_vpn6, 0, sizeof(pte_vpn6));
+               pte_vpn6.aid = prefix->aid;
+               if (prefixlen > 128)
+                       fatalx("pt_get: bad IPv6 prefixlen");
+               inet6applymask(&pte_vpn6.prefix6, &prefix->vpn6.addr,
+                   prefixlen);
+               pte_vpn6.prefixlen = prefixlen;
+               pte_vpn6.rd = prefix->vpn6.rd;
+               pte_vpn6.labellen = prefix->vpn6.labellen;
+               memcpy(pte_vpn6.labelstack, prefix->vpn6.labelstack,
+                   prefix->vpn6.labellen);
+               return ((struct pt_entry *)&pte_vpn6);
        default:
                fatalx("pt_fill: unknown af");
        }
@@ -183,6 +207,7 @@ pt_lookup(struct bgpd_addr *addr)
                i = 32;
                break;
        case AID_INET6:
+       case AID_VPN_IPv6:
                i = 128;
                break;
        default:
@@ -202,6 +227,7 @@ pt_prefix_cmp(const struct pt_entry *a, 
        const struct pt_entry4          *a4, *b4;
        const struct pt_entry6          *a6, *b6;
        const struct pt_entry_vpn4      *va4, *vb4;
+       const struct pt_entry_vpn6      *va6, *vb6;
        int                              i;
 
        if (a->aid > b->aid)
@@ -250,6 +276,24 @@ pt_prefix_cmp(const struct pt_entry *a, 
                if (betoh64(va4->rd) > betoh64(vb4->rd))
                        return (1);
                if (betoh64(va4->rd) < betoh64(vb4->rd))
+                       return (-1);
+               return (0);
+       case AID_VPN_IPv6:
+               va6 = (const struct pt_entry_vpn6 *)a;
+               vb6 = (const struct pt_entry_vpn6 *)b;
+               i = memcmp(&va6->prefix6, &vb6->prefix6,
+                   sizeof(struct in6_addr));
+               if (i > 0)
+                       return (1);
+               if (i < 0)
+                       return (-1);
+               if (va6->prefixlen > vb6->prefixlen)
+                       return (1);
+               if (va6->prefixlen < vb6->prefixlen)
+                       return (-1);
+               if (betoh64(va6->rd) > betoh64(vb6->rd))
+                       return (1);
+               if (betoh64(va6->rd) < betoh64(vb6->rd))
                        return (-1);
                return (0);
        default:
Index: bgpd/rde_rib.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_rib.c,v
retrieving revision 1.187
diff -u -p -r1.187 rde_rib.c
--- bgpd/rde_rib.c      11 Dec 2018 09:02:14 -0000      1.187
+++ bgpd/rde_rib.c      18 Dec 2018 11:04:07 -0000
@@ -264,6 +264,7 @@ rib_lookup(struct rib *rib, struct bgpd_
                }
                break;
        case AID_INET6:
+       case AID_VPN_IPv6:
                for (i = 128; i >= 0; i--) {
                        re = rib_get(rib, addr, i);
                        if (re != NULL)
@@ -1017,6 +1018,34 @@ prefix_write(u_char *buf, int len, struc
                buf += sizeof(prefix->vpn4.rd);
                memcpy(buf, &prefix->vpn4.addr, PREFIX_SIZE(plen) - 1);
                return (totlen);
+       case AID_VPN_IPv6:
+               totlen = PREFIX_SIZE(plen) + sizeof(prefix->vpn6.rd);
+               plen += sizeof(prefix->vpn6.rd) * 8;
+               if (withdraw) {
+                       /* withdraw have one compat label as placeholder */
+                       totlen += 3;
+                       plen += 3 * 8;
+               } else {
+                       totlen += prefix->vpn6.labellen;
+                       plen += prefix->vpn6.labellen * 8;
+               }
+               if (totlen > len)
+                       return (-1);
+               *buf++ = plen;
+               if (withdraw) {
+                       /* magic compatibility label as per rfc8277 */
+                       *buf++ = 0x80;
+                       *buf++ = 0x0;
+                       *buf++ = 0x0;
+               } else  {
+                       memcpy(buf, &prefix->vpn6.labelstack,
+                           prefix->vpn6.labellen);
+                       buf += prefix->vpn6.labellen;
+               }
+               memcpy(buf, &prefix->vpn6.rd, sizeof(prefix->vpn6.rd));
+               buf += sizeof(prefix->vpn6.rd);
+               memcpy(buf, &prefix->vpn6.addr, PREFIX_SIZE(plen) - 1);
+               return (totlen);
        default:
                return (-1);
        }
@@ -1036,6 +1065,10 @@ prefix_writebuf(struct ibuf *buf, struct
        case AID_VPN_IPv4:
                totlen = PREFIX_SIZE(plen) + sizeof(prefix->vpn4.rd) +
                    prefix->vpn4.labellen;
+               break;
+       case AID_VPN_IPv6:
+               totlen = PREFIX_SIZE(plen) + sizeof(prefix->vpn6.rd) +
+                   prefix->vpn6.labellen;
                break;
        default:
                return (-1);
Index: bgpd/rde_update.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_update.c,v
retrieving revision 1.105
diff -u -p -r1.105 rde_update.c
--- bgpd/rde_update.c   29 Nov 2018 12:10:51 -0000      1.105
+++ bgpd/rde_update.c   18 Dec 2018 11:04:07 -0000
@@ -184,6 +184,27 @@ up_prefix_cmp(struct update_prefix *a, s
                        return (1);
                return (memcmp(a->prefix.vpn4.labelstack,
                    b->prefix.vpn4.labelstack, a->prefix.vpn4.labellen));
+       case AID_VPN_IPv6:
+               if (betoh64(a->prefix.vpn6.rd) < betoh64(b->prefix.vpn6.rd))
+                       return (-1);
+               if (betoh64(a->prefix.vpn6.rd) > betoh64(b->prefix.vpn6.rd))
+                       return (1);
+               i = memcmp(&a->prefix.vpn6.addr, &b->prefix.vpn6.addr,
+                   sizeof(struct in6_addr));
+               if (i > 0)
+                       return (1);
+               if (i < 0)
+                       return (-1);
+               if (a->prefixlen < b->prefixlen)
+                       return (-1);
+               if (a->prefixlen > b->prefixlen)
+                       return (1);
+               if (a->prefix.vpn6.labellen < b->prefix.vpn6.labellen)
+                       return (-1);
+               if (a->prefix.vpn6.labellen > b->prefix.vpn6.labellen)
+                       return (1);
+               return (memcmp(a->prefix.vpn6.labelstack,
+                   b->prefix.vpn6.labelstack, a->prefix.vpn6.labellen));
        default:
                fatalx("up_prefix_cmp: unknown af");
        }
@@ -745,6 +766,68 @@ up_generate_mp_reach(struct rde_peer *pe
                        /* ebgp multihop */
                        memcpy(&upa->mpattr[12], &peer->local_v4_addr.v4,
                            sizeof(struct in_addr));
+               return (0);
+       case AID_VPN_IPv6:
+               upa->mpattr_len = 29; /* AFI + SAFI + NH LEN + NH + Reserved */
+               upa->mpattr = calloc(upa->mpattr_len, 1);
+               if (upa->mpattr == NULL)
+                       fatal("up_generate_mp_reach");
+               if (aid2afi(aid, &tmp, &upa->mpattr[2]))
+                       fatalx("up_generate_mp_reachi: bad AID");
+               tmp = htons(tmp);
+               memcpy(upa->mpattr, &tmp, sizeof(tmp));
+               upa->mpattr[3] = sizeof(u_int64_t) + sizeof(struct in6_addr);
+               upa->mpattr[28] = 0; /* Reserved must be 0 */
+
+               /* nexthop dance see also up_get_nexthop() */
+               if (state->nhflags & NEXTHOP_NOMODIFY) {
+                       /* no modify flag set */
+                       if (state->nexthop == NULL)
+                               memcpy(&upa->mpattr[12],
+                                   &peer->local_v6_addr.v6,
+                                   sizeof(struct in6_addr));
+                       else
+                               memcpy(&upa->mpattr[12],
+                                   &state->nexthop->exit_nexthop.v6,
+                                   sizeof(struct in6_addr));
+               } else if (state->nhflags & NEXTHOP_SELF)
+                       memcpy(&upa->mpattr[12], &peer->local_v6_addr.v6,
+                           sizeof(struct in6_addr));
+               else if (!peer->conf.ebgp) {
+                       /* ibgp */
+                       if (state->nexthop == NULL ||
+                           (state->nexthop->exit_nexthop.aid == AID_INET6 &&
+                           !memcmp(&state->nexthop->exit_nexthop.v6,
+                           &peer->remote_addr.v6, sizeof(struct in6_addr))))
+                               memcpy(&upa->mpattr[12],
+                                   &peer->local_v6_addr.v6,
+                                   sizeof(struct in6_addr));
+                       else
+                               memcpy(&upa->mpattr[12],
+                                   &state->nexthop->exit_nexthop.v6,
+                                   sizeof(struct in6_addr));
+               } else if (peer->conf.distance == 1) {
+                       /* ebgp directly connected */
+                       if (state->nexthop != NULL &&
+                           state->nexthop->flags & NEXTHOP_CONNECTED)
+                               if (prefix_compare(&peer->remote_addr,
+                                   &state->nexthop->nexthop_net,
+                                   state->nexthop->nexthop_netlen) == 0) {
+                                       /*
+                                       * nexthop and peer are in the same
+                                       * subnet
+                                       */
+                                       memcpy(&upa->mpattr[12],
+                                           &state->nexthop->exit_nexthop.v6,
+                                           sizeof(struct in6_addr));
+                                       return (0);
+                               }
+                       memcpy(&upa->mpattr[12], &peer->local_v6_addr.v6,
+                           sizeof(struct in6_addr));
+               } else
+                       /* ebgp multihop */
+                       memcpy(&upa->mpattr[12], &peer->local_v6_addr.v6,
+                           sizeof(struct in6_addr));
                return (0);
        default:
                break;
Index: bgpd/util.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/util.c,v
retrieving revision 1.41
diff -u -p -r1.41 util.c
--- bgpd/util.c 11 Dec 2018 09:02:14 -0000      1.41
+++ bgpd/util.c 18 Dec 2018 11:04:07 -0000
@@ -39,8 +39,8 @@ const char    *aspath_delim(u_int8_t, int);
 const char *
 log_addr(const struct bgpd_addr *addr)
 {
-       static char     buf[48];
-       char            tbuf[16];
+       static char     buf[74];
+       char            tbuf[40];
 
        switch (addr->aid) {
        case AID_INET:
@@ -56,6 +56,13 @@ log_addr(const struct bgpd_addr *addr)
                snprintf(buf, sizeof(buf), "%s %s", log_rd(addr->vpn4.rd),
                   tbuf);
                return (buf);
+       case AID_VPN_IPv6:
+               if (inet_ntop(aid2af(addr->aid), &addr->vpn6.addr, tbuf,
+                   sizeof(tbuf)) == NULL)
+                       return ("?");
+               snprintf(buf, sizeof(buf), "%s %s", log_rd(addr->vpn6.rd),
+                   tbuf);
+               return (buf);
        }
        return ("???");
 }
@@ -575,9 +582,77 @@ nlri_get_vpn4(u_char *p, u_int16_t len, 
        return (plen + rv);
 }
 
+int
+nlri_get_vpn6(u_char *p, u_int16_t len, struct bgpd_addr *prefix,
+    u_int8_t *prefixlen, int withdraw)
+{
+       int             rv, done = 0;
+       u_int8_t        pfxlen;
+       u_int16_t       plen;
+
+       if (len < 1)
+               return (-1);
+
+       memcpy(&pfxlen, p, 1);
+       p += 1;
+       plen = 1;
+
+       memset(prefix, 0, sizeof(struct bgpd_addr));
+
+       /* label stack */
+       do {
+               if (len - plen < 3 || pfxlen < 3 * 8)
+                       return (-1);
+               if (prefix->vpn6.labellen + 3U >
+                   sizeof(prefix->vpn6.labelstack))
+                       return (-1);
+               if (withdraw) {
+                       /* on withdraw ignore the labelstack all together */
+                       plen += 3;
+                       pfxlen -= 3 * 8;
+                       break;
+               }
+
+               prefix->vpn6.labelstack[prefix->vpn6.labellen++] = *p++;
+               prefix->vpn6.labelstack[prefix->vpn6.labellen++] = *p++;
+               prefix->vpn6.labelstack[prefix->vpn6.labellen] = *p++;
+               if (prefix->vpn6.labelstack[prefix->vpn6.labellen] &
+                   BGP_MPLS_BOS)
+                       done = 1;
+               prefix->vpn6.labellen++;
+               plen += 3;
+               pfxlen -= 3 * 8;
+       } while (!done);
+
+       /* RD */
+       if (len - plen < (int)sizeof(u_int64_t) ||
+           pfxlen < sizeof(u_int64_t) * 8)
+               return (-1);
+
+       memcpy(&prefix->vpn6.rd, p, sizeof(u_int64_t));
+       pfxlen -= sizeof(u_int64_t) * 8;
+       p += sizeof(u_int64_t);
+       plen += sizeof(u_int64_t);
+
+       /* prefix */
+       prefix->aid = AID_VPN_IPv6;
+       *prefixlen = pfxlen;
+
+       if (pfxlen > 128)
+               return (-1);
+
+       if ((rv = extract_prefix(p, len, &prefix->vpn6.addr,
+           pfxlen, sizeof(prefix->vpn6.addr))) == -1)
+               return (-1);
+
+       return (plen + rv);
+}
+
+
+
 /*
  * This function will have undefined behaviour if the passed in prefixlen is
- * to large for the respective bgpd_addr address family.
+ * too large for the respective bgpd_addr address family.
  */
 int
 prefix_compare(const struct bgpd_addr *a, const struct bgpd_addr *b,
@@ -620,6 +695,8 @@ prefix_compare(const struct bgpd_addr *a
                }
                return (0);
        case AID_VPN_IPv4:
+               if (prefixlen == 0)
+                       return (0);
                if (prefixlen > 32)
                        return (-1);
                if (betoh64(a->vpn4.rd) > betoh64(b->vpn4.rd))
@@ -637,6 +714,34 @@ prefix_compare(const struct bgpd_addr *a
                        return (-1);
                return (memcmp(a->vpn4.labelstack, b->vpn4.labelstack,
                    a->vpn4.labellen));
+       case AID_VPN_IPv6:
+               if (prefixlen == 0)
+                       return (0);
+               if (prefixlen > 128)
+                       return (-1);
+               for (i = 0; i < prefixlen / 8; i++)
+                       if (a->vpn6.addr.s6_addr[i] != b->vpn6.addr.s6_addr[i])
+                               return (a->vpn6.addr.s6_addr[i] -
+                                   b->vpn6.addr.s6_addr[i]);
+               i = prefixlen % 8;
+               if (i) {
+                       m = 0xff00 >> i;
+                       if ((a->vpn6.addr.s6_addr[prefixlen / 8] & m) !=
+                           (b->vpn6.addr.s6_addr[prefixlen / 8] & m))
+                               return ((a->vpn6.addr.s6_addr[prefixlen / 8] &
+                                   m) - (b->vpn6.addr.s6_addr[prefixlen / 8] &
+                                   m));
+               }
+               if (betoh64(a->vpn6.rd) > betoh64(b->vpn6.rd))
+                       return (1);
+               if (betoh64(a->vpn6.rd) < betoh64(b->vpn6.rd))
+                       return (-1);
+               if (a->vpn6.labellen > b->vpn6.labellen)
+                       return (1);
+               if (a->vpn6.labellen < b->vpn6.labellen)
+                       return (-1);
+               return (memcmp(a->vpn6.labelstack, b->vpn6.labelstack,
+                   a->vpn6.labellen));
        }
        return (-1);
 }

Reply via email to