1 /* $NetBSD: umbctl.c,v 1.4 2020/05/13 21:44:30 khorben Exp $ */ 2 /* 3 * Copyright (c) 2018 Pierre Pronchery <khorben (at) defora.org> 4 * 5 * All rights reserved. 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 28 29 30 #include <sys/endian.h> 31 #include <sys/ioctl.h> 32 #include <sys/socket.h> 33 34 #include <net/if.h> 35 36 #include <ctype.h> 37 #include <errno.h> 38 #include <stdarg.h> 39 #include <stdio.h> 40 #include <string.h> 41 #include <unistd.h> 42 43 #include <dev/usb/mbim.h> 44 #include <dev/usb/if_umbreg.h> 45 46 47 /* constants */ 48 static const struct umb_valdescr _umb_regstate[] = 49 MBIM_REGSTATE_DESCRIPTIONS; 50 51 static const struct umb_valdescr _umb_dataclass[] = 52 MBIM_DATACLASS_DESCRIPTIONS; 53 54 static const struct umb_valdescr _umb_state[] = 55 UMB_INTERNAL_STATE_DESCRIPTIONS; 56 57 static const struct umb_valdescr _umb_regmode[] = 58 { 59 { MBIM_REGMODE_UNKNOWN, "unknown" }, 60 { MBIM_REGMODE_AUTOMATIC, "automatic" }, 61 { MBIM_REGMODE_MANUAL, "manual" }, 62 { 0, NULL } 63 }; 64 65 static const struct umb_valdescr _umb_ber[] = 66 { 67 { UMB_BER_EXCELLENT, "excellent" }, 68 { UMB_BER_VERYGOOD, "very good" }, 69 { UMB_BER_GOOD, "good" }, 70 { UMB_BER_OK, "ok" }, 71 { UMB_BER_MEDIUM, "medium" }, 72 { UMB_BER_BAD, "bad" }, 73 { UMB_BER_VERYBAD, "very bad" }, 74 { UMB_BER_EXTREMELYBAD, "extremely bad" }, 75 { 0, NULL } 76 }; 77 78 79 /* prototypes */ 80 static int _char_to_utf16(const char * in, uint16_t * out, size_t outlen); 81 static int _error(int ret, char const * format, ...); 82 static int _umbctl(char const * ifname, int verbose, int argc, char * argv[]); 83 static int _umbctl_file(char const * ifname, char const * filename, 84 int verbose); 85 static void _umbctl_info(char const * ifname, struct umb_info * umbi); 86 static int _umbctl_ioctl(char const * ifname, int fd, unsigned long request, 87 struct ifreq * ifr); 88 static int _umbctl_set(char const * ifname, struct umb_parameter * umbp, 89 int argc, char * argv[]); 90 static int _umbctl_socket(void); 91 static int _usage(void); 92 static void _utf16_to_char(uint16_t *in, int inlen, char *out, size_t outlen); 93 94 95 /* functions */ 96 /* char_to_utf16 */ 97 /* this function is from OpenBSD's ifconfig(8) */ 98 static int _char_to_utf16(const char * in, uint16_t * out, size_t outlen) 99 { 100 int n = 0; 101 uint16_t c; 102 103 for (;;) { 104 c = *in++; 105 106 if (c == '\0') { 107 /* 108 * NUL termination is not required, but zero out the 109 * residual buffer 110 */ 111 memset(out, 0, outlen); 112 return n; 113 } 114 if (outlen < sizeof(*out)) 115 return -1; 116 117 *out++ = htole16(c); 118 n += sizeof(*out); 119 outlen -= sizeof(*out); 120 } 121 } 122 123 124 /* error */ 125 __printflike(2, 3) static int _error(int ret, char const * format, ...) 126 { 127 va_list ap; 128 129 fputs("umbctl: ", stderr); 130 va_start(ap, format); 131 vfprintf(stderr, format, ap); 132 va_end(ap); 133 fputs("\n", stderr); 134 return ret; 135 } 136 137 138 /* umbctl */ 139 static int _umbctl(char const * ifname, int verbose, int argc, char * argv[]) 140 { 141 int fd; 142 struct ifreq ifr; 143 struct umb_info umbi; 144 struct umb_parameter umbp; 145 146 if((fd = _umbctl_socket()) < 0) 147 return 2; 148 memset(&ifr, 0, sizeof(ifr)); 149 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 150 if(argc != 0) 151 { 152 memset(&umbp, 0, sizeof(umbp)); 153 ifr.ifr_data = &umbp; 154 if(_umbctl_ioctl(ifname, fd, SIOCGUMBPARAM, &ifr) != 0 155 || _umbctl_set(ifname, &umbp, argc, argv) != 0 156 || _umbctl_ioctl(ifname, fd, SIOCSUMBPARAM, 157 &ifr) != 0) 158 { 159 close(fd); 160 return 2; 161 } 162 } 163 if(argc == 0 || verbose > 0) 164 { 165 ifr.ifr_data = &umbi; 166 if(_umbctl_ioctl(ifname, fd, SIOCGUMBINFO, &ifr) != 0) 167 { 168 close(fd); 169 return 3; 170 } 171 _umbctl_info(ifname, &umbi); 172 } 173 if(close(fd) != 0) 174 return _error(2, "%s: %s", ifname, strerror(errno)); 175 return 0; 176 } 177 178 179 /* umbctl_file */ 180 static int _umbctl_file(char const * ifname, char const * filename, int verbose) 181 { 182 int ret = 0; 183 int fd; 184 struct ifreq ifr; 185 struct umb_info umbi; 186 struct umb_parameter umbp; 187 FILE * fp; 188 char buf[512]; 189 size_t len; 190 int i; 191 int eof; 192 char * tokens[3] = { buf, NULL, NULL }; 193 char * p; 194 195 if((fp = fopen(filename, "r")) == NULL) 196 return _error(2, "%s: %s", filename, strerror(errno)); 197 memset(&umbp, 0, sizeof(umbp)); 198 while(fgets(buf, sizeof(buf), fp) != NULL) 199 { 200 if(buf[0] == '#') 201 continue; 202 buf[sizeof(buf) - 1] = '\0'; 203 if((len = strlen(buf)) > 0) 204 { 205 if(buf[len - 1] != '\n') 206 { 207 ret = _error(2, "%s: %s", filename, 208 "Line too long"); 209 while((i = fgetc(fp)) != EOF && i != '\n'); 210 continue; 211 } 212 else 213 buf[len - 1] = '\0'; 214 } 215 if((p = strchr(buf, '=')) != NULL) 216 { 217 tokens[1] = p + 1; 218 *p = '\0'; 219 } else 220 tokens[1] = NULL; 221 ret |= _umbctl_set(ifname, &umbp, (p != NULL) ? 2 : 1, tokens) 222 ? 2 : 0; 223 } 224 eof = feof(fp); 225 if(fclose(fp) != 0 || !eof) 226 return _error(2, "%s: %s", filename, strerror(errno)); 227 if(ret != 0) 228 return ret; 229 if((fd = _umbctl_socket()) < 0) 230 return 2; 231 memset(&ifr, 0, sizeof(ifr)); 232 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 233 ifr.ifr_data = &umbp; 234 if(_umbctl_ioctl(ifname, fd, SIOCGUMBPARAM, &ifr) != 0 235 || _umbctl_ioctl(ifname, fd, SIOCSUMBPARAM, &ifr) != 0) 236 { 237 close(fd); 238 return 2; 239 } 240 if(verbose > 0) 241 { 242 ifr.ifr_data = &umbi; 243 if(_umbctl_ioctl(ifname, fd, SIOCGUMBINFO, &ifr) != 0) 244 { 245 close(fd); 246 return 3; 247 } 248 _umbctl_info(ifname, &umbi); 249 } 250 if(close(fd) != 0) 251 return _error(2, "%s: %s", ifname, strerror(errno)); 252 return 0; 253 } 254 255 256 /* umbctl_info */ 257 static void _umbctl_info(char const * ifname, struct umb_info * umbi) 258 { 259 char provider[UMB_PROVIDERNAME_MAXLEN + 1]; 260 char pn[UMB_PHONENR_MAXLEN + 1]; 261 char roaming[UMB_ROAMINGTEXT_MAXLEN + 1]; 262 char apn[UMB_APN_MAXLEN + 1]; 263 char fwinfo[UMB_FWINFO_MAXLEN + 1]; 264 char hwinfo[UMB_HWINFO_MAXLEN + 1]; 265 266 _utf16_to_char(umbi->provider, UMB_PROVIDERNAME_MAXLEN, 267 provider, sizeof(provider)); 268 _utf16_to_char(umbi->pn, UMB_PHONENR_MAXLEN, pn, sizeof(pn)); 269 _utf16_to_char(umbi->roamingtxt, UMB_ROAMINGTEXT_MAXLEN, 270 roaming, sizeof(roaming)); 271 _utf16_to_char(umbi->apn, UMB_APN_MAXLEN, apn, sizeof(apn)); 272 _utf16_to_char(umbi->fwinfo, UMB_FWINFO_MAXLEN, fwinfo, sizeof(fwinfo)); 273 _utf16_to_char(umbi->hwinfo, UMB_HWINFO_MAXLEN, hwinfo, sizeof(hwinfo)); 274 printf("%s: state %s, mode %s, registration %s\n" 275 "\tprovider \"%s\", dataclass %s, signal %s\n" 276 "\tphone number \"%s\", roaming \"%s\" (%s)\n" 277 "\tAPN \"%s\", TX %" PRIu64 ", RX %" PRIu64 "\n" 278 "\tfirmware \"%s\", hardware \"%s\"\n", 279 ifname, umb_val2descr(_umb_state, umbi->state), 280 umb_val2descr(_umb_regmode, umbi->regmode), 281 umb_val2descr(_umb_regstate, umbi->regstate), provider, 282 umb_val2descr(_umb_dataclass, umbi->cellclass), 283 umb_val2descr(_umb_ber, umbi->ber), pn, roaming, 284 umbi->enable_roaming ? "allowed" : "denied", 285 apn, umbi->uplink_speed, umbi->downlink_speed, 286 fwinfo, hwinfo); 287 } 288 289 290 /* umbctl_ioctl */ 291 static int _umbctl_ioctl(char const * ifname, int fd, unsigned long request, 292 struct ifreq * ifr) 293 { 294 if(ioctl(fd, request, ifr) != 0) 295 return _error(-1, "%s: %s", ifname, strerror(errno)); 296 return 0; 297 } 298 299 300 /* umbctl_set */ 301 /* callbacks */ 302 static int _set_apn(char const *, struct umb_parameter *, char const *); 303 static int _set_username(char const *, struct umb_parameter *, char const *); 304 static int _set_password(char const *, struct umb_parameter *, char const *); 305 static int _set_pin(char const *, struct umb_parameter *, char const *); 306 static int _set_puk(char const *, struct umb_parameter *, char const *); 307 static int _set_roaming_allow(char const *, struct umb_parameter *, 308 char const *); 309 static int _set_roaming_deny(char const *, struct umb_parameter *, 310 char const *); 311 312 static int _umbctl_set(char const * ifname, struct umb_parameter * umbp, 313 int argc, char * argv[]) 314 { 315 struct 316 { 317 char const * name; 318 int (*callback)(char const *, 319 struct umb_parameter *, char const *); 320 int parameter; 321 } callbacks[] = 322 { 323 { "apn", _set_apn, 1 }, 324 { "username", _set_username, 1 }, 325 { "password", _set_password, 1 }, 326 { "pin", _set_pin, 1 }, 327 { "puk", _set_puk, 1 }, 328 { "roaming", _set_roaming_allow, 0 }, 329 { "-roaming", _set_roaming_deny, 0 }, 330 }; 331 int i; 332 size_t j; 333 334 for(i = 0; i < argc; i++) 335 { 336 for(j = 0; j < sizeof(callbacks) / sizeof(*callbacks); j++) 337 if(strcmp(argv[i], callbacks[j].name) == 0) 338 { 339 if(callbacks[j].parameter && i + 1 == argc) 340 return _error(-1, "%s: Incomplete" 341 " parameter", argv[i]); 342 if(callbacks[j].callback(ifname, umbp, 343 callbacks[j].parameter 344 ? argv[i + 1] : NULL)) 345 return -1; 346 if(callbacks[j].parameter) 347 i++; 348 break; 349 } 350 if(j == sizeof(callbacks) / sizeof(*callbacks)) 351 return _error(-1, "%s: Unknown parameter", argv[i]); 352 } 353 return 0; 354 } 355 356 static int _set_apn(char const * ifname, struct umb_parameter * umbp, 357 char const * apn) 358 { 359 umbp->apnlen = _char_to_utf16(apn, umbp->apn, sizeof(umbp->apn)); 360 if(umbp->apnlen < 0 || (size_t)umbp->apnlen > sizeof(umbp->apn)) 361 return _error(-1, "%s: %s", ifname, "APN too long"); 362 return 0; 363 } 364 365 static int _set_username(char const * ifname, struct umb_parameter * umbp, 366 char const * username) 367 { 368 umbp->usernamelen = _char_to_utf16(username, umbp->username, 369 sizeof(umbp->username)); 370 if(umbp->usernamelen < 0 371 || (size_t)umbp->usernamelen > sizeof(umbp->username)) 372 return _error(-1, "%s: %s", ifname, "Username too long"); 373 return 0; 374 } 375 376 static int _set_password(char const * ifname, struct umb_parameter * umbp, 377 char const * password) 378 { 379 umbp->passwordlen = _char_to_utf16(password, umbp->password, 380 sizeof(umbp->password)); 381 if(umbp->passwordlen < 0 382 || (size_t)umbp->passwordlen > sizeof(umbp->password)) 383 return _error(-1, "%s: %s", ifname, "Password too long"); 384 return 0; 385 } 386 387 static int _set_pin(char const * ifname, struct umb_parameter * umbp, 388 char const * pin) 389 { 390 umbp->is_puk = 0; 391 umbp->op = MBIM_PIN_OP_ENTER; 392 umbp->pinlen = _char_to_utf16(pin, umbp->pin, sizeof(umbp->pin)); 393 if(umbp->pinlen < 0 || (size_t)umbp->pinlen 394 > sizeof(umbp->pin)) 395 return _error(-1, "%s: %s", ifname, "PIN code too long"); 396 return 0; 397 } 398 399 static int _set_puk(char const * ifname, struct umb_parameter * umbp, 400 char const * puk) 401 { 402 umbp->is_puk = 1; 403 umbp->op = MBIM_PIN_OP_ENTER; 404 umbp->pinlen = _char_to_utf16(puk, umbp->pin, sizeof(umbp->pin)); 405 if(umbp->pinlen < 0 || (size_t)umbp->pinlen > sizeof(umbp->pin)) 406 return _error(-1, "%s: %s", ifname, "PUK code too long"); 407 return 0; 408 } 409 410 static int _set_roaming_allow(char const * ifname, struct umb_parameter * umbp, 411 char const * unused) 412 { 413 (void) ifname; 414 (void) unused; 415 416 umbp->roaming = 1; 417 return 0; 418 } 419 420 static int _set_roaming_deny(char const * ifname, struct umb_parameter * umbp, 421 char const * unused) 422 { 423 (void) ifname; 424 (void) unused; 425 426 umbp->roaming = 0; 427 return 0; 428 } 429 430 431 /* umbctl_socket */ 432 static int _umbctl_socket(void) 433 { 434 int fd; 435 436 if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 437 return _error(-1, "socket: %s", strerror(errno)); 438 return fd; 439 } 440 441 442 /* usage */ 443 static int _usage(void) 444 { 445 fputs("Usage: umbctl [-v] ifname [parameter [value]] [...]\n" 446 " umbctl -f config-file ifname [...]\n", 447 stderr); 448 return 1; 449 } 450 451 452 /* utf16_to_char */ 453 static void _utf16_to_char(uint16_t *in, int inlen, char *out, size_t outlen) 454 { 455 uint16_t c; 456 457 while (outlen > 0) { 458 c = inlen > 0 ? htole16(*in) : 0; 459 if (c == 0 || --outlen == 0) { 460 /* always NUL terminate result */ 461 *out = '\0'; 462 break; 463 } 464 *out++ = isascii(c) ? (char)c : '?'; 465 in++; 466 inlen--; 467 } 468 } 469 470 471 /* main */ 472 int main(int argc, char * argv[]) 473 { 474 int o; 475 char const * filename = NULL; 476 int verbose = 0; 477 478 while((o = getopt(argc, argv, "f:v")) != -1) 479 switch(o) 480 { 481 case 'f': 482 filename = optarg; 483 break; 484 case 'v': 485 verbose++; 486 break; 487 default: 488 return _usage(); 489 } 490 if(optind == argc) 491 return _usage(); 492 if(filename != NULL) 493 { 494 if(optind + 1 != argc) 495 return _usage(); 496 return _umbctl_file(argv[optind], filename, verbose); 497 } 498 return _umbctl(argv[optind], verbose, argc - optind - 1, 499 &argv[optind + 1]); 500 } 501