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

Reply via email to