ubus.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  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. ubus_connect_handler(struct ubus_context *ctx)
  247. {
  248. ubus_add_object(ctx, &qosify_object);
  249. }
  250. static struct ubus_auto_conn conn;
  251. int qosify_ubus_init(void)
  252. {
  253. conn.cb = ubus_connect_handler;
  254. ubus_auto_connect(&conn);
  255. return 0;
  256. }
  257. void qosify_ubus_stop(void)
  258. {
  259. ubus_auto_shutdown(&conn);
  260. }
  261. struct iface_req {
  262. char *name;
  263. int len;
  264. };
  265. static void
  266. netifd_if_cb(struct ubus_request *req, int type, struct blob_attr *msg)
  267. {
  268. struct iface_req *ifr = req->priv;
  269. enum {
  270. IFS_ATTR_UP,
  271. IFS_ATTR_DEV,
  272. __IFS_ATTR_MAX
  273. };
  274. static const struct blobmsg_policy policy[__IFS_ATTR_MAX] = {
  275. [IFS_ATTR_UP] = { "up", BLOBMSG_TYPE_BOOL },
  276. [IFS_ATTR_DEV] = { "l3_device", BLOBMSG_TYPE_STRING },
  277. };
  278. struct blob_attr *tb[__IFS_ATTR_MAX];
  279. blobmsg_parse(policy, __IFS_ATTR_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
  280. if (!tb[IFS_ATTR_UP] || !tb[IFS_ATTR_DEV])
  281. return;
  282. if (!blobmsg_get_bool(tb[IFS_ATTR_UP]))
  283. return;
  284. snprintf(ifr->name, ifr->len, "%s", blobmsg_get_string(tb[IFS_ATTR_DEV]));
  285. }
  286. int qosify_ubus_check_interface(const char *name, char *ifname, int ifname_len)
  287. {
  288. struct iface_req req = { ifname, ifname_len };
  289. char *obj_name = "network.interface.";
  290. uint32_t id;
  291. #define PREFIX "network.interface."
  292. obj_name = alloca(sizeof(PREFIX) + strlen(name) + 1);
  293. sprintf(obj_name, PREFIX "%s", name);
  294. #undef PREFIX
  295. ifname[0] = 0;
  296. if (ubus_lookup_id(&conn.ctx, obj_name, &id))
  297. return -1;
  298. blob_buf_init(&b, 0);
  299. ubus_invoke(&conn.ctx, id, "status", b.head, netifd_if_cb, &req, 1000);
  300. if (!ifname[0])
  301. return -1;
  302. return 0;
  303. }