So far I've got only one ok from job@ and he'd like me to commit this.

So I asking if there are any objections against this going into the base.


Gerhard


On Tue, 28 Jan 2020 15:03:47 +0100 Gerhard Roth <gerhard_r...@genua.de> wrote:
> Hi,
> 
> this patch adds IPv6 support to umb(4).
> 
> It will try to obtain a IPv6 address if the kernel is compiled with INET6.
> Currently there is no option to disable IPv6 on such a kernel (other than
> manually calling "ifconfig umb0 -inet6"). Nor is there a IPv6-only mode which
> refrains from obtaining an IPv4 address from the kernel.
> 
> To get an IPv6 address, your provider has to offer one. But more importantly
> the firmware of your umb(4) device has to have IPv6 support. I stumbled
> across two older Sierra Wireless modules (EM8805 and MC3805) that refused
> to provide an IPv6 address.
> 
> Have fun,
> 
> Gerhard
> 
> 
> Index: sbin/ifconfig/ifconfig.c
> ===================================================================
> RCS file: /cvs/src/sbin/ifconfig/ifconfig.c,v
> retrieving revision 1.417
> diff -u -p -u -p -r1.417 ifconfig.c
> --- sbin/ifconfig/ifconfig.c  27 Dec 2019 14:34:46 -0000      1.417
> +++ sbin/ifconfig/ifconfig.c  23 Jan 2020 09:24:38 -0000
> @@ -5666,6 +5666,7 @@ umb_status(void)
>       char     apn[UMB_APN_MAXLEN+1];
>       char     pn[UMB_PHONENR_MAXLEN+1];
>       int      i, n;
> +     char     astr[INET6_ADDRSTRLEN];
>  
>       memset((char *)&mi, 0, sizeof(mi));
>       ifr.ifr_data = (caddr_t)&mi;
> @@ -5830,7 +5831,15 @@ umb_status(void)
>       for (i = 0, n = 0; i < UMB_MAX_DNSSRV; i++) {
>               if (mi.ipv4dns[i].s_addr == INADDR_ANY)
>                       break;
> -             printf("%s %s", n++ ? "" : "\tdns", inet_ntoa(mi.ipv4dns[i]));
> +             printf("%s %s", n++ ? "" : "\tdns",
> +                 inet_ntop(AF_INET, &mi.ipv4dns[i], astr, sizeof (astr)));
> +     }
> +     for (i = 0; i < UMB_MAX_DNSSRV; i++) {
> +             if (memcmp(&mi.ipv6dns[i], &in6addr_any,
> +                 sizeof (mi.ipv6dns[i])) == 0)
> +                     break;
> +             printf("%s %s", n++ ? "" : "\tdns",
> +                 inet_ntop(AF_INET6, &mi.ipv6dns[i], astr, sizeof (astr)));
>       }
>       if (n)
>               printf("\n");
> Index: share/man/man4/umb.4
> ===================================================================
> RCS file: /cvs/src/share/man/man4/umb.4,v
> retrieving revision 1.9
> diff -u -p -u -p -r1.9 umb.4
> --- share/man/man4/umb.4      23 Nov 2017 20:47:26 -0000      1.9
> +++ share/man/man4/umb.4      28 Jan 2020 09:04:20 -0000
> @@ -40,6 +40,11 @@ will remain in this state until the MBIM
>  In case the device is connected to an "always-on" USB port,
>  it may be possible to connect to a provider without entering the
>  PIN again even if the system was rebooted.
> +.Pp
> +If the kernel has been compiled with INET6, the driver will try to
> +obtain an IPv6 address from the provider. To succeed with the IPv6
> +configuration, both the ISP and the MBIM device have to offer IPv6
> +support.
>  .Sh HARDWARE
>  The following devices should work:
>  .Pp
> @@ -64,10 +69,6 @@ The following devices should work:
>  .%U http://www.usb.org/developers/docs/devclass_docs/MBIM10Errata1_073013.zip
>  .Re
>  .Sh CAVEATS
> -The
> -.Nm
> -driver does not support IPv6.
> -.Pp
>  Devices which fail to provide a conforming MBIM implementation will
>  probably be attached as some other driver, such as
>  .Xr umsm 4 .
> Index: sys/dev/usb/if_umb.c
> ===================================================================
> RCS file: /cvs/src/sys/dev/usb/if_umb.c,v
> retrieving revision 1.31
> diff -u -p -u -p -r1.31 if_umb.c
> --- sys/dev/usb/if_umb.c      26 Nov 2019 23:04:28 -0000      1.31
> +++ sys/dev/usb/if_umb.c      28 Jan 2020 09:08:16 -0000
> @@ -43,6 +43,14 @@
>  #include <netinet/in_var.h>
>  #include <netinet/ip.h>
>  
> +#ifdef INET6
> +#include <netinet/ip6.h>
> +#include <netinet6/in6_var.h>
> +#include <netinet6/ip6_var.h>
> +#include <netinet6/in6_ifattach.h>
> +#include <netinet6/nd6.h>
> +#endif
> +
>  #include <machine/bus.h>
>  
>  #include <dev/usb/usb.h>
> @@ -158,7 +166,11 @@ int               umb_decode_connect_info(struct umb
>  void          umb_clear_addr(struct umb_softc *);
>  int           umb_add_inet_config(struct umb_softc *, struct in_addr, u_int,
>                   struct in_addr);
> -void          umb_send_inet_proposal(struct umb_softc *);
> +#ifdef INET6
> +int           umb_add_inet6_config(struct umb_softc *, struct in6_addr *,
> +                 u_int, struct in6_addr *);
> +#endif
> +void          umb_send_inet_proposal(struct umb_softc *, int);
>  int           umb_decode_ip_configuration(struct umb_softc *, void *, int);
>  void          umb_rx(struct umb_softc *);
>  void          umb_rxeof(struct usbd_xfer *, void *, usbd_status);
> @@ -800,8 +812,8 @@ umb_input(struct ifnet *ifp, struct mbuf
>  #endif /* INET6 */
>       default:
>               ifp->if_ierrors++;
> -             DPRINTFN(4, "%s: dropping packet with bad IP version (%d)\n",
> -                 __func__, ipv);
> +             DPRINTFN(4, "%s: dropping packet with bad IP version (af %d)\n",
> +                 __func__, af);
>               m_freem(m);
>               return 1;
>       }
> @@ -902,7 +914,10 @@ umb_rtrequest(struct ifnet *ifp, int req
>       struct umb_softc *sc = ifp->if_softc;
>  
>       if (req == RTM_PROPOSAL) {
> -             umb_send_inet_proposal(sc);
> +             umb_send_inet_proposal(sc, AF_INET);
> +#ifdef INET6
> +             umb_send_inet_proposal(sc, AF_INET6);
> +#endif
>               return;
>       }
>  
> @@ -1596,11 +1611,6 @@ umb_decode_connect_info(struct umb_softc
>               if (ifp->if_flags & IFF_DEBUG)
>                       log(LOG_INFO, "%s: connection %s\n", DEVNAM(sc),
>                           umb_activation(act));
> -             if ((ifp->if_flags & IFF_DEBUG) &&
> -                 letoh32(ci->iptype) != MBIM_CONTEXT_IPTYPE_DEFAULT &&
> -                 letoh32(ci->iptype) != MBIM_CONTEXT_IPTYPE_IPV4)
> -                     log(LOG_DEBUG, "%s: got iptype %d connection\n",
> -                         DEVNAM(sc), letoh32(ci->iptype));
>  
>               sc->sc_info.activation = act;
>               sc->sc_info.nwerror = letoh32(ci->nwerror);
> @@ -1621,9 +1631,16 @@ umb_clear_addr(struct umb_softc *sc)
>       struct ifnet *ifp = GET_IFP(sc);
>  
>       memset(sc->sc_info.ipv4dns, 0, sizeof (sc->sc_info.ipv4dns));
> -     umb_send_inet_proposal(sc);
> +     memset(sc->sc_info.ipv6dns, 0, sizeof (sc->sc_info.ipv6dns));
> +     umb_send_inet_proposal(sc, AF_INET);
> +#ifdef INET6
> +     umb_send_inet_proposal(sc, AF_INET6);
> +#endif
>       NET_LOCK();
>       in_ifdetach(ifp);
> +#ifdef INET6
> +     in6_ifdetach(ifp);
> +#endif
>       NET_UNLOCK();
>  }
>  
> @@ -1698,29 +1715,125 @@ umb_add_inet_config(struct umb_softc *sc
>                   sockaddr_ntop(sintosa(&ifra.ifra_dstaddr), str[2],
>                   sizeof(str[2])));
>       }
> -     return rv;
> +     return 0;
>  }
>  
> +#ifdef INET6
> +int
> +umb_add_inet6_config(struct umb_softc *sc, struct in6_addr *ip, u_int 
> prefixlen,
> +    struct in6_addr *gw)
> +{
> +     struct ifnet *ifp = GET_IFP(sc);
> +     struct in6_aliasreq ifra;
> +     struct sockaddr_in6 *sin6, default_sin6;
> +     struct rt_addrinfo info;
> +     struct rtentry *rt;
> +     int      rv;
> +
> +     memset(&ifra, 0, sizeof (ifra));
> +     sin6 = &ifra.ifra_addr;
> +     sin6->sin6_family = AF_INET6;
> +     sin6->sin6_len = sizeof (*sin6);
> +     memcpy(&sin6->sin6_addr, ip, sizeof (sin6->sin6_addr));
> +
> +     sin6 = &ifra.ifra_dstaddr;
> +     sin6->sin6_family = AF_INET6;
> +     sin6->sin6_len = sizeof (*sin6);
> +     memcpy(&sin6->sin6_addr, gw, sizeof (sin6->sin6_addr));
> +
> +     /* XXX: in6_update_ifa() accepts only 128 bits for P2P interfaces. */
> +     prefixlen = 128;
> +
> +     sin6 = &ifra.ifra_prefixmask;
> +     sin6->sin6_family = AF_INET6;
> +     sin6->sin6_len = sizeof (*sin6);
> +     in6_prefixlen2mask(&sin6->sin6_addr, prefixlen);
> +
> +     ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
> +     ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
> +
> +     rv = in6_ioctl(SIOCAIFADDR_IN6, (caddr_t)&ifra, ifp, 1);
> +     if (rv != 0) {
> +             printf("%s: unable to set IPv6 address, error %d\n",
> +                 DEVNAM(ifp->if_softc), rv);
> +             return rv;
> +     }
> +
> +     memset(&default_sin6, 0, sizeof(default_sin6));
> +     default_sin6.sin6_family = AF_INET6;
> +     default_sin6.sin6_len = sizeof (default_sin6);
> +
> +     memset(&info, 0, sizeof(info));
> +     info.rti_flags = RTF_GATEWAY /* maybe | RTF_STATIC */;
> +     info.rti_ifa = ifa_ifwithaddr(sin6tosa(&ifra.ifra_addr),
> +         ifp->if_rdomain);
> +     info.rti_info[RTAX_DST] = sin6tosa(&default_sin6);
> +     info.rti_info[RTAX_NETMASK] = sin6tosa(&default_sin6);
> +     info.rti_info[RTAX_GATEWAY] = sin6tosa(&ifra.ifra_dstaddr);
> +
> +     NET_LOCK();
> +     rv = rtrequest(RTM_ADD, &info, 0, &rt, ifp->if_rdomain);
> +     NET_UNLOCK();
> +     if (rv) {
> +             printf("%s: unable to set IPv6 default route, "
> +                 "error %d\n", DEVNAM(ifp->if_softc), rv);
> +             rtm_miss(RTM_MISS, &info, 0, RTP_NONE, 0, rv,
> +                 ifp->if_rdomain);
> +     } else {
> +             /* Inform listeners of the new route */
> +             rtm_send(rt, RTM_ADD, rv, ifp->if_rdomain);
> +             rtfree(rt);
> +     }
> +
> +     if (ifp->if_flags & IFF_DEBUG) {
> +             char str[3][INET6_ADDRSTRLEN];
> +             log(LOG_INFO, "%s: IPv6 addr %s, mask %s, gateway %s\n",
> +                 DEVNAM(ifp->if_softc),
> +                 sockaddr_ntop(sin6tosa(&ifra.ifra_addr), str[0],
> +                 sizeof(str[0])),
> +                 sockaddr_ntop(sin6tosa(&ifra.ifra_prefixmask), str[1],
> +                 sizeof(str[1])),
> +                 sockaddr_ntop(sin6tosa(&ifra.ifra_dstaddr), str[2],
> +                 sizeof(str[2])));
> +     }
> +     return 0;
> +}
> +#endif
> +
>  void
> -umb_send_inet_proposal(struct umb_softc *sc)
> +umb_send_inet_proposal(struct umb_softc *sc, int af)
>  {
>       struct ifnet *ifp = GET_IFP(sc);
>       struct sockaddr_rtdns rtdns;
>       struct rt_addrinfo info;
>       int i, flag = 0;
> +     size_t sz = 0;
>  
>       memset(&rtdns, 0, sizeof(rtdns));
>       memset(&info, 0, sizeof(info));
>  
>       for (i = 0; i < UMB_MAX_DNSSRV; i++) {
> -             if (sc->sc_info.ipv4dns[i].s_addr == INADDR_ANY)
> -                     break;
> -             memcpy(rtdns.sr_dns + i * sizeof(struct in_addr),
> -                 &sc->sc_info.ipv4dns[i], sizeof(struct in_addr));
> -             flag = RTF_UP;
> +             if (af == AF_INET) {
> +                     sz = sizeof (sc->sc_info.ipv4dns[i]);
> +                     if (sc->sc_info.ipv4dns[i].s_addr == INADDR_ANY)
> +                             break;
> +                     memcpy(rtdns.sr_dns + i * sz, &sc->sc_info.ipv4dns[i],
> +                         sz);
> +                     flag = RTF_UP;
> +#ifdef INET6
> +             } if (af == AF_INET6) {
> +                     sz = sizeof (sc->sc_info.ipv6dns[i]);
> +                     if (IN6_ARE_ADDR_EQUAL(&sc->sc_info.ipv6dns[i],
> +                         &in6addr_any))
> +                             break;
> +                     memcpy(rtdns.sr_dns + i * sz, &sc->sc_info.ipv6dns[i],
> +                         sz);
> +                     flag = RTF_UP;
> +#endif
> +             }
>       }
> -     rtdns.sr_family = AF_INET;
> -     rtdns.sr_len = 2 + i * sizeof(struct in_addr);
> +     rtdns.sr_family = af;
> +     rtdns.sr_len = 2 + i * sz;
>       info.rti_info[RTAX_DNS] = srtdnstosa(&rtdns);
>  
>       rtm_proposal(ifp, &info, flag, RTP_PROPOSAL_UMB);
> @@ -1732,7 +1845,7 @@ umb_decode_ip_configuration(struct umb_s
>       struct mbim_cid_ip_configuration_info *ic = data;
>       struct ifnet *ifp = GET_IFP(sc);
>       int      s;
> -     uint32_t avail;
> +     uint32_t avail_v4;
>       uint32_t val;
>       int      n, i;
>       int      off;
> @@ -1740,6 +1853,12 @@ umb_decode_ip_configuration(struct umb_s
>       struct in_addr addr, gw;
>       int      state = -1;
>       int      rv;
> +     int      hasmtu = 0;
> +#ifdef INET6
> +     uint32_t avail_v6;
> +     struct mbim_cid_ipv6_element ipv6elem;
> +     struct in6_addr addr6, gw6;
> +#endif
>  
>       if (len < sizeof (*ic))
>               return 0;
> @@ -1750,17 +1869,20 @@ umb_decode_ip_configuration(struct umb_s
>       }
>       s = splnet();
>  
> +     memset(sc->sc_info.ipv4dns, 0, sizeof (sc->sc_info.ipv4dns));
> +     memset(sc->sc_info.ipv6dns, 0, sizeof (sc->sc_info.ipv6dns));
> +
>       /*
>        * IPv4 configuation
>        */
> -     avail = letoh32(ic->ipv4_available);
> -     if ((avail & (MBIM_IPCONF_HAS_ADDRINFO | MBIM_IPCONF_HAS_GWINFO)) ==
> +     avail_v4 = letoh32(ic->ipv4_available);
> +     if ((avail_v4 & (MBIM_IPCONF_HAS_ADDRINFO | MBIM_IPCONF_HAS_GWINFO)) ==
>           (MBIM_IPCONF_HAS_ADDRINFO | MBIM_IPCONF_HAS_GWINFO)) {
>               n = letoh32(ic->ipv4_naddr);
>               off = letoh32(ic->ipv4_addroffs);
>  
>               if (n == 0 || off + sizeof (ipv4elem) > len)
> -                     goto done;
> +                     goto tryv6;
>               if (n != 1 && ifp->if_flags & IFF_DEBUG)
>                       log(LOG_INFO, "%s: more than one IPv4 addr: %d\n",
>                           DEVNAM(ifp->if_softc), n);
> @@ -1780,12 +1902,12 @@ umb_decode_ip_configuration(struct umb_s
>       }
>  
>       memset(sc->sc_info.ipv4dns, 0, sizeof (sc->sc_info.ipv4dns));
> -     if (avail & MBIM_IPCONF_HAS_DNSINFO) {
> +     if (avail_v4 & MBIM_IPCONF_HAS_DNSINFO) {
>               n = letoh32(ic->ipv4_ndnssrv);
>               off = letoh32(ic->ipv4_dnssrvoffs);
>               i = 0;
>               while (n-- > 0) {
> -                     if (off + sizeof (uint32_t) > len)
> +                     if (off + sizeof (addr) > len)
>                               break;
>                       memcpy(&addr, data + off, sizeof(addr));
>                       if (i < UMB_MAX_DNSSRV)
> @@ -1798,29 +1920,93 @@ umb_decode_ip_configuration(struct umb_s
>                                   &addr, str, sizeof(str)));
>                       }
>               }
> -             umb_send_inet_proposal(sc);
> +             umb_send_inet_proposal(sc, AF_INET);
>       }
> -
> -     if ((avail & MBIM_IPCONF_HAS_MTUINFO)) {
> +     if ((avail_v4 & MBIM_IPCONF_HAS_MTUINFO)) {
>               val = letoh32(ic->ipv4_mtu);
>               if (ifp->if_hardmtu != val && val <= sc->sc_maxpktlen) {
> +                     hasmtu = 1;
>                       ifp->if_hardmtu = val;
>                       if (ifp->if_mtu > val)
>                               ifp->if_mtu = val;
> -                     if (ifp->if_flags & IFF_DEBUG)
> -                             log(LOG_INFO, "%s: MTU %d\n", DEVNAM(sc), val);
>               }
>       }
>  
> -     avail = letoh32(ic->ipv6_available);
> -     if ((ifp->if_flags & IFF_DEBUG) && avail & MBIM_IPCONF_HAS_ADDRINFO) {
> -             /* XXX FIXME: IPv6 configuation missing */
> -             log(LOG_INFO, "%s: ignoring IPv6 configuration\n", DEVNAM(sc));
> +tryv6:;
> +#ifdef INET6
> +     /*
> +      * IPv6 configuation
> +      */
> +     avail_v6 = letoh32(ic->ipv6_available);
> +     if (avail_v6 == 0) {
> +             if (ifp->if_flags & IFF_DEBUG)
> +                     log(LOG_INFO, "%s: ISP or WWAN module offers no IPv6 "
> +                         "support\n", DEVNAM(ifp->if_softc));
> +             goto done;
> +     }
> +
> +     if ((avail_v4 & (MBIM_IPCONF_HAS_ADDRINFO | MBIM_IPCONF_HAS_GWINFO)) ==
> +         (MBIM_IPCONF_HAS_ADDRINFO | MBIM_IPCONF_HAS_GWINFO)) {
> +             n = letoh32(ic->ipv6_naddr);
> +             off = letoh32(ic->ipv6_addroffs);
> +
> +             if (n == 0 || off + sizeof (ipv6elem) > len)
> +                     goto done;
> +             if (n != 1 && ifp->if_flags & IFF_DEBUG)
> +                     log(LOG_INFO, "%s: more than one IPv6 addr: %d\n",
> +                         DEVNAM(ifp->if_softc), n);
> +
> +             /* Only pick the first one */
> +             memcpy(&ipv6elem, data + off, sizeof (ipv6elem));
> +             memcpy(&addr6, ipv6elem.addr, sizeof (addr6));
> +
> +             off = letoh32(ic->ipv6_gwoffs);
> +             memcpy(&gw6, data + off, sizeof (gw6));
> +
> +             rv = umb_add_inet6_config(sc, &addr6, ipv6elem.prefixlen, &gw6);
> +             if (rv == 0)
> +                     state = UMB_S_UP;
> +     }
> +
> +     if (avail_v6 & MBIM_IPCONF_HAS_DNSINFO) {
> +             n = letoh32(ic->ipv6_ndnssrv);
> +             off = letoh32(ic->ipv6_dnssrvoffs);
> +             i = 0;
> +             while (n-- > 0) {
> +                     if (off + sizeof (addr6) > len)
> +                             break;
> +                     memcpy(&addr6, data + off, sizeof(addr6));
> +                     if (i < UMB_MAX_DNSSRV)
> +                             sc->sc_info.ipv6dns[i++] = addr6;
> +                     off += sizeof(addr6);
> +                     if (ifp->if_flags & IFF_DEBUG) {
> +                             char str[INET6_ADDRSTRLEN];
> +                             log(LOG_INFO, "%s: IPv6 nameserver %s\n",
> +                                 DEVNAM(ifp->if_softc), inet_ntop(AF_INET6,
> +                                 &addr6, str, sizeof(str)));
> +                     }
> +             }
> +             umb_send_inet_proposal(sc, AF_INET6);
> +     }
> +
> +     if ((avail_v6 & MBIM_IPCONF_HAS_MTUINFO)) {
> +             val = letoh32(ic->ipv6_mtu);
> +             if (ifp->if_hardmtu != val && val <= sc->sc_maxpktlen) {
> +                     hasmtu = 1;
> +                     ifp->if_hardmtu = val;
> +                     if (ifp->if_mtu > val)
> +                             ifp->if_mtu = val;
> +             }
>       }
> +#endif
> +
> +done:
> +     if (hasmtu && (ifp->if_flags & IFF_DEBUG))
> +             log(LOG_INFO, "%s: MTU %d\n", DEVNAM(sc), ifp->if_hardmtu);
> +
>       if (state != -1)
>               umb_newstate(sc, state, 0);
>  
> -done:
>       splx(s);
>       return 1;
>  }
> @@ -2392,7 +2578,12 @@ umb_send_connect(struct umb_softc *sc, i
>       c->passwd_size = htole32(0);
>       c->authprot = htole32(MBIM_AUTHPROT_NONE);
>       c->compression = htole32(MBIM_COMPRESSION_NONE);
> +#ifdef INET6
> +     /* XXX FIXME: support IPv6-only mode, too */
> +     c->iptype = htole32(MBIM_CONTEXT_IPTYPE_IPV4V6);
> +#else
>       c->iptype = htole32(MBIM_CONTEXT_IPTYPE_IPV4);
> +#endif
>       memcpy(c->context, umb_uuid_context_internet, sizeof (c->context));
>       umb_cmd(sc, MBIM_CID_CONNECT, MBIM_CMDOP_SET, c, off);
>  done:
> Index: sys/dev/usb/if_umb.h
> ===================================================================
> RCS file: /cvs/src/sys/dev/usb/if_umb.h,v
> retrieving revision 1.5
> diff -u -p -u -p -r1.5 if_umb.h
> --- sys/dev/usb/if_umb.h      26 Aug 2019 15:23:01 -0000      1.5
> +++ sys/dev/usb/if_umb.h      23 Jan 2020 09:24:38 -0000
> @@ -321,6 +321,7 @@ struct umb_info {
>  
>  #define UMB_MAX_DNSSRV                       2
>       struct in_addr          ipv4dns[UMB_MAX_DNSSRV];
> +     struct in6_addr         ipv6dns[UMB_MAX_DNSSRV];
>  };
>  
>  #ifdef _KERNEL
> @@ -380,6 +381,6 @@ struct umb_softc {
>  
>  #define sc_state             sc_info.state
>  #define sc_roaming           sc_info.enable_roaming
> -     struct umb_info         sc_info;
> +     struct umb_info          sc_info;
>  };
>  #endif /* _KERNEL */

Reply via email to