diff options
author | denis <denis@cvs.openbsd.org> | 2018-12-30 13:53:08 +0000 |
---|---|---|
committer | denis <denis@cvs.openbsd.org> | 2018-12-30 13:53:08 +0000 |
commit | a824537c7c90146373b79b2f57f5027a586cf979 (patch) | |
tree | 614696296938fbaf4389866bbb417c11158eaa6c /usr.sbin/bgpd | |
parent | d6ba61a601290d0dffba4585b85e02442086b8a4 (diff) |
add support for IPv6 VPN routes
The kernel bits are missing as of now. With input from claudio@ and kn@
OK claudio@
Diffstat (limited to 'usr.sbin/bgpd')
-rw-r--r-- | usr.sbin/bgpd/bgpd.h | 25 | ||||
-rw-r--r-- | usr.sbin/bgpd/kroute.c | 141 | ||||
-rw-r--r-- | usr.sbin/bgpd/mrt.c | 11 | ||||
-rw-r--r-- | usr.sbin/bgpd/parse.y | 3 | ||||
-rw-r--r-- | usr.sbin/bgpd/printconf.c | 3 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde.c | 86 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde.h | 15 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_prefix.c | 50 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_rib.c | 35 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_update.c | 85 | ||||
-rw-r--r-- | usr.sbin/bgpd/util.c | 109 |
11 files changed, 543 insertions, 20 deletions
diff --git a/usr.sbin/bgpd/bgpd.h b/usr.sbin/bgpd/bgpd.h index 322fb047d25..54da275acca 100644 --- a/usr.sbin/bgpd/bgpd.h +++ b/usr.sbin/bgpd/bgpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bgpd.h,v 1.360 2018/12/27 20:23:24 remi Exp $ */ +/* $OpenBSD: bgpd.h,v 1.361 2018/12/30 13:53:07 denis Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -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; @@ -1259,6 +1274,8 @@ 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); in_addr_t prefixlen2mask(u_int8_t); diff --git a/usr.sbin/bgpd/kroute.c b/usr.sbin/bgpd/kroute.c index 0bb1c9a2ea4..3403ed29607 100644 --- a/usr.sbin/bgpd/kroute.c +++ b/usr.sbin/bgpd/kroute.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kroute.c,v 1.227 2018/12/28 22:05:15 denis Exp $ */ +/* $OpenBSD: kroute.c,v 1.228 2018/12/30 13:53:07 denis Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -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_full *kl, u_int8_t fib_prio) 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 kroute_full *kl, u_int8_t fib_prio) } 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; + + /* 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 ((kr6 = kroute6_find(kt, &kl->prefix.vpn6.addr, kl->prefixlen, + fib_prio)) != NULL) + action = RTM_CHANGE; + + 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_full *kl, u_int8_t fib_prio) 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 kroute_full *kl, u_int8_t fib_prio) 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) @@ -2793,6 +2902,34 @@ send_rt6msg(int fd, int action, struct ktable *kt, struct kroute6 *kroute, 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)); label.sr_len = sizeof(label); diff --git a/usr.sbin/bgpd/mrt.c b/usr.sbin/bgpd/mrt.c index 7c7f2193db3..6629ac227e7 100644 --- a/usr.sbin/bgpd/mrt.c +++ b/usr.sbin/bgpd/mrt.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mrt.c,v 1.87 2018/10/24 08:26:37 claudio Exp $ */ +/* $OpenBSD: mrt.c,v 1.88 2018/12/30 13:53:07 denis Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org> @@ -222,6 +222,15 @@ mrt_attr_dump(struct ibuf *buf, struct rde_aspath *a, struct bgpd_addr *nexthop, 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); diff --git a/usr.sbin/bgpd/parse.y b/usr.sbin/bgpd/parse.y index 7a2a78accd3..602791d09e1 100644 --- a/usr.sbin/bgpd/parse.y +++ b/usr.sbin/bgpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.367 2018/12/28 14:28:52 denis Exp $ */ +/* $OpenBSD: parse.y,v 1.368 2018/12/30 13:53:07 denis Exp $ */ /* * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -3974,6 +3974,7 @@ merge_prefixspec(struct filter_prefix *p, struct filter_prefixlen *pl) max_len = 32; break; case AID_INET6: + case AID_VPN_IPv6: max_len = 128; break; } diff --git a/usr.sbin/bgpd/printconf.c b/usr.sbin/bgpd/printconf.c index 60a56ffa223..5717d64e23c 100644 --- a/usr.sbin/bgpd/printconf.c +++ b/usr.sbin/bgpd/printconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: printconf.c,v 1.125 2018/12/19 15:26:42 claudio Exp $ */ +/* $OpenBSD: printconf.c,v 1.126 2018/12/30 13:53:07 denis Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -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: diff --git a/usr.sbin/bgpd/rde.c b/usr.sbin/bgpd/rde.c index 7b82b2c7955..31cf46dca32 100644 --- a/usr.sbin/bgpd/rde.c +++ b/usr.sbin/bgpd/rde.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.c,v 1.456 2018/12/30 13:05:09 benno Exp $ */ +/* $OpenBSD: rde.c,v 1.457 2018/12/30 13:53:07 denis Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -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_t len, u_int8_t aid, } 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_request *req, pid_t pid, 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 prefix *new, struct prefix *old) 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, int flagstatic) 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, int flagstatic) 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); @@ -3746,6 +3813,23 @@ network_delete(struct network_config *nc) (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"); return; diff --git a/usr.sbin/bgpd/rde.h b/usr.sbin/bgpd/rde.h index b0bfbeb7ac7..2b8134d9fe4 100644 --- a/usr.sbin/bgpd/rde.h +++ b/usr.sbin/bgpd/rde.h @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.h,v 1.206 2018/12/19 15:26:42 claudio Exp $ */ +/* $OpenBSD: rde.h,v 1.207 2018/12/30 13:53:07 denis Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org> and @@ -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; diff --git a/usr.sbin/bgpd/rde_prefix.c b/usr.sbin/bgpd/rde_prefix.c index 63aeeb5f2aa..04eedce819e 100644 --- a/usr.sbin/bgpd/rde_prefix.c +++ b/usr.sbin/bgpd/rde_prefix.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde_prefix.c,v 1.35 2018/12/28 13:38:15 denis Exp $ */ +/* $OpenBSD: rde_prefix.c,v 1.36 2018/12/30 13:53:07 denis Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org> @@ -90,6 +90,16 @@ pt_getaddr(struct pt_entry *pte, struct bgpd_addr *addr) ((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 prefixlen) 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 prefixlen) 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_entry *b) 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, const struct pt_entry *b) 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, const struct pt_entry *b) 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: diff --git a/usr.sbin/bgpd/rde_rib.c b/usr.sbin/bgpd/rde_rib.c index 63190a3e639..8133753f450 100644 --- a/usr.sbin/bgpd/rde_rib.c +++ b/usr.sbin/bgpd/rde_rib.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde_rib.c,v 1.187 2018/12/11 09:02:14 claudio Exp $ */ +/* $OpenBSD: rde_rib.c,v 1.188 2018/12/30 13:53:07 denis Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org> @@ -264,6 +264,7 @@ rib_lookup(struct rib *rib, struct bgpd_addr *addr) } 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, struct bgpd_addr *prefix, u_int8_t plen, 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); } @@ -1037,6 +1066,10 @@ prefix_writebuf(struct ibuf *buf, struct bgpd_addr *prefix, u_int8_t plen) 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); } diff --git a/usr.sbin/bgpd/rde_update.c b/usr.sbin/bgpd/rde_update.c index 248b43ae3bf..0d0d60b0267 100644 --- a/usr.sbin/bgpd/rde_update.c +++ b/usr.sbin/bgpd/rde_update.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde_update.c,v 1.106 2018/12/19 15:26:42 claudio Exp $ */ +/* $OpenBSD: rde_update.c,v 1.107 2018/12/30 13:53:07 denis Exp $ */ /* * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org> @@ -184,6 +184,27 @@ up_prefix_cmp(struct update_prefix *a, struct update_prefix *b) 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"); } @@ -746,6 +767,68 @@ up_generate_mp_reach(struct rde_peer *peer, struct update_attr *upa, 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; } diff --git a/usr.sbin/bgpd/util.c b/usr.sbin/bgpd/util.c index 4e70d2a9aac..3c24500e750 100644 --- a/usr.sbin/bgpd/util.c +++ b/usr.sbin/bgpd/util.c @@ -1,4 +1,4 @@ -/* $OpenBSD: util.c,v 1.41 2018/12/11 09:02:14 claudio Exp $ */ +/* $OpenBSD: util.c,v 1.42 2018/12/30 13:53:07 denis Exp $ */ /* * Copyright (c) 2006 Claudio Jeker <claudio@openbsd.org> @@ -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, struct bgpd_addr *prefix, 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, const struct bgpd_addr *b, 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); } |