123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Copyright (C) 2021 Felix Fietkau <nbd@nbd.name>
- */
- #include <libubus.h>
- #include "qosify.h"
- static struct blob_buf b;
- static int
- qosify_ubus_add_array(struct blob_attr *attr, uint8_t val, enum qosify_map_id id)
- {
- struct blob_attr *cur;
- int rem;
- if (blobmsg_check_array(attr, BLOBMSG_TYPE_STRING) < 0)
- return UBUS_STATUS_INVALID_ARGUMENT;
- blobmsg_for_each_attr(cur, attr, rem)
- qosify_map_set_entry(id, false, blobmsg_get_string(cur), val);
- return 0;
- }
- static int
- qosify_ubus_set_files(struct blob_attr *attr)
- {
- struct blob_attr *cur;
- int rem;
- if (blobmsg_check_array(attr, BLOBMSG_TYPE_STRING) < 0)
- return UBUS_STATUS_INVALID_ARGUMENT;
- qosify_map_clear_files();
- blobmsg_for_each_attr(cur, attr, rem)
- qosify_map_load_file(blobmsg_get_string(cur));
- qosify_map_gc();
- return 0;
- }
- enum {
- CL_ADD_DSCP,
- CL_ADD_TIMEOUT,
- CL_ADD_IPV4,
- CL_ADD_IPV6,
- CL_ADD_TCP_PORT,
- CL_ADD_UDP_PORT,
- CL_ADD_DNS,
- __CL_ADD_MAX
- };
- static const struct blobmsg_policy qosify_add_policy[__CL_ADD_MAX] = {
- [CL_ADD_DSCP] = { "dscp", BLOBMSG_TYPE_STRING },
- [CL_ADD_TIMEOUT] = { "timeout", BLOBMSG_TYPE_INT32 },
- [CL_ADD_IPV4] = { "ipv4", BLOBMSG_TYPE_ARRAY },
- [CL_ADD_IPV6] = { "ipv6", BLOBMSG_TYPE_ARRAY },
- [CL_ADD_TCP_PORT] = { "tcp_port", BLOBMSG_TYPE_ARRAY },
- [CL_ADD_UDP_PORT] = { "udp_port", BLOBMSG_TYPE_ARRAY },
- [CL_ADD_DNS] = { "dns", BLOBMSG_TYPE_ARRAY },
- };
- static int
- qosify_ubus_reload(struct ubus_context *ctx, struct ubus_object *obj,
- struct ubus_request_data *req, const char *method,
- struct blob_attr *msg)
- {
- qosify_map_reload();
- return 0;
- }
- static int
- qosify_ubus_add(struct ubus_context *ctx, struct ubus_object *obj,
- struct ubus_request_data *req, const char *method,
- struct blob_attr *msg)
- {
- int prev_timemout = qosify_map_timeout;
- struct blob_attr *tb[__CL_ADD_MAX];
- struct blob_attr *cur;
- uint8_t dscp = 0xff;
- int ret;
- blobmsg_parse(qosify_add_policy, __CL_ADD_MAX, tb,
- blobmsg_data(msg), blobmsg_len(msg));
- if (!strcmp(method, "add")) {
- if ((cur = tb[CL_ADD_DSCP]) == NULL ||
- qosify_map_dscp_value(blobmsg_get_string(cur), &dscp))
- return UBUS_STATUS_INVALID_ARGUMENT;
- if ((cur = tb[CL_ADD_TIMEOUT]) != NULL)
- qosify_map_timeout = blobmsg_get_u32(cur);
- }
- if ((cur = tb[CL_ADD_IPV4]) != NULL &&
- (ret = qosify_ubus_add_array(cur, dscp, CL_MAP_IPV4_ADDR) != 0))
- return ret;
- if ((cur = tb[CL_ADD_IPV6]) != NULL &&
- (ret = qosify_ubus_add_array(cur, dscp, CL_MAP_IPV6_ADDR) != 0))
- return ret;
- if ((cur = tb[CL_ADD_TCP_PORT]) != NULL &&
- (ret = qosify_ubus_add_array(cur, dscp, CL_MAP_TCP_PORTS) != 0))
- return ret;
- if ((cur = tb[CL_ADD_UDP_PORT]) != NULL &&
- (ret = qosify_ubus_add_array(cur, dscp, CL_MAP_UDP_PORTS) != 0))
- return ret;
- if ((cur = tb[CL_ADD_DNS]) != NULL &&
- (ret = qosify_ubus_add_array(cur, dscp, CL_MAP_DNS) != 0))
- return ret;
- qosify_map_timeout = prev_timemout;
- return 0;
- }
- enum {
- CL_CONFIG_RESET,
- CL_CONFIG_FILES,
- CL_CONFIG_TIMEOUT,
- CL_CONFIG_DSCP_UDP,
- CL_CONFIG_DSCP_TCP,
- CL_CONFIG_DSCP_ICMP,
- CL_CONFIG_INTERFACES,
- CL_CONFIG_DEVICES,
- CL_CONFIG_CLASSES,
- __CL_CONFIG_MAX
- };
- static const struct blobmsg_policy qosify_config_policy[__CL_CONFIG_MAX] = {
- [CL_CONFIG_RESET] = { "reset", BLOBMSG_TYPE_BOOL },
- [CL_CONFIG_FILES] = { "files", BLOBMSG_TYPE_ARRAY },
- [CL_CONFIG_TIMEOUT] = { "timeout", BLOBMSG_TYPE_INT32 },
- [CL_CONFIG_DSCP_UDP] = { "dscp_default_udp", BLOBMSG_TYPE_STRING },
- [CL_CONFIG_DSCP_TCP] = { "dscp_default_tcp", BLOBMSG_TYPE_STRING },
- [CL_CONFIG_DSCP_ICMP] = { "dscp_icmp", BLOBMSG_TYPE_STRING },
- [CL_CONFIG_INTERFACES] = { "interfaces", BLOBMSG_TYPE_TABLE },
- [CL_CONFIG_DEVICES] = { "devices", BLOBMSG_TYPE_TABLE },
- [CL_CONFIG_CLASSES] = { "classes", BLOBMSG_TYPE_TABLE },
- };
- static int
- qosify_ubus_config(struct ubus_context *ctx, struct ubus_object *obj,
- struct ubus_request_data *req, const char *method,
- struct blob_attr *msg)
- {
- struct blob_attr *tb[__CL_CONFIG_MAX];
- struct blob_attr *cur;
- uint8_t dscp;
- bool reset = false;
- int ret;
- blobmsg_parse(qosify_config_policy, __CL_CONFIG_MAX, tb,
- blobmsg_data(msg), blobmsg_len(msg));
- if ((cur = tb[CL_CONFIG_RESET]) != NULL)
- reset = blobmsg_get_bool(cur);
- if (reset)
- qosify_map_reset_config();
- if ((cur = tb[CL_CONFIG_CLASSES]) != NULL || reset)
- qosify_map_set_classes(cur);
- if ((cur = tb[CL_CONFIG_TIMEOUT]) != NULL)
- qosify_map_timeout = blobmsg_get_u32(cur);
- if ((cur = tb[CL_CONFIG_FILES]) != NULL &&
- (ret = qosify_ubus_set_files(cur) != 0))
- return ret;
- if (map_parse_flow_config(&flow_config, msg, reset) ||
- map_fill_dscp_value(&config.dscp_icmp, tb[CL_CONFIG_DSCP_ICMP], reset))
- return UBUS_STATUS_INVALID_ARGUMENT;
- map_fill_dscp_value(&dscp, tb[CL_CONFIG_DSCP_UDP], true);
- if (dscp != 0xff)
- qosify_map_set_dscp_default(CL_MAP_UDP_PORTS, dscp);
- map_fill_dscp_value(&dscp, tb[CL_CONFIG_DSCP_TCP], true);
- if (dscp != 0xff)
- qosify_map_set_dscp_default(CL_MAP_TCP_PORTS, dscp);
- qosify_map_update_config();
- qosify_iface_config_update(tb[CL_CONFIG_INTERFACES], tb[CL_CONFIG_DEVICES]);
- qosify_iface_check();
- return 0;
- }
- static int
- qosify_ubus_dump(struct ubus_context *ctx, struct ubus_object *obj,
- struct ubus_request_data *req, const char *method,
- struct blob_attr *msg)
- {
- blob_buf_init(&b, 0);
- qosify_map_dump(&b);
- ubus_send_reply(ctx, req, b.head);
- blob_buf_free(&b);
- return 0;
- }
- static int
- qosify_ubus_status(struct ubus_context *ctx, struct ubus_object *obj,
- struct ubus_request_data *req, const char *method,
- struct blob_attr *msg)
- {
- blob_buf_init(&b, 0);
- qosify_iface_status(&b);
- ubus_send_reply(ctx, req, b.head);
- blob_buf_free(&b);
- return 0;
- }
- static int
- qosify_ubus_get_stats(struct ubus_context *ctx, struct ubus_object *obj,
- struct ubus_request_data *req, const char *method,
- struct blob_attr *msg)
- {
- static const struct blobmsg_policy policy =
- { "reset", BLOBMSG_TYPE_BOOL };
- struct blob_attr *tb;
- bool reset = false;
- blobmsg_parse(&policy, 1, &tb, blobmsg_data(msg), blobmsg_len(msg));
- reset = tb && blobmsg_get_u8(tb);
- blob_buf_init(&b, 0);
- qosify_map_stats(&b, reset);
- ubus_send_reply(ctx, req, b.head);
- blob_buf_free(&b);
- return 0;
- }
- static int
- qosify_ubus_check_devices(struct ubus_context *ctx, struct ubus_object *obj,
- struct ubus_request_data *req, const char *method,
- struct blob_attr *msg)
- {
- qosify_iface_check();
- return 0;
- }
- enum {
- CL_DNS_HOST_NAME,
- CL_DNS_HOST_TYPE,
- CL_DNS_HOST_ADDR,
- CL_DNS_HOST_TTL,
- __CL_DNS_HOST_MAX
- };
- static const struct blobmsg_policy qosify_dns_policy[__CL_DNS_HOST_MAX] = {
- [CL_DNS_HOST_NAME] = { "name", BLOBMSG_TYPE_STRING },
- [CL_DNS_HOST_TYPE] = { "type", BLOBMSG_TYPE_STRING },
- [CL_DNS_HOST_ADDR] = { "address", BLOBMSG_TYPE_STRING },
- [CL_DNS_HOST_TTL] = { "ttl", BLOBMSG_TYPE_INT32 },
- };
- static int
- __qosify_ubus_add_dns_host(struct blob_attr *msg)
- {
- struct blob_attr *tb[__CL_DNS_HOST_MAX];
- struct blob_attr *cur;
- uint32_t ttl = 0;
- blobmsg_parse(qosify_dns_policy, __CL_DNS_HOST_MAX, tb,
- blobmsg_data(msg), blobmsg_len(msg));
- if (!tb[CL_DNS_HOST_NAME] || !tb[CL_DNS_HOST_TYPE] ||
- !tb[CL_DNS_HOST_ADDR])
- return UBUS_STATUS_INVALID_ARGUMENT;
- if ((cur = tb[CL_DNS_HOST_TTL]) != NULL)
- ttl = blobmsg_get_u32(cur);
- if (qosify_map_add_dns_host(blobmsg_get_string(tb[CL_DNS_HOST_NAME]),
- blobmsg_get_string(tb[CL_DNS_HOST_ADDR]),
- blobmsg_get_string(tb[CL_DNS_HOST_TYPE]),
- ttl))
- return UBUS_STATUS_INVALID_ARGUMENT;
- return 0;
- }
- static int
- qosify_ubus_add_dns_host(struct ubus_context *ctx, struct ubus_object *obj,
- struct ubus_request_data *req, const char *method,
- struct blob_attr *msg)
- {
- return __qosify_ubus_add_dns_host(msg);
- }
- static const struct ubus_method qosify_methods[] = {
- UBUS_METHOD_NOARG("reload", qosify_ubus_reload),
- UBUS_METHOD("add", qosify_ubus_add, qosify_add_policy),
- UBUS_METHOD_MASK("remove", qosify_ubus_add, qosify_add_policy,
- ((1 << __CL_ADD_MAX) - 1) & ~(1 << CL_ADD_DSCP)),
- UBUS_METHOD("config", qosify_ubus_config, qosify_config_policy),
- UBUS_METHOD_NOARG("dump", qosify_ubus_dump),
- UBUS_METHOD_NOARG("status", qosify_ubus_status),
- UBUS_METHOD_NOARG("get_stats", qosify_ubus_get_stats),
- UBUS_METHOD("add_dns_host", qosify_ubus_add_dns_host, qosify_dns_policy),
- UBUS_METHOD_NOARG("check_devices", qosify_ubus_check_devices),
- };
- static struct ubus_object_type qosify_object_type =
- UBUS_OBJECT_TYPE("qosify", qosify_methods);
- static struct ubus_object qosify_object = {
- .name = "qosify",
- .type = &qosify_object_type,
- .methods = qosify_methods,
- .n_methods = ARRAY_SIZE(qosify_methods),
- };
- static void
- qosify_subscribe_dnsmasq(struct ubus_context *ctx)
- {
- static struct ubus_subscriber sub = {
- .cb = qosify_ubus_add_dns_host,
- };
- uint32_t id;
- if (!sub.obj.id &&
- ubus_register_subscriber(ctx, &sub))
- return;
- if (ubus_lookup_id(ctx, "dnsmasq.dns", &id))
- return;
- ubus_subscribe(ctx, &sub, id);
- }
- static void
- qosify_ubus_event_cb(struct ubus_context *ctx, struct ubus_event_handler *ev,
- const char *type, struct blob_attr *msg)
- {
- static const struct blobmsg_policy policy =
- { "path", BLOBMSG_TYPE_STRING };
- struct blob_attr *attr;
- const char *path;
- blobmsg_parse(&policy, 1, &attr, blobmsg_data(msg), blobmsg_len(msg));
- if (!attr)
- return;
- path = blobmsg_get_string(attr);
- if (!strcmp(path, "dnsmasq.dns"))
- qosify_subscribe_dnsmasq(ctx);
- else if (!strcmp(path, "bridger"))
- qosify_ubus_update_bridger(false);
- }
- static void
- ubus_connect_handler(struct ubus_context *ctx)
- {
- static struct ubus_event_handler ev = {
- .cb = qosify_ubus_event_cb
- };
- ubus_add_object(ctx, &qosify_object);
- ubus_register_event_handler(ctx, &ev, "ubus.object.add");
- qosify_subscribe_dnsmasq(ctx);
- }
- static struct ubus_auto_conn conn;
- void qosify_ubus_update_bridger(bool shutdown)
- {
- struct ubus_request req;
- uint32_t id;
- void *c;
- if (ubus_lookup_id(&conn.ctx, "bridger", &id))
- return;
- blob_buf_init(&b, 0);
- blobmsg_add_string(&b, "name", "qosify");
- c = blobmsg_open_array(&b, "devices");
- if (!shutdown)
- qosify_iface_get_devices(&b);
- blobmsg_close_array(&b, c);
- ubus_invoke_async(&conn.ctx, id, "set_blacklist", b.head, &req);
- }
- int qosify_ubus_init(void)
- {
- conn.cb = ubus_connect_handler;
- ubus_auto_connect(&conn);
- return 0;
- }
- void qosify_ubus_stop(void)
- {
- qosify_ubus_update_bridger(true);
- ubus_auto_shutdown(&conn);
- }
- struct iface_req {
- char *name;
- int len;
- };
- static void
- netifd_if_cb(struct ubus_request *req, int type, struct blob_attr *msg)
- {
- struct iface_req *ifr = req->priv;
- enum {
- IFS_ATTR_UP,
- IFS_ATTR_DEV,
- __IFS_ATTR_MAX
- };
- static const struct blobmsg_policy policy[__IFS_ATTR_MAX] = {
- [IFS_ATTR_UP] = { "up", BLOBMSG_TYPE_BOOL },
- [IFS_ATTR_DEV] = { "l3_device", BLOBMSG_TYPE_STRING },
- };
- struct blob_attr *tb[__IFS_ATTR_MAX];
- blobmsg_parse(policy, __IFS_ATTR_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
- if (!tb[IFS_ATTR_UP] || !tb[IFS_ATTR_DEV])
- return;
- if (!blobmsg_get_bool(tb[IFS_ATTR_UP]))
- return;
- snprintf(ifr->name, ifr->len, "%s", blobmsg_get_string(tb[IFS_ATTR_DEV]));
- }
- int qosify_ubus_check_interface(const char *name, char *ifname, int ifname_len)
- {
- struct iface_req req = { ifname, ifname_len };
- char *obj_name = "network.interface.";
- uint32_t id;
- #define PREFIX "network.interface."
- obj_name = alloca(sizeof(PREFIX) + strlen(name) + 1);
- sprintf(obj_name, PREFIX "%s", name);
- #undef PREFIX
- ifname[0] = 0;
- if (ubus_lookup_id(&conn.ctx, obj_name, &id))
- return -1;
- blob_buf_init(&b, 0);
- ubus_invoke(&conn.ctx, id, "status", b.head, netifd_if_cb, &req, 1000);
- if (!ifname[0])
- return -1;
- return 0;
- }
|