git: 0f1bf1c22a0c - main - umb: Introduce the USB umb(4) network driver
Date: Mon, 20 Jan 2025 23:47:40 UTC
The branch main has been updated by adrian:
URL: https://cgit.FreeBSD.org/src/commit/?id=0f1bf1c22a0c97e84a4db19197a75952487aa20b
commit 0f1bf1c22a0c97e84a4db19197a75952487aa20b
Author: Adrian Chadd <adrian@FreeBSD.org>
AuthorDate: 2025-01-20 23:46:15 +0000
Commit: Adrian Chadd <adrian@FreeBSD.org>
CommitDate: 2025-01-20 23:46:15 +0000
umb: Introduce the USB umb(4) network driver
This includes the port of a driver originally from OpenBSD, later
ported to NetBSD by the author:
* The umb(4) kernel driver
* The umbctl(8) companion tool
This driver supports USB network devices implementing the
Mobile Broadband Interface Model (MBIM), often found in modern
(internal) USB models for 4G/LTE mobile broadband access.
It is currently limited to IPv4.
umbctl has to be used to display or set MBIM cellular modem
interface parameters (4G/LTE).
Differential Revision: https://reviews.freebsd.org/D48167
Approved by: adrian, zlei
Sponsored by: FreeBSD Foundation
PR: kern/263783
Submitted by: Pierre Pronchery <khorben@defora.org>
---
sbin/Makefile | 1 +
sbin/umbctl/Makefile | 8 +
sbin/umbctl/umbctl.8 | 161 +++
sbin/umbctl/umbctl.c | 557 ++++++++
share/man/man4/Makefile | 1 +
share/man/man4/umb.4 | 119 ++
sys/conf/files | 1 +
sys/dev/usb/net/if_umb.c | 2930 ++++++++++++++++++++++++++++++++++++++++++
sys/dev/usb/net/if_umbreg.h | 443 +++++++
sys/dev/usb/net/mbim.h | 727 +++++++++++
sys/modules/usb/Makefile | 2 +-
sys/modules/usb/umb/Makefile | 33 +
12 files changed, 4982 insertions(+), 1 deletion(-)
diff --git a/sbin/Makefile b/sbin/Makefile
index 790112b05f6f..5e5a8943c67a 100644
--- a/sbin/Makefile
+++ b/sbin/Makefile
@@ -63,6 +63,7 @@ SUBDIR=adjkerntz \
swapon \
sysctl \
tunefs \
+ umbctl \
umount
.if ${MK_INET} != "no" || ${MK_INET6} != "no"
diff --git a/sbin/umbctl/Makefile b/sbin/umbctl/Makefile
new file mode 100644
index 000000000000..35afb1bcfd4b
--- /dev/null
+++ b/sbin/umbctl/Makefile
@@ -0,0 +1,8 @@
+CFLAGS+= -I${SRCTOP}/sys/dev/usb/net
+
+PROG= umbctl
+MAN= umbctl.8
+
+BINDIR= /sbin
+
+.include <bsd.prog.mk>
diff --git a/sbin/umbctl/umbctl.8 b/sbin/umbctl/umbctl.8
new file mode 100644
index 000000000000..55f8e315fabc
--- /dev/null
+++ b/sbin/umbctl/umbctl.8
@@ -0,0 +1,161 @@
+.\"-
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.\" Copyright (c) 2018 by Pierre Pronchery <khorben@defora.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
+.\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+.\" DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
+.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+.\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" From: pppoectl.8,v 1.30 2016/09/12 05:35:20 sevan Exp $
+.\"
+.\" $NetBSD: umbctl.8,v 1.3 2020/03/22 07:45:02 khorben Exp $
+.\"
+.\" last edit-date: [Fri Dec 20 18:20:00 2024]
+.\"
+.Dd December 20, 2024
+.Dt UMBCTL 8
+.Os
+.Sh NAME
+.Nm umbctl
+.Nd display or set MBIM cellular modem interface parameters (4G/LTE)
+.Sh SYNOPSIS
+.Nm
+.Op Fl v
+.Ar ifname
+.Op Ar parameter Op Ar value
+.Ar ...
+.Nm
+.Op Fl v
+.Fl f Ar config-file
+.Ar ifname
+.Sh DESCRIPTION
+.Nm
+supports the following options:
+.Bl -tag -width "-f config_file"
+.It Fl f Ar config-file
+Parse
+.Ar config-file
+for
+.Ar parameter Ns Op \&= Ns Ar value
+pairs, one per line, as if they had been specified on the command line.
+This allows the password or PIN codes to be not passed as command line
+arguments.
+Comments starting with # to the end of the current line are ignored.
+.It Fl v
+Enables verbose mode.
+.El
+.Pp
+The
+.Xr umb 4
+driver may require a number of additional arguments or optional
+parameters besides the settings that can be adjusted with
+.Xr ifconfig 8 .
+These may be credentials or other tunable connectivity variables.
+The
+.Nm
+utility can be used to display the current settings, or to adjust these
+parameters as required.
+.Pp
+For whatever intent
+.Nm
+is being called, at least the parameter
+.Ar ifname
+needs to be specified, naming the interface for which the settings
+are to be performed or displayed.
+Use
+.Xr ifconfig 8
+or
+.Xr netstat 1
+to see which interfaces are available.
+.Pp
+If no other parameter is given,
+.Nm
+will just list the current status for
+.Ar ifname
+and exit.
+.Pp
+If any additional parameter is supplied, superuser privileges are
+required, and the command works in
+.Ql set
+mode.
+This is normally done quietly, unless the option
+.Fl v
+is also enabled, which will cause a final printout of the status as
+described above once all other actions have been taken.
+.Pp
+The parameters currently supported include:
+.Bl -tag -width "username=username"
+.It Ar apn Ns \&= Ns Em access-point
+Set the APN to
+.Em access-point .
+.It Ar username Ns \&= Ns Em username
+Set the username to
+.Em username .
+.It Ar password Ns \&= Ns Em password
+Set the password to
+.Em password .
+.It Ar pin Ns \&= Ns Em pin-code
+Enter the PIN
+.Em pin-code .
+.It Ar puk Ns \&= Ns Em puk-code
+Enter the PUK
+.Em puk-code .
+.It Ar roaming
+Allow data connections when roaming.
+.It Ar -roaming
+Deny data connections when roaming.
+.El
+.Sh EXAMPLES
+Display the settings for umb0:
+.Bd -literal
+# umbctl umb0
+umb0: state up, mode automatic, registration home network
+ provider "BSD-Net", dataclass LTE, signal good
+ phone number "+15554242", roaming "" (denied)
+ APN "", TX 50000000, RX 100000000
+ firmware "MBIM_FW_V1.0", hardware "MBIM_HW_V1.0"
+.Ed
+.Pp
+Configure the connection parameters for umb0 from the command line:
+.Bd -literal
+# umbctl umb0 apn operator.internet username mobile password mobile
+.Ed
+.Pp
+Configure the connection parameters for umb0 from a file:
+.Bd -literal
+# umbctl -f /dev/stdin umb0 << EOF
+pin=1234
+EOF
+.Ed
+.Sh SEE ALSO
+.Xr netstat 1 ,
+.Xr umb 4 ,
+.Xr ifconfig 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Nx 9.0 ,
+and
+.Fx 15.0 .
+.Sh AUTHORS
+The program was written by
+.An Pierre Pronchery .
diff --git a/sbin/umbctl/umbctl.c b/sbin/umbctl/umbctl.c
new file mode 100644
index 000000000000..3d57b486ad80
--- /dev/null
+++ b/sbin/umbctl/umbctl.c
@@ -0,0 +1,557 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Original copyright (c) 2018 Pierre Pronchery <khorben@defora.org> (for the
+ * NetBSD Project)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Copyright (c) 2022 ADISTA SAS (FreeBSD updates)
+ *
+ * Updates for FreeBSD by Pierre Pronchery <pierre@defora.net>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * - Neither the name of the copyright holder nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $NetBSD: umbctl.c,v 1.4 2020/05/13 21:44:30 khorben Exp $
+ */
+
+#include <sys/endian.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include "mbim.h"
+#include "if_umbreg.h"
+
+/* constants */
+static const struct umb_valdescr _umb_actstate[] =
+ MBIM_ACTIVATION_STATE_DESCRIPTIONS;
+
+static const struct umb_valdescr _umb_simstate[] =
+ MBIM_SIMSTATE_DESCRIPTIONS;
+
+static const struct umb_valdescr _umb_regstate[] =
+ MBIM_REGSTATE_DESCRIPTIONS;
+
+static const struct umb_valdescr _umb_pktstate[] =
+ MBIM_PKTSRV_STATE_DESCRIPTIONS;
+
+static const struct umb_valdescr _umb_dataclass[] =
+ MBIM_DATACLASS_DESCRIPTIONS;
+
+static const struct umb_valdescr _umb_state[] =
+ UMB_INTERNAL_STATE_DESCRIPTIONS;
+
+static const struct umb_valdescr _umb_pin_state[] =
+{
+ { UMB_PIN_REQUIRED, "PIN required"},
+ { UMB_PIN_UNLOCKED, "PIN unlocked"},
+ { UMB_PUK_REQUIRED, "PUK required"},
+ { 0, NULL }
+};
+
+static const struct umb_valdescr _umb_regmode[] =
+{
+ { MBIM_REGMODE_UNKNOWN, "unknown" },
+ { MBIM_REGMODE_AUTOMATIC, "automatic" },
+ { MBIM_REGMODE_MANUAL, "manual" },
+ { 0, NULL }
+};
+
+static const struct umb_valdescr _umb_ber[] =
+{
+ { UMB_BER_EXCELLENT, "excellent" },
+ { UMB_BER_VERYGOOD, "very good" },
+ { UMB_BER_GOOD, "good" },
+ { UMB_BER_OK, "ok" },
+ { UMB_BER_MEDIUM, "medium" },
+ { UMB_BER_BAD, "bad" },
+ { UMB_BER_VERYBAD, "very bad" },
+ { UMB_BER_EXTREMELYBAD, "extremely bad" },
+ { 0, NULL }
+};
+
+
+/* prototypes */
+static int _char_to_utf16(const char * in, uint16_t * out, size_t outlen);
+static int _error(int ret, char const * format, ...);
+static int _umbctl(char const * ifname, int verbose, int argc, char * argv[]);
+static int _umbctl_file(char const * ifname, char const * filename,
+ int verbose);
+static void _umbctl_info(char const * ifname, struct umb_info * umbi);
+static int _umbctl_ioctl(char const * ifname, int fd, unsigned long request,
+ struct ifreq * ifr);
+static int _umbctl_set(char const * ifname, struct umb_parameter * umbp,
+ int argc, char * argv[]);
+static int _umbctl_socket(void);
+static int _usage(void);
+static void _utf16_to_char(uint16_t * in, int inlen, char * out, size_t outlen);
+
+
+/* functions */
+/* char_to_utf16 */
+/* this function is from OpenBSD's ifconfig(8) */
+static int _char_to_utf16(const char * in, uint16_t * out, size_t outlen)
+{
+ int n = 0;
+ uint16_t c;
+
+ for (;;) {
+ c = *in++;
+
+ if (c == '\0') {
+ /*
+ * NUL termination is not required, but zero out the
+ * residual buffer
+ */
+ memset(out, 0, outlen);
+ return n;
+ }
+ if (outlen < sizeof(*out))
+ return -1;
+
+ *out++ = htole16(c);
+ n += sizeof(*out);
+ outlen -= sizeof(*out);
+ }
+}
+
+
+/* error */
+static int _error(int ret, char const * format, ...)
+{
+ va_list ap;
+
+ fputs("umbctl: ", stderr);
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+ fputs("\n", stderr);
+ return ret;
+}
+
+
+/* umbctl */
+static int _umbctl(char const * ifname, int verbose, int argc, char * argv[])
+{
+ int fd;
+ struct ifreq ifr;
+ struct umb_info umbi;
+ struct umb_parameter umbp;
+
+ if((fd = _umbctl_socket()) < 0)
+ return 2;
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if(argc != 0)
+ {
+ memset(&umbp, 0, sizeof(umbp));
+ ifr.ifr_data = (caddr_t)&umbp;
+ if(_umbctl_ioctl(ifname, fd, SIOCGUMBPARAM, &ifr) != 0
+ || _umbctl_set(ifname, &umbp, argc, argv) != 0
+ || _umbctl_ioctl(ifname, fd, SIOCSUMBPARAM,
+ &ifr) != 0)
+ {
+ close(fd);
+ return 2;
+ }
+ }
+ if(argc == 0 || verbose > 0)
+ {
+ ifr.ifr_data = (caddr_t)&umbi;
+ if(_umbctl_ioctl(ifname, fd, SIOCGUMBINFO, &ifr) != 0)
+ {
+ close(fd);
+ return 3;
+ }
+ _umbctl_info(ifname, &umbi);
+ }
+ if(close(fd) != 0)
+ return _error(2, "%s: %s", ifname, strerror(errno));
+ return 0;
+}
+
+
+/* umbctl_file */
+static int _file_parse(char const * ifname, struct umb_parameter * umbp,
+ char const * filename);
+
+static int _umbctl_file(char const * ifname, char const * filename, int verbose)
+{
+ int fd;
+ struct ifreq ifr;
+ struct umb_parameter umbp;
+ struct umb_info umbi;
+
+ if((fd = _umbctl_socket()) < 0)
+ return 2;
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (caddr_t)&umbp;
+ memset(&umbp, 0, sizeof(umbp));
+ if(_umbctl_ioctl(ifname, fd, SIOCGUMBPARAM, &ifr) != 0
+ || _file_parse(ifname, &umbp, filename) != 0
+ || _umbctl_ioctl(ifname, fd, SIOCSUMBPARAM, &ifr) != 0)
+ {
+ close(fd);
+ return 2;
+ }
+ if(verbose > 0)
+ {
+ ifr.ifr_data = (caddr_t)&umbi;
+ if(_umbctl_ioctl(ifname, fd, SIOCGUMBINFO, &ifr) != 0)
+ {
+ close(fd);
+ return 3;
+ }
+ _umbctl_info(ifname, &umbi);
+ }
+ if(close(fd) != 0)
+ return _error(2, "%s: %s", ifname, strerror(errno));
+ return 0;
+}
+
+static int _file_parse(char const * ifname, struct umb_parameter * umbp,
+ char const * filename)
+{
+ int ret = 0;
+ FILE * fp;
+ char buf[512];
+ size_t len;
+ int i;
+ int eof;
+ char * tokens[3] = { buf, NULL, NULL };
+ char * p;
+
+ if((fp = fopen(filename, "r")) == NULL)
+ return _error(2, "%s: %s", filename, strerror(errno));
+ while(fgets(buf, sizeof(buf), fp) != NULL)
+ {
+ if(buf[0] == '#')
+ continue;
+ buf[sizeof(buf) - 1] = '\0';
+ if((len = strlen(buf)) > 0)
+ {
+ if(buf[len - 1] != '\n')
+ {
+ ret = _error(2, "%s: %s", filename,
+ "Line too long");
+ while((i = fgetc(fp)) != EOF && i != '\n');
+ continue;
+ }
+ buf[len - 1] = '\0';
+ }
+ if((p = strchr(buf, '=')) != NULL)
+ {
+ tokens[1] = p + 1;
+ *p = '\0';
+ } else
+ tokens[1] = NULL;
+ ret |= _umbctl_set(ifname, umbp, (p != NULL) ? 2 : 1, tokens)
+ ? 2 : 0;
+ }
+ eof = feof(fp);
+ if(fclose(fp) != 0 || !eof)
+ return _error(2, "%s: %s", filename, strerror(errno));
+ return ret;
+}
+
+
+/* umbctl_info */
+static void _umbctl_info(char const * ifname, struct umb_info * umbi)
+{
+ char provider[UMB_PROVIDERNAME_MAXLEN + 1];
+ char pn[UMB_PHONENR_MAXLEN + 1];
+ char roaming[UMB_ROAMINGTEXT_MAXLEN + 1];
+ char apn[UMB_APN_MAXLEN + 1];
+ char fwinfo[UMB_FWINFO_MAXLEN + 1];
+ char hwinfo[UMB_HWINFO_MAXLEN + 1];
+
+ _utf16_to_char(umbi->provider, UMB_PROVIDERNAME_MAXLEN,
+ provider, sizeof(provider));
+ _utf16_to_char(umbi->pn, UMB_PHONENR_MAXLEN, pn, sizeof(pn));
+ _utf16_to_char(umbi->roamingtxt, UMB_ROAMINGTEXT_MAXLEN,
+ roaming, sizeof(roaming));
+ _utf16_to_char(umbi->apn, UMB_APN_MAXLEN, apn, sizeof(apn));
+ _utf16_to_char(umbi->fwinfo, UMB_FWINFO_MAXLEN, fwinfo, sizeof(fwinfo));
+ _utf16_to_char(umbi->hwinfo, UMB_HWINFO_MAXLEN, hwinfo, sizeof(hwinfo));
+ printf("%s: state %s, mode %s, registration %s\n"
+ "\tprovider \"%s\", dataclass %s, signal %s\n"
+ "\tphone number \"%s\", roaming \"%s\" (%s)\n"
+ "\tAPN \"%s\", TX %" PRIu64 ", RX %" PRIu64 "\n"
+ "\tfirmware \"%s\", hardware \"%s\"\n",
+ ifname, umb_val2descr(_umb_state, umbi->state),
+ umb_val2descr(_umb_regmode, umbi->regmode),
+ umb_val2descr(_umb_regstate, umbi->regstate), provider,
+ umb_val2descr(_umb_dataclass, umbi->cellclass),
+ umb_val2descr(_umb_ber, umbi->ber), pn, roaming,
+ umbi->enable_roaming ? "allowed" : "denied",
+ apn, umbi->uplink_speed, umbi->downlink_speed,
+ fwinfo, hwinfo);
+}
+
+
+/* umbctl_ioctl */
+static int _umbctl_ioctl(char const * ifname, int fd, unsigned long request,
+ struct ifreq * ifr)
+{
+ if(ioctl(fd, request, ifr) != 0)
+ return _error(-1, "%s: %s", ifname, strerror(errno));
+ return 0;
+}
+
+
+/* umbctl_set */
+/* callbacks */
+static int _set_apn(char const *, struct umb_parameter *, char const *);
+static int _set_username(char const *, struct umb_parameter *, char const *);
+static int _set_password(char const *, struct umb_parameter *, char const *);
+static int _set_pin(char const *, struct umb_parameter *, char const *);
+static int _set_puk(char const *, struct umb_parameter *, char const *);
+static int _set_roaming_allow(char const *, struct umb_parameter *,
+ char const *);
+static int _set_roaming_deny(char const *, struct umb_parameter *,
+ char const *);
+
+static int _umbctl_set(char const * ifname, struct umb_parameter * umbp,
+ int argc, char * argv[])
+{
+ struct
+ {
+ char const * name;
+ int (*callback)(char const *,
+ struct umb_parameter *, char const *);
+ int parameter;
+ } callbacks[] =
+ {
+ { "apn", _set_apn, 1 },
+ { "username", _set_username, 1 },
+ { "password", _set_password, 1 },
+ { "pin", _set_pin, 1 },
+ { "puk", _set_puk, 1 },
+ { "roaming", _set_roaming_allow, 0 },
+ { "-roaming", _set_roaming_deny, 0 },
+ };
+ int i;
+ size_t j;
+
+ for(i = 0; i < argc; i++)
+ {
+ for(j = 0; j < sizeof(callbacks) / sizeof(*callbacks); j++)
+ if(strcmp(argv[i], callbacks[j].name) == 0)
+ {
+ if(callbacks[j].parameter && i + 1 == argc)
+ return _error(-1, "%s: Incomplete"
+ " parameter", argv[i]);
+ if(callbacks[j].callback(ifname, umbp,
+ callbacks[j].parameter
+ ? argv[i + 1] : NULL))
+ return -1;
+ if(callbacks[j].parameter)
+ i++;
+ break;
+ }
+ if(j == sizeof(callbacks) / sizeof(*callbacks))
+ return _error(-1, "%s: Unknown parameter", argv[i]);
+ }
+ return 0;
+}
+
+static int _set_apn(char const * ifname, struct umb_parameter * umbp,
+ char const * apn)
+{
+ umbp->apnlen = _char_to_utf16(apn, umbp->apn, sizeof(umbp->apn));
+ if(umbp->apnlen < 0 || (size_t)umbp->apnlen > sizeof(umbp->apn))
+ return _error(-1, "%s: %s", ifname, "APN too long");
+ return 0;
+}
+
+static int _set_username(char const * ifname, struct umb_parameter * umbp,
+ char const * username)
+{
+ umbp->usernamelen = _char_to_utf16(username, umbp->username,
+ sizeof(umbp->username));
+ if(umbp->usernamelen < 0
+ || (size_t)umbp->usernamelen > sizeof(umbp->username))
+ return _error(-1, "%s: %s", ifname, "Username too long");
+ return 0;
+}
+
+static int _set_password(char const * ifname, struct umb_parameter * umbp,
+ char const * password)
+{
+ umbp->passwordlen = _char_to_utf16(password, umbp->password,
+ sizeof(umbp->password));
+ if(umbp->passwordlen < 0
+ || (size_t)umbp->passwordlen > sizeof(umbp->password))
+ return _error(-1, "%s: %s", ifname, "Password too long");
+ return 0;
+}
+
+static int _set_pin(char const * ifname, struct umb_parameter * umbp,
+ char const * pin)
+{
+ umbp->is_puk = 0;
+ umbp->op = MBIM_PIN_OP_ENTER;
+ umbp->pinlen = _char_to_utf16(pin, umbp->pin, sizeof(umbp->pin));
+ if(umbp->pinlen < 0 || (size_t)umbp->pinlen
+ > sizeof(umbp->pin))
+ return _error(-1, "%s: %s", ifname, "PIN code too long");
+ return 0;
+}
+
+static int _set_puk(char const * ifname, struct umb_parameter * umbp,
+ char const * puk)
+{
+ umbp->is_puk = 1;
+ umbp->op = MBIM_PIN_OP_ENTER;
+ umbp->pinlen = _char_to_utf16(puk, umbp->pin, sizeof(umbp->pin));
+ if(umbp->pinlen < 0 || (size_t)umbp->pinlen > sizeof(umbp->pin))
+ return _error(-1, "%s: %s", ifname, "PUK code too long");
+ return 0;
+}
+
+static int _set_roaming_allow(char const * ifname, struct umb_parameter * umbp,
+ char const * unused)
+{
+ (void) ifname;
+ (void) unused;
+
+ umbp->roaming = 1;
+ return 0;
+}
+
+static int _set_roaming_deny(char const * ifname, struct umb_parameter * umbp,
+ char const * unused)
+{
+ (void) ifname;
+ (void) unused;
+
+ umbp->roaming = 0;
+ return 0;
+}
+
+
+/* umbctl_socket */
+static int _umbctl_socket(void)
+{
+ int fd;
+
+ if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ return _error(-1, "socket: %s", strerror(errno));
+ return fd;
+}
+
+
+/* usage */
+static int _usage(void)
+{
+ fputs("Usage: umbctl [-v] ifname [parameter [value]] [...]\n"
+" umbctl -f config-file ifname\n",
+ stderr);
+ return 1;
+}
+
+
+/* utf16_to_char */
+static void _utf16_to_char(uint16_t * in, int inlen, char * out, size_t outlen)
+{
+ uint16_t c;
+
+ while (outlen > 0) {
+ c = inlen > 0 ? htole16(*in) : 0;
+ if (c == 0 || --outlen == 0) {
+ /* always NUL terminate result */
+ *out = '\0';
+ break;
+ }
+ *out++ = isascii(c) ? (char)c : '?';
+ in++;
+ inlen--;
+ }
+}
+
+
+/* main */
+int main(int argc, char * argv[])
+{
+ int o;
+ char const * filename = NULL;
+ int verbose = 0;
+
+ while((o = getopt(argc, argv, "f:gv")) != -1)
+ switch(o)
+ {
+ case 'f':
+ filename = optarg;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ default:
+ return _usage();
+ }
+ if(optind == argc)
+ return _usage();
+ if(filename != NULL)
+ {
+ if(optind + 1 != argc)
+ return _usage();
+ return _umbctl_file(argv[optind], filename, verbose);
+ }
+ return _umbctl(argv[optind], verbose, argc - optind - 1,
+ &argv[optind + 1]);
+}
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index c03ba63c349f..9dc1c7f9bc12 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -1041,6 +1041,7 @@ MAN+= \
uled.4 \
ulpt.4 \
umass.4 \
+ umb.4 \
umcs.4 \
umct.4 \
umodem.4 \
diff --git a/share/man/man4/umb.4 b/share/man/man4/umb.4
new file mode 100644
index 000000000000..b0d517095933
--- /dev/null
+++ b/share/man/man4/umb.4
@@ -0,0 +1,119 @@
+.\"-
+.\" SPDX-License-Identifier: 0BSD
+.\"
+.\" Copyright (c) 2016 genua mbH
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" $NetBSD: umb.4,v 1.4 2019/08/30 09:22:17 wiz Exp $
+.\"
+.Dd August 24, 2019
+.Dt UMB 4
+.Os
+.Sh NAME
+.Nm umb
+.Nd USB Mobile Broadband Interface Model (MBIM)
+.Sh SYNOPSIS
+To compile this driver into the kernel,
+place the following lines in your
+kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device usb"
+.Cd "device umb"
+.Ed
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+umb_load="YES"
+.Ed
+.Pp
+If neither of the above is done, the driver will automatically be loaded
+by devd(8) when the device is connected.
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for USB MBIM devices.
+.Pp
+MBIM devices establish connections via cellular networks such as
+GPRS, UMTS, and LTE.
+They appear as a regular point-to-point network interface,
+transporting raw IP frames.
+.Pp
+Required configuration parameters like PIN and APN have to be set
+with
+.Xr umbctl 8 .
+Once the SIM card has been unlocked with the correct PIN, it
+will remain in this state until the MBIM device is power-cycled.
+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.
+.Sh HARDWARE
+The
+.Nm
+driver should support any USB device implementing MBIM, including
+the following cellular modems:
+.Pp
+.Bl -bullet -compact
+.It
+Ericsson H5321gw and N5321gw
+.It
+Fibocom L831-EAU
+.It
+Medion Mobile S4222 (MediaTek OEM)
+.It
+Sierra Wireless EM7345
+.It
+Sierra Wireless EM7455
+.It
+Sierra Wireless EM8805
+.It
+Sierra Wireless MC8305
+.El
+.Sh SEE ALSO
+.Xr intro 4 ,
+.Xr netintro 4 ,
+.Xr usb 4 ,
+.Xr ifconfig 8 ,
+.Xr umbctl 8
+.Rs
+.%T "Universal Serial Bus Communications Class Subclass Specification for Mobile Broadband Interface Model"
+.%U http://www.usb.org/developers/docs/devclass_docs/MBIM10Errata1_073013.zip
+.Re
+.Sh HISTORY
+The
+.Nm
+device driver first appeared in
+.Ox 6.0 ,
+.Nx 9.0 ,
+and
+.Fx 15.0 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Gerhard Roth Aq Mt gerhard@openbsd.org
+and ported from
+.Ox
+by
+.An Pierre Pronchery Aq Mt khorben@defora.org .
+.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 u3g 4 .
diff --git a/sys/conf/files b/sys/conf/files
index a02174f3d954..5fd7b887581a 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -3293,6 +3293,7 @@ dev/usb/net/if_muge.c optional muge
dev/usb/net/if_rue.c optional rue
dev/usb/net/if_smsc.c optional smsc
dev/usb/net/if_udav.c optional udav
+dev/usb/net/if_umb.c optional umb
dev/usb/net/if_ure.c optional ure
dev/usb/net/if_usie.c optional usie
dev/usb/net/if_urndis.c optional urndis
diff --git a/sys/dev/usb/net/if_umb.c b/sys/dev/usb/net/if_umb.c
new file mode 100644
index 000000000000..9b2b504cfa6b
--- /dev/null
+++ b/sys/dev/usb/net/if_umb.c
@@ -0,0 +1,2930 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Original copyright (c) 2016 genua mbH (OpenBSD version)
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Copyright (c) 2022 ADISTA SAS (re-write for FreeBSD)
+ *
+ * Re-write for FreeBSD by Pierre Pronchery <pierre@defora.net>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
*** 4136 LINES SKIPPED ***