On Fri, Dec 28, 2018 at 03:15:35PM +0100, Claudio Jeker wrote: > > /* > > * 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); > > I think this is not right. If at all then this check should happen after > checking the RD for equality. Else two different VPN default routes (with > different RD value) would be conidered the same. > > > 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); > > See above. > > > + 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); > > I would check the RD first like it is done in the AID_VPN_IPv4 case. > Then address and then labelstack. > > > + 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); > > } > > > > The diff is OK apart from that bit in prefix_compare(). >
Thank you for the comments. I removed the "if (prefixlen == 0)". I added it because it was missing but I did not think it was on purpose. I also reordered the tests as you suggest. I copied the order of up_prefix_cmp() (rd, address then labelstack) and updated pt_prefix_cmp() as well. Index: bgpd.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v retrieving revision 1.360 diff -u -p -r1.360 bgpd.h --- bgpd.h 27 Dec 2018 20:23:24 -0000 1.360 +++ bgpd.h 28 Dec 2018 16:00:23 -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 @@ -598,6 +612,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; @@ -1258,6 +1273,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: kroute.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/kroute.c,v retrieving revision 1.226 diff -u -p -r1.226 kroute.c --- kroute.c 6 Dec 2018 13:04:40 -0000 1.226 +++ kroute.c 28 Dec 2018 16:00:23 -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: mrt.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/mrt.c,v retrieving revision 1.87 diff -u -p -r1.87 mrt.c --- mrt.c 24 Oct 2018 08:26:37 -0000 1.87 +++ mrt.c 28 Dec 2018 16:00:23 -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: parse.y =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/parse.y,v retrieving revision 1.367 diff -u -p -r1.367 parse.y --- parse.y 28 Dec 2018 14:28:52 -0000 1.367 +++ parse.y 28 Dec 2018 16:00:23 -0000 @@ -3974,6 +3974,7 @@ merge_prefixspec(struct filter_prefix *p max_len = 32; break; case AID_INET6: + case AID_VPN_IPv6: max_len = 128; break; } Index: printconf.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/printconf.c,v retrieving revision 1.125 diff -u -p -r1.125 printconf.c --- printconf.c 19 Dec 2018 15:26:42 -0000 1.125 +++ printconf.c 28 Dec 2018 16:00:23 -0000 @@ -67,6 +67,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: rde.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v retrieving revision 1.455 diff -u -p -r1.455 rde.c --- rde.c 26 Dec 2018 13:24:28 -0000 1.455 +++ rde.c 28 Dec 2018 16:00:23 -0000 @@ -1240,6 +1240,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; @@ -1363,6 +1380,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; @@ -1904,6 +1940,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 @@ -2352,6 +2398,7 @@ rde_dump_ctx_new(struct ctl_show_rib_req hostplen = 32; break; case AID_INET6: + case AID_VPN_IPv6: hostplen = 128; break; default: @@ -2517,6 +2564,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; @@ -3643,6 +3691,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; @@ -3667,6 +3716,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); @@ -3724,6 +3791,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; @@ -3749,6 +3817,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: rde.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v retrieving revision 1.206 diff -u -p -r1.206 rde.h --- rde.h 19 Dec 2018 15:26:42 -0000 1.206 +++ rde.h 28 Dec 2018 16:00:23 -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: 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 --- rde_prefix.c 4 Sep 2018 12:00:29 -0000 1.34 +++ rde_prefix.c 28 Dec 2018 16:00:23 -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) @@ -239,6 +265,10 @@ pt_prefix_cmp(const struct pt_entry *a, case AID_VPN_IPv4: va4 = (const struct pt_entry_vpn4 *)a; vb4 = (const struct pt_entry_vpn4 *)b; + if (betoh64(va4->rd) > betoh64(vb4->rd)) + return (1); + if (betoh64(va4->rd) < betoh64(vb4->rd)) + return (-1); if (ntohl(va4->prefix4.s_addr) > ntohl(vb4->prefix4.s_addr)) return (1); if (ntohl(va4->prefix4.s_addr) < ntohl(vb4->prefix4.s_addr)) @@ -247,9 +277,23 @@ pt_prefix_cmp(const struct pt_entry *a, return (1); if (va4->prefixlen < vb4->prefixlen) return (-1); - if (betoh64(va4->rd) > betoh64(vb4->rd)) + return (0); + case AID_VPN_IPv6: + va6 = (const struct pt_entry_vpn6 *)a; + vb6 = (const struct pt_entry_vpn6 *)b; + if (betoh64(va6->rd) > betoh64(vb6->rd)) return (1); - if (betoh64(va4->rd) < betoh64(vb4->rd)) + if (betoh64(va6->rd) < betoh64(vb6->rd)) + return (-1); + 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); return (0); default: Index: 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 --- rde_rib.c 11 Dec 2018 09:02:14 -0000 1.187 +++ rde_rib.c 28 Dec 2018 16:00:23 -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: rde_update.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde_update.c,v retrieving revision 1.106 diff -u -p -r1.106 rde_update.c --- rde_update.c 19 Dec 2018 15:26:42 -0000 1.106 +++ rde_update.c 28 Dec 2018 16:00:23 -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: util.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/util.c,v retrieving revision 1.41 diff -u -p -r1.41 util.c --- util.c 11 Dec 2018 09:02:14 -0000 1.41 +++ util.c 28 Dec 2018 16:00:23 -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, @@ -637,6 +712,32 @@ 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 > 128) + return (-1); + if (betoh64(a->vpn6.rd) > betoh64(b->vpn6.rd)) + return (1); + if (betoh64(a->vpn6.rd) < betoh64(b->vpn6.rd)) + 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 (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); }