ubus.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  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_get_stats(struct ubus_context *ctx, struct ubus_object *obj,
  180. struct ubus_request_data *req, const char *method,
  181. struct blob_attr *msg)
  182. {
  183. static const struct blobmsg_policy policy =
  184. { "reset", BLOBMSG_TYPE_BOOL };
  185. struct blob_attr *tb;
  186. bool reset = false;
  187. blobmsg_parse(&policy, 1, &tb, blobmsg_data(msg), blobmsg_len(msg));
  188. reset = tb && blobmsg_get_u8(tb);
  189. blob_buf_init(&b, 0);
  190. qosify_map_stats(&b, reset);
  191. ubus_send_reply(ctx, req, b.head);
  192. blob_buf_free(&b);
  193. return 0;
  194. }
  195. static int
  196. qosify_ubus_check_devices(struct ubus_context *ctx, struct ubus_object *obj,
  197. struct ubus_request_data *req, const char *method,
  198. struct blob_attr *msg)
  199. {
  200. qosify_iface_check();
  201. return 0;
  202. }
  203. enum {
  204. CL_DNS_HOST_NAME,
  205. CL_DNS_HOST_TYPE,
  206. CL_DNS_HOST_ADDR,
  207. CL_DNS_HOST_TTL,
  208. __CL_DNS_HOST_MAX
  209. };
  210. static const struct blobmsg_policy qosify_dns_policy[__CL_DNS_HOST_MAX] = {
  211. [CL_DNS_HOST_NAME] = { "name", BLOBMSG_TYPE_STRING },
  212. [CL_DNS_HOST_TYPE] = { "type", BLOBMSG_TYPE_STRING },
  213. [CL_DNS_HOST_ADDR] = { "address", BLOBMSG_TYPE_STRING },
  214. [CL_DNS_HOST_TTL] = { "ttl", BLOBMSG_TYPE_INT32 },
  215. };
  216. static int
  217. __qosify_ubus_add_dns_host(struct blob_attr *msg)
  218. {
  219. struct blob_attr *tb[__CL_DNS_HOST_MAX];
  220. struct blob_attr *cur;
  221. uint32_t ttl = 0;
  222. blobmsg_parse(qosify_dns_policy, __CL_DNS_HOST_MAX, tb,
  223. blobmsg_data(msg), blobmsg_len(msg));
  224. if (!tb[CL_DNS_HOST_NAME] || !tb[CL_DNS_HOST_TYPE] ||
  225. !tb[CL_DNS_HOST_ADDR])
  226. return UBUS_STATUS_INVALID_ARGUMENT;
  227. if ((cur = tb[CL_DNS_HOST_TTL]) != NULL)
  228. ttl = blobmsg_get_u32(cur);
  229. if (qosify_map_add_dns_host(blobmsg_get_string(tb[CL_DNS_HOST_NAME]),
  230. blobmsg_get_string(tb[CL_DNS_HOST_ADDR]),
  231. blobmsg_get_string(tb[CL_DNS_HOST_TYPE]),
  232. ttl))
  233. return UBUS_STATUS_INVALID_ARGUMENT;
  234. return 0;
  235. }
  236. static int
  237. qosify_ubus_add_dns_host(struct ubus_context *ctx, struct ubus_object *obj,
  238. struct ubus_request_data *req, const char *method,
  239. struct blob_attr *msg)
  240. {
  241. return __qosify_ubus_add_dns_host(msg);
  242. }
  243. static const struct ubus_method qosify_methods[] = {
  244. UBUS_METHOD_NOARG("reload", qosify_ubus_reload),
  245. UBUS_METHOD("add", qosify_ubus_add, qosify_add_policy),
  246. UBUS_METHOD_MASK("remove", qosify_ubus_add, qosify_add_policy,
  247. ((1 << __CL_ADD_MAX) - 1) & ~(1 << CL_ADD_DSCP)),
  248. UBUS_METHOD("config", qosify_ubus_config, qosify_config_policy),
  249. UBUS_METHOD_NOARG("dump", qosify_ubus_dump),
  250. UBUS_METHOD_NOARG("status", qosify_ubus_status),
  251. UBUS_METHOD_NOARG("get_stats", qosify_ubus_get_stats),
  252. UBUS_METHOD("add_dns_host", qosify_ubus_add_dns_host, qosify_dns_policy),
  253. UBUS_METHOD_NOARG("check_devices", qosify_ubus_check_devices),
  254. };
  255. static struct ubus_object_type qosify_object_type =
  256. UBUS_OBJECT_TYPE("qosify", qosify_methods);
  257. static struct ubus_object qosify_object = {
  258. .name = "qosify",
  259. .type = &qosify_object_type,
  260. .methods = qosify_methods,
  261. .n_methods = ARRAY_SIZE(qosify_methods),
  262. };
  263. static void
  264. qosify_subscribe_dnsmasq(struct ubus_context *ctx)
  265. {
  266. static struct ubus_subscriber sub = {
  267. .cb = qosify_ubus_add_dns_host,
  268. };
  269. uint32_t id;
  270. if (!sub.obj.id &&
  271. ubus_register_subscriber(ctx, &sub))
  272. return;
  273. if (ubus_lookup_id(ctx, "dnsmasq.dns", &id))
  274. return;
  275. ubus_subscribe(ctx, &sub, id);
  276. }
  277. static void
  278. qosify_ubus_event_cb(struct ubus_context *ctx, struct ubus_event_handler *ev,
  279. const char *type, struct blob_attr *msg)
  280. {
  281. static const struct blobmsg_policy policy =
  282. { "path", BLOBMSG_TYPE_STRING };
  283. struct blob_attr *attr;
  284. const char *path;
  285. blobmsg_parse(&policy, 1, &attr, blobmsg_data(msg), blobmsg_len(msg));
  286. if (!attr)
  287. return;
  288. path = blobmsg_get_string(attr);
  289. if (!strcmp(path, "dnsmasq.dns"))
  290. qosify_subscribe_dnsmasq(ctx);
  291. else if (!strcmp(path, "bridger"))
  292. qosify_ubus_update_bridger(false);
  293. }
  294. static void
  295. ubus_connect_handler(struct ubus_context *ctx)
  296. {
  297. static struct ubus_event_handler ev = {
  298. .cb = qosify_ubus_event_cb
  299. };
  300. ubus_add_object(ctx, &qosify_object);
  301. ubus_register_event_handler(ctx, &ev, "ubus.object.add");
  302. qosify_subscribe_dnsmasq(ctx);
  303. }
  304. static struct ubus_auto_conn conn;
  305. void qosify_ubus_update_bridger(bool shutdown)
  306. {
  307. struct ubus_request req;
  308. uint32_t id;
  309. void *c;
  310. if (ubus_lookup_id(&conn.ctx, "bridger", &id))
  311. return;
  312. blob_buf_init(&b, 0);
  313. blobmsg_add_string(&b, "name", "qosify");
  314. c = blobmsg_open_array(&b, "devices");
  315. if (!shutdown)
  316. qosify_iface_get_devices(&b);
  317. blobmsg_close_array(&b, c);
  318. ubus_invoke_async(&conn.ctx, id, "set_blacklist", b.head, &req);
  319. }
  320. int qosify_ubus_init(void)
  321. {
  322. conn.cb = ubus_connect_handler;
  323. ubus_auto_connect(&conn);
  324. return 0;
  325. }
  326. void qosify_ubus_stop(void)
  327. {
  328. qosify_ubus_update_bridger(true);
  329. ubus_auto_shutdown(&conn);
  330. }
  331. struct iface_req {
  332. char *name;
  333. int len;
  334. };
  335. static void
  336. netifd_if_cb(struct ubus_request *req, int type, struct blob_attr *msg)
  337. {
  338. struct iface_req *ifr = req->priv;
  339. enum {
  340. IFS_ATTR_UP,
  341. IFS_ATTR_DEV,
  342. __IFS_ATTR_MAX
  343. };
  344. static const struct blobmsg_policy policy[__IFS_ATTR_MAX] = {
  345. [IFS_ATTR_UP] = { "up", BLOBMSG_TYPE_BOOL },
  346. [IFS_ATTR_DEV] = { "l3_device", BLOBMSG_TYPE_STRING },
  347. };
  348. struct blob_attr *tb[__IFS_ATTR_MAX];
  349. blobmsg_parse(policy, __IFS_ATTR_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
  350. if (!tb[IFS_ATTR_UP] || !tb[IFS_ATTR_DEV])
  351. return;
  352. if (!blobmsg_get_bool(tb[IFS_ATTR_UP]))
  353. return;
  354. snprintf(ifr->name, ifr->len, "%s", blobmsg_get_string(tb[IFS_ATTR_DEV]));
  355. }
  356. int qosify_ubus_check_interface(const char *name, char *ifname, int ifname_len)
  357. {
  358. struct iface_req req = { ifname, ifname_len };
  359. char *obj_name = "network.interface.";
  360. uint32_t id;
  361. #define PREFIX "network.interface."
  362. obj_name = alloca(sizeof(PREFIX) + strlen(name) + 1);
  363. sprintf(obj_name, PREFIX "%s", name);
  364. #undef PREFIX
  365. ifname[0] = 0;
  366. if (ubus_lookup_id(&conn.ctx, obj_name, &id))
  367. return -1;
  368. blob_buf_init(&b, 0);
  369. ubus_invoke(&conn.ctx, id, "status", b.head, netifd_if_cb, &req, 1000);
  370. if (!ifname[0])
  371. return -1;
  372. return 0;
  373. }