• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/uqmi/uqmid/ubus.c

  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