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