From cf5008fd390252ba68ec5a3569d475b1aa640be8 Mon Sep 17 00:00:00 2001 From: claudio Date: Mon, 16 Oct 2023 10:25:45 +0000 Subject: [PATCH] Improve IPv6 link-local address handling When a session is established determine the possible interface scope of that session. The scope is only set when the remote address is directly connected. This interface scope is passed to the RDE that uses this information when link-local nexthops are received. Again checking that a link-local nexthop is actually acceptable. OK tb@ --- usr.sbin/bgpd/bgpd.h | 4 ++- usr.sbin/bgpd/kroute.c | 37 +++++++++++++++++------- usr.sbin/bgpd/rde.c | 48 ++++++++++++++++++++++--------- usr.sbin/bgpd/rde.h | 3 +- usr.sbin/bgpd/rde_peer.c | 3 +- usr.sbin/bgpd/rde_rib.c | 4 +-- usr.sbin/bgpd/session.c | 62 +++++++++++++++++++++++++++++----------- usr.sbin/bgpd/session.h | 3 +- 8 files changed, 118 insertions(+), 46 deletions(-) diff --git a/usr.sbin/bgpd/bgpd.h b/usr.sbin/bgpd/bgpd.h index c19e2acfcc2..79d3c0638b0 100644 --- a/usr.sbin/bgpd/bgpd.h +++ b/usr.sbin/bgpd/bgpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bgpd.h,v 1.477 2023/08/30 08:16:28 claudio Exp $ */ +/* $OpenBSD: bgpd.h,v 1.478 2023/10/16 10:25:45 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -796,6 +796,7 @@ struct session_up { struct bgpd_addr remote_addr; struct capabilities capa; uint32_t remote_bgpid; + unsigned int if_scope; uint16_t short_as; }; @@ -1439,6 +1440,7 @@ void kr_ifinfo(char *); void kr_net_reload(u_int, uint64_t, struct network_head *); int kr_reload(void); int get_mpe_config(const char *, u_int *, u_int *); +uint8_t mask2prefixlen(sa_family_t, struct sockaddr *); /* log.c */ void log_peer_info(const struct peer_config *, const char *, ...) diff --git a/usr.sbin/bgpd/kroute.c b/usr.sbin/bgpd/kroute.c index ac55f1033da..69c09c67041 100644 --- a/usr.sbin/bgpd/kroute.c +++ b/usr.sbin/bgpd/kroute.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kroute.c,v 1.305 2023/06/01 09:47:34 claudio Exp $ */ +/* $OpenBSD: kroute.c,v 1.306 2023/10/16 10:25:45 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -168,8 +168,6 @@ struct kroute6 *kroute6_match(struct ktable *, struct bgpd_addr *, int); void kroute_detach_nexthop(struct ktable *, struct knexthop *); uint8_t prefixlen_classful(in_addr_t); -uint8_t mask2prefixlen(in_addr_t); -uint8_t mask2prefixlen6(struct sockaddr_in6 *); uint64_t ift2ifm(uint8_t); const char *get_media_descr(uint64_t); const char *get_linkstate(uint8_t, int); @@ -2419,21 +2417,28 @@ prefixlen_classful(in_addr_t ina) return (8); } -uint8_t -mask2prefixlen(in_addr_t ina) +static uint8_t +mask2prefixlen4(struct sockaddr_in *sa_in) { + in_addr_t ina; + + if (sa_in->sin_len == 0) + return (0); + ina = sa_in->sin_addr.s_addr; if (ina == 0) return (0); else return (33 - ffs(ntohl(ina))); } -uint8_t +static uint8_t mask2prefixlen6(struct sockaddr_in6 *sa_in6) { uint8_t *ap, *ep; u_int l = 0; + if (sa_in6->sin6_len == 0) + return (0); /* * sin6_len is the size of the sockaddr so subtract the offset of * the possibly truncated sin6_addr struct. @@ -2480,6 +2485,19 @@ mask2prefixlen6(struct sockaddr_in6 *sa_in6) return (l); } +uint8_t +mask2prefixlen(sa_family_t af, struct sockaddr *mask) +{ + switch (af) { + case AF_INET: + return mask2prefixlen4((struct sockaddr_in *)mask); + case AF_INET6: + return mask2prefixlen6((struct sockaddr_in6 *)mask); + default: + fatalx("%s: unsupported af", __func__); + } +} + const struct if_status_description if_status_descriptions[] = LINK_STATE_DESCRIPTIONS; const struct ifmedia_description @@ -3079,9 +3097,7 @@ dispatch_rtmsg_addr(struct rt_msghdr *rtm, struct kroute_full *kf) case AF_INET: sa_in = (struct sockaddr_in *)rti_info[RTAX_NETMASK]; if (sa_in != NULL) { - if (sa_in->sin_len != 0) - kf->prefixlen = - mask2prefixlen(sa_in->sin_addr.s_addr); + kf->prefixlen = mask2prefixlen4(sa_in); } else if (rtm->rtm_flags & RTF_HOST) kf->prefixlen = 32; else @@ -3091,8 +3107,7 @@ dispatch_rtmsg_addr(struct rt_msghdr *rtm, struct kroute_full *kf) case AF_INET6: sa_in6 = (struct sockaddr_in6 *)rti_info[RTAX_NETMASK]; if (sa_in6 != NULL) { - if (sa_in6->sin6_len != 0) - kf->prefixlen = mask2prefixlen6(sa_in6); + kf->prefixlen = mask2prefixlen6(sa_in6); } else if (rtm->rtm_flags & RTF_HOST) kf->prefixlen = 128; else diff --git a/usr.sbin/bgpd/rde.c b/usr.sbin/bgpd/rde.c index dc6a318f747..659622bb065 100644 --- a/usr.sbin/bgpd/rde.c +++ b/usr.sbin/bgpd/rde.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.c,v 1.610 2023/08/16 08:26:35 claudio Exp $ */ +/* $OpenBSD: rde.c,v 1.611 2023/10/16 10:25:45 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -59,7 +59,7 @@ int rde_attr_parse(u_char *, uint16_t, struct rde_peer *, int rde_attr_add(struct filterstate *, u_char *, uint16_t); uint8_t rde_attr_missing(struct rde_aspath *, int, uint16_t); int rde_get_mp_nexthop(u_char *, uint16_t, uint8_t, - struct filterstate *); + struct rde_peer *, struct filterstate *); void rde_as4byte_fixup(struct rde_peer *, struct rde_aspath *); uint8_t rde_aspa_validity(struct rde_peer *, struct rde_aspath *, uint8_t); @@ -1797,7 +1797,8 @@ rde_update_dispatch(struct rde_peer *peer, struct imsg *imsg) /* unlock the previously locked nexthop, it is no longer used */ nexthop_unref(state.nexthop); state.nexthop = NULL; - if ((pos = rde_get_mp_nexthop(mpp, mplen, aid, &state)) == -1) { + if ((pos = rde_get_mp_nexthop(mpp, mplen, aid, peer, + &state)) == -1) { log_peer_warnx(&peer->conf, "bad nlri nexthop"); rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR, mpa.reach, mpa.reach_len); @@ -2482,7 +2483,7 @@ rde_attr_missing(struct rde_aspath *a, int ebgp, uint16_t nlrilen) int rde_get_mp_nexthop(u_char *data, uint16_t len, uint8_t aid, - struct filterstate *state) + struct rde_peer *peer, struct filterstate *state) { struct bgpd_addr nexthop; uint8_t totlen, nhlen; @@ -2509,12 +2510,22 @@ rde_get_mp_nexthop(u_char *data, uint16_t len, uint8_t aid, * traffic. */ if (nhlen != 16 && nhlen != 32) { - log_warnx("bad %s nexthop, bad size %d", aid2str(aid), - nhlen); + log_peer_warnx(&peer->conf, "bad %s nexthop, " + "bad size %d", aid2str(aid), nhlen); return (-1); } memcpy(&nexthop.v6.s6_addr, data, 16); nexthop.aid = AID_INET6; + if (IN6_IS_ADDR_LINKLOCAL(&nexthop.v6)) { + if (peer->local_if_scope != 0) { + nexthop.scope_id = peer->local_if_scope; + } else { + log_peer_warnx(&peer->conf, + "unexpected link-local nexthop: %s", + log_addr(&nexthop)); + return (-1); + } + } break; case AID_VPN_IPv4: /* @@ -2531,8 +2542,8 @@ rde_get_mp_nexthop(u_char *data, uint16_t len, uint8_t aid, * AID_VPN_IPv4 in nexthop and kroute. */ if (nhlen != 12) { - log_warnx("bad %s nexthop, bad size %d", aid2str(aid), - nhlen); + log_peer_warnx(&peer->conf, "bad %s nexthop, " + "bad size %d", aid2str(aid), nhlen); return (-1); } nexthop.aid = AID_INET; @@ -2541,26 +2552,37 @@ rde_get_mp_nexthop(u_char *data, uint16_t len, uint8_t aid, break; case AID_VPN_IPv6: if (nhlen != 24) { - log_warnx("bad %s nexthop, bad size %d", aid2str(aid), - nhlen); + log_peer_warnx(&peer->conf, "bad %s nexthop, " + "bad size %d", aid2str(aid), nhlen); return (-1); } memcpy(&nexthop.v6, data + sizeof(uint64_t), sizeof(nexthop.v6)); nexthop.aid = AID_INET6; + if (IN6_IS_ADDR_LINKLOCAL(&nexthop.v6)) { + if (peer->local_if_scope != 0) { + nexthop.scope_id = peer->local_if_scope; + } else { + log_peer_warnx(&peer->conf, + "unexpected link-local nexthop: %s", + log_addr(&nexthop)); + return (-1); + } + } break; case AID_FLOWSPECv4: case AID_FLOWSPECv6: /* nexthop must be 0 and ignored for flowspec */ if (nhlen != 0) { - log_warnx("bad %s nexthop, bad size %d", aid2str(aid), - nhlen); + log_peer_warnx(&peer->conf, "bad %s nexthop, " + "bad size %d", aid2str(aid), nhlen); return (-1); } /* also ignore reserved (old SNPA) field as per RFC4760 */ return (totlen + 1); default: - log_warnx("bad multiprotocol nexthop, bad AID"); + log_peer_warnx(&peer->conf, "bad multiprotocol nexthop, " + "bad AID"); return (-1); } diff --git a/usr.sbin/bgpd/rde.h b/usr.sbin/bgpd/rde.h index c700aca75a3..ee8987cce1c 100644 --- a/usr.sbin/bgpd/rde.h +++ b/usr.sbin/bgpd/rde.h @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.h,v 1.296 2023/08/16 08:26:35 claudio Exp $ */ +/* $OpenBSD: rde.h,v 1.297 2023/10/16 10:25:46 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker and @@ -92,6 +92,7 @@ struct rde_peer { time_t staletime[AID_MAX]; uint32_t remote_bgpid; /* host byte order! */ uint32_t path_id_tx; + unsigned int local_if_scope; enum peer_state state; enum export_type export_type; enum role role; diff --git a/usr.sbin/bgpd/rde_peer.c b/usr.sbin/bgpd/rde_peer.c index bbf0a9e0ec9..fe67303fd42 100644 --- a/usr.sbin/bgpd/rde_peer.c +++ b/usr.sbin/bgpd/rde_peer.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde_peer.c,v 1.32 2023/04/19 13:23:33 claudio Exp $ */ +/* $OpenBSD: rde_peer.c,v 1.33 2023/10/16 10:25:46 claudio Exp $ */ /* * Copyright (c) 2019 Claudio Jeker @@ -430,6 +430,7 @@ peer_up(struct rde_peer *peer, struct session_up *sup) peer->remote_addr = sup->remote_addr; peer->local_v4_addr = sup->local_v4_addr; peer->local_v6_addr = sup->local_v6_addr; + peer->local_if_scope = sup->if_scope; memcpy(&peer->capa, &sup->capa, sizeof(peer->capa)); /* clear eor markers depending on GR flags */ diff --git a/usr.sbin/bgpd/rde_rib.c b/usr.sbin/bgpd/rde_rib.c index cd56b20300b..04f7e63d03b 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.260 2023/04/23 11:39:10 claudio Exp $ */ +/* $OpenBSD: rde_rib.c,v 1.261 2023/10/16 10:25:46 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker @@ -1910,7 +1910,7 @@ nexthop_compare(struct nexthop *na, struct nexthop *nb) case AID_INET6: return (memcmp(&a->v6, &b->v6, sizeof(struct in6_addr))); default: - fatalx("nexthop_cmp: unknown af"); + fatalx("nexthop_cmp: %s is unsupported", aid2str(a->aid)); } return (-1); } diff --git a/usr.sbin/bgpd/session.c b/usr.sbin/bgpd/session.c index 30d40153643..ed809c4ce6b 100644 --- a/usr.sbin/bgpd/session.c +++ b/usr.sbin/bgpd/session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: session.c,v 1.448 2023/10/09 07:11:20 claudio Exp $ */ +/* $OpenBSD: session.c,v 1.449 2023/10/16 10:25:46 claudio Exp $ */ /* * Copyright (c) 2003, 2004, 2005 Henning Brauer @@ -1202,37 +1202,64 @@ session_setup_socket(struct peer *p) return (0); } -/* compare two sockaddrs by converting them into bgpd_addr */ +/* + * compare the bgpd_addr with the sockaddr by converting the latter into + * a bgpd_addr. Return true if the two are equal, including any scope + */ static int -sa_equal(struct sockaddr *a, struct sockaddr *b) +sa_equal(struct bgpd_addr *ba, struct sockaddr *b) { - struct bgpd_addr ba, bb; + struct bgpd_addr bb; - sa2addr(a, &ba, NULL); sa2addr(b, &bb, NULL); - - return (memcmp(&ba, &bb, sizeof(ba)) == 0); + return (memcmp(ba, &bb, sizeof(*ba)) == 0); } static void -get_alternate_addr(struct sockaddr *sa, struct bgpd_addr *alt) +get_alternate_addr(struct bgpd_addr *local, struct bgpd_addr *remote, + struct bgpd_addr *alt, unsigned int *scope) { struct ifaddrs *ifap, *ifa, *match; + int connected = 0; + u_int8_t plen; if (getifaddrs(&ifap) == -1) fatal("getifaddrs"); - for (match = ifap; match != NULL; match = match->ifa_next) - if (match->ifa_addr != NULL && sa_equal(sa, match->ifa_addr)) + for (match = ifap; match != NULL; match = match->ifa_next) { + if (match->ifa_addr == NULL) + continue; + if (match->ifa_addr->sa_family != AF_INET && + match->ifa_addr->sa_family != AF_INET6) + continue; + if (sa_equal(local, match->ifa_addr)) { + if (match->ifa_flags & IFF_POINTOPOINT && + match->ifa_dstaddr) { + if (sa_equal(remote, match->ifa_dstaddr)) + connected = 1; + } else if (match->ifa_netmask) { + plen = mask2prefixlen( + match->ifa_addr->sa_family, + match->ifa_netmask); + if (plen != 0xff && + prefix_compare(local, remote, plen) == 0) + connected = 1; + } break; + } + } if (match == NULL) { log_warnx("%s: local address not found", __func__); return; } + if (connected) + *scope = if_nametoindex(match->ifa_name); + else + *scope = 0; - switch (sa->sa_family) { - case AF_INET6: + switch (local->aid) { + case AID_INET6: for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_addr != NULL && ifa->ifa_addr->sa_family == AF_INET && @@ -1242,7 +1269,7 @@ get_alternate_addr(struct sockaddr *sa, struct bgpd_addr *alt) } } break; - case AF_INET: + case AID_INET: for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_addr != NULL && ifa->ifa_addr->sa_family == AF_INET6 && @@ -1260,8 +1287,8 @@ get_alternate_addr(struct sockaddr *sa, struct bgpd_addr *alt) } break; default: - log_warnx("%s: unsupported address family %d", __func__, - sa->sa_family); + log_warnx("%s: unsupported address family %s", __func__, + aid2str(local->aid)); break; } @@ -1278,11 +1305,13 @@ session_tcp_established(struct peer *peer) if (getsockname(peer->fd, (struct sockaddr *)&ss, &len) == -1) log_warn("getsockname"); sa2addr((struct sockaddr *)&ss, &peer->local, &peer->local_port); - get_alternate_addr((struct sockaddr *)&ss, &peer->local_alt); len = sizeof(ss); if (getpeername(peer->fd, (struct sockaddr *)&ss, &len) == -1) log_warn("getpeername"); sa2addr((struct sockaddr *)&ss, &peer->remote, &peer->remote_port); + + get_alternate_addr(&peer->local, &peer->remote, &peer->local_alt, + &peer->if_scope); } void @@ -3546,6 +3575,7 @@ session_up(struct peer *p) sup.local_v4_addr = p->local_alt; } sup.remote_addr = p->remote; + sup.if_scope = p->if_scope; sup.remote_bgpid = p->remote_bgpid; sup.short_as = p->short_as; diff --git a/usr.sbin/bgpd/session.h b/usr.sbin/bgpd/session.h index 65e96d6c87f..9c0969d6655 100644 --- a/usr.sbin/bgpd/session.h +++ b/usr.sbin/bgpd/session.h @@ -1,4 +1,4 @@ -/* $OpenBSD: session.h,v 1.162 2023/03/28 12:15:23 claudio Exp $ */ +/* $OpenBSD: session.h,v 1.163 2023/10/16 10:25:46 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -240,6 +240,7 @@ struct peer { int lasterr; u_int errcnt; u_int IdleHoldTime; + unsigned int if_scope; /* interface scope for IPv6 */ uint32_t remote_bgpid; enum session_state state; enum session_state prev_state; -- 2.20.1