2
0

ubus.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Copyright (C) 2021 Felix Fietkau <nbd@nbd.name>
  4. */
  5. #include <libubus.h>
  6. #include "qosify.h"
  7. static struct blob_buf b;
  8. static int
  9. qosify_ubus_add_array(struct blob_attr *attr, uint8_t val, enum qosify_map_id id)
  10. {
  11. struct blob_attr *cur;
  12. int rem;
  13. if (blobmsg_check_array(attr, BLOBMSG_TYPE_STRING) < 0)
  14. return UBUS_STATUS_INVALID_ARGUMENT;
  15. blobmsg_for_each_attr(cur, attr, rem)
  16. qosify_map_set_entry(id, false, blobmsg_get_string(cur), val);
  17. return 0;
  18. }
  19. static int
  20. qosify_ubus_set_files(struct blob_attr *attr)
  21. {
  22. struct blob_attr *cur;
  23. int rem;
  24. if (blobmsg_check_array(attr, BLOBMSG_TYPE_STRING) < 0)
  25. return UBUS_STATUS_INVALID_ARGUMENT;
  26. qosify_map_clear_files();
  27. blobmsg_for_each_attr(cur, attr, rem)
  28. qosify_map_load_file(blobmsg_get_string(cur));
  29. qosify_map_gc();
  30. return 0;
  31. }
  32. enum {
  33. CL_ADD_DSCP,
  34. CL_ADD_TIMEOUT,
  35. CL_ADD_IPV4,
  36. CL_ADD_IPV6,
  37. CL_ADD_TCP_PORT,
  38. CL_ADD_UDP_PORT,
  39. CL_ADD_DNS,
  40. __CL_ADD_MAX
  41. };
  42. static const struct blobmsg_policy qosify_add_policy[__CL_ADD_MAX] = {
  43. [CL_ADD_DSCP] = { "dscp", BLOBMSG_TYPE_STRING },
  44. [CL_ADD_TIMEOUT] = { "timeout", BLOBMSG_TYPE_INT32 },
  45. [CL_ADD_IPV4] = { "ipv4", BLOBMSG_TYPE_ARRAY },
  46. [CL_ADD_IPV6] = { "ipv6", BLOBMSG_TYPE_ARRAY },
  47. [CL_ADD_TCP_PORT] = { "tcp_port", BLOBMSG_TYPE_ARRAY },
  48. [CL_ADD_UDP_PORT] = { "udp_port", BLOBMSG_TYPE_ARRAY },
  49. [CL_ADD_DNS] = { "dns", BLOBMSG_TYPE_ARRAY },
  50. };
  51. static int
  52. qosify_ubus_reload(struct ubus_context *ctx, struct ubus_object *obj,
  53. struct ubus_request_data *req, const char *method,
  54. struct blob_attr *msg)
  55. {
  56. qosify_map_reload();
  57. return 0;
  58. }
  59. static int
  60. qosify_ubus_add(struct ubus_context *ctx, struct ubus_object *obj,
  61. struct ubus_request_data *req, const char *method,
  62. struct blob_attr *msg)
  63. {
  64. int prev_timemout = qosify_map_timeout;
  65. struct blob_attr *tb[__CL_ADD_MAX];
  66. struct blob_attr *cur;
  67. uint8_t dscp = 0xff;
  68. int ret;
  69. blobmsg_parse(qosify_add_policy, __CL_ADD_MAX, tb,
  70. blobmsg_data(msg), blobmsg_len(msg));
  71. if (!strcmp(method, "add")) {
  72. if ((cur = tb[CL_ADD_DSCP]) == NULL ||
  73. qosify_map_dscp_value(blobmsg_get_string(cur), &dscp))
  74. return UBUS_STATUS_INVALID_ARGUMENT;
  75. if ((cur = tb[CL_ADD_TIMEOUT]) != NULL)
  76. qosify_map_timeout = blobmsg_get_u32(cur);
  77. }
  78. if ((cur = tb[CL_ADD_IPV4]) != NULL &&
  79. (ret = qosify_ubus_add_array(cur, dscp, CL_MAP_IPV4_ADDR) != 0))
  80. return ret;
  81. if ((cur = tb[CL_ADD_IPV6]) != NULL &&
  82. (ret = qosify_ubus_add_array(cur, dscp, CL_MAP_IPV6_ADDR) != 0))
  83. return ret;
  84. if ((cur = tb[CL_ADD_TCP_PORT]) != NULL &&
  85. (ret = qosify_ubus_add_array(cur, dscp, CL_MAP_TCP_PORTS) != 0))
  86. return ret;
  87. if ((cur = tb[CL_ADD_UDP_PORT]) != NULL &&
  88. (ret = qosify_ubus_add_array(cur, dscp, CL_MAP_UDP_PORTS) != 0))
  89. return ret;
  90. if ((cur = tb[CL_ADD_DNS]) != NULL &&
  91. (ret = qosify_ubus_add_array(cur, dscp, CL_MAP_DNS) != 0))
  92. return ret;
  93. qosify_map_timeout = prev_timemout;
  94. return 0;
  95. }
  96. enum {
  97. CL_CONFIG_RESET,
  98. CL_CONFIG_FILES,
  99. CL_CONFIG_TIMEOUT,
  100. CL_CONFIG_DSCP_UDP,
  101. CL_CONFIG_DSCP_TCP,
  102. CL_CONFIG_DSCP_ICMP,
  103. CL_CONFIG_INTERFACES,
  104. CL_CONFIG_DEVICES,
  105. CL_CONFIG_CLASSES,
  106. __CL_CONFIG_MAX
  107. };
  108. static const struct blobmsg_policy qosify_config_policy[__CL_CONFIG_MAX] = {
  109. [CL_CONFIG_RESET] = { "reset", BLOBMSG_TYPE_BOOL },
  110. [CL_CONFIG_FILES] = { "files", BLOBMSG_TYPE_ARRAY },
  111. [CL_CONFIG_TIMEOUT] = { "timeout", BLOBMSG_TYPE_INT32 },
  112. [CL_CONFIG_DSCP_UDP] = { "dscp_default_udp", BLOBMSG_TYPE_STRING },
  113. [CL_CONFIG_DSCP_TCP] = { "dscp_default_tcp", BLOBMSG_TYPE_STRING },
  114. [CL_CONFIG_DSCP_ICMP] = { "dscp_icmp", BLOBMSG_TYPE_STRING },
  115. [CL_CONFIG_INTERFACES] = { "interfaces", BLOBMSG_TYPE_TABLE },
  116. [CL_CONFIG_DEVICES] = { "devices", BLOBMSG_TYPE_TABLE },
  117. [CL_CONFIG_CLASSES] = { "classes", BLOBMSG_TYPE_TABLE },
  118. };
  119. static int
  120. qosify_ubus_config(struct ubus_context *ctx, struct ubus_object *obj,
  121. struct ubus_request_data *req, const char *method,
  122. struct blob_attr *msg)
  123. {
  124. struct blob_attr *tb[__CL_CONFIG_MAX];
  125. struct blob_attr *cur;
  126. uint8_t dscp;
  127. bool reset = false;
  128. int ret;
  129. blobmsg_parse(qosify_config_policy, __CL_CONFIG_MAX, tb,
  130. blobmsg_data(msg), blobmsg_len(msg));
  131. if ((cur = tb[CL_CONFIG_RESET]) != NULL)
  132. reset = blobmsg_get_bool(cur);
  133. if (reset)
  134. qosify_map_reset_config();
  135. if ((cur = tb[CL_CONFIG_CLASSES]) != NULL || reset)
  136. qosify_map_set_classes(cur);
  137. if ((cur = tb[CL_CONFIG_TIMEOUT]) != NULL)
  138. qosify_map_timeout = blobmsg_get_u32(cur);
  139. if ((cur = tb[CL_CONFIG_FILES]) != NULL &&
  140. (ret = qosify_ubus_set_files(cur) != 0))
  141. return ret;
  142. if (map_parse_flow_config(&flow_config, msg, reset) ||
  143. map_fill_dscp_value(&config.dscp_icmp, tb[CL_CONFIG_DSCP_ICMP], reset))
  144. return UBUS_STATUS_INVALID_ARGUMENT;
  145. map_fill_dscp_value(&dscp, tb[CL_CONFIG_DSCP_UDP], true);
  146. if (dscp != 0xff)
  147. qosify_map_set_dscp_default(CL_MAP_UDP_PORTS, dscp);
  148. map_fill_dscp_value(&dscp, tb[CL_CONFIG_DSCP_TCP], true);
  149. if (dscp != 0xff)
  150. qosify_map_set_dscp_default(CL_MAP_TCP_PORTS, dscp);
  151. qosify_map_update_config();
  152. qosify_iface_config_update(tb[CL_CONFIG_INTERFACES], tb[CL_CONFIG_DEVICES]);
  153. qosify_iface_check();
  154. return 0;
  155. }
  156. static int
  157. qosify_ubus_dump(struct ubus_context *ctx, struct ubus_object *obj,
  158. struct ubus_request_data *req, const char *method,
  159. struct blob_attr *msg)
  160. {
  161. blob_buf_init(&b, 0);
  162. qosify_map_dump(&b);
  163. ubus_send_reply(ctx, req, b.head);
  164. blob_buf_free(&b);
  165. return 0;
  166. }
  167. static int
  168. qosify_ubus_status(struct ubus_context *ctx, struct ubus_object *obj,
  169. struct ubus_request_data *req, const char *method,
  170. struct blob_attr *msg)
  171. {
  172. blob_buf_init(&b, 0);
  173. qosify_iface_status(&b);
  174. ubus_send_reply(ctx, req, b.head);
  175. blob_buf_free(&b);
  176. return 0;
  177. }
  178. static int
  179. qosify_ubus_check_devices(struct ubus_context *ctx, struct ubus_object *obj,
  180. struct ubus_request_data *req, const char *method,
  181. struct blob_attr *msg)
  182. {
  183. qosify_iface_check();
  184. return 0;
  185. }
  186. enum {
  187. CL_DNS_HOST_NAME,
  188. CL_DNS_HOST_TYPE,
  189. CL_DNS_HOST_ADDR,
  190. CL_DNS_HOST_TTL,
  191. __CL_DNS_HOST_MAX
  192. };
  193. static const struct blobmsg_policy qosify_dns_policy[__CL_DNS_HOST_MAX] = {
  194. [CL_DNS_HOST_NAME] = { "name", BLOBMSG_TYPE_STRING },
  195. [CL_DNS_HOST_TYPE] = { "type", BLOBMSG_TYPE_STRING },
  196. [CL_DNS_HOST_ADDR] = { "address", BLOBMSG_TYPE_STRING },
  197. [CL_DNS_HOST_TTL] = { "ttl", BLOBMSG_TYPE_INT32 },
  198. };
  199. static int
  200. __qosify_ubus_add_dns_host(struct blob_attr *msg)
  201. {
  202. struct blob_attr *tb[__CL_DNS_HOST_MAX];
  203. struct blob_attr *cur;
  204. uint32_t ttl = 0;
  205. blobmsg_parse(qosify_dns_policy, __CL_DNS_HOST_MAX, tb,
  206. blobmsg_data(msg), blobmsg_len(msg));
  207. if (!tb[CL_DNS_HOST_NAME] || !tb[CL_DNS_HOST_TYPE] ||
  208. !tb[CL_DNS_HOST_ADDR])
  209. return UBUS_STATUS_INVALID_ARGUMENT;
  210. if ((cur = tb[CL_DNS_HOST_TTL]) != NULL)
  211. ttl = blobmsg_get_u32(cur);
  212. if (qosify_map_add_dns_host(blobmsg_get_string(tb[CL_DNS_HOST_NAME]),
  213. blobmsg_get_string(tb[CL_DNS_HOST_ADDR]),
  214. blobmsg_get_string(tb[CL_DNS_HOST_TYPE]),
  215. ttl))
  216. return UBUS_STATUS_INVALID_ARGUMENT;
  217. return 0;
  218. }
  219. static int
  220. qosify_ubus_add_dns_host(struct ubus_context *ctx, struct ubus_object *obj,
  221. struct ubus_request_data *req, const char *method,
  222. struct blob_attr *msg)
  223. {
  224. return __qosify_ubus_add_dns_host(msg);
  225. }
  226. static const struct ubus_method qosify_methods[] = {
  227. UBUS_METHOD_NOARG("reload", qosify_ubus_reload),
  228. UBUS_METHOD("add", qosify_ubus_add, qosify_add_policy),
  229. UBUS_METHOD_MASK("remove", qosify_ubus_add, qosify_add_policy,
  230. ((1 << __CL_ADD_MAX) - 1) & ~(1 << CL_ADD_DSCP)),
  231. UBUS_METHOD("config", qosify_ubus_config, qosify_config_policy),
  232. UBUS_METHOD_NOARG("dump", qosify_ubus_dump),
  233. UBUS_METHOD_NOARG("status", qosify_ubus_status),
  234. UBUS_METHOD("add_dns_host", qosify_ubus_add_dns_host, qosify_dns_policy),
  235. UBUS_METHOD_NOARG("check_devices", qosify_ubus_check_devices),
  236. };
  237. static struct ubus_object_type qosify_object_type =
  238. UBUS_OBJECT_TYPE("qosify", qosify_methods);
  239. static struct ubus_object qosify_object = {
  240. .name = "qosify",
  241. .type = &qosify_object_type,
  242. .methods = qosify_methods,
  243. .n_methods = ARRAY_SIZE(qosify_methods),
  244. };
  245. static void
  246. qosify_subscribe_dnsmasq(struct ubus_context *ctx)
  247. {
  248. static struct ubus_subscriber sub = {
  249. .cb = qosify_ubus_add_dns_host,
  250. };
  251. uint32_t id;
  252. if (!sub.obj.id &&
  253. ubus_register_subscriber(ctx, &sub))
  254. return;
  255. if (ubus_lookup_id(ctx, "dnsmasq.dns", &id))
  256. return;
  257. ubus_subscribe(ctx, &sub, id);
  258. }
  259. static void
  260. qosify_ubus_event_cb(struct ubus_context *ctx, struct ubus_event_handler *ev,
  261. const char *type, struct blob_attr *msg)
  262. {
  263. static const struct blobmsg_policy policy =
  264. { "path", BLOBMSG_TYPE_STRING };
  265. struct blob_attr *attr;
  266. const char *path;
  267. blobmsg_parse(&policy, 1, &attr, blobmsg_data(msg), blobmsg_len(msg));
  268. if (!attr)
  269. return;
  270. path = blobmsg_get_string(attr);
  271. if (!strcmp(path, "dnsmasq.dns"))
  272. qosify_subscribe_dnsmasq(ctx);
  273. else if (!strcmp(path, "bridger"))
  274. qosify_ubus_update_bridger(false);
  275. }
  276. static void
  277. ubus_connect_handler(struct ubus_context *ctx)
  278. {
  279. static struct ubus_event_handler ev = {
  280. .cb = qosify_ubus_event_cb
  281. };
  282. ubus_add_object(ctx, &qosify_object);
  283. ubus_register_event_handler(ctx, &ev, "ubus.object.add");
  284. qosify_subscribe_dnsmasq(ctx);
  285. }
  286. static struct ubus_auto_conn conn;
  287. void qosify_ubus_update_bridger(bool shutdown)
  288. {
  289. struct ubus_request req;
  290. uint32_t id;
  291. void *c;
  292. if (ubus_lookup_id(&conn.ctx, "bridger", &id))
  293. return;
  294. blob_buf_init(&b, 0);
  295. blobmsg_add_string(&b, "name", "qosify");
  296. c = blobmsg_open_array(&b, "devices");
  297. if (!shutdown)
  298. qosify_iface_get_devices(&b);
  299. blobmsg_close_array(&b, c);
  300. ubus_invoke_async(&conn.ctx, id, "set_blacklist", b.head, &req);
  301. }
  302. int qosify_ubus_init(void)
  303. {
  304. conn.cb = ubus_connect_handler;
  305. ubus_auto_connect(&conn);
  306. return 0;
  307. }
  308. void qosify_ubus_stop(void)
  309. {
  310. qosify_ubus_update_bridger(true);
  311. ubus_auto_shutdown(&conn);
  312. }
  313. struct iface_req {
  314. char *name;
  315. int len;
  316. };
  317. static void
  318. netifd_if_cb(struct ubus_request *req, int type, struct blob_attr *msg)
  319. {
  320. struct iface_req *ifr = req->priv;
  321. enum {
  322. IFS_ATTR_UP,
  323. IFS_ATTR_DEV,
  324. __IFS_ATTR_MAX
  325. };
  326. static const struct blobmsg_policy policy[__IFS_ATTR_MAX] = {
  327. [IFS_ATTR_UP] = { "up", BLOBMSG_TYPE_BOOL },
  328. [IFS_ATTR_DEV] = { "l3_device", BLOBMSG_TYPE_STRING },
  329. };
  330. struct blob_attr *tb[__IFS_ATTR_MAX];
  331. blobmsg_parse(policy, __IFS_ATTR_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
  332. if (!tb[IFS_ATTR_UP] || !tb[IFS_ATTR_DEV])
  333. return;
  334. if (!blobmsg_get_bool(tb[IFS_ATTR_UP]))
  335. return;
  336. snprintf(ifr->name, ifr->len, "%s", blobmsg_get_string(tb[IFS_ATTR_DEV]));
  337. }
  338. int qosify_ubus_check_interface(const char *name, char *ifname, int ifname_len)
  339. {
  340. struct iface_req req = { ifname, ifname_len };
  341. char *obj_name = "network.interface.";
  342. uint32_t id;
  343. #define PREFIX "network.interface."
  344. obj_name = alloca(sizeof(PREFIX) + strlen(name) + 1);
  345. sprintf(obj_name, PREFIX "%s", name);
  346. #undef PREFIX
  347. ifname[0] = 0;
  348. if (ubus_lookup_id(&conn.ctx, obj_name, &id))
  349. return -1;
  350. blob_buf_init(&b, 0);
  351. ubus_invoke(&conn.ctx, id, "status", b.head, netifd_if_cb, &req, 1000);
  352. if (!ifname[0])
  353. return -1;
  354. return 0;
  355. }