1941e2863SAndrew Thompson /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 31de7b4b8SPedro F. Giffuni * 4941e2863SAndrew Thompson * Copyright (c) 2008-2009 Fredrik Lindberg 5941e2863SAndrew Thompson * All rights reserved. 6941e2863SAndrew Thompson * 7941e2863SAndrew Thompson * Redistribution and use in source and binary forms, with or without 8941e2863SAndrew Thompson * modification, are permitted provided that the following conditions 9941e2863SAndrew Thompson * are met: 10941e2863SAndrew Thompson * 1. Redistributions of source code must retain the above copyright 11941e2863SAndrew Thompson * notice, this list of conditions and the following disclaimer. 12941e2863SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 13941e2863SAndrew Thompson * notice, this list of conditions and the following disclaimer in the 14941e2863SAndrew Thompson * documentation and/or other materials provided with the distribution. 15941e2863SAndrew Thompson * 16941e2863SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17941e2863SAndrew Thompson * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18941e2863SAndrew Thompson * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19941e2863SAndrew Thompson * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20941e2863SAndrew Thompson * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21941e2863SAndrew Thompson * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22941e2863SAndrew Thompson * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23941e2863SAndrew Thompson * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24941e2863SAndrew Thompson * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25941e2863SAndrew Thompson * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26941e2863SAndrew Thompson */ 27941e2863SAndrew Thompson 28941e2863SAndrew Thompson #include <sys/types.h> 29941e2863SAndrew Thompson #include <sys/param.h> 30941e2863SAndrew Thompson #include <sys/socket.h> 31941e2863SAndrew Thompson #include <sys/sockio.h> 32941e2863SAndrew Thompson #include <sys/select.h> 33941e2863SAndrew Thompson #include <sys/stat.h> 34941e2863SAndrew Thompson #include <sys/sysctl.h> 35941e2863SAndrew Thompson #include <sys/time.h> 36941e2863SAndrew Thompson #include <sys/queue.h> 37941e2863SAndrew Thompson 38941e2863SAndrew Thompson #include <arpa/inet.h> 39941e2863SAndrew Thompson #include <net/if.h> 40941e2863SAndrew Thompson #include <net/if_dl.h> 41941e2863SAndrew Thompson #include <net/route.h> 42941e2863SAndrew Thompson #include <netinet/in.h> 43941e2863SAndrew Thompson #include <netinet/in_var.h> 44941e2863SAndrew Thompson 45941e2863SAndrew Thompson #include <err.h> 46941e2863SAndrew Thompson #include <errno.h> 47941e2863SAndrew Thompson #include <fcntl.h> 48941e2863SAndrew Thompson #include <termios.h> 49941e2863SAndrew Thompson #include <stdarg.h> 50941e2863SAndrew Thompson #include <stdio.h> 51941e2863SAndrew Thompson #include <stdlib.h> 52941e2863SAndrew Thompson #include <stdint.h> 53941e2863SAndrew Thompson #include <string.h> 54941e2863SAndrew Thompson #include <signal.h> 55941e2863SAndrew Thompson #include <syslog.h> 56941e2863SAndrew Thompson #include <unistd.h> 57941e2863SAndrew Thompson #include <ifaddrs.h> 58941e2863SAndrew Thompson #include <libutil.h> 59941e2863SAndrew Thompson #include <time.h> 60941e2863SAndrew Thompson 61941e2863SAndrew Thompson /* 62941e2863SAndrew Thompson * Connection utility to ease connectivity using the raw IP packet interface 63941e2863SAndrew Thompson * available on uhso(4) devices. 64941e2863SAndrew Thompson */ 65941e2863SAndrew Thompson 66941e2863SAndrew Thompson #define TTY_NAME "/dev/%s" 67941e2863SAndrew Thompson #define SYSCTL_TEST "dev.uhso.%d.%%driver" 68eea19fceSAndrew Thompson #define SYSCTL_LOCATION "dev.uhso.%d.%%location" 69941e2863SAndrew Thompson #define SYSCTL_PORTS "dev.uhso.%d.ports" 70941e2863SAndrew Thompson #define SYSCTL_NETIF "dev.uhso.%d.netif" 71941e2863SAndrew Thompson #define SYSCTL_NAME_TTY "dev.uhso.%d.port.%s.tty" 72941e2863SAndrew Thompson #define SYSCTL_NAME_DESC "dev.uhso.%d.port.%s.desc" 73941e2863SAndrew Thompson #define RESOLV_PATH "/etc/resolv.conf" 74941e2863SAndrew Thompson #define PIDFILE "/var/run/uhsoctl.%s.pid" 75941e2863SAndrew Thompson 76941e2863SAndrew Thompson static const char *network_access_type[] = { 77941e2863SAndrew Thompson "GSM", 78941e2863SAndrew Thompson "Compact GSM", 79941e2863SAndrew Thompson "UMTS", 80941e2863SAndrew Thompson "GSM (EGPRS)", 81941e2863SAndrew Thompson "HSDPA", 82941e2863SAndrew Thompson "HSUPA", 83941e2863SAndrew Thompson "HSDPA/HSUPA" 84941e2863SAndrew Thompson }; 85941e2863SAndrew Thompson 86941e2863SAndrew Thompson static const char *network_reg_status[] = { 87941e2863SAndrew Thompson "Not registered", 88941e2863SAndrew Thompson "Registered", 89941e2863SAndrew Thompson "Searching for network", 90941e2863SAndrew Thompson "Network registration denied", 91941e2863SAndrew Thompson "Unknown", 92941e2863SAndrew Thompson "Registered (roaming)" 93941e2863SAndrew Thompson }; 94941e2863SAndrew Thompson 95941e2863SAndrew Thompson struct ctx { 96941e2863SAndrew Thompson int fd; 97941e2863SAndrew Thompson int flags; 98941e2863SAndrew Thompson #define IPASSIGNED 0x01 99941e2863SAndrew Thompson #define FLG_NODAEMON 0x02 /* Don't detach from terminal */ 100941e2863SAndrew Thompson #define FLG_DAEMON 0x04 /* Running as daemon */ 101941e2863SAndrew Thompson #define FLG_DELAYED 0x08 /* Fork into background after connect */ 102941e2863SAndrew Thompson #define FLG_NEWDATA 0x10 103941e2863SAndrew Thompson #define FLG_WATCHDOG 0x20 /* Watchdog enabled */ 104941e2863SAndrew Thompson #define FLG_WDEXP 0x40 /* Watchdog expired */ 105941e2863SAndrew Thompson const char *ifnam; 106941e2863SAndrew Thompson const char *pin; /* device PIN */ 107941e2863SAndrew Thompson 108941e2863SAndrew Thompson char pidfile[128]; 109941e2863SAndrew Thompson struct pidfh *pfh; 110941e2863SAndrew Thompson 111941e2863SAndrew Thompson time_t watchdog; 112941e2863SAndrew Thompson 113941e2863SAndrew Thompson /* PDP context settings */ 114941e2863SAndrew Thompson int pdp_ctx; 115941e2863SAndrew Thompson const char *pdp_apn; 116941e2863SAndrew Thompson const char *pdp_user; 117941e2863SAndrew Thompson const char *pdp_pwd; 118941e2863SAndrew Thompson 119941e2863SAndrew Thompson /* Connection status */ 120941e2863SAndrew Thompson int con_status; /* Connected? */ 121941e2863SAndrew Thompson char *con_apn; /* Connected APN */ 122941e2863SAndrew Thompson char *con_oper; /* Operator name */ 123941e2863SAndrew Thompson int con_net_stat; /* Network connection status */ 124941e2863SAndrew Thompson int con_net_type; /* Network connection type */ 125941e2863SAndrew Thompson 126941e2863SAndrew Thompson /* Misc. status */ 127941e2863SAndrew Thompson int dbm; 128941e2863SAndrew Thompson 129941e2863SAndrew Thompson /* IP and nameserver settings */ 130941e2863SAndrew Thompson struct in_addr ip; 131941e2863SAndrew Thompson char **ns; 132941e2863SAndrew Thompson const char *resolv_path; 133941e2863SAndrew Thompson char *resolv; /* Old resolv.conf */ 134941e2863SAndrew Thompson size_t resolv_sz; 135941e2863SAndrew Thompson }; 136941e2863SAndrew Thompson 137941e2863SAndrew Thompson static int readline_buf(const char *, const char *, char *, size_t); 138941e2863SAndrew Thompson static int readline(int, char *, size_t); 139941e2863SAndrew Thompson static void daemonize(struct ctx *); 140941e2863SAndrew Thompson 141941e2863SAndrew Thompson static int at_cmd_async(int, const char *, ...); 142941e2863SAndrew Thompson 143941e2863SAndrew Thompson typedef union { 144941e2863SAndrew Thompson void *ptr; 145941e2863SAndrew Thompson uint32_t int32; 146941e2863SAndrew Thompson } resp_data; 147941e2863SAndrew Thompson typedef struct { 148941e2863SAndrew Thompson resp_data val[2]; 149941e2863SAndrew Thompson } resp_arg; 150941e2863SAndrew Thompson typedef void (*resp_cb)(resp_arg *, const char *, const char *); 151941e2863SAndrew Thompson 152941e2863SAndrew Thompson typedef void (*async_cb)(void *, const char *); 153941e2863SAndrew Thompson struct async_handle { 154941e2863SAndrew Thompson const char *cmd; 155941e2863SAndrew Thompson async_cb func; 156941e2863SAndrew Thompson }; 157941e2863SAndrew Thompson 158941e2863SAndrew Thompson static void at_async_creg(void *, const char *); 159941e2863SAndrew Thompson static void at_async_cgreg(void *, const char *); 160941e2863SAndrew Thompson static void at_async_cops(void *, const char *); 161941e2863SAndrew Thompson static void at_async_owancall(void *, const char *); 162941e2863SAndrew Thompson static void at_async_owandata(void *, const char *); 163941e2863SAndrew Thompson static void at_async_csq(void *, const char *); 164941e2863SAndrew Thompson 165941e2863SAndrew Thompson static struct async_handle async_cmd[] = { 166941e2863SAndrew Thompson { "+CREG", at_async_creg }, 167941e2863SAndrew Thompson { "+CGREG", at_async_cgreg }, 168941e2863SAndrew Thompson { "+COPS", at_async_cops }, 169941e2863SAndrew Thompson { "+CSQ", at_async_csq }, 170941e2863SAndrew Thompson { "_OWANCALL", at_async_owancall }, 171941e2863SAndrew Thompson { "_OWANDATA", at_async_owandata }, 172941e2863SAndrew Thompson { NULL, NULL } 173941e2863SAndrew Thompson }; 174941e2863SAndrew Thompson 175941e2863SAndrew Thompson struct timer_entry; 176941e2863SAndrew Thompson struct timers { 177941e2863SAndrew Thompson TAILQ_HEAD(, timer_entry) head; 178941e2863SAndrew Thompson int res; 179941e2863SAndrew Thompson }; 180941e2863SAndrew Thompson 181941e2863SAndrew Thompson typedef void (*tmr_cb)(int, void *); 182941e2863SAndrew Thompson struct timer_entry { 183941e2863SAndrew Thompson TAILQ_ENTRY(timer_entry) next; 184941e2863SAndrew Thompson int id; 185941e2863SAndrew Thompson int timeout; 186941e2863SAndrew Thompson tmr_cb func; 187941e2863SAndrew Thompson void *arg; 188941e2863SAndrew Thompson }; 189941e2863SAndrew Thompson 190941e2863SAndrew Thompson 191941e2863SAndrew Thompson static struct timers timers; 192941e2863SAndrew Thompson static volatile int running = 1; 193941e2863SAndrew Thompson static int syslog_open = 0; 194941e2863SAndrew Thompson static char syslog_title[64]; 195941e2863SAndrew Thompson 196941e2863SAndrew Thompson /* Periodic timer, runs ready timer tasks every tick */ 197941e2863SAndrew Thompson static void 198941e2863SAndrew Thompson tmr_run(struct timers *tmrs) 199941e2863SAndrew Thompson { 200941e2863SAndrew Thompson struct timer_entry *te, *te2; 201941e2863SAndrew Thompson 202941e2863SAndrew Thompson te = TAILQ_FIRST(&tmrs->head); 203941e2863SAndrew Thompson if (te == NULL) 204941e2863SAndrew Thompson return; 205941e2863SAndrew Thompson 206941e2863SAndrew Thompson te->timeout -= tmrs->res; 207941e2863SAndrew Thompson while (te->timeout <= 0) { 208941e2863SAndrew Thompson te2 = TAILQ_NEXT(te, next); 209941e2863SAndrew Thompson TAILQ_REMOVE(&tmrs->head, te, next); 210941e2863SAndrew Thompson te->func(te->id, te->arg); 211941e2863SAndrew Thompson free(te); 212941e2863SAndrew Thompson te = te2; 213941e2863SAndrew Thompson if (te == NULL) 214941e2863SAndrew Thompson break; 215941e2863SAndrew Thompson } 216941e2863SAndrew Thompson } 217941e2863SAndrew Thompson 218941e2863SAndrew Thompson /* Add a new timer */ 219941e2863SAndrew Thompson static void 220941e2863SAndrew Thompson tmr_add(struct timers *tmrs, int id, int timeout, tmr_cb func, void *arg) 221941e2863SAndrew Thompson { 222941e2863SAndrew Thompson struct timer_entry *te, *te2, *te3; 223941e2863SAndrew Thompson 224941e2863SAndrew Thompson te = malloc(sizeof(struct timer_entry)); 225941e2863SAndrew Thompson memset(te, 0, sizeof(struct timer_entry)); 226941e2863SAndrew Thompson 227941e2863SAndrew Thompson te->timeout = timeout; 228941e2863SAndrew Thompson te->func = func; 229941e2863SAndrew Thompson te->arg = arg; 230941e2863SAndrew Thompson te->id = id; 231941e2863SAndrew Thompson 232941e2863SAndrew Thompson te2 = TAILQ_FIRST(&tmrs->head); 233941e2863SAndrew Thompson 234941e2863SAndrew Thompson if (TAILQ_EMPTY(&tmrs->head)) { 235941e2863SAndrew Thompson TAILQ_INSERT_HEAD(&tmrs->head, te, next); 236941e2863SAndrew Thompson } else if (te->timeout < te2->timeout) { 237941e2863SAndrew Thompson te2->timeout -= te->timeout; 238941e2863SAndrew Thompson TAILQ_INSERT_HEAD(&tmrs->head, te, next); 239941e2863SAndrew Thompson } else { 240941e2863SAndrew Thompson while (te->timeout >= te2->timeout) { 241941e2863SAndrew Thompson te->timeout -= te2->timeout; 242941e2863SAndrew Thompson te3 = TAILQ_NEXT(te2, next); 243941e2863SAndrew Thompson if (te3 == NULL || te3->timeout > te->timeout) 244941e2863SAndrew Thompson break; 245941e2863SAndrew Thompson te2 = te3; 246941e2863SAndrew Thompson } 247941e2863SAndrew Thompson TAILQ_INSERT_AFTER(&tmrs->head, te2, te, next); 248941e2863SAndrew Thompson } 249941e2863SAndrew Thompson } 250941e2863SAndrew Thompson 251941e2863SAndrew Thompson #define watchdog_enable(ctx) (ctx)->flags |= FLG_WATCHDOG 252941e2863SAndrew Thompson #define watchdog_disable(ctx) (ctx)->flags &= ~FLG_WATCHDOG 253941e2863SAndrew Thompson 254941e2863SAndrew Thompson static void 255941e2863SAndrew Thompson watchdog_reset(struct ctx *ctx, int timeout) 256941e2863SAndrew Thompson { 257941e2863SAndrew Thompson struct timespec tp; 258941e2863SAndrew Thompson 259941e2863SAndrew Thompson clock_gettime(CLOCK_MONOTONIC, &tp), 260941e2863SAndrew Thompson ctx->watchdog = tp.tv_sec + timeout; 261941e2863SAndrew Thompson 262941e2863SAndrew Thompson watchdog_enable(ctx); 263941e2863SAndrew Thompson } 264941e2863SAndrew Thompson 265941e2863SAndrew Thompson static void 266941e2863SAndrew Thompson tmr_creg(int id, void *arg) 267941e2863SAndrew Thompson { 268941e2863SAndrew Thompson struct ctx *ctx = arg; 269941e2863SAndrew Thompson 270941e2863SAndrew Thompson at_cmd_async(ctx->fd, "AT+CREG?\r\n"); 271941e2863SAndrew Thompson watchdog_reset(ctx, 10); 272941e2863SAndrew Thompson } 273941e2863SAndrew Thompson 274941e2863SAndrew Thompson static void 275941e2863SAndrew Thompson tmr_cgreg(int id, void *arg) 276941e2863SAndrew Thompson { 277941e2863SAndrew Thompson struct ctx *ctx = arg; 278941e2863SAndrew Thompson 279941e2863SAndrew Thompson at_cmd_async(ctx->fd, "AT+CGREG?\r\n"); 280941e2863SAndrew Thompson watchdog_reset(ctx, 10); 281941e2863SAndrew Thompson } 282941e2863SAndrew Thompson 283941e2863SAndrew Thompson static void 284941e2863SAndrew Thompson tmr_status(int id, void *arg) 285941e2863SAndrew Thompson { 286941e2863SAndrew Thompson struct ctx *ctx = arg; 287941e2863SAndrew Thompson 288941e2863SAndrew Thompson at_cmd_async(ctx->fd, "AT+CSQ\r\n"); 289941e2863SAndrew Thompson watchdog_reset(ctx, 10); 290941e2863SAndrew Thompson } 291941e2863SAndrew Thompson 292941e2863SAndrew Thompson static void 293941e2863SAndrew Thompson tmr_watchdog(int id, void *arg) 294941e2863SAndrew Thompson { 295941e2863SAndrew Thompson struct ctx *ctx = arg; 296941e2863SAndrew Thompson pid_t self; 297941e2863SAndrew Thompson struct timespec tp; 298941e2863SAndrew Thompson 299941e2863SAndrew Thompson tmr_add(&timers, 1, 5, tmr_watchdog, ctx); 300941e2863SAndrew Thompson 301941e2863SAndrew Thompson if (!(ctx->flags & FLG_WATCHDOG)) 302941e2863SAndrew Thompson return; 303941e2863SAndrew Thompson 304941e2863SAndrew Thompson clock_gettime(CLOCK_MONOTONIC, &tp); 305941e2863SAndrew Thompson 306941e2863SAndrew Thompson if (tp.tv_sec >= ctx->watchdog) { 307941e2863SAndrew Thompson #ifdef DEBUG 308941e2863SAndrew Thompson fprintf(stderr, "Watchdog expired\n"); 309941e2863SAndrew Thompson #endif 310941e2863SAndrew Thompson ctx->flags |= FLG_WDEXP; 311941e2863SAndrew Thompson self = getpid(); 312941e2863SAndrew Thompson kill(self, SIGHUP); 313941e2863SAndrew Thompson } 314941e2863SAndrew Thompson } 315941e2863SAndrew Thompson 316941e2863SAndrew Thompson static void 317941e2863SAndrew Thompson sig_handle(int sig) 318941e2863SAndrew Thompson { 319941e2863SAndrew Thompson 320941e2863SAndrew Thompson switch (sig) { 321941e2863SAndrew Thompson case SIGHUP: 322941e2863SAndrew Thompson case SIGINT: 323941e2863SAndrew Thompson case SIGQUIT: 324941e2863SAndrew Thompson case SIGTERM: 325941e2863SAndrew Thompson running = 0; 326941e2863SAndrew Thompson break; 327941e2863SAndrew Thompson case SIGALRM: 328941e2863SAndrew Thompson tmr_run(&timers); 329941e2863SAndrew Thompson break; 330941e2863SAndrew Thompson } 331941e2863SAndrew Thompson } 332941e2863SAndrew Thompson 333941e2863SAndrew Thompson static void 334941e2863SAndrew Thompson logger(int pri, const char *fmt, ...) 335941e2863SAndrew Thompson { 336941e2863SAndrew Thompson char *buf; 337941e2863SAndrew Thompson va_list ap; 338941e2863SAndrew Thompson 339941e2863SAndrew Thompson va_start(ap, fmt); 340941e2863SAndrew Thompson vasprintf(&buf, fmt, ap); 341941e2863SAndrew Thompson if (syslog_open) 3425eeae9a7SDimitry Andric syslog(pri, "%s", buf); 343941e2863SAndrew Thompson else { 344941e2863SAndrew Thompson switch (pri) { 345941e2863SAndrew Thompson case LOG_INFO: 346941e2863SAndrew Thompson case LOG_NOTICE: 347941e2863SAndrew Thompson printf("%s\n", buf); 348941e2863SAndrew Thompson break; 349941e2863SAndrew Thompson default: 350941e2863SAndrew Thompson fprintf(stderr, "%s: %s\n", getprogname(), buf); 351941e2863SAndrew Thompson break; 352941e2863SAndrew Thompson } 353941e2863SAndrew Thompson } 354941e2863SAndrew Thompson 355941e2863SAndrew Thompson free(buf); 356941e2863SAndrew Thompson va_end(ap); 357941e2863SAndrew Thompson } 358941e2863SAndrew Thompson 359941e2863SAndrew Thompson #define if_ifup(ifnam) if_setflags(ifnam, IFF_UP) 360941e2863SAndrew Thompson #define if_ifdown(ifnam) if_setflags(ifnam, -IFF_UP) 361941e2863SAndrew Thompson 362941e2863SAndrew Thompson static int 363941e2863SAndrew Thompson if_setflags(const char *ifnam, int flags) 364941e2863SAndrew Thompson { 365941e2863SAndrew Thompson struct ifreq ifr; 366941e2863SAndrew Thompson int fd, error; 367941e2863SAndrew Thompson unsigned int oflags = 0; 368941e2863SAndrew Thompson 369941e2863SAndrew Thompson memset(&ifr, 0, sizeof(struct ifreq)); 370941e2863SAndrew Thompson strlcpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name)); 371941e2863SAndrew Thompson 372941e2863SAndrew Thompson fd = socket(AF_INET, SOCK_DGRAM, 0); 373941e2863SAndrew Thompson if (fd < 0) 374941e2863SAndrew Thompson return (-1); 375941e2863SAndrew Thompson 376941e2863SAndrew Thompson error = ioctl(fd, SIOCGIFFLAGS, &ifr); 377941e2863SAndrew Thompson if (error == 0) { 378941e2863SAndrew Thompson oflags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16); 379941e2863SAndrew Thompson } 380941e2863SAndrew Thompson 381941e2863SAndrew Thompson if (flags < 0) 382941e2863SAndrew Thompson oflags &= ~(-flags); 383941e2863SAndrew Thompson else 384941e2863SAndrew Thompson oflags |= flags; 385941e2863SAndrew Thompson 386941e2863SAndrew Thompson ifr.ifr_flags = oflags & 0xffff; 387941e2863SAndrew Thompson ifr.ifr_flagshigh = oflags >> 16; 388941e2863SAndrew Thompson 389941e2863SAndrew Thompson error = ioctl(fd, SIOCSIFFLAGS, &ifr); 390941e2863SAndrew Thompson if (error != 0) 391941e2863SAndrew Thompson warn("ioctl SIOCSIFFLAGS"); 392941e2863SAndrew Thompson 393941e2863SAndrew Thompson close(fd); 394941e2863SAndrew Thompson return (error); 395941e2863SAndrew Thompson } 396941e2863SAndrew Thompson 397941e2863SAndrew Thompson static int 398941e2863SAndrew Thompson ifaddr_add(const char *ifnam, struct sockaddr *sa, struct sockaddr *mask) 399941e2863SAndrew Thompson { 400*c74a4ceaSKonrad Witaszczyk struct ifaliasreq ifra; 401*c74a4ceaSKonrad Witaszczyk int error, fd; 402941e2863SAndrew Thompson 403*c74a4ceaSKonrad Witaszczyk memset(&ifra, 0, sizeof(ifra)); 404*c74a4ceaSKonrad Witaszczyk strlcpy(ifra.ifra_name, ifnam, sizeof(ifra.ifra_name)); 405*c74a4ceaSKonrad Witaszczyk memcpy(&ifra.ifra_addr, sa, sa->sa_len); 406*c74a4ceaSKonrad Witaszczyk memcpy(&ifra.ifra_mask, mask, mask->sa_len); 407*c74a4ceaSKonrad Witaszczyk 408*c74a4ceaSKonrad Witaszczyk fd = socket(AF_INET, SOCK_DGRAM, 0); 409*c74a4ceaSKonrad Witaszczyk if (fd < 0) 410*c74a4ceaSKonrad Witaszczyk return (-1); 411*c74a4ceaSKonrad Witaszczyk 412*c74a4ceaSKonrad Witaszczyk error = ioctl(fd, SIOCAIFADDR, (caddr_t)&ifra); 413941e2863SAndrew Thompson if (error != 0) 414941e2863SAndrew Thompson warn("ioctl SIOCAIFADDR"); 415*c74a4ceaSKonrad Witaszczyk 416*c74a4ceaSKonrad Witaszczyk close(fd); 417941e2863SAndrew Thompson return (error); 418941e2863SAndrew Thompson } 419941e2863SAndrew Thompson 420941e2863SAndrew Thompson static int 421*c74a4ceaSKonrad Witaszczyk ifaddr_del(const char *ifnam, struct sockaddr *sa) 422941e2863SAndrew Thompson { 423*c74a4ceaSKonrad Witaszczyk struct ifreq ifr; 424*c74a4ceaSKonrad Witaszczyk int error, fd; 425941e2863SAndrew Thompson 426*c74a4ceaSKonrad Witaszczyk memset(&ifr, 0, sizeof(ifr)); 427*c74a4ceaSKonrad Witaszczyk strlcpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name)); 428*c74a4ceaSKonrad Witaszczyk memcpy(&ifr.ifr_addr, sa, sa->sa_len); 429*c74a4ceaSKonrad Witaszczyk 430*c74a4ceaSKonrad Witaszczyk fd = socket(AF_INET, SOCK_DGRAM, 0); 431*c74a4ceaSKonrad Witaszczyk if (fd < 0) 432*c74a4ceaSKonrad Witaszczyk return (-1); 433*c74a4ceaSKonrad Witaszczyk 434*c74a4ceaSKonrad Witaszczyk error = ioctl(fd, SIOCDIFADDR, (caddr_t)&ifr); 435941e2863SAndrew Thompson if (error != 0) 436941e2863SAndrew Thompson warn("ioctl SIOCDIFADDR"); 437*c74a4ceaSKonrad Witaszczyk 438*c74a4ceaSKonrad Witaszczyk close(fd); 439941e2863SAndrew Thompson return (error); 440941e2863SAndrew Thompson } 441941e2863SAndrew Thompson 442941e2863SAndrew Thompson static int 443941e2863SAndrew Thompson set_nameservers(struct ctx *ctx, const char *respath, int ns, ...) 444941e2863SAndrew Thompson { 445941e2863SAndrew Thompson int i, n, fd; 446941e2863SAndrew Thompson FILE *fp; 447941e2863SAndrew Thompson char *p; 448941e2863SAndrew Thompson va_list ap; 449941e2863SAndrew Thompson struct stat sb; 450941e2863SAndrew Thompson char buf[512]; 451941e2863SAndrew Thompson 452941e2863SAndrew Thompson if (ctx->ns != NULL) { 453941e2863SAndrew Thompson for (i = 0; ctx->ns[i] != NULL; i++) { 454941e2863SAndrew Thompson free(ctx->ns[i]); 455941e2863SAndrew Thompson } 456941e2863SAndrew Thompson free(ctx->ns); 457e830a247SEnji Cooper ctx->ns = NULL; 458941e2863SAndrew Thompson } 459941e2863SAndrew Thompson 4609a492fd1SPawel Jakub Dawidek fd = open(respath, O_RDWR | O_CREAT | O_NOFOLLOW, 0666); 461941e2863SAndrew Thompson if (fd < 0) 462941e2863SAndrew Thompson return (-1); 463941e2863SAndrew Thompson 464941e2863SAndrew Thompson if (ns == 0) { 465941e2863SAndrew Thompson /* Attempt to restore old resolv.conf */ 466941e2863SAndrew Thompson if (ctx->resolv != NULL) { 467941e2863SAndrew Thompson ftruncate(fd, 0); 468941e2863SAndrew Thompson lseek(fd, 0, SEEK_SET); 469941e2863SAndrew Thompson write(fd, ctx->resolv, ctx->resolv_sz); 470941e2863SAndrew Thompson free(ctx->resolv); 471941e2863SAndrew Thompson ctx->resolv = NULL; 472941e2863SAndrew Thompson ctx->resolv_sz = 0; 473941e2863SAndrew Thompson } 474941e2863SAndrew Thompson close(fd); 475941e2863SAndrew Thompson return (0); 476941e2863SAndrew Thompson } 477941e2863SAndrew Thompson 478941e2863SAndrew Thompson 479941e2863SAndrew Thompson ctx->ns = malloc(sizeof(char *) * (ns + 1)); 480941e2863SAndrew Thompson if (ctx->ns == NULL) { 481941e2863SAndrew Thompson close(fd); 482941e2863SAndrew Thompson return (-1); 483941e2863SAndrew Thompson } 484941e2863SAndrew Thompson 485941e2863SAndrew Thompson va_start(ap, ns); 486941e2863SAndrew Thompson for (i = 0; i < ns; i++) { 487941e2863SAndrew Thompson p = va_arg(ap, char *); 488941e2863SAndrew Thompson ctx->ns[i] = strdup(p); 489941e2863SAndrew Thompson } 490941e2863SAndrew Thompson ctx->ns[i] = NULL; 491941e2863SAndrew Thompson va_end(ap); 492941e2863SAndrew Thompson 493941e2863SAndrew Thompson /* Attempt to backup the old resolv.conf */ 494941e2863SAndrew Thompson if (ctx->resolv == NULL) { 495941e2863SAndrew Thompson i = fstat(fd, &sb); 496941e2863SAndrew Thompson if (i == 0 && sb.st_size != 0) { 497941e2863SAndrew Thompson ctx->resolv_sz = sb.st_size; 498941e2863SAndrew Thompson ctx->resolv = malloc(sb.st_size); 499941e2863SAndrew Thompson if (ctx->resolv != NULL) { 500941e2863SAndrew Thompson n = read(fd, ctx->resolv, sb.st_size); 501941e2863SAndrew Thompson if (n != sb.st_size) { 502941e2863SAndrew Thompson free(ctx->resolv); 503941e2863SAndrew Thompson ctx->resolv = NULL; 504941e2863SAndrew Thompson } 505941e2863SAndrew Thompson } 506941e2863SAndrew Thompson } 507941e2863SAndrew Thompson } 508941e2863SAndrew Thompson 509941e2863SAndrew Thompson 510941e2863SAndrew Thompson ftruncate(fd, 0); 511941e2863SAndrew Thompson lseek(fd, 0, SEEK_SET); 512941e2863SAndrew Thompson fp = fdopen(fd, "w"); 513941e2863SAndrew Thompson 514941e2863SAndrew Thompson /* 515941e2863SAndrew Thompson * Write back everything other than nameserver entries to the 516941e2863SAndrew Thompson * new resolv.conf 517941e2863SAndrew Thompson */ 518941e2863SAndrew Thompson if (ctx->resolv != NULL) { 519941e2863SAndrew Thompson p = ctx->resolv; 520941e2863SAndrew Thompson while ((i = readline_buf(p, ctx->resolv + ctx->resolv_sz, buf, 521941e2863SAndrew Thompson sizeof(buf))) > 0) { 522941e2863SAndrew Thompson p += i; 523941e2863SAndrew Thompson if (strncasecmp(buf, "nameserver", 10) == 0) 524941e2863SAndrew Thompson continue; 525941e2863SAndrew Thompson fprintf(fp, "%s", buf); 526941e2863SAndrew Thompson } 527941e2863SAndrew Thompson } 528941e2863SAndrew Thompson 529941e2863SAndrew Thompson for (i = 0; ctx->ns[i] != NULL; i++) { 530941e2863SAndrew Thompson fprintf(fp, "nameserver %s\n", ctx->ns[i]); 531941e2863SAndrew Thompson } 532941e2863SAndrew Thompson fclose(fp); 533941e2863SAndrew Thompson return (0); 534941e2863SAndrew Thompson } 535941e2863SAndrew Thompson 536941e2863SAndrew Thompson /* Read a \n-terminated line from buffer */ 537941e2863SAndrew Thompson static int 538941e2863SAndrew Thompson readline_buf(const char *s, const char *e, char *buf, size_t bufsz) 539941e2863SAndrew Thompson { 540941e2863SAndrew Thompson int pos = 0; 541941e2863SAndrew Thompson char *p = buf; 542941e2863SAndrew Thompson 543941e2863SAndrew Thompson for (; s < e; s++) { 544941e2863SAndrew Thompson *p = *s; 545941e2863SAndrew Thompson pos++; 546941e2863SAndrew Thompson if (pos >= (bufsz - 1)) 547941e2863SAndrew Thompson break; 548941e2863SAndrew Thompson if (*p++ == '\n') 549941e2863SAndrew Thompson break; 550941e2863SAndrew Thompson } 551941e2863SAndrew Thompson *p = '\0'; 552941e2863SAndrew Thompson return (pos); 553941e2863SAndrew Thompson } 554941e2863SAndrew Thompson 555941e2863SAndrew Thompson /* Read a \n-terminated line from file */ 556941e2863SAndrew Thompson static int 557941e2863SAndrew Thompson readline(int fd, char *buf, size_t bufsz) 558941e2863SAndrew Thompson { 559941e2863SAndrew Thompson int n = 0, pos = 0; 560941e2863SAndrew Thompson char *p = buf; 561941e2863SAndrew Thompson 562941e2863SAndrew Thompson for (;;) { 563941e2863SAndrew Thompson n = read(fd, p, 1); 564941e2863SAndrew Thompson if (n <= 0) 565941e2863SAndrew Thompson break; 566941e2863SAndrew Thompson pos++; 567941e2863SAndrew Thompson if (pos >= (bufsz - 1)) 568941e2863SAndrew Thompson break; 569941e2863SAndrew Thompson if (*p++ == '\n') 570941e2863SAndrew Thompson break; 571941e2863SAndrew Thompson } 572941e2863SAndrew Thompson *p = '\0'; 573941e2863SAndrew Thompson return (n <= 0 ? n : pos); 574941e2863SAndrew Thompson } 575941e2863SAndrew Thompson 576941e2863SAndrew Thompson /* 577941e2863SAndrew Thompson * Synchronous AT command 578941e2863SAndrew Thompson */ 579941e2863SAndrew Thompson static int 580941e2863SAndrew Thompson at_cmd(struct ctx *ctx, const char *resp, resp_cb cb, resp_arg *ra, const char *cf, ...) 581941e2863SAndrew Thompson { 582eea19fceSAndrew Thompson char buf[512]; 583eea19fceSAndrew Thompson char cmd[64]; 584941e2863SAndrew Thompson size_t l; 585941e2863SAndrew Thompson int n, error, retval = 0; 586941e2863SAndrew Thompson va_list ap; 587941e2863SAndrew Thompson fd_set set; 588eea19fceSAndrew Thompson char *p; 589941e2863SAndrew Thompson 590941e2863SAndrew Thompson va_start(ap, cf); 591941e2863SAndrew Thompson vsnprintf(cmd, sizeof(cmd), cf, ap); 592941e2863SAndrew Thompson va_end(ap); 593941e2863SAndrew Thompson 594941e2863SAndrew Thompson #ifdef DEBUG 595941e2863SAndrew Thompson fprintf(stderr, "SYNC_CMD: %s", cmd); 596941e2863SAndrew Thompson #endif 597941e2863SAndrew Thompson 598941e2863SAndrew Thompson l = strlen(cmd); 599941e2863SAndrew Thompson n = write(ctx->fd, cmd, l); 600941e2863SAndrew Thompson if (n <= 0) 601941e2863SAndrew Thompson return (-1); 602941e2863SAndrew Thompson 603941e2863SAndrew Thompson if (resp != NULL) { 604941e2863SAndrew Thompson l = strlen(resp); 605941e2863SAndrew Thompson #ifdef DEBUG 606c57440ecSGavin Atkinson fprintf(stderr, "SYNC_EXP: %s (%zu)\n", resp, l); 607941e2863SAndrew Thompson #endif 608941e2863SAndrew Thompson } 609941e2863SAndrew Thompson 610941e2863SAndrew Thompson for (;;) { 611941e2863SAndrew Thompson bzero(buf, sizeof(buf)); 612941e2863SAndrew Thompson 613941e2863SAndrew Thompson FD_ZERO(&set); 614941e2863SAndrew Thompson watchdog_reset(ctx, 5); 615941e2863SAndrew Thompson do { 616941e2863SAndrew Thompson FD_SET(ctx->fd, &set); 617941e2863SAndrew Thompson error = select(ctx->fd + 1, &set, NULL, NULL, NULL); 618eea19fceSAndrew Thompson if (ctx->flags & FLG_WDEXP) { 619941e2863SAndrew Thompson watchdog_disable(ctx); 620eea19fceSAndrew Thompson return (-2); 621941e2863SAndrew Thompson } 622941e2863SAndrew Thompson } while (error <= 0 && errno == EINTR); 623eea19fceSAndrew Thompson watchdog_disable(ctx); 624941e2863SAndrew Thompson 625941e2863SAndrew Thompson if (error <= 0) { 626941e2863SAndrew Thompson retval = -2; 627941e2863SAndrew Thompson break; 628941e2863SAndrew Thompson } 629941e2863SAndrew Thompson 630941e2863SAndrew Thompson n = readline(ctx->fd, buf, sizeof(buf)); 631941e2863SAndrew Thompson if (n <= 0) { 632941e2863SAndrew Thompson retval = -2; 633941e2863SAndrew Thompson break; 634941e2863SAndrew Thompson } 635941e2863SAndrew Thompson 636941e2863SAndrew Thompson if (strcmp(buf, "\r\n") == 0 || strcmp(buf, "\n") == 0) 637941e2863SAndrew Thompson continue; 638941e2863SAndrew Thompson 639eea19fceSAndrew Thompson if ((p = strchr(buf, '\r')) != NULL) 640eea19fceSAndrew Thompson *p = '\0'; 641eea19fceSAndrew Thompson else if ((p = strchr(buf, '\n')) != NULL) 642eea19fceSAndrew Thompson *p = '\0'; 643941e2863SAndrew Thompson #ifdef DEBUG 644eea19fceSAndrew Thompson fprintf(stderr, "SYNC_RESP: %s\n", buf); 645941e2863SAndrew Thompson #endif 646941e2863SAndrew Thompson 647eea19fceSAndrew Thompson /* Skip local echo */ 648eea19fceSAndrew Thompson if (strncasecmp(cmd, buf, strlen(buf)) == 0) 649eea19fceSAndrew Thompson continue; 650eea19fceSAndrew Thompson 651eea19fceSAndrew Thompson if (cb != NULL) 652eea19fceSAndrew Thompson cb(ra, cmd, buf); 653eea19fceSAndrew Thompson 654941e2863SAndrew Thompson if (strncmp(buf, "OK", 2) == 0) { 655eea19fceSAndrew Thompson retval = retval ? retval : 0; 656941e2863SAndrew Thompson break; 657eea19fceSAndrew Thompson } else if (strstr(buf, "ERROR") != NULL) { 658941e2863SAndrew Thompson retval = -1; 659941e2863SAndrew Thompson break; 660941e2863SAndrew Thompson } 661eea19fceSAndrew Thompson if (resp != NULL) 662eea19fceSAndrew Thompson retval = strncmp(buf, resp, l); 663941e2863SAndrew Thompson } 664941e2863SAndrew Thompson #ifdef DEBUG 665941e2863SAndrew Thompson fprintf(stderr, "SYNC_RETVAL=%d\n", retval); 666941e2863SAndrew Thompson #endif 667941e2863SAndrew Thompson return (retval); 668941e2863SAndrew Thompson } 669941e2863SAndrew Thompson 670941e2863SAndrew Thompson static int 671941e2863SAndrew Thompson at_cmd_async(int fd, const char *cf, ...) 672941e2863SAndrew Thompson { 673941e2863SAndrew Thompson size_t l; 674941e2863SAndrew Thompson va_list ap; 675941e2863SAndrew Thompson char cmd[64]; 676941e2863SAndrew Thompson 677941e2863SAndrew Thompson va_start(ap, cf); 678941e2863SAndrew Thompson vsnprintf(cmd, sizeof(cmd), cf, ap); 679941e2863SAndrew Thompson va_end(ap); 680941e2863SAndrew Thompson 681941e2863SAndrew Thompson #ifdef DEBUG 682941e2863SAndrew Thompson fprintf(stderr, "CMD: %s", cmd); 683941e2863SAndrew Thompson #endif 684941e2863SAndrew Thompson l = strlen(cmd); 685941e2863SAndrew Thompson return (write(fd, cmd, l)); 686941e2863SAndrew Thompson } 687941e2863SAndrew Thompson 688941e2863SAndrew Thompson static void 689941e2863SAndrew Thompson saveresp(resp_arg *ra, const char *cmd, const char *resp) 690941e2863SAndrew Thompson { 691941e2863SAndrew Thompson char **buf; 692941e2863SAndrew Thompson int i = ra->val[1].int32; 693941e2863SAndrew Thompson 694eea19fceSAndrew Thompson #ifdef DEBUG 695eea19fceSAndrew Thompson fprintf(stderr, "Save '%s'\n", resp); 696eea19fceSAndrew Thompson #endif 697eea19fceSAndrew Thompson 698941e2863SAndrew Thompson buf = realloc(ra->val[0].ptr, sizeof(char *) * (i + 1)); 699941e2863SAndrew Thompson if (buf == NULL) 700941e2863SAndrew Thompson return; 701941e2863SAndrew Thompson 702941e2863SAndrew Thompson buf[i] = strdup(resp); 703941e2863SAndrew Thompson 704941e2863SAndrew Thompson ra->val[0].ptr = buf; 705941e2863SAndrew Thompson ra->val[1].int32 = i + 1; 706941e2863SAndrew Thompson } 707941e2863SAndrew Thompson 708941e2863SAndrew Thompson static void 709eea19fceSAndrew Thompson freeresp(resp_arg *ra) 710eea19fceSAndrew Thompson { 711eea19fceSAndrew Thompson char **buf; 712eea19fceSAndrew Thompson int i; 713eea19fceSAndrew Thompson 714eea19fceSAndrew Thompson buf = ra->val[0].ptr; 715eea19fceSAndrew Thompson for (i = 0; i < ra->val[1].int32; i++) { 716eea19fceSAndrew Thompson free(buf[i]); 717eea19fceSAndrew Thompson } 718eea19fceSAndrew Thompson free(buf); 719eea19fceSAndrew Thompson } 720eea19fceSAndrew Thompson 721eea19fceSAndrew Thompson static void 722941e2863SAndrew Thompson at_async_creg(void *arg, const char *resp) 723941e2863SAndrew Thompson { 724941e2863SAndrew Thompson struct ctx *ctx = arg; 725941e2863SAndrew Thompson int n, reg; 726941e2863SAndrew Thompson 727941e2863SAndrew Thompson n = sscanf(resp, "+CREG: %*d,%d", ®); 728941e2863SAndrew Thompson if (n != 1) { 729941e2863SAndrew Thompson n = sscanf(resp, "+CREG: %d", ®); 730941e2863SAndrew Thompson if (n != 1) 731941e2863SAndrew Thompson return; 732941e2863SAndrew Thompson } 733941e2863SAndrew Thompson 734941e2863SAndrew Thompson if (ctx->con_net_stat != 1 && ctx->con_net_stat != 5) { 735941e2863SAndrew Thompson tmr_add(&timers, 1, 1, tmr_creg, ctx); 736941e2863SAndrew Thompson } 737941e2863SAndrew Thompson else { 738941e2863SAndrew Thompson tmr_add(&timers, 1, 30, tmr_creg, ctx); 739941e2863SAndrew Thompson } 740941e2863SAndrew Thompson 741941e2863SAndrew Thompson if (ctx->con_net_stat == reg) 742941e2863SAndrew Thompson return; 743941e2863SAndrew Thompson 744941e2863SAndrew Thompson ctx->con_net_stat = reg; 745941e2863SAndrew Thompson at_cmd_async(ctx->fd, "AT+COPS?\r\n"); 746941e2863SAndrew Thompson } 747941e2863SAndrew Thompson 748941e2863SAndrew Thompson static void 749941e2863SAndrew Thompson at_async_cgreg(void *arg, const char *resp) 750941e2863SAndrew Thompson { 751941e2863SAndrew Thompson struct ctx *ctx = arg; 752941e2863SAndrew Thompson int n, reg; 753941e2863SAndrew Thompson 754941e2863SAndrew Thompson n = sscanf(resp, "+CGREG: %*d,%d", ®); 755941e2863SAndrew Thompson if (n != 1) { 756941e2863SAndrew Thompson n = sscanf(resp, "+CGREG: %d", ®); 757941e2863SAndrew Thompson if (n != 1) 758941e2863SAndrew Thompson return; 759941e2863SAndrew Thompson } 760941e2863SAndrew Thompson 761941e2863SAndrew Thompson if (ctx->con_net_stat != 1 && ctx->con_net_stat != 5) { 762941e2863SAndrew Thompson tmr_add(&timers, 1, 1, tmr_cgreg, ctx); 763941e2863SAndrew Thompson } 764941e2863SAndrew Thompson else { 765941e2863SAndrew Thompson tmr_add(&timers, 1, 30, tmr_cgreg, ctx); 766941e2863SAndrew Thompson } 767941e2863SAndrew Thompson 768941e2863SAndrew Thompson if (ctx->con_net_stat == reg) 769941e2863SAndrew Thompson return; 770941e2863SAndrew Thompson 771941e2863SAndrew Thompson ctx->con_net_stat = reg; 772941e2863SAndrew Thompson at_cmd_async(ctx->fd, "AT+COPS?\r\n"); 773941e2863SAndrew Thompson } 774941e2863SAndrew Thompson 775941e2863SAndrew Thompson 776941e2863SAndrew Thompson static void 777941e2863SAndrew Thompson at_async_cops(void *arg, const char *resp) 778941e2863SAndrew Thompson { 779941e2863SAndrew Thompson struct ctx *ctx = arg; 780941e2863SAndrew Thompson int n, at; 781941e2863SAndrew Thompson char opr[64]; 782941e2863SAndrew Thompson 783941e2863SAndrew Thompson n = sscanf(resp, "+COPS: %*d,%*d,\"%[^\"]\",%d", 784941e2863SAndrew Thompson opr, &at); 785941e2863SAndrew Thompson if (n != 2) 786941e2863SAndrew Thompson return; 787941e2863SAndrew Thompson 788941e2863SAndrew Thompson if (ctx->con_oper != NULL) { 789941e2863SAndrew Thompson if (ctx->con_net_type == at && 790941e2863SAndrew Thompson strcasecmp(opr, ctx->con_oper) == 0) 791941e2863SAndrew Thompson return; 792941e2863SAndrew Thompson free(ctx->con_oper); 793941e2863SAndrew Thompson } 794941e2863SAndrew Thompson 795941e2863SAndrew Thompson ctx->con_oper = strdup(opr); 796941e2863SAndrew Thompson ctx->con_net_type = at; 797941e2863SAndrew Thompson 798941e2863SAndrew Thompson if (ctx->con_net_stat == 1 || ctx->con_net_stat == 5) { 799941e2863SAndrew Thompson logger(LOG_NOTICE, "%s to \"%s\" (%s)", 800941e2863SAndrew Thompson network_reg_status[ctx->con_net_stat], 801941e2863SAndrew Thompson ctx->con_oper, network_access_type[ctx->con_net_type]); 802941e2863SAndrew Thompson if (ctx->con_status != 1) { 803941e2863SAndrew Thompson at_cmd_async(ctx->fd, "AT_OWANCALL=%d,1,1\r\n", 804941e2863SAndrew Thompson ctx->pdp_ctx); 805941e2863SAndrew Thompson } 806941e2863SAndrew Thompson } 807941e2863SAndrew Thompson else { 808941e2863SAndrew Thompson logger(LOG_NOTICE, "%s (%s)", 809941e2863SAndrew Thompson network_reg_status[ctx->con_net_stat], 810941e2863SAndrew Thompson network_access_type[ctx->con_net_type]); 811941e2863SAndrew Thompson } 812941e2863SAndrew Thompson } 813941e2863SAndrew Thompson 814941e2863SAndrew Thompson /* 815941e2863SAndrew Thompson * Signal strength for pretty console output 816941e2863SAndrew Thompson * 817941e2863SAndrew Thompson * From 3GPP TS 27.007 V8.3.0, Section 8.5 818941e2863SAndrew Thompson * 0 = -113 dBm or less 819941e2863SAndrew Thompson * 1 = -111 dBm 820941e2863SAndrew Thompson * 2...30 = -109...-53 dBm 821941e2863SAndrew Thompson * 31 = -51 dBm or greater 822941e2863SAndrew Thompson * 823941e2863SAndrew Thompson * So, dbm = (rssi * 2) - 113 824941e2863SAndrew Thompson */ 825941e2863SAndrew Thompson static void 826941e2863SAndrew Thompson at_async_csq(void *arg, const char *resp) 827941e2863SAndrew Thompson { 828941e2863SAndrew Thompson struct ctx *ctx = arg; 829941e2863SAndrew Thompson int n, rssi; 830941e2863SAndrew Thompson 831941e2863SAndrew Thompson n = sscanf(resp, "+CSQ: %d,%*d", &rssi); 832941e2863SAndrew Thompson if (n != 1) 833941e2863SAndrew Thompson return; 834941e2863SAndrew Thompson if (rssi == 99) 835941e2863SAndrew Thompson ctx->dbm = 0; 836941e2863SAndrew Thompson else { 837941e2863SAndrew Thompson ctx->dbm = (rssi * 2) - 113; 838941e2863SAndrew Thompson tmr_add(&timers, 1, 15, tmr_status, ctx); 839941e2863SAndrew Thompson } 840941e2863SAndrew Thompson 841941e2863SAndrew Thompson ctx->flags |= FLG_NEWDATA; 842941e2863SAndrew Thompson } 843941e2863SAndrew Thompson 844941e2863SAndrew Thompson static void 845941e2863SAndrew Thompson at_async_owancall(void *arg, const char *resp) 846941e2863SAndrew Thompson { 847941e2863SAndrew Thompson struct ctx *ctx = arg; 848941e2863SAndrew Thompson int n, i; 849941e2863SAndrew Thompson 850941e2863SAndrew Thompson n = sscanf(resp, "_OWANCALL: %*d,%d", &i); 851941e2863SAndrew Thompson if (n != 1) 852941e2863SAndrew Thompson return; 853941e2863SAndrew Thompson 854941e2863SAndrew Thompson if (i == ctx->con_status) 855941e2863SAndrew Thompson return; 856941e2863SAndrew Thompson 857941e2863SAndrew Thompson at_cmd_async(ctx->fd, "AT_OWANDATA=%d\r\n", ctx->pdp_ctx); 858941e2863SAndrew Thompson 859941e2863SAndrew Thompson ctx->con_status = i; 860941e2863SAndrew Thompson if (ctx->con_status == 1) { 861941e2863SAndrew Thompson logger(LOG_NOTICE, "Connected to \"%s\" (%s), %s", 862941e2863SAndrew Thompson ctx->con_oper, ctx->con_apn, 863941e2863SAndrew Thompson network_access_type[ctx->con_net_type]); 864941e2863SAndrew Thompson } 865941e2863SAndrew Thompson else { 866941e2863SAndrew Thompson logger(LOG_NOTICE, "Disconnected from \"%s\" (%s)", 867941e2863SAndrew Thompson ctx->con_oper, ctx->con_apn); 868941e2863SAndrew Thompson } 869941e2863SAndrew Thompson } 870941e2863SAndrew Thompson 871941e2863SAndrew Thompson static void 872941e2863SAndrew Thompson at_async_owandata(void *arg, const char *resp) 873941e2863SAndrew Thompson { 874941e2863SAndrew Thompson struct ctx *ctx = arg; 875941e2863SAndrew Thompson char ip[40], ns1[40], ns2[40]; 876941e2863SAndrew Thompson int n, error, rs; 877941e2863SAndrew Thompson struct ifaddrs *ifap, *ifa; 878941e2863SAndrew Thompson struct sockaddr_in sin, mask; 879941e2863SAndrew Thompson struct sockaddr_dl sdl; 880941e2863SAndrew Thompson struct { 881941e2863SAndrew Thompson struct rt_msghdr rtm; 882941e2863SAndrew Thompson char buf[512]; 883941e2863SAndrew Thompson } r; 884941e2863SAndrew Thompson char *cp = r.buf; 885941e2863SAndrew Thompson 886941e2863SAndrew Thompson n = sscanf(resp, "_OWANDATA: %*d, %[^,], %*[^,], %[^,], %[^,]", 887941e2863SAndrew Thompson ip, ns1, ns2); 888941e2863SAndrew Thompson if (n != 3) 889941e2863SAndrew Thompson return; 890941e2863SAndrew Thompson 891941e2863SAndrew Thompson /* XXX: AF_INET assumption */ 892941e2863SAndrew Thompson 893941e2863SAndrew Thompson logger(LOG_NOTICE, "IP address: %s, Nameservers: %s, %s", ip, ns1, ns2); 894941e2863SAndrew Thompson 895941e2863SAndrew Thompson sin.sin_len = mask.sin_len = sizeof(struct sockaddr_in); 896941e2863SAndrew Thompson memset(&mask.sin_addr.s_addr, 0xff, sizeof(mask.sin_addr.s_addr)); 897941e2863SAndrew Thompson sin.sin_family = mask.sin_family = AF_INET; 898941e2863SAndrew Thompson 899941e2863SAndrew Thompson if (ctx->flags & IPASSIGNED) { 900941e2863SAndrew Thompson memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr, 901941e2863SAndrew Thompson sizeof(sin.sin_addr.s_addr)); 902*c74a4ceaSKonrad Witaszczyk ifaddr_del(ctx->ifnam, (struct sockaddr *)&sin); 903941e2863SAndrew Thompson } 904941e2863SAndrew Thompson inet_pton(AF_INET, ip, &ctx->ip.s_addr); 905941e2863SAndrew Thompson memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr, 906941e2863SAndrew Thompson sizeof(sin.sin_addr.s_addr)); 907941e2863SAndrew Thompson 908941e2863SAndrew Thompson error = ifaddr_add(ctx->ifnam, (struct sockaddr *)&sin, 909941e2863SAndrew Thompson (struct sockaddr *)&mask); 910941e2863SAndrew Thompson if (error != 0) { 911941e2863SAndrew Thompson logger(LOG_ERR, "failed to set ip-address"); 912941e2863SAndrew Thompson return; 913941e2863SAndrew Thompson } 914941e2863SAndrew Thompson 915941e2863SAndrew Thompson if_ifup(ctx->ifnam); 916941e2863SAndrew Thompson 917941e2863SAndrew Thompson ctx->flags |= IPASSIGNED; 918941e2863SAndrew Thompson 919941e2863SAndrew Thompson set_nameservers(ctx, ctx->resolv_path, 0); 920941e2863SAndrew Thompson error = set_nameservers(ctx, ctx->resolv_path, 2, ns1, ns2); 921941e2863SAndrew Thompson if (error != 0) { 922941e2863SAndrew Thompson logger(LOG_ERR, "failed to set nameservers"); 923941e2863SAndrew Thompson } 924941e2863SAndrew Thompson 925941e2863SAndrew Thompson error = getifaddrs(&ifap); 926941e2863SAndrew Thompson if (error != 0) { 927941e2863SAndrew Thompson logger(LOG_ERR, "getifaddrs: %s", strerror(errno)); 928941e2863SAndrew Thompson return; 929941e2863SAndrew Thompson } 930941e2863SAndrew Thompson 931941e2863SAndrew Thompson for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 932941e2863SAndrew Thompson if (ifa->ifa_addr->sa_family != AF_LINK) 933941e2863SAndrew Thompson continue; 934941e2863SAndrew Thompson if (strcmp(ctx->ifnam, ifa->ifa_name) == 0) { 935941e2863SAndrew Thompson memcpy(&sdl, (struct sockaddr_dl *)ifa->ifa_addr, 936941e2863SAndrew Thompson sizeof(struct sockaddr_dl)); 937941e2863SAndrew Thompson break; 938941e2863SAndrew Thompson } 939941e2863SAndrew Thompson } 940941e2863SAndrew Thompson if (ifa == NULL) 941941e2863SAndrew Thompson return; 942941e2863SAndrew Thompson 943941e2863SAndrew Thompson rs = socket(PF_ROUTE, SOCK_RAW, 0); 944941e2863SAndrew Thompson if (rs < 0) { 945941e2863SAndrew Thompson logger(LOG_ERR, "socket PF_ROUTE: %s", strerror(errno)); 946941e2863SAndrew Thompson return; 947941e2863SAndrew Thompson } 948941e2863SAndrew Thompson 949941e2863SAndrew Thompson memset(&r, 0, sizeof(r)); 950941e2863SAndrew Thompson 951941e2863SAndrew Thompson r.rtm.rtm_version = RTM_VERSION; 952941e2863SAndrew Thompson r.rtm.rtm_type = RTM_ADD; 953941e2863SAndrew Thompson r.rtm.rtm_flags = RTF_UP | RTF_STATIC; 954941e2863SAndrew Thompson r.rtm.rtm_pid = getpid(); 955941e2863SAndrew Thompson memset(&sin, 0, sizeof(struct sockaddr_in)); 956941e2863SAndrew Thompson sin.sin_family = AF_INET; 957941e2863SAndrew Thompson sin.sin_len = sizeof(struct sockaddr_in); 958941e2863SAndrew Thompson 959941e2863SAndrew Thompson memcpy(cp, &sin, sin.sin_len); 960941e2863SAndrew Thompson cp += SA_SIZE(&sin); 961941e2863SAndrew Thompson memcpy(cp, &sdl, sdl.sdl_len); 962941e2863SAndrew Thompson cp += SA_SIZE(&sdl); 963941e2863SAndrew Thompson memcpy(cp, &sin, sin.sin_len); 964941e2863SAndrew Thompson r.rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; 965941e2863SAndrew Thompson r.rtm.rtm_msglen = sizeof(r); 966941e2863SAndrew Thompson 967941e2863SAndrew Thompson n = write(rs, &r, r.rtm.rtm_msglen); 968941e2863SAndrew Thompson if (n != r.rtm.rtm_msglen) { 969941e2863SAndrew Thompson r.rtm.rtm_type = RTM_DELETE; 970941e2863SAndrew Thompson n = write(rs, &r, r.rtm.rtm_msglen); 971941e2863SAndrew Thompson r.rtm.rtm_type = RTM_ADD; 972941e2863SAndrew Thompson n = write(rs, &r, r.rtm.rtm_msglen); 973941e2863SAndrew Thompson } 974941e2863SAndrew Thompson 975941e2863SAndrew Thompson if (n != r.rtm.rtm_msglen) { 976941e2863SAndrew Thompson logger(LOG_ERR, "failed to set default route: %s", 977941e2863SAndrew Thompson strerror(errno)); 978941e2863SAndrew Thompson } 979941e2863SAndrew Thompson close(rs); 980941e2863SAndrew Thompson 981941e2863SAndrew Thompson /* Delayed daemonization */ 982941e2863SAndrew Thompson if ((ctx->flags & FLG_DELAYED) && !(ctx->flags & FLG_NODAEMON)) 983941e2863SAndrew Thompson daemonize(ctx); 984941e2863SAndrew Thompson } 985941e2863SAndrew Thompson 986941e2863SAndrew Thompson static int 987941e2863SAndrew Thompson at_async(struct ctx *ctx, void *arg) 988941e2863SAndrew Thompson { 989941e2863SAndrew Thompson int n, i; 990941e2863SAndrew Thompson size_t l; 991941e2863SAndrew Thompson char buf[512]; 992941e2863SAndrew Thompson 993941e2863SAndrew Thompson watchdog_reset(ctx, 15); 994941e2863SAndrew Thompson 995941e2863SAndrew Thompson bzero(buf, sizeof(buf)); 996941e2863SAndrew Thompson n = readline(ctx->fd, buf, sizeof(buf)); 997941e2863SAndrew Thompson if (n <= 0) 998941e2863SAndrew Thompson return (n <= 0 ? -1 : 0); 999941e2863SAndrew Thompson 1000941e2863SAndrew Thompson #ifdef DEBUG 1001941e2863SAndrew Thompson fprintf(stderr, "AT_ASYNC_RESP: %s", buf); 1002941e2863SAndrew Thompson #endif 1003941e2863SAndrew Thompson for (i = 0; async_cmd[i].cmd != NULL; i++) { 1004941e2863SAndrew Thompson l = strlen(async_cmd[i].cmd); 1005941e2863SAndrew Thompson if (strncmp(buf, async_cmd[i].cmd, l) == 0) { 1006941e2863SAndrew Thompson async_cmd[i].func(arg, buf); 1007941e2863SAndrew Thompson } 1008941e2863SAndrew Thompson } 1009941e2863SAndrew Thompson return (0); 1010941e2863SAndrew Thompson } 1011941e2863SAndrew Thompson 1012941e2863SAndrew Thompson static const char *port_type_list[] = { 1013941e2863SAndrew Thompson "control", "application", "application2", NULL 1014941e2863SAndrew Thompson }; 1015941e2863SAndrew Thompson 1016941e2863SAndrew Thompson /* 1017941e2863SAndrew Thompson * Attempts to find a list of control tty for the interface 1018eea19fceSAndrew Thompson * FreeBSD attaches USB devices per interface so we have to go through 1019941e2863SAndrew Thompson * hoops to find which ttys that belong to our network interface. 1020941e2863SAndrew Thompson */ 1021941e2863SAndrew Thompson static char ** 1022941e2863SAndrew Thompson get_tty(struct ctx *ctx) 1023941e2863SAndrew Thompson { 1024eea19fceSAndrew Thompson char buf[64], data[128]; 1025eea19fceSAndrew Thompson int error, i, usbport, usbport0, list_size = 0; 1026941e2863SAndrew Thompson char **list = NULL; 1027eea19fceSAndrew Thompson size_t len; 1028eea19fceSAndrew Thompson const char **p, *q; 1029941e2863SAndrew Thompson 1030eea19fceSAndrew Thompson /* 1031eea19fceSAndrew Thompson * Look for the network interface first 1032eea19fceSAndrew Thompson */ 1033941e2863SAndrew Thompson for (i = 0; ; i++) { 1034eea19fceSAndrew Thompson /* Check if we still have uhso nodes to check */ 1035941e2863SAndrew Thompson snprintf(buf, 64, SYSCTL_TEST, i); 1036941e2863SAndrew Thompson len = 127; 1037941e2863SAndrew Thompson error = sysctlbyname(buf, data, &len, NULL, 0); 1038eea19fceSAndrew Thompson data[len] = '\0'; 1039941e2863SAndrew Thompson #ifdef DEBUG 1040941e2863SAndrew Thompson fprintf(stderr, "sysctl %s returned(%d): %s\n", 1041941e2863SAndrew Thompson buf, error, error == 0 ? data : "FAILED"); 1042941e2863SAndrew Thompson #endif 1043eea19fceSAndrew Thompson if (error < 0 || strcasecmp(data, "uhso") != 0) 1044941e2863SAndrew Thompson return NULL; 1045941e2863SAndrew Thompson 1046eea19fceSAndrew Thompson /* Check if this node contains the network interface we want */ 1047941e2863SAndrew Thompson snprintf(buf, 64, SYSCTL_NETIF, i); 1048941e2863SAndrew Thompson len = 127; 1049941e2863SAndrew Thompson error = sysctlbyname(buf, data, &len, NULL, 0); 1050eea19fceSAndrew Thompson data[len] = '\0'; 1051941e2863SAndrew Thompson #ifdef DEBUG 1052941e2863SAndrew Thompson fprintf(stderr, "sysctl %s returned(%d): %s\n", 1053941e2863SAndrew Thompson buf, error, error == 0 ? data : "FAILED"); 1054941e2863SAndrew Thompson #endif 1055eea19fceSAndrew Thompson if (error == 0 && strcasecmp(data, ctx->ifnam) == 0) 1056941e2863SAndrew Thompson break; 1057941e2863SAndrew Thompson } 1058941e2863SAndrew Thompson 1059eea19fceSAndrew Thompson /* Figure out the USB port location */ 1060eea19fceSAndrew Thompson snprintf(buf, 64, SYSCTL_LOCATION, i); 1061eea19fceSAndrew Thompson len = 127; 1062eea19fceSAndrew Thompson error = sysctlbyname(buf, data, &len, NULL, 0); 1063eea19fceSAndrew Thompson data[len] = '\0'; 1064eea19fceSAndrew Thompson #ifdef DEBUG 1065eea19fceSAndrew Thompson fprintf(stderr, "sysctl %s returned(%d): %s\n", 1066eea19fceSAndrew Thompson buf, error, error == 0 ? data : "FAILED"); 1067eea19fceSAndrew Thompson #endif 1068eea19fceSAndrew Thompson if (error != 0) 1069eea19fceSAndrew Thompson return (NULL); 1070eea19fceSAndrew Thompson 1071eea19fceSAndrew Thompson q = strstr(data, "port="); 1072eea19fceSAndrew Thompson if (q != NULL) { 1073eea19fceSAndrew Thompson error = sscanf(q, " port=%d", &usbport); 1074eea19fceSAndrew Thompson if (error != 1) { 1075eea19fceSAndrew Thompson #ifdef DEBUG 1076eea19fceSAndrew Thompson fprintf(stderr, "failed to read usb port location from '%s'\n", data); 1077eea19fceSAndrew Thompson #endif 1078eea19fceSAndrew Thompson return (NULL); 1079eea19fceSAndrew Thompson } 1080eea19fceSAndrew Thompson } else { 1081eea19fceSAndrew Thompson #ifdef DEBUG 1082eea19fceSAndrew Thompson fprintf(stderr, "failed to parse location '%s'\n", data); 1083eea19fceSAndrew Thompson #endif 1084eea19fceSAndrew Thompson return (NULL); 1085eea19fceSAndrew Thompson } 1086eea19fceSAndrew Thompson #ifdef DEBUG 1087eea19fceSAndrew Thompson fprintf(stderr, "USB port location=%d\n", usbport); 1088eea19fceSAndrew Thompson #endif 1089eea19fceSAndrew Thompson 1090eea19fceSAndrew Thompson /* 1091eea19fceSAndrew Thompson * Now go through it all again but only look at those matching the 1092eea19fceSAndrew Thompson * usb port location we found. 1093eea19fceSAndrew Thompson */ 1094eea19fceSAndrew Thompson for (i = 0; ; i++) { 1095eea19fceSAndrew Thompson snprintf(buf, 64, SYSCTL_LOCATION, i); 1096eea19fceSAndrew Thompson len = 127; 1097eea19fceSAndrew Thompson memset(&data, 0, sizeof(data)); 1098eea19fceSAndrew Thompson error = sysctlbyname(buf, data, &len, NULL, 0); 1099eea19fceSAndrew Thompson if (error != 0) 1100eea19fceSAndrew Thompson break; 1101eea19fceSAndrew Thompson data[len] = '\0'; 1102eea19fceSAndrew Thompson q = strstr(data, "port="); 1103eea19fceSAndrew Thompson if (q == NULL) 1104eea19fceSAndrew Thompson continue; 1105eea19fceSAndrew Thompson sscanf(q, " port=%d", &usbport0); 1106eea19fceSAndrew Thompson if (usbport != usbport0) 1107eea19fceSAndrew Thompson continue; 1108eea19fceSAndrew Thompson 1109eea19fceSAndrew Thompson /* Try to add ports */ 1110941e2863SAndrew Thompson for (p = port_type_list; *p != NULL; p++) { 1111941e2863SAndrew Thompson snprintf(buf, 64, SYSCTL_NAME_TTY, i, *p); 1112941e2863SAndrew Thompson len = 127; 1113eea19fceSAndrew Thompson memset(&data, 0, sizeof(data)); 1114941e2863SAndrew Thompson error = sysctlbyname(buf, data, &len, NULL, 0); 1115eea19fceSAndrew Thompson data[len] = '\0'; 1116941e2863SAndrew Thompson #ifdef DEBUG 1117941e2863SAndrew Thompson fprintf(stderr, "sysctl %s returned(%d): %s\n", 1118941e2863SAndrew Thompson buf, error, error == 0 ? data : "FAILED"); 1119941e2863SAndrew Thompson #endif 1120941e2863SAndrew Thompson if (error == 0) { 1121941e2863SAndrew Thompson list = realloc(list, (list_size + 1) * sizeof(char *)); 1122941e2863SAndrew Thompson list[list_size] = malloc(strlen(data) + strlen(TTY_NAME)); 1123941e2863SAndrew Thompson sprintf(list[list_size], TTY_NAME, data); 1124941e2863SAndrew Thompson list_size++; 1125941e2863SAndrew Thompson } 1126941e2863SAndrew Thompson } 1127eea19fceSAndrew Thompson } 1128941e2863SAndrew Thompson list = realloc(list, (list_size + 1) * sizeof(char *)); 1129941e2863SAndrew Thompson list[list_size] = NULL; 1130eea19fceSAndrew Thompson return (list); 1131941e2863SAndrew Thompson } 1132941e2863SAndrew Thompson 1133941e2863SAndrew Thompson static int 1134941e2863SAndrew Thompson do_connect(struct ctx *ctx, const char *tty) 1135941e2863SAndrew Thompson { 1136941e2863SAndrew Thompson int i, error, needcfg; 1137941e2863SAndrew Thompson resp_arg ra; 1138941e2863SAndrew Thompson struct termios t; 1139941e2863SAndrew Thompson char **buf; 1140941e2863SAndrew Thompson 1141941e2863SAndrew Thompson #ifdef DEBUG 1142941e2863SAndrew Thompson fprintf(stderr, "Attempting to open %s\n", tty); 1143941e2863SAndrew Thompson #endif 1144941e2863SAndrew Thompson 1145941e2863SAndrew Thompson ctx->fd = open(tty, O_RDWR); 1146941e2863SAndrew Thompson if (ctx->fd < 0) { 1147941e2863SAndrew Thompson #ifdef DEBUG 1148941e2863SAndrew Thompson fprintf(stderr, "Failed to open %s\n", tty); 1149941e2863SAndrew Thompson #endif 1150941e2863SAndrew Thompson return (-1); 1151941e2863SAndrew Thompson } 1152941e2863SAndrew Thompson 1153941e2863SAndrew Thompson tcgetattr(ctx->fd, &t); 1154941e2863SAndrew Thompson t.c_oflag = 0; 1155941e2863SAndrew Thompson t.c_iflag = 0; 1156941e2863SAndrew Thompson t.c_cflag = CLOCAL | CREAD; 1157941e2863SAndrew Thompson t.c_lflag = 0; 1158941e2863SAndrew Thompson tcsetattr(ctx->fd, TCSAFLUSH, &t); 1159941e2863SAndrew Thompson 1160941e2863SAndrew Thompson error = at_cmd(ctx, NULL, NULL, NULL, "AT\r\n"); 1161941e2863SAndrew Thompson if (error == -2) { 1162eea19fceSAndrew Thompson warnx("failed to read from device %s", tty); 1163941e2863SAndrew Thompson return (-1); 1164941e2863SAndrew Thompson } 1165941e2863SAndrew Thompson 1166941e2863SAndrew Thompson /* Check for PIN */ 1167941e2863SAndrew Thompson error = at_cmd(ctx, "+CPIN: READY", NULL, NULL, "AT+CPIN?\r\n"); 1168941e2863SAndrew Thompson if (error != 0) { 1169eea19fceSAndrew Thompson ra.val[0].ptr = NULL; 1170eea19fceSAndrew Thompson ra.val[1].int32 = 0; 1171eea19fceSAndrew Thompson error = at_cmd(ctx, "+CME ERROR", saveresp, &ra, "AT+CPIN?\r\n"); 1172eea19fceSAndrew Thompson if (ra.val[1].int32 > 0) { 1173eea19fceSAndrew Thompson char *p; 1174eea19fceSAndrew Thompson 1175eea19fceSAndrew Thompson buf = ra.val[0].ptr; 1176eea19fceSAndrew Thompson if (strstr(buf[0], "+CME ERROR:") != NULL) { 1177eea19fceSAndrew Thompson buf[0] += 12; 11785eeae9a7SDimitry Andric errx(1, "%s", buf[0]); 1179eea19fceSAndrew Thompson } 1180eea19fceSAndrew Thompson freeresp(&ra); 1181eea19fceSAndrew Thompson } else 1182eea19fceSAndrew Thompson freeresp(&ra); 1183eea19fceSAndrew Thompson 1184941e2863SAndrew Thompson if (ctx->pin == NULL) { 1185941e2863SAndrew Thompson errx(1, "device requires PIN"); 1186941e2863SAndrew Thompson } 1187941e2863SAndrew Thompson 1188941e2863SAndrew Thompson error = at_cmd(ctx, NULL, NULL, NULL, "AT+CPIN=\"%s\"\r\n", 1189941e2863SAndrew Thompson ctx->pin); 1190941e2863SAndrew Thompson if (error != 0) { 1191941e2863SAndrew Thompson errx(1, "wrong PIN"); 1192941e2863SAndrew Thompson } 1193941e2863SAndrew Thompson } 1194941e2863SAndrew Thompson 1195941e2863SAndrew Thompson /* 1196941e2863SAndrew Thompson * Check if a PDP context has been configured and configure one 1197941e2863SAndrew Thompson * if needed. 1198941e2863SAndrew Thompson */ 1199941e2863SAndrew Thompson ra.val[0].ptr = NULL; 1200941e2863SAndrew Thompson ra.val[1].int32 = 0; 1201941e2863SAndrew Thompson error = at_cmd(ctx, "+CGDCONT", saveresp, &ra, "AT+CGDCONT?\r\n"); 1202941e2863SAndrew Thompson buf = ra.val[0].ptr; 1203941e2863SAndrew Thompson needcfg = 1; 1204941e2863SAndrew Thompson for (i = 0; i < ra.val[1].int32; i++) { 1205941e2863SAndrew Thompson char apn[256]; 1206941e2863SAndrew Thompson int cid; 1207941e2863SAndrew Thompson error = sscanf(buf[i], "+CGDCONT: %d,\"%*[^\"]\",\"%[^\"]\"", 1208941e2863SAndrew Thompson &cid, apn); 1209941e2863SAndrew Thompson if (error != 2) { 1210941e2863SAndrew Thompson free(buf[i]); 1211941e2863SAndrew Thompson continue; 1212941e2863SAndrew Thompson } 1213941e2863SAndrew Thompson 1214941e2863SAndrew Thompson if (cid == ctx->pdp_ctx) { 1215941e2863SAndrew Thompson ctx->con_apn = strdup(apn); 1216941e2863SAndrew Thompson if (ctx->pdp_apn != NULL) { 1217941e2863SAndrew Thompson if (strcmp(apn, ctx->pdp_apn) == 0) 1218941e2863SAndrew Thompson needcfg = 0; 1219941e2863SAndrew Thompson } 1220941e2863SAndrew Thompson else { 1221941e2863SAndrew Thompson needcfg = 0; 1222941e2863SAndrew Thompson } 1223941e2863SAndrew Thompson } 1224941e2863SAndrew Thompson free(buf[i]); 1225941e2863SAndrew Thompson } 1226941e2863SAndrew Thompson free(buf); 1227941e2863SAndrew Thompson 1228941e2863SAndrew Thompson if (needcfg) { 1229941e2863SAndrew Thompson if (ctx->pdp_apn == NULL) 1230941e2863SAndrew Thompson errx(1, "device is not configured and no APN given"); 1231941e2863SAndrew Thompson 1232941e2863SAndrew Thompson error = at_cmd(ctx, NULL, NULL, NULL, 1233941e2863SAndrew Thompson "AT+CGDCONT=%d,,\"%s\"\r\n", ctx->pdp_ctx, ctx->pdp_apn); 1234941e2863SAndrew Thompson if (error != 0) { 1235941e2863SAndrew Thompson errx(1, "failed to configure device"); 1236941e2863SAndrew Thompson } 1237941e2863SAndrew Thompson ctx->con_apn = strdup(ctx->pdp_apn); 1238941e2863SAndrew Thompson } 1239941e2863SAndrew Thompson 1240941e2863SAndrew Thompson if (ctx->pdp_user != NULL || ctx->pdp_pwd != NULL) { 1241941e2863SAndrew Thompson at_cmd(ctx, NULL, NULL, NULL, 1242941e2863SAndrew Thompson "AT$QCPDPP=%d,1,\"%s\",\"%s\"\r\n", ctx->pdp_ctx, 1243941e2863SAndrew Thompson (ctx->pdp_user != NULL) ? ctx->pdp_user : "", 1244941e2863SAndrew Thompson (ctx->pdp_pwd != NULL) ? ctx->pdp_pwd : ""); 1245941e2863SAndrew Thompson } 1246941e2863SAndrew Thompson 1247941e2863SAndrew Thompson error = at_cmd(ctx, NULL, NULL, NULL, "AT_OWANCALL=%d,0,0\r\n", 1248941e2863SAndrew Thompson ctx->pdp_ctx); 1249941e2863SAndrew Thompson if (error != 0) 1250941e2863SAndrew Thompson return (-1); 1251941e2863SAndrew Thompson 1252941e2863SAndrew Thompson at_cmd_async(ctx->fd, "AT+CGREG?\r\n"); 1253941e2863SAndrew Thompson at_cmd_async(ctx->fd, "AT+CREG?\r\n"); 1254941e2863SAndrew Thompson 1255941e2863SAndrew Thompson tmr_add(&timers, 1, 5, tmr_status, ctx); 1256941e2863SAndrew Thompson return (0); 1257941e2863SAndrew Thompson } 1258941e2863SAndrew Thompson 1259941e2863SAndrew Thompson static void 1260941e2863SAndrew Thompson do_disconnect(struct ctx *ctx) 1261941e2863SAndrew Thompson { 1262*c74a4ceaSKonrad Witaszczyk struct sockaddr_in sin; 1263941e2863SAndrew Thompson 1264941e2863SAndrew Thompson /* Disconnect */ 1265941e2863SAndrew Thompson at_cmd(ctx, NULL, NULL, NULL, "AT_OWANCALL=%d,0,0\r\n", 1266941e2863SAndrew Thompson ctx->pdp_ctx); 1267941e2863SAndrew Thompson close(ctx->fd); 1268941e2863SAndrew Thompson 1269941e2863SAndrew Thompson /* Remove ip-address from interface */ 1270941e2863SAndrew Thompson if (ctx->flags & IPASSIGNED) { 1271*c74a4ceaSKonrad Witaszczyk sin.sin_len = sizeof(struct sockaddr_in); 1272*c74a4ceaSKonrad Witaszczyk sin.sin_family = AF_INET; 1273941e2863SAndrew Thompson memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr, 1274941e2863SAndrew Thompson sizeof(sin.sin_addr.s_addr)); 1275*c74a4ceaSKonrad Witaszczyk ifaddr_del(ctx->ifnam, (struct sockaddr *)&sin); 1276941e2863SAndrew Thompson 1277941e2863SAndrew Thompson if_ifdown(ctx->ifnam); 1278941e2863SAndrew Thompson ctx->flags &= ~IPASSIGNED; 1279941e2863SAndrew Thompson } 1280941e2863SAndrew Thompson 1281941e2863SAndrew Thompson /* Attempt to reset resolv.conf */ 1282941e2863SAndrew Thompson set_nameservers(ctx, ctx->resolv_path, 0); 1283941e2863SAndrew Thompson } 1284941e2863SAndrew Thompson 1285941e2863SAndrew Thompson static void 1286941e2863SAndrew Thompson daemonize(struct ctx *ctx) 1287941e2863SAndrew Thompson { 1288941e2863SAndrew Thompson struct pidfh *pfh; 1289941e2863SAndrew Thompson pid_t opid; 1290941e2863SAndrew Thompson 1291941e2863SAndrew Thompson snprintf(ctx->pidfile, 127, PIDFILE, ctx->ifnam); 1292941e2863SAndrew Thompson 1293941e2863SAndrew Thompson pfh = pidfile_open(ctx->pidfile, 0600, &opid); 1294941e2863SAndrew Thompson if (pfh == NULL) { 1295941e2863SAndrew Thompson warn("Cannot create pidfile %s", ctx->pidfile); 1296941e2863SAndrew Thompson return; 1297941e2863SAndrew Thompson } 1298941e2863SAndrew Thompson 1299941e2863SAndrew Thompson if (daemon(0, 0) == -1) { 1300941e2863SAndrew Thompson warn("Cannot daemonize"); 1301941e2863SAndrew Thompson pidfile_remove(pfh); 1302941e2863SAndrew Thompson return; 1303941e2863SAndrew Thompson } 1304941e2863SAndrew Thompson 1305941e2863SAndrew Thompson pidfile_write(pfh); 1306941e2863SAndrew Thompson ctx->pfh = pfh; 1307941e2863SAndrew Thompson ctx->flags |= FLG_DAEMON; 1308941e2863SAndrew Thompson 1309941e2863SAndrew Thompson snprintf(syslog_title, 63, "%s:%s", getprogname(), ctx->ifnam); 1310941e2863SAndrew Thompson openlog(syslog_title, LOG_PID, LOG_USER); 1311941e2863SAndrew Thompson syslog_open = 1; 1312941e2863SAndrew Thompson } 1313941e2863SAndrew Thompson 1314941e2863SAndrew Thompson static void 1315941e2863SAndrew Thompson send_disconnect(const char *ifnam) 1316941e2863SAndrew Thompson { 1317941e2863SAndrew Thompson char pidfile[128]; 1318941e2863SAndrew Thompson FILE *fp; 1319941e2863SAndrew Thompson pid_t pid; 1320941e2863SAndrew Thompson int n; 1321941e2863SAndrew Thompson 1322941e2863SAndrew Thompson snprintf(pidfile, 127, PIDFILE, ifnam); 1323941e2863SAndrew Thompson fp = fopen(pidfile, "r"); 1324941e2863SAndrew Thompson if (fp == NULL) { 1325941e2863SAndrew Thompson warn("Cannot open %s", pidfile); 1326941e2863SAndrew Thompson return; 1327941e2863SAndrew Thompson } 1328941e2863SAndrew Thompson 1329941e2863SAndrew Thompson n = fscanf(fp, "%d", &pid); 1330941e2863SAndrew Thompson fclose(fp); 1331941e2863SAndrew Thompson if (n != 1) { 1332941e2863SAndrew Thompson warnx("unable to read daemon pid"); 1333941e2863SAndrew Thompson return; 1334941e2863SAndrew Thompson } 1335941e2863SAndrew Thompson #ifdef DEBUG 1336941e2863SAndrew Thompson fprintf(stderr, "Sending SIGTERM to %d\n", pid); 1337941e2863SAndrew Thompson #endif 1338941e2863SAndrew Thompson kill(pid, SIGTERM); 1339941e2863SAndrew Thompson } 1340941e2863SAndrew Thompson 1341941e2863SAndrew Thompson static void 1342941e2863SAndrew Thompson usage(const char *exec) 1343941e2863SAndrew Thompson { 1344941e2863SAndrew Thompson 1345941e2863SAndrew Thompson printf("usage %s [-b] [-n] [-a apn] [-c cid] [-p pin] [-u username] " 1346941e2863SAndrew Thompson "[-k password] [-r resolvpath] [-f tty] interface\n", exec); 1347941e2863SAndrew Thompson printf("usage %s -d interface\n", exec); 1348941e2863SAndrew Thompson } 1349941e2863SAndrew Thompson 1350941e2863SAndrew Thompson enum { 1351941e2863SAndrew Thompson MODE_CONN, 1352941e2863SAndrew Thompson MODE_DISC 1353941e2863SAndrew Thompson }; 1354941e2863SAndrew Thompson 1355941e2863SAndrew Thompson int 1356941e2863SAndrew Thompson main(int argc, char *argv[]) 1357941e2863SAndrew Thompson { 1358941e2863SAndrew Thompson int ch, error, mode; 1359941e2863SAndrew Thompson const char *ifnam = NULL; 1360941e2863SAndrew Thompson char *tty = NULL; 1361941e2863SAndrew Thompson char **p, **tty_list; 1362941e2863SAndrew Thompson fd_set set; 1363941e2863SAndrew Thompson struct ctx ctx; 1364941e2863SAndrew Thompson struct itimerval it; 1365941e2863SAndrew Thompson 1366941e2863SAndrew Thompson TAILQ_INIT(&timers.head); 1367941e2863SAndrew Thompson timers.res = 1; 1368941e2863SAndrew Thompson 1369941e2863SAndrew Thompson ctx.pdp_ctx = 1; 1370941e2863SAndrew Thompson ctx.pdp_apn = ctx.pdp_user = ctx.pdp_pwd = NULL; 1371941e2863SAndrew Thompson ctx.pin = NULL; 1372941e2863SAndrew Thompson 1373941e2863SAndrew Thompson ctx.con_status = 0; 1374941e2863SAndrew Thompson ctx.con_apn = NULL; 1375941e2863SAndrew Thompson ctx.con_oper = NULL; 1376941e2863SAndrew Thompson ctx.con_net_stat = 0; 1377941e2863SAndrew Thompson ctx.con_net_type = -1; 1378941e2863SAndrew Thompson ctx.flags = 0; 1379941e2863SAndrew Thompson ctx.resolv_path = RESOLV_PATH; 1380941e2863SAndrew Thompson ctx.resolv = NULL; 1381941e2863SAndrew Thompson ctx.ns = NULL; 1382941e2863SAndrew Thompson ctx.dbm = 0; 1383941e2863SAndrew Thompson 1384941e2863SAndrew Thompson mode = MODE_CONN; 1385941e2863SAndrew Thompson ctx.flags |= FLG_DELAYED; 1386941e2863SAndrew Thompson 1387941e2863SAndrew Thompson while ((ch = getopt(argc, argv, "?ha:p:c:u:k:r:f:dbn")) != -1) { 1388941e2863SAndrew Thompson switch (ch) { 1389941e2863SAndrew Thompson case 'a': 1390941e2863SAndrew Thompson ctx.pdp_apn = argv[optind - 1]; 1391941e2863SAndrew Thompson break; 1392941e2863SAndrew Thompson case 'c': 1393941e2863SAndrew Thompson ctx.pdp_ctx = strtol(argv[optind - 1], NULL, 10); 1394941e2863SAndrew Thompson if (ctx.pdp_ctx < 1) { 1395941e2863SAndrew Thompson warnx("Invalid context ID, defaulting to 1"); 1396941e2863SAndrew Thompson ctx.pdp_ctx = 1; 1397941e2863SAndrew Thompson } 1398941e2863SAndrew Thompson break; 1399941e2863SAndrew Thompson case 'p': 1400941e2863SAndrew Thompson ctx.pin = argv[optind - 1]; 1401941e2863SAndrew Thompson break; 1402941e2863SAndrew Thompson case 'u': 1403941e2863SAndrew Thompson ctx.pdp_user = argv[optind - 1]; 1404941e2863SAndrew Thompson break; 1405941e2863SAndrew Thompson case 'k': 1406941e2863SAndrew Thompson ctx.pdp_pwd = argv[optind - 1]; 1407941e2863SAndrew Thompson break; 1408941e2863SAndrew Thompson case 'r': 1409941e2863SAndrew Thompson ctx.resolv_path = argv[optind - 1]; 1410941e2863SAndrew Thompson break; 1411941e2863SAndrew Thompson case 'd': 1412941e2863SAndrew Thompson mode = MODE_DISC; 1413941e2863SAndrew Thompson break; 1414941e2863SAndrew Thompson case 'b': 1415941e2863SAndrew Thompson ctx.flags &= ~FLG_DELAYED; 1416941e2863SAndrew Thompson break; 1417941e2863SAndrew Thompson case 'n': 1418941e2863SAndrew Thompson ctx.flags |= FLG_NODAEMON; 1419941e2863SAndrew Thompson break; 1420941e2863SAndrew Thompson case 'f': 1421941e2863SAndrew Thompson tty = argv[optind - 1]; 1422941e2863SAndrew Thompson break; 1423941e2863SAndrew Thompson case 'h': 1424941e2863SAndrew Thompson case '?': 1425941e2863SAndrew Thompson default: 1426941e2863SAndrew Thompson usage(argv[0]); 1427941e2863SAndrew Thompson exit(EXIT_SUCCESS); 1428941e2863SAndrew Thompson } 1429941e2863SAndrew Thompson } 1430941e2863SAndrew Thompson 1431941e2863SAndrew Thompson argc -= optind; 1432941e2863SAndrew Thompson argv += optind; 1433941e2863SAndrew Thompson 1434941e2863SAndrew Thompson if (argc < 1) 1435941e2863SAndrew Thompson errx(1, "no interface given"); 1436941e2863SAndrew Thompson 1437941e2863SAndrew Thompson ifnam = argv[argc - 1]; 1438941e2863SAndrew Thompson ctx.ifnam = strdup(ifnam); 1439941e2863SAndrew Thompson 1440941e2863SAndrew Thompson switch (mode) { 1441941e2863SAndrew Thompson case MODE_DISC: 1442941e2863SAndrew Thompson printf("Disconnecting %s\n", ifnam); 1443941e2863SAndrew Thompson send_disconnect(ifnam); 1444941e2863SAndrew Thompson exit(EXIT_SUCCESS); 1445941e2863SAndrew Thompson default: 1446941e2863SAndrew Thompson break; 1447941e2863SAndrew Thompson } 1448941e2863SAndrew Thompson 1449941e2863SAndrew Thompson signal(SIGHUP, sig_handle); 1450941e2863SAndrew Thompson signal(SIGINT, sig_handle); 1451941e2863SAndrew Thompson signal(SIGQUIT, sig_handle); 1452941e2863SAndrew Thompson signal(SIGTERM, sig_handle); 1453941e2863SAndrew Thompson signal(SIGALRM, sig_handle); 1454941e2863SAndrew Thompson 1455941e2863SAndrew Thompson it.it_interval.tv_sec = 1; 1456941e2863SAndrew Thompson it.it_interval.tv_usec = 0; 1457941e2863SAndrew Thompson it.it_value.tv_sec = 1; 1458941e2863SAndrew Thompson it.it_value.tv_usec = 0; 1459941e2863SAndrew Thompson error = setitimer(ITIMER_REAL, &it, NULL); 1460941e2863SAndrew Thompson if (error != 0) 1461941e2863SAndrew Thompson errx(1, "setitimer"); 1462941e2863SAndrew Thompson 1463941e2863SAndrew Thompson tmr_add(&timers, 1, 5, &tmr_watchdog, &ctx); 1464941e2863SAndrew Thompson watchdog_reset(&ctx, 15); 1465941e2863SAndrew Thompson 1466941e2863SAndrew Thompson if (tty != NULL) { 1467941e2863SAndrew Thompson error = do_connect(&ctx, tty); 1468941e2863SAndrew Thompson if (error != 0) 1469941e2863SAndrew Thompson errx(1, "Failed to open %s", tty); 1470941e2863SAndrew Thompson } 1471941e2863SAndrew Thompson else { 1472941e2863SAndrew Thompson tty_list = get_tty(&ctx); 1473eea19fceSAndrew Thompson if (tty_list == NULL) 1474eea19fceSAndrew Thompson errx(1, "%s does not appear to be a uhso device", ifnam); 1475941e2863SAndrew Thompson #ifdef DEBUG 1476941e2863SAndrew Thompson if (tty_list == NULL) { 1477941e2863SAndrew Thompson fprintf(stderr, "get_tty returned empty list\n"); 1478941e2863SAndrew Thompson } else { 1479941e2863SAndrew Thompson fprintf(stderr, "tty list:\n"); 1480941e2863SAndrew Thompson for (p = tty_list; *p != NULL; p++) { 1481941e2863SAndrew Thompson fprintf(stderr, "\t %s\n", *p); 1482941e2863SAndrew Thompson } 1483941e2863SAndrew Thompson } 1484941e2863SAndrew Thompson #endif 1485941e2863SAndrew Thompson for (p = tty_list; *p != NULL; p++) { 1486941e2863SAndrew Thompson error = do_connect(&ctx, *p); 1487941e2863SAndrew Thompson if (error == 0) { 1488941e2863SAndrew Thompson tty = *p; 1489941e2863SAndrew Thompson break; 1490941e2863SAndrew Thompson } 1491941e2863SAndrew Thompson } 1492941e2863SAndrew Thompson if (*p == NULL) 1493941e2863SAndrew Thompson errx(1, "Failed to obtain a control port, " 1494941e2863SAndrew Thompson "try specifying one manually"); 1495941e2863SAndrew Thompson } 1496941e2863SAndrew Thompson 1497941e2863SAndrew Thompson if (!(ctx.flags & FLG_DELAYED) && !(ctx.flags & FLG_NODAEMON)) 1498941e2863SAndrew Thompson daemonize(&ctx); 1499941e2863SAndrew Thompson 1500941e2863SAndrew Thompson 1501941e2863SAndrew Thompson FD_ZERO(&set); 1502941e2863SAndrew Thompson FD_SET(ctx.fd, &set); 1503941e2863SAndrew Thompson for (;;) { 1504941e2863SAndrew Thompson 1505941e2863SAndrew Thompson watchdog_disable(&ctx); 1506941e2863SAndrew Thompson error = select(ctx.fd + 1, &set, NULL, NULL, NULL); 1507941e2863SAndrew Thompson if (error <= 0) { 1508941e2863SAndrew Thompson if (running && errno == EINTR) 1509941e2863SAndrew Thompson continue; 1510941e2863SAndrew Thompson if (ctx.flags & FLG_WDEXP) { 1511941e2863SAndrew Thompson ctx.flags &= ~FLG_WDEXP; 1512941e2863SAndrew Thompson watchdog_reset(&ctx, 5); 1513941e2863SAndrew Thompson do_disconnect(&ctx); 1514941e2863SAndrew Thompson watchdog_reset(&ctx, 15); 1515941e2863SAndrew Thompson do_connect(&ctx, tty); 1516941e2863SAndrew Thompson running = 1; 1517941e2863SAndrew Thompson continue; 1518941e2863SAndrew Thompson } 1519941e2863SAndrew Thompson 1520941e2863SAndrew Thompson break; 1521941e2863SAndrew Thompson } 1522941e2863SAndrew Thompson 1523941e2863SAndrew Thompson if (FD_ISSET(ctx.fd, &set)) { 1524941e2863SAndrew Thompson watchdog_reset(&ctx, 15); 1525941e2863SAndrew Thompson error = at_async(&ctx, &ctx); 1526941e2863SAndrew Thompson if (error != 0) 1527941e2863SAndrew Thompson break; 1528941e2863SAndrew Thompson } 1529941e2863SAndrew Thompson FD_SET(ctx.fd, &set); 1530941e2863SAndrew Thompson 1531941e2863SAndrew Thompson if (!(ctx.flags & FLG_DAEMON) && (ctx.flags & IPASSIGNED)) { 1532941e2863SAndrew Thompson printf("Status: %s (%s)", 1533941e2863SAndrew Thompson ctx.con_status ? "connected" : "disconnected", 1534941e2863SAndrew Thompson network_access_type[ctx.con_net_type]); 1535941e2863SAndrew Thompson if (ctx.dbm < 0) 1536941e2863SAndrew Thompson printf(", signal: %d dBm", ctx.dbm); 1537eea19fceSAndrew Thompson printf("\t\t\t\r"); 1538941e2863SAndrew Thompson fflush(stdout); 1539941e2863SAndrew Thompson } 1540941e2863SAndrew Thompson } 1541941e2863SAndrew Thompson if (!(ctx.flags & FLG_DAEMON) && (ctx.flags & IPASSIGNED)) 1542941e2863SAndrew Thompson printf("\n"); 1543941e2863SAndrew Thompson 1544941e2863SAndrew Thompson signal(SIGHUP, SIG_DFL); 1545941e2863SAndrew Thompson signal(SIGINT, SIG_DFL); 1546941e2863SAndrew Thompson signal(SIGQUIT, SIG_DFL); 1547941e2863SAndrew Thompson signal(SIGTERM, SIG_DFL); 1548941e2863SAndrew Thompson signal(SIGALRM, SIG_IGN); 1549941e2863SAndrew Thompson 1550941e2863SAndrew Thompson do_disconnect(&ctx); 1551941e2863SAndrew Thompson 1552941e2863SAndrew Thompson if (ctx.flags & FLG_DAEMON) { 1553941e2863SAndrew Thompson pidfile_remove(ctx.pfh); 1554941e2863SAndrew Thompson if (syslog_open) 1555941e2863SAndrew Thompson closelog(); 1556941e2863SAndrew Thompson } 1557941e2863SAndrew Thompson 1558941e2863SAndrew Thompson return (0); 1559941e2863SAndrew Thompson } 1560