Once again struct sockaddr_in6 causes 64bit systems to cry. This time in
relayd. You can not statically setup a route message and think it will
work. All our routing daemons switched to iov for building the route
message out of various components. This diff does the same for relayd.
With this it is possible to use router blocks with IPv6 addrs.

Btw. this does not work with link local addressing but I do not care
about that dumpster fire.
-- 
:wq Claudio

Index: pfe_route.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/pfe_route.c,v
retrieving revision 1.12
diff -u -p -r1.12 pfe_route.c
--- pfe_route.c 28 May 2017 10:39:15 -0000      1.12
+++ pfe_route.c 29 Jun 2023 12:55:59 -0000
@@ -19,12 +19,14 @@
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/socket.h>
+#include <sys/uio.h>
 
 #include <netinet/in.h>
 #include <net/route.h>
 #include <arpa/inet.h>
 
 #include <limits.h>
+#include <stddef.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <string.h>
@@ -32,24 +34,6 @@
 
 #include "relayd.h"
 
-struct relay_rtmsg {
-       struct rt_msghdr        rm_hdr;
-       union {
-               struct {
-                       struct sockaddr_in      rm_dst;
-                       struct sockaddr_in      rm_gateway;
-                       struct sockaddr_in      rm_netmask;
-                       struct sockaddr_rtlabel rm_label;
-               }                u4;
-               struct {
-                       struct sockaddr_in6     rm_dst;
-                       struct sockaddr_in6     rm_gateway;
-                       struct sockaddr_in6     rm_netmask;
-                       struct sockaddr_rtlabel rm_label;
-               }                u6;
-       }                        rm_u;
-};
-
 void
 init_routes(struct relayd *env)
 {
@@ -103,110 +87,97 @@ sync_routes(struct relayd *env, struct r
        }
 }
 
+static void
+pfe_apply_prefixlen(struct sockaddr_storage *ss, int af, int len)
+{
+       int q, r, off;
+       uint8_t *b = (uint8_t *)ss;
+
+        q = len >> 3;
+       r = len & 7;
+
+       bzero(ss, sizeof(*ss));
+       ss->ss_family = af;
+       switch (af) {
+       case AF_INET:
+               ss->ss_len = sizeof(struct sockaddr_in);
+               off = offsetof(struct sockaddr_in, sin_addr);
+               break;
+       case AF_INET6:
+               ss->ss_len = sizeof(struct sockaddr_in6);
+               off = offsetof(struct sockaddr_in6, sin6_addr);
+               break;
+       default:
+               fatal("%s: invalid address family", __func__);
+       }
+       if (q > 0)
+               memset(b + off, 0xff, q);
+       if (r > 0)
+               b[off + q] = (0xff00 >> r) & 0xff;
+}
+
+#define ROUNDUP(a) \
+       ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+
 int
 pfe_route(struct relayd *env, struct ctl_netroute *crt)
 {
-       struct relay_rtmsg               rm;
-       struct sockaddr_rtlabel          sr;
-       struct sockaddr_storage         *gw;
-       struct sockaddr_in              *s4;
-       struct sockaddr_in6             *s6;
-       size_t                           len = 0;
+       struct iovec                     iov[5];
+       struct rt_msghdr                 hdr;
+       struct sockaddr_storage          dst, gw, mask, label;
+       struct sockaddr_rtlabel         *sr = (struct sockaddr_rtlabel *)&label;
+       int                              iovcnt = 0;
        char                            *gwname;
-       int                              i = 0;
 
-       gw = &crt->host.ss;
-       gwname = crt->host.name;
+       bzero(&hdr, sizeof(hdr));
+       hdr.rtm_msglen = sizeof(hdr);
+       hdr.rtm_version = RTM_VERSION;
+       hdr.rtm_type = HOST_ISUP(crt->up) ? RTM_ADD : RTM_DELETE;
+       hdr.rtm_flags = RTF_STATIC | RTF_GATEWAY | RTF_MPATH;
+       hdr.rtm_seq = env->sc_rtseq++;
+       hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
+       hdr.rtm_tableid = crt->rt.rtable;
+       hdr.rtm_priority = crt->host.priority;
 
-       bzero(&rm, sizeof(rm));
-       bzero(&sr, sizeof(sr));
+       iov[iovcnt].iov_base = &hdr;
+       iov[iovcnt++].iov_len = sizeof(hdr);
+
+       dst = crt->nr.ss;
+       gw = crt->host.ss;
+       gwname = crt->host.name;
+       pfe_apply_prefixlen(&mask, dst.ss_family, crt->nr.prefixlen);
 
-       rm.rm_hdr.rtm_msglen = len;
-       rm.rm_hdr.rtm_version = RTM_VERSION;
-       rm.rm_hdr.rtm_type = HOST_ISUP(crt->up) ? RTM_ADD : RTM_DELETE;
-       rm.rm_hdr.rtm_flags = RTF_STATIC | RTF_GATEWAY | RTF_MPATH;
-       rm.rm_hdr.rtm_seq = env->sc_rtseq++;
-       rm.rm_hdr.rtm_addrs = RTA_DST | RTA_GATEWAY;
-       rm.rm_hdr.rtm_tableid = crt->rt.rtable;
-       rm.rm_hdr.rtm_priority = crt->host.priority;
+       iov[iovcnt].iov_base = &dst;
+       iov[iovcnt++].iov_len = ROUNDUP(dst.ss_len);
+       hdr.rtm_msglen += ROUNDUP(dst.ss_len);
+
+       iov[iovcnt].iov_base = &gw;
+       iov[iovcnt++].iov_len = ROUNDUP(gw.ss_len);
+       hdr.rtm_msglen += ROUNDUP(gw.ss_len);
+
+       iov[iovcnt].iov_base = &mask;
+       iov[iovcnt++].iov_len = ROUNDUP(mask.ss_len);
+       hdr.rtm_msglen += ROUNDUP(mask.ss_len);
 
        if (strlen(crt->rt.label)) {
-               rm.rm_hdr.rtm_addrs |= RTA_LABEL;
-               sr.sr_len = sizeof(sr);
-               if (snprintf(sr.sr_label, sizeof(sr.sr_label),
-                   "%s", crt->rt.label) == -1)
-                       goto bad;
-       }
+               sr->sr_len = sizeof(*sr);
+               strlcpy(sr->sr_label, crt->rt.label, sizeof(sr->sr_label));
 
-       if (crt->nr.ss.ss_family == AF_INET) {
-               rm.rm_hdr.rtm_msglen = len =
-                   sizeof(rm.rm_hdr) + sizeof(rm.rm_u.u4);
-
-               bcopy(&sr, &rm.rm_u.u4.rm_label, sizeof(sr));
-
-               s4 = &rm.rm_u.u4.rm_dst;
-               s4->sin_family = AF_INET;
-               s4->sin_len = sizeof(rm.rm_u.u4.rm_dst);
-               s4->sin_addr.s_addr =
-                   ((struct sockaddr_in *)&crt->nr.ss)->sin_addr.s_addr;
-
-               s4 = &rm.rm_u.u4.rm_gateway;
-               s4->sin_family = AF_INET;
-               s4->sin_len = sizeof(rm.rm_u.u4.rm_gateway);
-               s4->sin_addr.s_addr =
-                   ((struct sockaddr_in *)gw)->sin_addr.s_addr;
-
-               rm.rm_hdr.rtm_addrs |= RTA_NETMASK;
-               s4 = &rm.rm_u.u4.rm_netmask;
-               s4->sin_family = AF_INET;
-               s4->sin_len = sizeof(rm.rm_u.u4.rm_netmask);
-               if (crt->nr.prefixlen)
-                       s4->sin_addr.s_addr =
-                           htonl(0xffffffff << (32 - crt->nr.prefixlen));
-               else if (crt->nr.prefixlen < 0)
-                       rm.rm_hdr.rtm_flags |= RTF_HOST;
-       } else if (crt->nr.ss.ss_family == AF_INET6) {
-               rm.rm_hdr.rtm_msglen = len =
-                   sizeof(rm.rm_hdr) + sizeof(rm.rm_u.u6);
-
-               bcopy(&sr, &rm.rm_u.u6.rm_label, sizeof(sr));
-
-               s6 = &rm.rm_u.u6.rm_dst;
-               bcopy(((struct sockaddr_in6 *)&crt->nr.ss),
-                   s6, sizeof(*s6));
-               s6->sin6_family = AF_INET6;
-               s6->sin6_len = sizeof(*s6);
-
-               s6 = &rm.rm_u.u6.rm_gateway;
-               bcopy(((struct sockaddr_in6 *)gw), s6, sizeof(*s6));
-               s6->sin6_family = AF_INET6;
-               s6->sin6_len = sizeof(*s6);
-
-               rm.rm_hdr.rtm_addrs |= RTA_NETMASK;
-               s6 = &rm.rm_u.u6.rm_netmask;
-               s6->sin6_family = AF_INET6;
-               s6->sin6_len = sizeof(*s6);
-               if (crt->nr.prefixlen) {
-                       for (i = 0; i < crt->nr.prefixlen / 8; i++)
-                               s6->sin6_addr.s6_addr[i] = 0xff;
-                       i = crt->nr.prefixlen % 8;
-                       if (i)
-                               s6->sin6_addr.s6_addr[crt->nr.prefixlen
-                                   / 8] = 0xff00 >> i;
-               } else if (crt->nr.prefixlen < 0)
-                       rm.rm_hdr.rtm_flags |= RTF_HOST;
-       } else
-               fatal("%s: invalid address family", __func__);
+               iov[iovcnt].iov_base = &label;
+               iov[iovcnt++].iov_len = ROUNDUP(label.ss_len);
+               hdr.rtm_msglen += ROUNDUP(label.ss_len);
+               hdr.rtm_addrs |= RTA_LABEL;
+       }
 
  retry:
-       if (write(env->sc_rtsock, &rm, len) == -1) {
+       if (writev(env->sc_rtsock, iov, iovcnt) == -1) {
                switch (errno) {
                case EEXIST:
                case ESRCH:
-                       if (rm.rm_hdr.rtm_type == RTM_ADD) {
-                               rm.rm_hdr.rtm_type = RTM_CHANGE;
+                       if (hdr.rtm_type == RTM_ADD) {
+                               hdr.rtm_type = RTM_CHANGE;
                                goto retry;
-                       } else if (rm.rm_hdr.rtm_type == RTM_DELETE) {
+                       } else if (hdr.rtm_type == RTM_DELETE) {
                                /* Ignore */
                                break;
                        }

Reply via email to