This diff fixes a few more things when establishing connections with
link-local IPv6 addresses. In get_alternate_addr() the interface scope
of the connection is recovered and then passed to the RDE. The RDE can
then use this scope id to insert link-local addresses with the correct
scope.

I built a regress test for this which passes with this diff.
Now probably more is needed because IPv6 link-local addresses are a gift
that keep on giving. One thing to implement on top of this is template
matching for link local -- which allows to auto-configure sessions more
easily. This will probably follow soon.

-- 
:wq Claudio

Index: bgpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v
retrieving revision 1.477
diff -u -p -r1.477 bgpd.h
--- bgpd.h      30 Aug 2023 08:16:28 -0000      1.477
+++ bgpd.h      9 Oct 2023 13:50:36 -0000
@@ -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 *, ...)
Index: kroute.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/kroute.c,v
retrieving revision 1.305
diff -u -p -r1.305 kroute.c
--- kroute.c    1 Jun 2023 09:47:34 -0000       1.305
+++ kroute.c    9 Oct 2023 13:54:25 -0000
@@ -168,8 +168,6 @@ struct kroute6      *kroute6_match(struct kta
 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_
        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 *rt
        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 *rt
        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
Index: logmsg.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/logmsg.c,v
retrieving revision 1.9
diff -u -p -r1.9 logmsg.c
--- logmsg.c    24 Aug 2022 17:14:02 -0000      1.9
+++ logmsg.c    14 Oct 2023 09:49:36 -0000
@@ -60,55 +60,54 @@ log_fmt_peer(const struct peer_config *p
 void
 log_peer_info(const struct peer_config *peer, const char *emsg, ...)
 {
-       char    *p, *nfmt;
+       char    *p, *msg;
        va_list  ap;
 
        p = log_fmt_peer(peer);
-       if (asprintf(&nfmt, "%s: %s", p, emsg) == -1)
-               fatal(NULL);
        va_start(ap, emsg);
-       vlog(LOG_INFO, nfmt, ap);
+       if (vasprintf(&msg, emsg, ap) == -1)
+               fatal(NULL);
        va_end(ap);
+       logit(LOG_INFO, "%s: %s", p, msg);
+       free(msg);
        free(p);
-       free(nfmt);
 }
 
 void
 log_peer_warn(const struct peer_config *peer, const char *emsg, ...)
 {
-       char    *p, *nfmt;
+       char    *p, *msg;
        va_list  ap;
+       int      saved_errno = errno;
 
        p = log_fmt_peer(peer);
        if (emsg == NULL) {
-               if (asprintf(&nfmt, "%s: %s", p, strerror(errno)) == -1)
-                       fatal(NULL);
+               logit(LOG_ERR, "%s: %s", p, strerror(saved_errno));
        } else {
-               if (asprintf(&nfmt, "%s: %s: %s", p, emsg, strerror(errno)) ==
-                   -1)
+               va_start(ap, emsg);
+               if (vasprintf(&msg, emsg, ap) == -1)
                        fatal(NULL);
+               va_end(ap);
+               logit(LOG_ERR, "%s: %s: %s", p, msg, strerror(saved_errno));
+               free(msg);
        }
-       va_start(ap, emsg);
-       vlog(LOG_ERR, nfmt, ap);
-       va_end(ap);
        free(p);
-       free(nfmt);
 }
 
 void
 log_peer_warnx(const struct peer_config *peer, const char *emsg, ...)
 {
-       char    *p, *nfmt;
+       char    *p, *msg;
        va_list  ap;
 
        p = log_fmt_peer(peer);
-       if (asprintf(&nfmt, "%s: %s", p, emsg) == -1)
-               fatal(NULL);
        va_start(ap, emsg);
-       vlog(LOG_ERR, nfmt, ap);
+       if (vasprintf(&msg, emsg, ap) == -1)
+               fatal(NULL);
        va_end(ap);
+       logit(LOG_ERR, "%s: %s", p, msg);
+       free(msg);
        free(p);
-       free(nfmt);
 }
 
 void
Index: rde.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v
retrieving revision 1.610
diff -u -p -r1.610 rde.c
--- rde.c       16 Aug 2023 08:26:35 -0000      1.610
+++ rde.c       9 Oct 2023 14:16:23 -0000
@@ -59,7 +59,7 @@ int            rde_attr_parse(u_char *, uint16_t,
 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 *pee
                /* 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, i
 
 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_
                 * 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_
                 * 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_
                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);
        }
 
Index: rde.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v
retrieving revision 1.296
diff -u -p -r1.296 rde.h
--- rde.h       16 Aug 2023 08:26:35 -0000      1.296
+++ rde.h       9 Oct 2023 07:26:49 -0000
@@ -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;
Index: rde_peer.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_peer.c,v
retrieving revision 1.32
diff -u -p -r1.32 rde_peer.c
--- rde_peer.c  19 Apr 2023 13:23:33 -0000      1.32
+++ rde_peer.c  26 Apr 2023 14:04:35 -0000
@@ -430,6 +430,7 @@ peer_up(struct rde_peer *peer, struct se
        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 */
Index: rde_rib.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_rib.c,v
retrieving revision 1.260
diff -u -p -r1.260 rde_rib.c
--- rde_rib.c   23 Apr 2023 11:39:10 -0000      1.260
+++ rde_rib.c   9 Oct 2023 14:05:30 -0000
@@ -1910,7 +1910,7 @@ nexthop_compare(struct nexthop *na, stru
        case AID_INET6:
                return (memcmp(&a->v6, &b->v6, sizeof(struct in6_addr)));
        default:
-               fatalx("nexthop_cmp: unknown af");
+               fatalx("nexthop_cmp: unknown aid %s", aid2str(a->aid));
        }
        return (-1);
 }
Index: session.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/session.c,v
retrieving revision 1.448
diff -u -p -r1.448 session.c
--- session.c   9 Oct 2023 07:11:20 -0000       1.448
+++ session.c   16 Oct 2023 07:13:58 -0000
@@ -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, 
                        }
                }
                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, 
                }
                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 *pee
        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;
Index: session.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/session.h,v
retrieving revision 1.162
diff -u -p -r1.162 session.h
--- session.h   28 Mar 2023 12:15:23 -0000      1.162
+++ session.h   30 Mar 2023 07:17:19 -0000
@@ -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;

Reply via email to