1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Original copyright (c) 2018 Pierre Pronchery <khorben@defora.org> (for the 5 * NetBSD Project) 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 * 27 * Copyright (c) 2022 ADISTA SAS (FreeBSD updates) 28 * 29 * Updates for FreeBSD by Pierre Pronchery <pierre@defora.net> 30 * 31 * Redistribution and use in source and binary forms, with or without 32 * modification, are permitted provided that the following conditions are met: 33 * 34 * - Redistributions of source code must retain the above copyright notice, 35 * this list of conditions and the following disclaimer. 36 * - Redistributions in binary form must reproduce the above copyright notice, 37 * this list of conditions and the following disclaimer in the documentation 38 * and/or other materials provided with the distribution. 39 * - Neither the name of the copyright holder nor the names of its contributors 40 * may be used to endorse or promote products derived from this software 41 * without specific prior written permission. 42 * 43 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 44 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 46 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 47 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 48 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 49 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 50 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 51 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 52 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 53 * POSSIBILITY OF SUCH DAMAGE. 54 * 55 * $NetBSD: umbctl.c,v 1.4 2020/05/13 21:44:30 khorben Exp $ 56 */ 57 58 #include <sys/endian.h> 59 #include <sys/ioctl.h> 60 #include <sys/socket.h> 61 #include <sys/sockio.h> 62 63 #include <net/if.h> 64 #include <netinet/in.h> 65 #include <arpa/inet.h> 66 67 #include <ctype.h> 68 #include <errno.h> 69 #include <inttypes.h> 70 #include <stdarg.h> 71 #include <stdio.h> 72 #include <string.h> 73 #include <strings.h> 74 #include <unistd.h> 75 76 #include "mbim.h" 77 #include "if_umbreg.h" 78 79 /* constants */ 80 static const struct umb_valdescr _umb_actstate[] = 81 MBIM_ACTIVATION_STATE_DESCRIPTIONS; 82 83 static const struct umb_valdescr _umb_simstate[] = 84 MBIM_SIMSTATE_DESCRIPTIONS; 85 86 static const struct umb_valdescr _umb_regstate[] = 87 MBIM_REGSTATE_DESCRIPTIONS; 88 89 static const struct umb_valdescr _umb_pktstate[] = 90 MBIM_PKTSRV_STATE_DESCRIPTIONS; 91 92 static const struct umb_valdescr _umb_dataclass[] = 93 MBIM_DATACLASS_DESCRIPTIONS; 94 95 static const struct umb_valdescr _umb_state[] = 96 UMB_INTERNAL_STATE_DESCRIPTIONS; 97 98 static const struct umb_valdescr _umb_pin_state[] = 99 { 100 { UMB_PIN_REQUIRED, "PIN required"}, 101 { UMB_PIN_UNLOCKED, "PIN unlocked"}, 102 { UMB_PUK_REQUIRED, "PUK required"}, 103 { 0, NULL } 104 }; 105 106 static const struct umb_valdescr _umb_regmode[] = 107 { 108 { MBIM_REGMODE_UNKNOWN, "unknown" }, 109 { MBIM_REGMODE_AUTOMATIC, "automatic" }, 110 { MBIM_REGMODE_MANUAL, "manual" }, 111 { 0, NULL } 112 }; 113 114 static const struct umb_valdescr _umb_ber[] = 115 { 116 { UMB_BER_EXCELLENT, "excellent" }, 117 { UMB_BER_VERYGOOD, "very good" }, 118 { UMB_BER_GOOD, "good" }, 119 { UMB_BER_OK, "ok" }, 120 { UMB_BER_MEDIUM, "medium" }, 121 { UMB_BER_BAD, "bad" }, 122 { UMB_BER_VERYBAD, "very bad" }, 123 { UMB_BER_EXTREMELYBAD, "extremely bad" }, 124 { 0, NULL } 125 }; 126 127 128 /* prototypes */ 129 static int _char_to_utf16(const char * in, uint16_t * out, size_t outlen); 130 static int _error(int ret, char const * format, ...); 131 static int _umbctl(char const * ifname, int verbose, int argc, char * argv[]); 132 static int _umbctl_file(char const * ifname, char const * filename, 133 int verbose); 134 static void _umbctl_info(char const * ifname, struct umb_info * umbi); 135 static int _umbctl_ioctl(char const * ifname, int fd, unsigned long request, 136 struct ifreq * ifr); 137 static int _umbctl_set(char const * ifname, struct umb_parameter * umbp, 138 int argc, char * argv[]); 139 static int _umbctl_socket(void); 140 static int _usage(void); 141 static void _utf16_to_char(uint16_t * in, int inlen, char * out, size_t outlen); 142 143 144 /* functions */ 145 /* char_to_utf16 */ 146 /* this function is from OpenBSD's ifconfig(8) */ 147 static int _char_to_utf16(const char * in, uint16_t * out, size_t outlen) 148 { 149 int n = 0; 150 uint16_t c; 151 152 for (;;) { 153 c = *in++; 154 155 if (c == '\0') { 156 /* 157 * NUL termination is not required, but zero out the 158 * residual buffer 159 */ 160 memset(out, 0, outlen); 161 return n; 162 } 163 if (outlen < sizeof(*out)) 164 return -1; 165 166 *out++ = htole16(c); 167 n += sizeof(*out); 168 outlen -= sizeof(*out); 169 } 170 } 171 172 173 /* error */ 174 static int _error(int ret, char const * format, ...) 175 { 176 va_list ap; 177 178 fputs("umbctl: ", stderr); 179 va_start(ap, format); 180 vfprintf(stderr, format, ap); 181 va_end(ap); 182 fputs("\n", stderr); 183 return ret; 184 } 185 186 187 /* umbctl */ 188 static int _umbctl(char const * ifname, int verbose, int argc, char * argv[]) 189 { 190 int fd; 191 struct ifreq ifr; 192 struct umb_info umbi; 193 struct umb_parameter umbp; 194 195 if((fd = _umbctl_socket()) < 0) 196 return 2; 197 memset(&ifr, 0, sizeof(ifr)); 198 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 199 if(argc != 0) 200 { 201 memset(&umbp, 0, sizeof(umbp)); 202 ifr.ifr_data = (caddr_t)&umbp; 203 if(_umbctl_ioctl(ifname, fd, SIOCGUMBPARAM, &ifr) != 0 204 || _umbctl_set(ifname, &umbp, argc, argv) != 0 205 || _umbctl_ioctl(ifname, fd, SIOCSUMBPARAM, 206 &ifr) != 0) 207 { 208 close(fd); 209 return 2; 210 } 211 } 212 if(argc == 0 || verbose > 0) 213 { 214 ifr.ifr_data = (caddr_t)&umbi; 215 if(_umbctl_ioctl(ifname, fd, SIOCGUMBINFO, &ifr) != 0) 216 { 217 close(fd); 218 return 3; 219 } 220 _umbctl_info(ifname, &umbi); 221 } 222 if(close(fd) != 0) 223 return _error(2, "%s: %s", ifname, strerror(errno)); 224 return 0; 225 } 226 227 228 /* umbctl_file */ 229 static int _file_parse(char const * ifname, struct umb_parameter * umbp, 230 char const * filename); 231 232 static int _umbctl_file(char const * ifname, char const * filename, int verbose) 233 { 234 int fd; 235 struct ifreq ifr; 236 struct umb_parameter umbp; 237 struct umb_info umbi; 238 239 if((fd = _umbctl_socket()) < 0) 240 return 2; 241 memset(&ifr, 0, sizeof(ifr)); 242 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 243 ifr.ifr_data = (caddr_t)&umbp; 244 memset(&umbp, 0, sizeof(umbp)); 245 if(_umbctl_ioctl(ifname, fd, SIOCGUMBPARAM, &ifr) != 0 246 || _file_parse(ifname, &umbp, filename) != 0 247 || _umbctl_ioctl(ifname, fd, SIOCSUMBPARAM, &ifr) != 0) 248 { 249 close(fd); 250 return 2; 251 } 252 if(verbose > 0) 253 { 254 ifr.ifr_data = (caddr_t)&umbi; 255 if(_umbctl_ioctl(ifname, fd, SIOCGUMBINFO, &ifr) != 0) 256 { 257 close(fd); 258 return 3; 259 } 260 _umbctl_info(ifname, &umbi); 261 } 262 if(close(fd) != 0) 263 return _error(2, "%s: %s", ifname, strerror(errno)); 264 return 0; 265 } 266 267 static int _file_parse(char const * ifname, struct umb_parameter * umbp, 268 char const * filename) 269 { 270 int ret = 0; 271 FILE * fp; 272 char buf[512]; 273 size_t len; 274 int i; 275 int eof; 276 char * tokens[3] = { buf, NULL, NULL }; 277 char * p; 278 279 if((fp = fopen(filename, "r")) == NULL) 280 return _error(2, "%s: %s", filename, strerror(errno)); 281 while(fgets(buf, sizeof(buf), fp) != NULL) 282 { 283 if(buf[0] == '#') 284 continue; 285 buf[sizeof(buf) - 1] = '\0'; 286 if((len = strlen(buf)) > 0) 287 { 288 if(buf[len - 1] != '\n') 289 { 290 ret = _error(2, "%s: %s", filename, 291 "Line too long"); 292 while((i = fgetc(fp)) != EOF && i != '\n'); 293 continue; 294 } 295 buf[len - 1] = '\0'; 296 } 297 if((p = strchr(buf, '=')) != NULL) 298 { 299 tokens[1] = p + 1; 300 *p = '\0'; 301 } else 302 tokens[1] = NULL; 303 ret |= _umbctl_set(ifname, umbp, (p != NULL) ? 2 : 1, tokens) 304 ? 2 : 0; 305 } 306 eof = feof(fp); 307 if(fclose(fp) != 0 || !eof) 308 return _error(2, "%s: %s", filename, strerror(errno)); 309 return ret; 310 } 311 312 313 /* umbctl_info */ 314 static void _umbctl_info(char const * ifname, struct umb_info * umbi) 315 { 316 char provider[UMB_PROVIDERNAME_MAXLEN + 1]; 317 char pn[UMB_PHONENR_MAXLEN + 1]; 318 char roaming[UMB_ROAMINGTEXT_MAXLEN + 1]; 319 char apn[UMB_APN_MAXLEN + 1]; 320 char fwinfo[UMB_FWINFO_MAXLEN + 1]; 321 char hwinfo[UMB_HWINFO_MAXLEN + 1]; 322 323 _utf16_to_char(umbi->provider, UMB_PROVIDERNAME_MAXLEN, 324 provider, sizeof(provider)); 325 _utf16_to_char(umbi->pn, UMB_PHONENR_MAXLEN, pn, sizeof(pn)); 326 _utf16_to_char(umbi->roamingtxt, UMB_ROAMINGTEXT_MAXLEN, 327 roaming, sizeof(roaming)); 328 _utf16_to_char(umbi->apn, UMB_APN_MAXLEN, apn, sizeof(apn)); 329 _utf16_to_char(umbi->fwinfo, UMB_FWINFO_MAXLEN, fwinfo, sizeof(fwinfo)); 330 _utf16_to_char(umbi->hwinfo, UMB_HWINFO_MAXLEN, hwinfo, sizeof(hwinfo)); 331 printf("%s: state %s, mode %s, registration %s\n" 332 "\tprovider \"%s\", dataclass %s, signal %s\n" 333 "\tphone number \"%s\", roaming \"%s\" (%s)\n" 334 "\tAPN \"%s\", TX %" PRIu64 ", RX %" PRIu64 "\n" 335 "\tfirmware \"%s\", hardware \"%s\"\n", 336 ifname, umb_val2descr(_umb_state, umbi->state), 337 umb_val2descr(_umb_regmode, umbi->regmode), 338 umb_val2descr(_umb_regstate, umbi->regstate), provider, 339 umb_val2descr(_umb_dataclass, umbi->cellclass), 340 umb_val2descr(_umb_ber, umbi->ber), pn, roaming, 341 umbi->enable_roaming ? "allowed" : "denied", 342 apn, umbi->uplink_speed, umbi->downlink_speed, 343 fwinfo, hwinfo); 344 } 345 346 347 /* umbctl_ioctl */ 348 static int _umbctl_ioctl(char const * ifname, int fd, unsigned long request, 349 struct ifreq * ifr) 350 { 351 if(ioctl(fd, request, ifr) != 0) 352 return _error(-1, "%s: %s", ifname, strerror(errno)); 353 return 0; 354 } 355 356 357 /* umbctl_set */ 358 /* callbacks */ 359 static int _set_apn(char const *, struct umb_parameter *, char const *); 360 static int _set_username(char const *, struct umb_parameter *, char const *); 361 static int _set_password(char const *, struct umb_parameter *, char const *); 362 static int _set_pin(char const *, struct umb_parameter *, char const *); 363 static int _set_puk(char const *, struct umb_parameter *, char const *); 364 static int _set_roaming_allow(char const *, struct umb_parameter *, 365 char const *); 366 static int _set_roaming_deny(char const *, struct umb_parameter *, 367 char const *); 368 369 static int _umbctl_set(char const * ifname, struct umb_parameter * umbp, 370 int argc, char * argv[]) 371 { 372 struct 373 { 374 char const * name; 375 int (*callback)(char const *, 376 struct umb_parameter *, char const *); 377 int parameter; 378 } callbacks[] = 379 { 380 { "apn", _set_apn, 1 }, 381 { "username", _set_username, 1 }, 382 { "password", _set_password, 1 }, 383 { "pin", _set_pin, 1 }, 384 { "puk", _set_puk, 1 }, 385 { "roaming", _set_roaming_allow, 0 }, 386 { "-roaming", _set_roaming_deny, 0 }, 387 }; 388 int i; 389 size_t j; 390 391 for(i = 0; i < argc; i++) 392 { 393 for(j = 0; j < sizeof(callbacks) / sizeof(*callbacks); j++) 394 if(strcmp(argv[i], callbacks[j].name) == 0) 395 { 396 if(callbacks[j].parameter && i + 1 == argc) 397 return _error(-1, "%s: Incomplete" 398 " parameter", argv[i]); 399 if(callbacks[j].callback(ifname, umbp, 400 callbacks[j].parameter 401 ? argv[i + 1] : NULL)) 402 return -1; 403 if(callbacks[j].parameter) 404 i++; 405 break; 406 } 407 if(j == sizeof(callbacks) / sizeof(*callbacks)) 408 return _error(-1, "%s: Unknown parameter", argv[i]); 409 } 410 return 0; 411 } 412 413 static int _set_apn(char const * ifname, struct umb_parameter * umbp, 414 char const * apn) 415 { 416 umbp->apnlen = _char_to_utf16(apn, umbp->apn, sizeof(umbp->apn)); 417 if(umbp->apnlen < 0 || (size_t)umbp->apnlen > sizeof(umbp->apn)) 418 return _error(-1, "%s: %s", ifname, "APN too long"); 419 return 0; 420 } 421 422 static int _set_username(char const * ifname, struct umb_parameter * umbp, 423 char const * username) 424 { 425 umbp->usernamelen = _char_to_utf16(username, umbp->username, 426 sizeof(umbp->username)); 427 if(umbp->usernamelen < 0 428 || (size_t)umbp->usernamelen > sizeof(umbp->username)) 429 return _error(-1, "%s: %s", ifname, "Username too long"); 430 return 0; 431 } 432 433 static int _set_password(char const * ifname, struct umb_parameter * umbp, 434 char const * password) 435 { 436 umbp->passwordlen = _char_to_utf16(password, umbp->password, 437 sizeof(umbp->password)); 438 if(umbp->passwordlen < 0 439 || (size_t)umbp->passwordlen > sizeof(umbp->password)) 440 return _error(-1, "%s: %s", ifname, "Password too long"); 441 return 0; 442 } 443 444 static int _set_pin(char const * ifname, struct umb_parameter * umbp, 445 char const * pin) 446 { 447 umbp->is_puk = 0; 448 umbp->op = MBIM_PIN_OP_ENTER; 449 umbp->pinlen = _char_to_utf16(pin, umbp->pin, sizeof(umbp->pin)); 450 if(umbp->pinlen < 0 || (size_t)umbp->pinlen 451 > sizeof(umbp->pin)) 452 return _error(-1, "%s: %s", ifname, "PIN code too long"); 453 return 0; 454 } 455 456 static int _set_puk(char const * ifname, struct umb_parameter * umbp, 457 char const * puk) 458 { 459 umbp->is_puk = 1; 460 umbp->op = MBIM_PIN_OP_ENTER; 461 umbp->pinlen = _char_to_utf16(puk, umbp->pin, sizeof(umbp->pin)); 462 if(umbp->pinlen < 0 || (size_t)umbp->pinlen > sizeof(umbp->pin)) 463 return _error(-1, "%s: %s", ifname, "PUK code too long"); 464 return 0; 465 } 466 467 static int _set_roaming_allow(char const * ifname, struct umb_parameter * umbp, 468 char const * unused) 469 { 470 (void) ifname; 471 (void) unused; 472 473 umbp->roaming = 1; 474 return 0; 475 } 476 477 static int _set_roaming_deny(char const * ifname, struct umb_parameter * umbp, 478 char const * unused) 479 { 480 (void) ifname; 481 (void) unused; 482 483 umbp->roaming = 0; 484 return 0; 485 } 486 487 488 /* umbctl_socket */ 489 static int _umbctl_socket(void) 490 { 491 int fd; 492 493 if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 494 return _error(-1, "socket: %s", strerror(errno)); 495 return fd; 496 } 497 498 499 /* usage */ 500 static int _usage(void) 501 { 502 fputs("Usage: umbctl [-v] ifname [parameter [value]] [...]\n" 503 " umbctl -f config-file ifname\n", 504 stderr); 505 return 1; 506 } 507 508 509 /* utf16_to_char */ 510 static void _utf16_to_char(uint16_t * in, int inlen, char * out, size_t outlen) 511 { 512 uint16_t c; 513 514 while (outlen > 0) { 515 c = inlen > 0 ? htole16(*in) : 0; 516 if (c == 0 || --outlen == 0) { 517 /* always NUL terminate result */ 518 *out = '\0'; 519 break; 520 } 521 *out++ = isascii(c) ? (char)c : '?'; 522 in++; 523 inlen--; 524 } 525 } 526 527 528 /* main */ 529 int main(int argc, char * argv[]) 530 { 531 int o; 532 char const * filename = NULL; 533 int verbose = 0; 534 535 while((o = getopt(argc, argv, "f:gv")) != -1) 536 switch(o) 537 { 538 case 'f': 539 filename = optarg; 540 break; 541 case 'v': 542 verbose++; 543 break; 544 default: 545 return _usage(); 546 } 547 if(optind == argc) 548 return _usage(); 549 if(filename != NULL) 550 { 551 if(optind + 1 != argc) 552 return _usage(); 553 return _umbctl_file(argv[optind], filename, verbose); 554 } 555 return _umbctl(argv[optind], verbose, argc - optind - 1, 556 &argv[optind + 1]); 557 } 558