From 137f9002269ec56522cf1cb335bb0fdf03c4a810 Mon Sep 17 00:00:00 2001 From: claudio Date: Thu, 29 Jun 2023 16:11:02 +0000 Subject: [PATCH] Rewrite pfe_route() to actually work on 64bit archs since IPv6 had to be special. One can not define a struct for the route message since there is different padding between 32 and 64 bit systems for struct sockaddr_in6. Instead do what all other daemons do and use struct sockaddr_storage, iovec and writev. Problem reported by Joerg Streckfuss (streckfuss at dfn-cert.de) OK tb@ --- usr.sbin/relayd/pfe_route.c | 183 +++++++++++++++--------------------- 1 file changed, 77 insertions(+), 106 deletions(-) diff --git a/usr.sbin/relayd/pfe_route.c b/usr.sbin/relayd/pfe_route.c index b968a340ec4..29f01eb9d60 100644 --- a/usr.sbin/relayd/pfe_route.c +++ b/usr.sbin/relayd/pfe_route.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pfe_route.c,v 1.12 2017/05/28 10:39:15 benno Exp $ */ +/* $OpenBSD: pfe_route.c,v 1.13 2023/06/29 16:11:02 claudio Exp $ */ /* * Copyright (c) 2009 - 2011 Reyk Floeter @@ -19,12 +19,14 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -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 router *rt) } } +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; + 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; + + 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); + + iov[iovcnt].iov_base = &dst; + iov[iovcnt++].iov_len = ROUNDUP(dst.ss_len); + hdr.rtm_msglen += ROUNDUP(dst.ss_len); - bzero(&rm, sizeof(rm)); - bzero(&sr, sizeof(sr)); + iov[iovcnt].iov_base = &gw; + iov[iovcnt++].iov_len = ROUNDUP(gw.ss_len); + hdr.rtm_msglen += ROUNDUP(gw.ss_len); - 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 = &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; } -- 2.20.1