1 /* 2 * uqmid 3 * Copyright (C) 2023 Alexander Couzens <lynxis@fe80.eu> 4 * 5 * based on netifd/ubus.c by 6 * Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 10 * as published by the Free Software Foundation 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 */ 17 #include "gsmtap_util.h" 18 #include "osmocom/fsm.h" 19 #include "qmi-enums-wds.h" 20 21 #include <arpa/inet.h> 22 #include <string.h> 23 #include <stdio.h> 24 25 #include <libubus.h> 26 #include <talloc.h> 27 28 #include "uqmid.h" 29 #include "logging.h" 30 #include "modem.h" 31 #include "utils.h" 32 33 #include "ubus.h" 34 35 struct ubus_context *ubus_ctx = NULL; 36 static struct blob_buf b; 37 static const char *ubus_path; 38 /* global object */ 39 40 static void uqmid_ubus_add_fd(void) 41 { 42 ubus_add_uloop(ubus_ctx); 43 system_fd_set_cloexec(ubus_ctx->sock.fd); 44 } 45 46 static int uqmid_handle_restart(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, 47 const char *method, struct blob_attr *msg) 48 { 49 /* TODO: netifd is calling itself via exec, unsure if we need this also. 50 uqmid_restart(); */ 51 return 0; 52 } 53 54 static int uqmid_reload(void) 55 { 56 return 1; 57 } 58 59 static int uqmid_handle_reload(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, 60 const char *method, struct blob_attr *msg) 61 { 62 if (uqmid_reload()) 63 return UBUS_STATUS_NOT_FOUND; 64 65 return UBUS_STATUS_OK; 66 } 67 68 enum { 69 GSMTAP_TARGET, 70 __GSMTAP_MAX 71 }; 72 73 static const struct blobmsg_policy enable_gsmtap_policy[__GSMTAP_MAX] = { 74 [GSMTAP_TARGET] = { .name = "target", .type = BLOBMSG_TYPE_STRING }, 75 }; 76 77 static int enable_gsmtap(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, 78 const char *method, struct blob_attr *msg) 79 { 80 struct blob_attr *tb[__GSMTAP_MAX]; 81 char *target = NULL; 82 int ret; 83 84 blobmsg_parse(enable_gsmtap_policy, __GSMTAP_MAX, tb, blob_data(msg), blob_len(msg)); 85 if (tb[GSMTAP_TARGET]) { 86 target = blobmsg_get_string(tb[GSMTAP_TARGET]); 87 } else { 88 target = "127.0.0.1"; 89 } 90 ret = gsmtap_enable(target); 91 if (ret) 92 return UBUS_STATUS_UNKNOWN_ERROR; 93 94 return UBUS_STATUS_OK; 95 } 96 97 static int disable_gsmtap_policy(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, 98 const char *method, struct blob_attr *msg) 99 { 100 gsmtap_disable(); 101 return UBUS_STATUS_OK; 102 } 103 104 static int uqmid_add_object(struct ubus_object *obj) 105 { 106 int ret = ubus_add_object(ubus_ctx, obj); 107 108 if (ret != 0) 109 LOG_ERROR("Failed to publish object '%s': %s\n", obj->name, ubus_strerror(ret)); 110 111 return ret; 112 } 113 114 enum { 115 AM_NAME, 116 AM_DEVICE, 117 AM_DRIVER, 118 __AM_MAX 119 }; 120 121 /** ubus call add_modem{name: slot1, device: /dev/cdc-wdm0, driver: qmi or mbim (optional, default qmi)}` */ 122 static const struct blobmsg_policy add_modem_policy[__AM_MAX] = { 123 [AM_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING }, 124 [AM_DEVICE] = { .name = "device", .type = BLOBMSG_TYPE_STRING }, 125 [AM_DRIVER] = { .name = "driver", .type = BLOBMSG_TYPE_STRING }, 126 }; 127 128 static int add_modem(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, 129 const char *method, struct blob_attr *msg) 130 { 131 struct blob_attr *tb[__AM_MAX]; 132 char *name = NULL; 133 char *device = NULL; 134 /* tunnel qmi over mbim? */ 135 bool qmi_over_mbim = false; 136 int ret; 137 138 blobmsg_parse(add_modem_policy, __AM_MAX, tb, blob_data(msg), blob_len(msg)); 139 if (!tb[AM_NAME]) { 140 LOG_ERROR("ubus add_modem: missing required argument name."); 141 return UBUS_STATUS_INVALID_ARGUMENT; 142 } 143 name = blobmsg_get_string(tb[AM_NAME]); 144 145 if (!tb[AM_DEVICE]) { 146 LOG_ERROR("ubus add_modem: missing required argument device."); 147 return UBUS_STATUS_INVALID_ARGUMENT; 148 } 149 device = blobmsg_get_string(tb[AM_DEVICE]); 150 151 if (tb[AM_DRIVER]) { 152 const char *driver = blobmsg_get_string(tb[AM_DRIVER]); 153 if (!strcmp(driver, "qmi")) { 154 /* default */ 155 } else if (!strcmp(driver, "mbim")) { 156 qmi_over_mbim = true; 157 } else { 158 LOG_ERROR("ubus add_modem: invalid driver. Valid qmi or mbim."); 159 return UBUS_STATUS_INVALID_ARGUMENT; 160 } 161 } 162 163 ret = uqmid_modem_add(name, device, qmi_over_mbim); 164 if (ret) 165 return UBUS_STATUS_UNKNOWN_ERROR; 166 167 return UBUS_STATUS_OK; 168 } 169 170 /** ubus call remove_modem{name: slot1}` */ 171 enum { 172 RM_NAME, 173 __RM_MAX, 174 }; 175 176 static const struct blobmsg_policy remove_modem_policy[__RM_MAX] = { 177 [RM_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING }, 178 }; 179 180 static int remove_modem(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, 181 const char *method, struct blob_attr *msg) 182 { 183 struct blob_attr *tb[__RM_MAX]; 184 struct modem *modem; 185 char *name = NULL; 186 int ret; 187 188 blobmsg_parse(remove_modem_policy, __RM_MAX, tb, blob_data(msg), blob_len(msg)); 189 if (!tb[RM_NAME]) { 190 LOG_ERROR("ubus add_modem: missing required argument name."); 191 return UBUS_STATUS_INVALID_ARGUMENT; 192 } 193 name = blobmsg_get_string(tb[RM_NAME]); 194 modem = uqmid_modem_find_by_name(name); 195 if (!modem) { 196 LOG_ERROR("Couldn't find modem %s.", name); 197 return UBUS_STATUS_NOT_FOUND; 198 } 199 200 ret = uqmid_modem_remove(modem); 201 if (ret) 202 return UBUS_STATUS_UNKNOWN_ERROR; 203 204 return UBUS_STATUS_OK; 205 } 206 207 /** clean up the ubus state of a modem */ 208 void uqmid_ubus_modem_destroy(struct modem *modem) 209 { 210 ubus_remove_object(ubus_ctx, &modem->ubus); 211 } 212 213 /** inform ubus subscribers of a state change of a modem */ 214 void uqmid_ubus_modem_notify_change(struct modem *modem, int event) 215 { 216 /* TODO: */ 217 } 218 219 static void uqmid_ubus_reconnect_timer(struct uloop_timeout *timeout) 220 { 221 static struct uloop_timeout retry = { 222 .cb = uqmid_ubus_reconnect_timer, 223 }; 224 int t = 2; 225 226 if (ubus_reconnect(ubus_ctx, ubus_path) != 0) { 227 LOG_INFO("failed to reconnect, trying again in %d seconds\n", t); 228 uloop_timeout_set(&retry, t * 1000); 229 return; 230 } 231 232 LOG_INFO("reconnected to ubus, new id: %08x\n", ubus_ctx->local_id); 233 uqmid_ubus_add_fd(); 234 } 235 236 static void uqmid_ubus_connection_lost(struct ubus_context *ctx) 237 { 238 uqmid_ubus_reconnect_timer(NULL); 239 } 240 241 static struct ubus_method main_object_methods[] = { 242 { .name = "restart", .handler = uqmid_handle_restart }, 243 { .name = "reload", .handler = uqmid_handle_reload }, 244 UBUS_METHOD("enable_gsmtap", enable_gsmtap, enable_gsmtap_policy), 245 { .name = "disable_gsmtap", .handler = disable_gsmtap_policy }, 246 UBUS_METHOD("add_modem", add_modem, add_modem_policy), 247 UBUS_METHOD("remove_modem", remove_modem, remove_modem_policy), 248 }; 249 250 static struct ubus_object_type main_object_type = UBUS_OBJECT_TYPE("uqmid", main_object_methods); 251 252 static struct ubus_object main_object = { 253 .name = "uqmid", 254 .type = &main_object_type, 255 .methods = main_object_methods, 256 .n_methods = ARRAY_SIZE(main_object_methods), 257 }; 258 259 /* modem object */ 260 261 /** ubus call uqmid.modem.some1 remove_modem{name: slot1}` */ 262 static int modem_remove(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, 263 const char *method, struct blob_attr *msg) 264 { 265 struct modem *modem = container_of(obj, struct modem, ubus); 266 int ret; 267 268 ret = uqmid_modem_remove(modem); 269 if (ret) 270 return UBUS_STATUS_UNKNOWN_ERROR; 271 272 return UBUS_STATUS_OK; 273 } 274 275 enum { CFG_APN, CFG_PIN, CFG_PUK, CFG_ROAMING, CFG_USERNAME, CFG_PASSWORD, __CFG_MAX }; 276 277 /** ubus call modem_configure{apn: internet, pin: 2342, roaming: false}` */ 278 static const struct blobmsg_policy modem_configure_policy[__CFG_MAX] = { 279 [CFG_APN] = { .name = "apn", .type = BLOBMSG_TYPE_STRING }, 280 [CFG_PIN] = { .name = "pin", .type = BLOBMSG_TYPE_STRING }, 281 [CFG_PUK] = { .name = "puk", .type = BLOBMSG_TYPE_STRING }, 282 [CFG_ROAMING] = { .name = "roaming", .type = BLOBMSG_TYPE_BOOL }, 283 [CFG_USERNAME] = { .name = "username", .type = BLOBMSG_TYPE_STRING }, 284 [CFG_PASSWORD] = { .name = "password", .type = BLOBMSG_TYPE_STRING }, 285 }; 286 287 static int modem_configure(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, 288 const char *method, struct blob_attr *msg) 289 { 290 struct modem *modem = container_of(obj, struct modem, ubus); 291 struct blob_attr *tb[__CFG_MAX]; 292 char *value; 293 int ret; 294 295 /* prevent mixing previous configure calls */ 296 memset(&modem->config, 0, sizeof(struct modem_config)); 297 blobmsg_parse(modem_configure_policy, __CFG_MAX, tb, blob_data(msg), blob_len(msg)); 298 if (tb[CFG_APN]) { 299 TALLOC_FREE(modem->config.apn); 300 value = blobmsg_get_string(tb[CFG_APN]); 301 if (value && strlen(value)) 302 modem->config.apn = talloc_strdup(modem, blobmsg_get_string(tb[CFG_APN])); 303 } 304 305 if (tb[CFG_PIN]) { 306 TALLOC_FREE(modem->config.pin); 307 value = blobmsg_get_string(tb[CFG_PIN]); 308 if (value && strlen(value)) 309 modem->config.pin = talloc_strdup(modem, blobmsg_get_string(tb[CFG_PIN])); 310 } 311 312 if (tb[CFG_PUK]) { 313 TALLOC_FREE(modem->config.puk); 314 value = blobmsg_get_string(tb[CFG_PUK]); 315 if (value && strlen(value)) 316 modem->config.puk = talloc_strdup(modem, blobmsg_get_string(tb[CFG_PUK])); 317 } 318 319 if (tb[CFG_ROAMING]) { 320 modem->config.roaming = blobmsg_get_bool(tb[CFG_ROAMING]); 321 } 322 323 if (tb[CFG_USERNAME]) { 324 TALLOC_FREE(modem->config.username); 325 value = blobmsg_get_string(tb[CFG_USERNAME]); 326 if (value && strlen(value)) 327 modem->config.username = talloc_strdup(modem, blobmsg_get_string(tb[CFG_USERNAME])); 328 } 329 330 if (tb[CFG_PASSWORD]) { 331 TALLOC_FREE(modem->config.password); 332 value = blobmsg_get_string(tb[CFG_PASSWORD]); 333 if (value && strlen(value)) 334 modem->config.password = talloc_strdup(modem, blobmsg_get_string(tb[CFG_PASSWORD])); 335 } 336 337 modem->config.pdp_type = QMI_WDS_PDP_TYPE_IPV4; 338 modem->config.configured = true; 339 340 ret = uqmid_modem_configured(modem); 341 if (ret) 342 return UBUS_STATUS_UNKNOWN_ERROR; 343 344 return UBUS_STATUS_OK; 345 } 346 347 static void modem_get_opmode_cb(void *data, int opmode_err, int opmode) 348 { 349 struct ubus_request_data *req = data; 350 if (opmode_err) { 351 blob_buf_init(&b, 0); 352 blobmsg_add_u16(&b, "operror", opmode_err & 0xff); 353 } else { 354 blob_buf_init(&b, 0); 355 blobmsg_add_u16(&b, "opmode", opmode & 0xff); 356 } 357 /* TODO: add additional opmode as str */ 358 ubus_send_reply(ubus_ctx, req, b.head); 359 ubus_complete_deferred_request(ubus_ctx, req, 0); 360 free(req); 361 } 362 363 static int modem_get_opmode(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, 364 const char *method, struct blob_attr *msg) 365 { 366 struct modem *modem = container_of(obj, struct modem, ubus); 367 int ret; 368 struct ubus_request_data *new_req = calloc(sizeof(*req), 1); 369 if (!new_req) 370 return UBUS_STATUS_NO_MEMORY; 371 372 ubus_defer_request(ctx, req, new_req); 373 ret = uqmid_modem_get_opmode(modem, modem_get_opmode_cb, new_req); 374 if (ret) 375 return UBUS_STATUS_UNKNOWN_ERROR; 376 377 return UBUS_STATUS_OK; 378 } 379 380 static int modem_networkstatus(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, 381 const char *method, struct blob_attr *msg) 382 { 383 struct modem *modem = container_of(obj, struct modem, ubus); 384 int ret; 385 386 ret = uqmid_modem_networkstatus(modem); 387 if (ret) 388 return UBUS_STATUS_UNKNOWN_ERROR; 389 390 return UBUS_STATUS_OK; 391 } 392 393 static void blob_add_addr(struct blob_buf *blob, const char *name, struct sockaddr *addr) 394 { 395 struct sockaddr_in *v4 = (struct sockaddr_in *)addr; 396 struct sockaddr_in6 *v6 = (struct sockaddr_in6 *)addr; 397 char buf[INET6_ADDRSTRLEN]; 398 399 if (!addr) { 400 blobmsg_add_string(blob, name, ""); 401 return; 402 } 403 404 switch (addr->sa_family) { 405 case AF_INET: 406 blobmsg_add_string(blob, name, inet_ntop(AF_INET, &v4->sin_addr, buf, sizeof(buf))); 407 break; 408 case AF_INET6: 409 blobmsg_add_string(blob, name, inet_ntop(AF_INET6, &v6->sin6_addr, buf, sizeof(buf))); 410 break; 411 } 412 } 413 414 #define BLOBMSG_ADD_STR_CHECK(buffer, field, value) blobmsg_add_string(buffer, field, value ? value : "") 415 416 static int modem_dump_state(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, 417 const char *method, struct blob_attr *msg) 418 { 419 struct modem *modem = container_of(obj, struct modem, ubus); 420 421 blob_buf_init(&b, 0); 422 blobmsg_add_string(&b, "name", modem->name); 423 blobmsg_add_string(&b, "device", modem->device); 424 blobmsg_add_u32(&b, "state", modem->fi->state); 425 blobmsg_add_string(&b, "state_name", osmo_fsm_inst_state_name(modem->fi)); 426 BLOBMSG_ADD_STR_CHECK(&b, "manufactor", modem->manuf); 427 BLOBMSG_ADD_STR_CHECK(&b, "model", modem->model); 428 BLOBMSG_ADD_STR_CHECK(&b, "rev", modem->rev); 429 BLOBMSG_ADD_STR_CHECK(&b, "imei", modem->imei); 430 BLOBMSG_ADD_STR_CHECK(&b, "imeisv", modem->imeisv); 431 BLOBMSG_ADD_STR_CHECK(&b, "meid", modem->meid); 432 BLOBMSG_ADD_STR_CHECK(&b, "imsi", modem->imsi); 433 BLOBMSG_ADD_STR_CHECK(&b, "iccid", modem->iccid); 434 /* session state */ 435 BLOBMSG_ADD_STR_CHECK(&b, "config_apn", modem->config.apn); 436 blobmsg_add_u8(&b, "roaming", modem->config.roaming); 437 blob_add_addr(&b, "ipv4_addr", (struct sockaddr *)&modem->brearer.v4_addr); 438 blob_add_addr(&b, "ipv4_netmask", (struct sockaddr *)&modem->brearer.v4_netmask); 439 blob_add_addr(&b, "ipv4_gateway", (struct sockaddr *)&modem->brearer.v4_gateway); 440 blob_add_addr(&b, "ipv6", (struct sockaddr *)&modem->brearer.v6); 441 blob_add_addr(&b, "dns1", (struct sockaddr *)&modem->brearer.dns1); 442 blob_add_addr(&b, "dns2", (struct sockaddr *)&modem->brearer.dns2); 443 /* sim */ 444 /* TODO: add human readable enum values */ 445 blobmsg_add_u16(&b, "sim_state", modem->sim.state); 446 blobmsg_add_string(&b, "sim_state_name", osmo_fsm_inst_state_name(modem->sim.fi)); 447 blobmsg_add_u8(&b, "sim_use_upin", modem->sim.use_upin); 448 blobmsg_add_u16(&b, "sim_pin_retries", modem->sim.pin_retries & 0xff); 449 blobmsg_add_u16(&b, "sim_puk_retries", modem->sim.puk_retries & 0xff); 450 451 ubus_send_reply(ubus_ctx, req, b.head); 452 return UBUS_STATUS_OK; 453 } 454 455 static struct ubus_method modem_instance_object_methods[] = { 456 UBUS_METHOD("configure", modem_configure, modem_configure_policy), 457 UBUS_METHOD_NOARG("remove", modem_remove), 458 UBUS_METHOD_NOARG("opmode", modem_get_opmode), 459 UBUS_METHOD_NOARG("networkstatus", modem_networkstatus), 460 UBUS_METHOD_NOARG("dump", modem_dump_state), 461 // { .name = "serving_system", .handler = modem_get_serving_system}, 462 }; 463 464 static struct ubus_object_type modem_instance_object_type = 465 UBUS_OBJECT_TYPE("uqmid_modem_instance", modem_instance_object_methods); 466 467 /* TODO: rename modem, modem instance */ 468 static struct ubus_method modem_object_methods[] = {}; 469 470 static struct ubus_object_type modem_object_type = UBUS_OBJECT_TYPE("uqmid_modem", modem_object_methods); 471 472 /* empty uqmid.modem object, only used as paraent */ 473 static struct ubus_object modem_object = { 474 .name = "uqmid.modem", 475 .type = &modem_object_type, 476 .methods = modem_object_methods, 477 .n_methods = 0, 478 }; 479 480 /** called by modem.c to create the ubus object for a modem */ 481 int uqmid_ubus_modem_add(struct modem *modem) 482 { 483 struct ubus_object *obj = &modem->ubus; 484 int ret = 0; 485 486 obj->name = talloc_asprintf(modem, "%s.modem.%s", main_object.name, modem->name); 487 obj->type = &modem_instance_object_type; 488 obj->methods = modem_instance_object_methods; 489 obj->n_methods = ARRAY_SIZE(modem_instance_object_methods); 490 if ((ret = ubus_add_object(ubus_ctx, &modem->ubus))) { 491 LOG_ERROR("failed to publish ubus object for modem '%s' (ret = %d).\n", modem->name, ret); 492 talloc_free((void *)obj->name); 493 obj->name = NULL; 494 return ret; 495 } 496 497 return ret; 498 } 499 500 int uqmid_ubus_init(const char *path) 501 { 502 int ret; 503 504 ubus_path = path; 505 ubus_ctx = ubus_connect(path); 506 if (!ubus_ctx) 507 return -EIO; 508 509 LOG_DEBUG("connected as %08x\n", ubus_ctx->local_id); 510 ubus_ctx->connection_lost = uqmid_ubus_connection_lost; 511 uqmid_ubus_add_fd(); 512 513 ret = uqmid_add_object(&main_object); 514 if ((ret = uqmid_add_object(&modem_object))) { 515 fprintf(stderr, "Failed to add modem object %d", ret); 516 return ret; 517 } 518 519 return 0; 520 } 521
This page was automatically generated by LXR 0.3.1. • OpenWrt