Here's a new diff that removes the duplicate parsing bits as mentioned before but leaves masking the address to mask_addr() instead of doing it manually.
Furthermore, it also stops route(8) from assuming address strings without explicit prefix length to be /64. The old behaviour described in RFC 2374 from 1998 is obsolete as per RFC 3587 which states RFC 2374 was the definition of addresses for Format Prefix 001 (2000::/3) which is formally made historic by this document. Even though currently only 2000::/3 is being delegated by the IANA, implementations should not make any assumptions about 2000::/3 being special. In the future, the IANA might be directed to delegate currently unassigned portions of the IPv6 address space for the purpose of Global Unicast as well. This was brought to my attention by the (recent) misc@ thread "Confusing IPv6 route(8) results"[0] in which benno@ mentioned an important side effect when it comes to using `-prefixlen' before the address string since order is relavant here and the following would therefore blackhole a single /128 with after my patch applied: # route add -inet6 -prefixlen 56 2001:db8:: ::1 -blackhole Thus, I'll put a note into current.html in case this change should go in. FWIW, both FreeBSD and NetBSD already use `prefixlen()' and do not mask the address in `inet6_makenetandmask()'. NetBSD still assumes /64 as per RFC 2374 while FreeBSD does the same already I'm aiming for. Regress passes, no regressions found when manually adding/removing routes. Feedback? Objections? OK? 0: https://marc.info/?l=openbsd-misc&m=152712936731762 Index: route.c =================================================================== RCS file: /cvs/src/sbin/route/route.c,v retrieving revision 1.214 diff -u -p -r1.214 route.c --- route.c 1 May 2018 18:14:10 -0000 1.214 +++ route.c 10 Jun 2018 22:35:43 -0000 @@ -786,50 +786,19 @@ inet_makenetandmask(u_int32_t net, struc sin->sin_len = 1 + cp - (char *)sin; } -/* - * XXX the function may need more improvement... - */ int inet6_makenetandmask(struct sockaddr_in6 *sin6, char *plen) { - struct in6_addr in6; - const char *errstr; - int i, len, q, r; - - if (NULL==plen) { + if (!plen) { if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) && - sin6->sin6_scope_id == 0) { + sin6->sin6_scope_id == 0) plen = "0"; - } else if ((sin6->sin6_addr.s6_addr[0] & 0xe0) == 0x20) { - /* aggregatable global unicast - RFC2374 */ - memset(&in6, 0, sizeof(in6)); - if (!memcmp(&sin6->sin6_addr.s6_addr[8], - &in6.s6_addr[8], 8)) - plen = "64"; - } } - if (!plen || strcmp(plen, "128") == 0) + if (!plen || prefixlen(AF_INET6, plen)) return (1); - else { - rtm_addrs |= RTA_NETMASK; - prefixlen(AF_INET6, plen); - - len = strtonum(plen, 0, 128, &errstr); - if (errstr) - errx(1, "prefixlen %s is %s", plen, errstr); - - q = (128-len) >> 3; - r = (128-len) & 7; - i = 15; - while (q-- > 0) - sin6->sin6_addr.s6_addr[i--] = 0; - if (r > 0) - sin6->sin6_addr.s6_addr[i] &= 0xff << r; - - return (0); - } + return (0); } /* =================================================================== Stats: --- 35 lines 798 chars Stats: +++ 4 lines 100 chars Stats: -31 lines Stats: -698 chars