summaryrefslogtreecommitdiff
path: root/usr.sbin/bgpd
diff options
context:
space:
mode:
authordenis <denis@cvs.openbsd.org>2018-12-30 13:53:08 +0000
committerdenis <denis@cvs.openbsd.org>2018-12-30 13:53:08 +0000
commita824537c7c90146373b79b2f57f5027a586cf979 (patch)
tree614696296938fbaf4389866bbb417c11158eaa6c /usr.sbin/bgpd
parentd6ba61a601290d0dffba4585b85e02442086b8a4 (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.h25
-rw-r--r--usr.sbin/bgpd/kroute.c141
-rw-r--r--usr.sbin/bgpd/mrt.c11
-rw-r--r--usr.sbin/bgpd/parse.y3
-rw-r--r--usr.sbin/bgpd/printconf.c3
-rw-r--r--usr.sbin/bgpd/rde.c86
-rw-r--r--usr.sbin/bgpd/rde.h15
-rw-r--r--usr.sbin/bgpd/rde_prefix.c50
-rw-r--r--usr.sbin/bgpd/rde_rib.c35
-rw-r--r--usr.sbin/bgpd/rde_update.c85
-rw-r--r--usr.sbin/bgpd/util.c109
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);
}