Pass sockaddr_in6 arround so that we have space to store the scope in
authorflorian <florian@openbsd.org>
Sun, 24 Jan 2021 08:57:10 +0000 (08:57 +0000)
committerflorian <florian@openbsd.org>
Sun, 24 Jan 2021 08:57:10 +0000 (08:57 +0000)
a proper field. Move KAME hack to kernel / userland boundaries.
Due to the way -d (delete) works in ndp(8), once we flip the kernel
over to not pass down embedded scope it also must not expect embeded
scope passed to the kernel.
OK claudio

usr.sbin/ndp/ndp.c

index a4e4a3c..ad5c56a 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ndp.c,v 1.101 2021/01/22 16:10:01 florian Exp $       */
+/*     $OpenBSD: ndp.c,v 1.102 2021/01/24 08:57:10 florian Exp $       */
 /*     $KAME: ndp.c,v 1.101 2002/07/17 08:46:33 itojun Exp $   */
 
 /*
@@ -119,11 +119,11 @@ char ifix_buf[IFNAMSIZ];          /* if_indextoname() */
 
 int file(char *);
 void getsocket(void);
-int parse_host(const char *, struct in6_addr *);
+int parse_host(const char *, struct sockaddr_in6 *);
 int set(int, char **);
 void get(const char *);
 int delete(const char *);
-void dump(struct in6_addr *, int);
+void dump(struct sockaddr_in6 *, int);
 static struct in6_nbrinfo *getnbrinfo(struct in6_addr *, int, int);
 static char *ether_str(struct sockaddr_dl *);
 int ndp_ether_aton(const char *, u_char *);
@@ -288,10 +288,9 @@ getsocket(void)
 }
 
 int
-parse_host(const char *host, struct in6_addr *in6)
+parse_host(const char *host, struct sockaddr_in6 *sin6)
 {
        struct addrinfo hints, *res;
-       struct sockaddr_in6 *sin6;
        int gai_error;
 
        bzero(&hints, sizeof(hints));
@@ -304,15 +303,7 @@ parse_host(const char *host, struct in6_addr *in6)
                warnx("%s: %s", host, gai_strerror(gai_error));
                return 1;
        }
-
-       sin6 = (struct sockaddr_in6 *)res->ai_addr;
-       *in6 = sin6->sin6_addr;
-#ifdef __KAME__
-       if (IN6_IS_ADDR_LINKLOCAL(in6)) {
-               *(u_int16_t *)&in6->s6_addr[2] = htons(sin6->sin6_scope_id);
-       }
-#endif
-
+       *sin6 = *(struct sockaddr_in6 *)res->ai_addr;
        freeaddrinfo(res);
        return 0;
 }
@@ -346,7 +337,7 @@ set(int argc, char *argv[])
        sdl_m = blank_sdl;
        sin_m = blank_sin;
 
-       if (parse_host(host, &sin->sin6_addr))
+       if (parse_host(host, sin))
                return 1;
        ea = (u_char *)LLADDR(&sdl_m);
        if (ndp_ether_aton(eaddr, ea) == 0)
@@ -368,7 +359,8 @@ set(int argc, char *argv[])
                errx(1, "RTM_GET(%s) failed", host);
        }
 
-       if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) {
+       if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr) &&
+           sin->sin6_scope_id == sin_m.sin6_scope_id) {
                if (sdl->sdl_family == AF_LINK &&
                    (rtm->rtm_flags & RTF_LLINFO) &&
                    !(rtm->rtm_flags & RTF_GATEWAY)) {
@@ -404,10 +396,10 @@ get(const char *host)
        struct sockaddr_in6 *sin = &sin_m;
 
        sin_m = blank_sin;
-       if (parse_host(host, &sin->sin6_addr))
+       if (parse_host(host, sin))
                return;
 
-       dump(&sin->sin6_addr, 0);
+       dump(sin, 0);
        if (found_entry == 0) {
                getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf,
                    sizeof(host_buf), NULL ,0,
@@ -429,13 +421,14 @@ delete(const char *host)
 
        getsocket();
        sin_m = blank_sin;
-       if (parse_host(host, &sin->sin6_addr))
+       if (parse_host(host, sin))
                return 1;
        if (rtget(&sin, &sdl)) {
                errx(1, "RTM_GET(%s) failed", host);
        }
 
-       if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) {
+       if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr) &&
+           sin->sin6_scope_id == sin_m.sin6_scope_id) {
                if (sdl->sdl_family == AF_LINK && rtm->rtm_flags & RTF_LLINFO) {
                        if (rtm->rtm_flags & RTF_LOCAL)
                                return (0);
@@ -455,16 +448,8 @@ delete:
                return (1);
        }
        if (rtmsg(RTM_DELETE) == 0) {
-               struct sockaddr_in6 s6 = *sin; /* XXX: for safety */
-
-#ifdef __KAME__
-               if (IN6_IS_ADDR_LINKLOCAL(&s6.sin6_addr)) {
-                       s6.sin6_scope_id = ntohs(*(u_int16_t *)&s6.sin6_addr.s6_addr[2]);
-                       *(u_int16_t *)&s6.sin6_addr.s6_addr[2] = 0;
-               }
-#endif
-               getnameinfo((struct sockaddr *)&s6,
-                   s6.sin6_len, host_buf,
+               getnameinfo((struct sockaddr *)sin,
+                   sin->sin6_len, host_buf,
                    sizeof(host_buf), NULL, 0,
                    (nflag ? NI_NUMERICHOST : 0));
                printf("%s (%s) deleted\n", host, host_buf);
@@ -481,7 +466,7 @@ delete:
  * Dump the entire neighbor cache
  */
 void
-dump(struct in6_addr *addr, int cflag)
+dump(struct sockaddr_in6 *addr, int cflag)
 {
        int mib[7];
        size_t needed;
@@ -534,6 +519,19 @@ again:;
                if (rtm->rtm_version != RTM_VERSION)
                        continue;
                sin = (struct sockaddr_in6 *)(next + rtm->rtm_hdrlen);
+#ifdef __KAME__
+               {
+                       struct in6_addr *in6 = &sin->sin6_addr;
+                       if ((IN6_IS_ADDR_LINKLOCAL(in6) ||
+                           IN6_IS_ADDR_MC_LINKLOCAL(in6) ||
+                           IN6_IS_ADDR_MC_INTFACELOCAL(in6)) &&
+                           sin->sin6_scope_id == 0) {
+                               sin->sin6_scope_id = (u_int32_t)
+                                   ntohs(*(u_short *)&in6->s6_addr[2]);
+                               *(u_short *)&in6->s6_addr[2] = 0;
+                       }
+               }
+#endif
                sdl = (struct sockaddr_dl *)((char *)sin + ROUNDUP(sin->sin6_len));
 
                /*
@@ -556,21 +554,13 @@ again:;
                        continue;
 
                if (addr) {
-                       if (!IN6_ARE_ADDR_EQUAL(addr, &sin->sin6_addr))
+                       if (!IN6_ARE_ADDR_EQUAL(&addr->sin6_addr,
+                           &sin->sin6_addr) || addr->sin6_scope_id !=
+                           sin->sin6_scope_id)
                                continue;
                        found_entry = 1;
                } else if (IN6_IS_ADDR_MULTICAST(&sin->sin6_addr))
                        continue;
-               if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) ||
-                   IN6_IS_ADDR_MC_LINKLOCAL(&sin->sin6_addr)) {
-                       /* XXX: should scope id be filled in the kernel? */
-                       if (sin->sin6_scope_id == 0)
-                               sin->sin6_scope_id = sdl->sdl_index;
-#ifdef __KAME__
-                       /* KAME specific hack; removed the embedded id */
-                       *(u_int16_t *)&sin->sin6_addr.s6_addr[2] = 0;
-#endif
-               }
                getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf,
                    sizeof(host_buf), NULL, 0, (nflag ? NI_NUMERICHOST : 0));
                if (cflag) {
@@ -778,7 +768,22 @@ rtmsg(int cmd)
                ADVANCE(cp, (struct sockaddr *)&(s));                   \
        }
 
+#ifdef __KAME__
+       {
+               struct sockaddr_in6 sin6 = sin_m;
+               struct in6_addr *in6 = &sin6.sin6_addr;
+               if (IN6_IS_ADDR_LINKLOCAL(in6) ||
+                   IN6_IS_ADDR_MC_LINKLOCAL(in6) ||
+                   IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
+                       *(u_int16_t *)& in6->s6_addr[2] =
+                           htons(sin6.sin6_scope_id);
+                       sin6.sin6_scope_id = 0;
+               }
+               NEXTADDR(RTA_DST, sin6);
+       }
+#else
        NEXTADDR(RTA_DST, sin_m);
+#endif
        NEXTADDR(RTA_GATEWAY, sdl_m);
 #if 0  /* we don't support ipv6addr/128 type proxying. */
        memset(&so_mask.sin6_addr, 0xff, sizeof(so_mask.sin6_addr));
@@ -841,6 +846,26 @@ rtget(struct sockaddr_in6 **sinp, struct sockaddr_dl **sdlp)
        if (sin == NULL || sdl == NULL)
                return (1);
 
+#ifdef __KAME__
+       {
+               static struct sockaddr_in6 ksin;
+               struct in6_addr *in6;
+
+               /* do not damage the route message, we need it for delete */
+               ksin = *sin;
+               sin = &ksin;
+               in6 = &sin->sin6_addr;
+
+               if ((IN6_IS_ADDR_LINKLOCAL(in6) ||
+                   IN6_IS_ADDR_MC_LINKLOCAL(in6) ||
+                   IN6_IS_ADDR_MC_INTFACELOCAL(in6)) &&
+                   sin->sin6_scope_id == 0) {
+                       sin->sin6_scope_id = (u_int32_t)ntohs(*(u_short *)
+                           &in6->s6_addr[2]);
+                       *(u_short *)&in6->s6_addr[2] = 0;
+               }
+       }
+#endif
        *sinp = sin;
        *sdlp = sdl;