--- embedaddon/dnsmasq/src/ubus.c 2021/03/17 00:56:46 1.1.1.1 +++ embedaddon/dnsmasq/src/ubus.c 2023/09/27 11:02:07 1.1.1.2 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,17 +21,44 @@ #include static struct blob_buf b; -static int notify; static int error_logged = 0; static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, struct blob_attr *msg); +#ifdef HAVE_CONNTRACK +enum { + SET_CONNMARK_ALLOWLIST_MARK, + SET_CONNMARK_ALLOWLIST_MASK, + SET_CONNMARK_ALLOWLIST_PATTERNS +}; +static const struct blobmsg_policy set_connmark_allowlist_policy[] = { + [SET_CONNMARK_ALLOWLIST_MARK] = { + .name = "mark", + .type = BLOBMSG_TYPE_INT32 + }, + [SET_CONNMARK_ALLOWLIST_MASK] = { + .name = "mask", + .type = BLOBMSG_TYPE_INT32 + }, + [SET_CONNMARK_ALLOWLIST_PATTERNS] = { + .name = "patterns", + .type = BLOBMSG_TYPE_ARRAY + } +}; +static int ubus_handle_set_connmark_allowlist(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg); +#endif + static void ubus_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj); static const struct ubus_method ubus_object_methods[] = { UBUS_METHOD_NOARG("metrics", ubus_handle_metrics), +#ifdef HAVE_CONNTRACK + UBUS_METHOD("set_connmark_allowlist", ubus_handle_set_connmark_allowlist, set_connmark_allowlist_policy), +#endif }; static struct ubus_object_type ubus_object_type = @@ -50,17 +77,16 @@ static void ubus_subscribe_cb(struct ubus_context *ctx (void)ctx; my_syslog(LOG_DEBUG, _("UBus subscription callback: %s subscriber(s)"), obj->has_subscribers ? "1" : "0"); - notify = obj->has_subscribers; } static void ubus_destroy(struct ubus_context *ubus) { - // Forces re-initialization when we're reusing the same definitions later on. - ubus_object.id = 0; - ubus_object_type.id = 0; - ubus_free(ubus); daemon->ubus = NULL; + + /* Forces re-initialization when we're reusing the same definitions later on. */ + ubus_object.id = 0; + ubus_object_type.id = 0; } static void ubus_disconnect_cb(struct ubus_context *ubus) @@ -76,42 +102,27 @@ static void ubus_disconnect_cb(struct ubus_context *ub } } -void ubus_init() +char *ubus_init() { struct ubus_context *ubus = NULL; int ret = 0; - ubus = ubus_connect(NULL); - if (!ubus) - { - if (!error_logged) - { - my_syslog(LOG_ERR, _("Cannot initialize UBus: connection failed")); - error_logged = 1; - } - - ubus_destroy(ubus); - return; - } - + if (!(ubus = ubus_connect(NULL))) + return NULL; + ubus_object.name = daemon->ubus_name; ret = ubus_add_object(ubus, &ubus_object); if (ret) { - if (!error_logged) - { - my_syslog(LOG_ERR, _("Cannot add object to UBus: %s"), ubus_strerror(ret)); - error_logged = 1; - } ubus_destroy(ubus); - return; - } - + return (char *)ubus_strerror(ret); + } + ubus->connection_lost = ubus_disconnect_cb; daemon->ubus = ubus; error_logged = 0; - my_syslog(LOG_INFO, _("Connected to system UBus")); + return NULL; } void set_ubus_listeners() @@ -160,6 +171,16 @@ void check_ubus_listeners() } } +#define CHECK(stmt) \ + do { \ + int e = (stmt); \ + if (e) \ + { \ + my_syslog(LOG_ERR, _("UBus command failed: %d (%s)"), e, #stmt); \ + return (UBUS_STATUS_UNKNOWN_ERROR); \ + } \ + } while (0) + static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, struct blob_attr *msg) @@ -170,36 +191,196 @@ static int ubus_handle_metrics(struct ubus_context *ct (void)method; (void)msg; - blob_buf_init(&b, BLOBMSG_TYPE_TABLE); + CHECK(blob_buf_init(&b, BLOBMSG_TYPE_TABLE)); for (i=0; i < __METRIC_MAX; i++) - blobmsg_add_u32(&b, get_metric_name(i), daemon->metrics[i]); + CHECK(blobmsg_add_u32(&b, get_metric_name(i), daemon->metrics[i])); - return ubus_send_reply(ctx, req, b.head); + CHECK(ubus_send_reply(ctx, req, b.head)); + return UBUS_STATUS_OK; } +#ifdef HAVE_CONNTRACK +static int ubus_handle_set_connmark_allowlist(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + const struct blobmsg_policy *policy = set_connmark_allowlist_policy; + size_t policy_len = countof(set_connmark_allowlist_policy); + struct allowlist *allowlists = NULL, **allowlists_pos; + char **patterns = NULL, **patterns_pos; + u32 mark, mask = UINT32_MAX; + size_t num_patterns = 0; + struct blob_attr *tb[policy_len]; + struct blob_attr *attr; + + if (blobmsg_parse(policy, policy_len, tb, blob_data(msg), blob_len(msg))) + return UBUS_STATUS_INVALID_ARGUMENT; + + if (!tb[SET_CONNMARK_ALLOWLIST_MARK]) + return UBUS_STATUS_INVALID_ARGUMENT; + mark = blobmsg_get_u32(tb[SET_CONNMARK_ALLOWLIST_MARK]); + if (!mark) + return UBUS_STATUS_INVALID_ARGUMENT; + + if (tb[SET_CONNMARK_ALLOWLIST_MASK]) + { + mask = blobmsg_get_u32(tb[SET_CONNMARK_ALLOWLIST_MASK]); + if (!mask || (mark & ~mask)) + return UBUS_STATUS_INVALID_ARGUMENT; + } + + if (tb[SET_CONNMARK_ALLOWLIST_PATTERNS]) + { + struct blob_attr *head = blobmsg_data(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]); + size_t len = blobmsg_data_len(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]); + __blob_for_each_attr(attr, head, len) + { + char *pattern; + if (blob_id(attr) != BLOBMSG_TYPE_STRING) + return UBUS_STATUS_INVALID_ARGUMENT; + if (!(pattern = blobmsg_get_string(attr))) + return UBUS_STATUS_INVALID_ARGUMENT; + if (strcmp(pattern, "*") && !is_valid_dns_name_pattern(pattern)) + return UBUS_STATUS_INVALID_ARGUMENT; + num_patterns++; + } + } + + for (allowlists_pos = &daemon->allowlists; *allowlists_pos; allowlists_pos = &(*allowlists_pos)->next) + if ((*allowlists_pos)->mark == mark && (*allowlists_pos)->mask == mask) + { + struct allowlist *allowlists_next = (*allowlists_pos)->next; + for (patterns_pos = (*allowlists_pos)->patterns; *patterns_pos; patterns_pos++) + { + free(*patterns_pos); + *patterns_pos = NULL; + } + free((*allowlists_pos)->patterns); + (*allowlists_pos)->patterns = NULL; + free(*allowlists_pos); + *allowlists_pos = allowlists_next; + break; + } + + if (!num_patterns) + return UBUS_STATUS_OK; + + patterns = whine_malloc((num_patterns + 1) * sizeof(char *)); + if (!patterns) + goto fail; + patterns_pos = patterns; + if (tb[SET_CONNMARK_ALLOWLIST_PATTERNS]) + { + struct blob_attr *head = blobmsg_data(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]); + size_t len = blobmsg_data_len(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]); + __blob_for_each_attr(attr, head, len) + { + char *pattern; + if (!(pattern = blobmsg_get_string(attr))) + goto fail; + if (!(*patterns_pos = whine_malloc(strlen(pattern) + 1))) + goto fail; + strcpy(*patterns_pos++, pattern); + } + } + + allowlists = whine_malloc(sizeof(struct allowlist)); + if (!allowlists) + goto fail; + memset(allowlists, 0, sizeof(struct allowlist)); + allowlists->mark = mark; + allowlists->mask = mask; + allowlists->patterns = patterns; + allowlists->next = daemon->allowlists; + daemon->allowlists = allowlists; + return UBUS_STATUS_OK; + +fail: + if (patterns) + { + for (patterns_pos = patterns; *patterns_pos; patterns_pos++) + { + free(*patterns_pos); + *patterns_pos = NULL; + } + free(patterns); + patterns = NULL; + } + if (allowlists) + { + free(allowlists); + allowlists = NULL; + } + return UBUS_STATUS_UNKNOWN_ERROR; +} +#endif + +#undef CHECK + +#define CHECK(stmt) \ + do { \ + int e = (stmt); \ + if (e) \ + { \ + my_syslog(LOG_ERR, _("UBus command failed: %d (%s)"), e, #stmt); \ + return; \ + } \ + } while (0) + void ubus_event_bcast(const char *type, const char *mac, const char *ip, const char *name, const char *interface) { struct ubus_context *ubus = (struct ubus_context *)daemon->ubus; - int ret; - if (!ubus || !notify) + if (!ubus || !ubus_object.has_subscribers) return; - blob_buf_init(&b, BLOBMSG_TYPE_TABLE); + CHECK(blob_buf_init(&b, BLOBMSG_TYPE_TABLE)); if (mac) - blobmsg_add_string(&b, "mac", mac); + CHECK(blobmsg_add_string(&b, "mac", mac)); if (ip) - blobmsg_add_string(&b, "ip", ip); + CHECK(blobmsg_add_string(&b, "ip", ip)); if (name) - blobmsg_add_string(&b, "name", name); + CHECK(blobmsg_add_string(&b, "name", name)); if (interface) - blobmsg_add_string(&b, "interface", interface); + CHECK(blobmsg_add_string(&b, "interface", interface)); - ret = ubus_notify(ubus, &ubus_object, type, b.head, -1); - if (!ret) - my_syslog(LOG_ERR, _("Failed to send UBus event: %s"), ubus_strerror(ret)); + CHECK(ubus_notify(ubus, &ubus_object, type, b.head, -1)); } +#ifdef HAVE_CONNTRACK +void ubus_event_bcast_connmark_allowlist_refused(u32 mark, const char *name) +{ + struct ubus_context *ubus = (struct ubus_context *)daemon->ubus; + + if (!ubus || !ubus_object.has_subscribers) + return; + + CHECK(blob_buf_init(&b, 0)); + CHECK(blobmsg_add_u32(&b, "mark", mark)); + CHECK(blobmsg_add_string(&b, "name", name)); + + CHECK(ubus_notify(ubus, &ubus_object, "connmark-allowlist.refused", b.head, -1)); +} + +void ubus_event_bcast_connmark_allowlist_resolved(u32 mark, const char *name, const char *value, u32 ttl) +{ + struct ubus_context *ubus = (struct ubus_context *)daemon->ubus; + + if (!ubus || !ubus_object.has_subscribers) + return; + + CHECK(blob_buf_init(&b, 0)); + CHECK(blobmsg_add_u32(&b, "mark", mark)); + CHECK(blobmsg_add_string(&b, "name", name)); + CHECK(blobmsg_add_string(&b, "value", value)); + CHECK(blobmsg_add_u32(&b, "ttl", ttl)); + + /* Set timeout to allow UBus subscriber to configure firewall rules before returning. */ + CHECK(ubus_notify(ubus, &ubus_object, "connmark-allowlist.resolved", b.head, /* timeout: */ 1000)); +} +#endif + +#undef CHECK #endif /* HAVE_UBUS */