On Tue, Jul 04, 2023 at 11:39:19AM -0400, Dave Voutila wrote: > vmd's doing something close to shotgun parsing of the "local prefix" and > "local inet6 prefix" settings in vm.conf(5). The parser intermixes ipv4 > and ipv6 parsing even when we know which one is valid in the parsing > context. This makes me sad. > > Even worse, we're not validating the inputs at time of parsing and > deferring to vm creation time. This makes me even sadder. > > The diff below: > - splits parsing ipv4 and ipv6 into distinct functions > - puts the validation into those functions (e.g prefix length, prefix > has properly zero'd octets) > - does *not* muck (yet) with the existing validation sprinkled in > priv.c or config.c > > I thought about making pulling apart the prefix from prefix length > parsing, but getaddrinfo(3) appears to not like parsing addresses like > "192.168.0.0" vs "192.168.0.0/16". (I'm not a network person...maybe I'm > being dumb here.) > > kn: this addresses some of your feedback on my previous diff from a few > weeks ago. > > ok?
Most of these issues have been solved in for example bgpd. The code there is able to parse most address forms also shortcuts like 192.168/24. Have a look at bgpd/config.c::host() and host_ip() on how it works. > diff refs/heads/master refs/heads/vmd-parse > commit - 5d90c77abd2d7447f16f88ac9ea9e0485eac9f73 > commit + 228fe48802ec6250e3487aa791daceba4626b03f > blob - b538d40be1a1e600c1021d95e3fadd310079fa7a > blob + f5a2507ff5742ea3a62b0112e14d17aa8cbdf99d > --- usr.sbin/vmd/config.c > +++ usr.sbin/vmd/config.c > @@ -49,7 +49,7 @@ config_init_localprefix(struct vmd_config *cfg) > { > struct sockaddr_in6 *sin6; > > - if (host(VMD_DHCP_PREFIX, &cfg->cfg_localprefix) == -1) > + if (parse_prefix4(VMD_DHCP_PREFIX, &cfg->cfg_localprefix, NULL) == -1) > return (-1); > > /* IPv6 is disabled by default */ > @@ -58,7 +58,7 @@ config_init_localprefix(struct vmd_config *cfg) > /* Generate random IPv6 prefix only once */ > if (cfg->cfg_flags & VMD_CFG_AUTOINET6) > return (0); > - if (host(VMD_ULA_PREFIX, &cfg->cfg_localprefix6) == -1) > + if (parse_prefix6(VMD_ULA_PREFIX, &cfg->cfg_localprefix6, NULL) == -1) > return (-1); > /* Randomize the 56 bits "Global ID" and "Subnet ID" */ > sin6 = ss2sin6(&cfg->cfg_localprefix6.ss); > blob - 09468e3fe2c9f4f9193710c65667132f79a90df3 > blob + 3d030c201db3e8167831846cb1c8f6e3facc40fc > --- usr.sbin/vmd/parse.y > +++ usr.sbin/vmd/parse.y > @@ -190,31 +190,30 @@ main : LOCAL INET6 { > } > | LOCAL INET6 PREFIX STRING { > struct address h; > + const char *err; > > - if (host($4, &h) == -1 || > - h.ss.ss_family != AF_INET6 || > - h.prefixlen > 64 || h.prefixlen < 0) { > - yyerror("invalid local inet6 prefix: %s", $4); > - free($4); > + if (parse_prefix6($4, &h, &err)) { > + yyerror("invalid local inet6 prefix: %s", err); > YYERROR; > + } else { > + env->vmd_cfg.cfg_flags |= VMD_CFG_INET6; > + env->vmd_cfg.cfg_flags &= ~VMD_CFG_AUTOINET6; > + memcpy(&env->vmd_cfg.cfg_localprefix6, &h, > + sizeof(h)); > } > - > - env->vmd_cfg.cfg_flags |= VMD_CFG_INET6; > - env->vmd_cfg.cfg_flags &= ~VMD_CFG_AUTOINET6; > - memcpy(&env->vmd_cfg.cfg_localprefix6, &h, sizeof(h)); > + free($4); > } > | LOCAL PREFIX STRING { > struct address h; > + const char *err; > > - if (host($3, &h) == -1 || > - h.ss.ss_family != AF_INET || > - h.prefixlen > 32 || h.prefixlen < 0) { > - yyerror("invalid local prefix: %s", $3); > - free($3); > + if (parse_prefix4($3, &h, &err)) { > + yyerror("invalid local prefix: %s", err); > YYERROR; > - } > - > - memcpy(&env->vmd_cfg.cfg_localprefix, &h, sizeof(h)); > + } else > + memcpy(&env->vmd_cfg.cfg_localprefix, &h, > + sizeof(h)); > + free($3); > } > | SOCKET OWNER owner_id { > env->vmd_ps.ps_csock.cs_uid = $3.uid; > @@ -1404,42 +1403,119 @@ int > return (0); > } > > +/* > + * Parse an ipv4 address and prefix for local interfaces and validate > + * constraints for vmd networking. > + */ > int > -host(const char *str, struct address *h) > +parse_prefix4(const char *str, struct address *h, const char **errstr) > { > - struct addrinfo hints, *res; > - int prefixlen; > - char *s, *p; > - const char *errstr; > + struct addrinfo hints, *res = NULL; > + in_addr_t addr; > + int prefixlen, ret; > + char *s = NULL, *p = NULL; > > - if ((s = strdup(str)) == NULL) { > - log_warn("%s", __func__); > - goto fail; > + if ((s = strdup(str)) == NULL) > + fatal("%s: strdup", __func__); > + p = strrchr(s, '/'); > + if (p == NULL) { > + if (errstr) > + *errstr = "missing netmask"; > + return (1); > } > + *p++ = '\0'; > > - if ((p = strrchr(s, '/')) != NULL) { > - *p++ = '\0'; > - prefixlen = strtonum(p, 0, 128, &errstr); > - if (errstr) { > - log_warnx("prefixlen is %s: %s", errstr, p); > - goto fail; > + /* Parse our prefix. For ipv4, we accept 16 as maximum. */ > + prefixlen = strtonum(p, 1, 16, errstr); > + if (prefixlen == 0) { > + free(s); > + return (1); > + } > + > + /* Attempt to construct an address from the user input. */ > + memset(&hints, 0, sizeof(hints)); > + hints.ai_family = AF_INET; > + hints.ai_flags = AI_NUMERICHOST; > + if ((ret = getaddrinfo(s, NULL, &hints, &res)) != 0) { > + if (errstr != NULL) > + *errstr = gai_strerror(ret); > + free(s); > + return (1); > + } > + free(s); > + > + memset(h, 0, sizeof(*h)); > + memcpy(&h->ss, res->ai_addr, res->ai_addrlen); > + h->prefixlen = prefixlen; > + freeaddrinfo(res); > + > + /* Our prefix address should end with zeros. */ > + addr = ss2sin(&h->ss)->sin_addr.s_addr; > + if (addr & 0xFFFF0000) { > + if (errstr != NULL) > + *errstr = "last two octets must be zero"; > + return (1); > + } > + > + return (0); > +} > + > +/* > + * Parse an ipv6 address and prefix for local interfaces and validate > + * constraints for vmd networking. > + */ > +int > +parse_prefix6(const char *str, struct address *h, const char **errstr) > +{ > + struct addrinfo hints, *res = NULL; > + struct in6_addr *addr6 = NULL; > + int prefixlen, ret; > + char *s = NULL, *p = NULL; > + size_t i; > + > + if ((s = strdup(str)) == NULL) > + fatal("%s: strdup", __func__); > + p = strrchr(s, '/'); > + if (p == NULL) { > + if (errstr) > + *errstr = "missing netmask"; > + return (1); > + } > + *p++ = '\0'; > + > + /* Parse our prefix. For ipv6, we accept 64 as a maximum. */ > + prefixlen = strtonum(p, 1, 64, errstr); > + if (prefixlen == 0) { > + free(s); > + return (1); > + } > + > + /* Attempt to construct an address from the user input. */ > + memset(&hints, 0, sizeof(hints)); > + hints.ai_family = AF_INET6; > + hints.ai_flags = AI_NUMERICHOST; > + if ((ret = getaddrinfo(s, NULL, &hints, &res)) != 0) { > + if (errstr != NULL) > + *errstr = gai_strerror(ret); > + free(s); > + return (1); > + } > + free(s); > + > + memset(h, 0, sizeof(*h)); > + memcpy(&h->ss, res->ai_addr, res->ai_addrlen); > + h->prefixlen = prefixlen; > + freeaddrinfo(res); > + > + /* Last 8 octets are reserved for vmd. */ > + addr6 = &ss2sin6(&h->ss)->sin6_addr; > + for (i = 8; i < 16; i++) { > + if (addr6->s6_addr[i] != 0) { > + if (errstr != NULL) > + *errstr = "last eight octets must be zero"; > + return (1); > } > - } else > - prefixlen = 128; > - > - memset(&hints, 0, sizeof(hints)); > - hints.ai_family = AF_UNSPEC; > - hints.ai_flags = AI_NUMERICHOST; > - if (getaddrinfo(s, NULL, &hints, &res) == 0) { > - memset(h, 0, sizeof(*h)); > - memcpy(&h->ss, res->ai_addr, res->ai_addrlen); > - h->prefixlen = prefixlen; > - freeaddrinfo(res); > - free(s); > - return (0); > } > > - fail: > - free(s); > - return (-1); > + return (0); > } > blob - 9c25b0c92ade2e1a1d3a1f67548becfc3d4eca7b > blob + 91a8e1117f4859026db8adaa01d951ce1d9b4c11 > --- usr.sbin/vmd/vmd.h > +++ usr.sbin/vmd/vmd.h > @@ -518,7 +518,8 @@ int host(const char *, struct address *); > /* parse.y */ > int parse_config(const char *); > int cmdline_symset(char *); > -int host(const char *, struct address *); > +int parse_prefix4(const char *, struct address *, const char **); > +int parse_prefix6(const char *, struct address *, const char **); > > /* virtio.c */ > int virtio_get_base(int, char *, size_t, int, const char *); > -- :wq Claudio