map.c 21 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Copyright (C) 2021 Felix Fietkau <nbd@nbd.name>
  4. */
  5. #include <arpa/inet.h>
  6. #include <errno.h>
  7. #include <stdio.h>
  8. #include <ctype.h>
  9. #include <stdlib.h>
  10. #include <time.h>
  11. #include <fnmatch.h>
  12. #include <glob.h>
  13. #include <libubox/uloop.h>
  14. #include <libubox/avl-cmp.h>
  15. #include "qosify.h"
  16. struct qosify_map_class;
  17. static int qosify_map_entry_cmp(const void *k1, const void *k2, void *ptr);
  18. static int qosify_map_fds[__CL_MAP_MAX];
  19. static AVL_TREE(map_data, qosify_map_entry_cmp, false, NULL);
  20. static LIST_HEAD(map_files);
  21. static struct qosify_map_class *map_class[QOSIFY_MAX_CLASS_ENTRIES];
  22. static uint32_t next_timeout;
  23. static uint8_t qosify_dscp_default[2] = { 0xff, 0xff };
  24. int qosify_map_timeout;
  25. int qosify_active_timeout;
  26. struct qosify_config config;
  27. struct qosify_flow_config flow_config;
  28. static uint32_t map_dns_seq;
  29. struct qosify_map_file {
  30. struct list_head list;
  31. char filename[];
  32. };
  33. struct qosify_map_class {
  34. const char *name;
  35. struct qosify_class data;
  36. };
  37. static const struct {
  38. const char *name;
  39. const char *type_name;
  40. } qosify_map_info[] = {
  41. [CL_MAP_TCP_PORTS] = { "tcp_ports", "tcp_port" },
  42. [CL_MAP_UDP_PORTS] = { "udp_ports", "udp_port" },
  43. [CL_MAP_IPV4_ADDR] = { "ipv4_map", "ipv4_addr" },
  44. [CL_MAP_IPV6_ADDR] = { "ipv6_map", "ipv6_addr" },
  45. [CL_MAP_CONFIG] = { "config", "config" },
  46. [CL_MAP_CLASS] = { "class_map", "class" },
  47. [CL_MAP_DNS] = { "dns", "dns" },
  48. };
  49. static const struct {
  50. const char name[5];
  51. uint8_t val;
  52. } codepoints[] = {
  53. { "CS0", 0 },
  54. { "CS1", 8 },
  55. { "CS2", 16 },
  56. { "CS3", 24 },
  57. { "CS4", 32 },
  58. { "CS5", 40 },
  59. { "CS6", 48 },
  60. { "CS7", 56 },
  61. { "AF11", 10 },
  62. { "AF12", 12 },
  63. { "AF13", 14 },
  64. { "AF21", 18 },
  65. { "AF22", 20 },
  66. { "AF23", 22 },
  67. { "AF31", 26 },
  68. { "AF32", 28 },
  69. { "AF33", 30 },
  70. { "AF41", 34 },
  71. { "AF42", 36 },
  72. { "AF43", 38 },
  73. { "EF", 46 },
  74. { "VA", 44 },
  75. { "LE", 1 },
  76. { "DF", 0 },
  77. };
  78. static void qosify_map_timer_cb(struct uloop_timeout *t)
  79. {
  80. qosify_map_gc();
  81. }
  82. static struct uloop_timeout qosify_map_timer = {
  83. .cb = qosify_map_timer_cb,
  84. };
  85. static uint32_t qosify_gettime(void)
  86. {
  87. struct timespec ts;
  88. clock_gettime(CLOCK_MONOTONIC, &ts);
  89. return ts.tv_sec;
  90. }
  91. static const char *
  92. qosify_map_path(enum qosify_map_id id)
  93. {
  94. static char path[128];
  95. const char *name;
  96. if (id >= ARRAY_SIZE(qosify_map_info))
  97. return NULL;
  98. name = qosify_map_info[id].name;
  99. if (!name)
  100. return NULL;
  101. snprintf(path, sizeof(path), "%s/%s", CLASSIFY_DATA_PATH, name);
  102. return path;
  103. }
  104. static int qosify_map_get_fd(enum qosify_map_id id)
  105. {
  106. const char *path = qosify_map_path(id);
  107. int fd;
  108. if (!path)
  109. return -1;
  110. fd = bpf_obj_get(path);
  111. if (fd < 0)
  112. fprintf(stderr, "Failed to open map %s: %s\n", path, strerror(errno));
  113. return fd;
  114. }
  115. static void qosify_map_clear_list(enum qosify_map_id id)
  116. {
  117. int fd = qosify_map_fds[id];
  118. __u32 key[4] = {};
  119. while (bpf_map_get_next_key(fd, &key, &key) != -1)
  120. bpf_map_delete_elem(fd, &key);
  121. }
  122. static void __qosify_map_set_dscp_default(enum qosify_map_id id, uint8_t val)
  123. {
  124. struct qosify_map_data data = {
  125. .id = id,
  126. };
  127. struct qosify_class class = {
  128. .val.ingress = val,
  129. .val.egress = val,
  130. };
  131. uint32_t key;
  132. int fd;
  133. int i;
  134. if (!(val & QOSIFY_DSCP_CLASS_FLAG)) {
  135. if (id == CL_MAP_TCP_PORTS)
  136. key = QOSIFY_MAX_CLASS_ENTRIES;
  137. else if (id == CL_MAP_UDP_PORTS)
  138. key = QOSIFY_MAX_CLASS_ENTRIES + 1;
  139. else
  140. return;
  141. fd = qosify_map_fds[CL_MAP_CLASS];
  142. memcpy(&class.config, &flow_config, sizeof(class.config));
  143. bpf_map_update_elem(fd, &key, &class, BPF_ANY);
  144. val = key | QOSIFY_DSCP_CLASS_FLAG;
  145. }
  146. fd = qosify_map_fds[id];
  147. for (i = 0; i < (1 << 16); i++) {
  148. data.addr.port = htons(i);
  149. if (avl_find(&map_data, &data))
  150. continue;
  151. bpf_map_update_elem(fd, &data.addr, &val, BPF_ANY);
  152. }
  153. }
  154. void qosify_map_set_dscp_default(enum qosify_map_id id, uint8_t val)
  155. {
  156. bool udp;
  157. if (id == CL_MAP_TCP_PORTS)
  158. udp = false;
  159. else if (id == CL_MAP_UDP_PORTS)
  160. udp = true;
  161. else
  162. return;
  163. if (!memcmp(&qosify_dscp_default[udp], &val, sizeof(val)))
  164. return;
  165. qosify_dscp_default[udp] = val;
  166. __qosify_map_set_dscp_default(id, val);
  167. }
  168. int qosify_map_init(void)
  169. {
  170. int i;
  171. for (i = 0; i < CL_MAP_DNS; i++) {
  172. qosify_map_fds[i] = qosify_map_get_fd(i);
  173. if (qosify_map_fds[i] < 0)
  174. return -1;
  175. }
  176. qosify_map_clear_list(CL_MAP_IPV4_ADDR);
  177. qosify_map_clear_list(CL_MAP_IPV6_ADDR);
  178. qosify_map_reset_config();
  179. return 0;
  180. }
  181. static char *str_skip(char *str, bool space)
  182. {
  183. while (*str && isspace(*str) == space)
  184. str++;
  185. return str;
  186. }
  187. static int
  188. qosify_map_codepoint(const char *val)
  189. {
  190. int i;
  191. for (i = 0; i < ARRAY_SIZE(codepoints); i++)
  192. if (!strcmp(codepoints[i].name, val))
  193. return codepoints[i].val;
  194. return 0xff;
  195. }
  196. static int qosify_map_entry_cmp(const void *k1, const void *k2, void *ptr)
  197. {
  198. const struct qosify_map_data *d1 = k1;
  199. const struct qosify_map_data *d2 = k2;
  200. if (d1->id != d2->id)
  201. return d2->id - d1->id;
  202. if (d1->id == CL_MAP_DNS)
  203. return strcmp(d1->addr.dns.pattern, d2->addr.dns.pattern);
  204. return memcmp(&d1->addr, &d2->addr, sizeof(d1->addr));
  205. }
  206. static struct qosify_map_entry *
  207. __qosify_map_alloc_entry(struct qosify_map_data *data)
  208. {
  209. struct qosify_map_entry *e;
  210. char *pattern;
  211. char *c;
  212. if (data->id < CL_MAP_DNS) {
  213. e = calloc(1, sizeof(*e));
  214. memcpy(&e->data.addr, &data->addr, sizeof(e->data.addr));
  215. return e;
  216. }
  217. e = calloc_a(sizeof(*e), &pattern, strlen(data->addr.dns.pattern) + 1);
  218. strcpy(pattern, data->addr.dns.pattern);
  219. e->data.addr.dns.pattern = pattern;
  220. for (c = pattern; *c; c++)
  221. *c = tolower(*c);
  222. if (pattern[0] == '/' &&
  223. regcomp(&e->data.addr.dns.regex, pattern + 1,
  224. REG_EXTENDED | REG_NOSUB)) {
  225. free(e);
  226. return NULL;
  227. }
  228. return e;
  229. }
  230. void __qosify_map_set_entry(struct qosify_map_data *data)
  231. {
  232. int fd = qosify_map_fds[data->id];
  233. struct qosify_map_entry *e;
  234. bool file = data->file;
  235. uint8_t prev_dscp = 0xff;
  236. int32_t delta = 0;
  237. bool add = data->dscp != 0xff;
  238. e = avl_find_element(&map_data, data, e, avl);
  239. if (!e) {
  240. if (!add)
  241. return;
  242. e = __qosify_map_alloc_entry(data);
  243. if (!e)
  244. return;
  245. e->avl.key = &e->data;
  246. e->data.id = data->id;
  247. avl_insert(&map_data, &e->avl);
  248. } else {
  249. prev_dscp = e->data.dscp;
  250. }
  251. if (file)
  252. e->data.file = add;
  253. else
  254. e->data.user = add;
  255. if (add) {
  256. if (file)
  257. e->data.file_dscp = data->dscp;
  258. if (!e->data.user || !file)
  259. e->data.dscp = data->dscp;
  260. } else if (e->data.file && !file) {
  261. e->data.dscp = e->data.file_dscp;
  262. }
  263. if (e->data.dscp != prev_dscp && data->id < CL_MAP_DNS) {
  264. struct qosify_ip_map_val val = {
  265. .dscp = e->data.dscp,
  266. .seen = 1,
  267. };
  268. bpf_map_update_elem(fd, &data->addr, &val, BPF_ANY);
  269. }
  270. if (data->id == CL_MAP_DNS)
  271. e->data.addr.dns.seq = ++map_dns_seq;
  272. if (add) {
  273. if (qosify_map_timeout == ~0 || file) {
  274. e->timeout = ~0;
  275. return;
  276. }
  277. e->timeout = qosify_gettime() + qosify_map_timeout;
  278. delta = e->timeout - next_timeout;
  279. if (next_timeout && delta >= 0)
  280. return;
  281. }
  282. uloop_timeout_set(&qosify_map_timer, 1);
  283. }
  284. static int
  285. qosify_map_set_port(struct qosify_map_data *data, const char *str)
  286. {
  287. unsigned long start_port, end_port;
  288. char *err;
  289. int i;
  290. start_port = end_port = strtoul(str, &err, 0);
  291. if (err && *err) {
  292. if (*err == '-')
  293. end_port = strtoul(err + 1, &err, 0);
  294. if (*err)
  295. return -1;
  296. }
  297. if (!start_port || end_port < start_port ||
  298. end_port >= 65535)
  299. return -1;
  300. for (i = start_port; i <= end_port; i++) {
  301. data->addr.port = htons(i);
  302. __qosify_map_set_entry(data);
  303. }
  304. return 0;
  305. }
  306. static int
  307. qosify_map_fill_ip(struct qosify_map_data *data, const char *str)
  308. {
  309. int af;
  310. if (data->id == CL_MAP_IPV6_ADDR)
  311. af = AF_INET6;
  312. else
  313. af = AF_INET;
  314. if (inet_pton(af, str, &data->addr) != 1)
  315. return -1;
  316. return 0;
  317. }
  318. int qosify_map_set_entry(enum qosify_map_id id, bool file, const char *str,
  319. uint8_t dscp)
  320. {
  321. struct qosify_map_data data = {
  322. .id = id,
  323. .file = file,
  324. .dscp = dscp,
  325. };
  326. switch (id) {
  327. case CL_MAP_DNS:
  328. data.addr.dns.pattern = str;
  329. if (str[-2] == 'c')
  330. data.addr.dns.only_cname = 1;
  331. break;
  332. case CL_MAP_TCP_PORTS:
  333. case CL_MAP_UDP_PORTS:
  334. return qosify_map_set_port(&data, str);
  335. case CL_MAP_IPV4_ADDR:
  336. case CL_MAP_IPV6_ADDR:
  337. if (qosify_map_fill_ip(&data, str))
  338. return -1;
  339. break;
  340. default:
  341. return -1;
  342. }
  343. __qosify_map_set_entry(&data);
  344. return 0;
  345. }
  346. static int
  347. __qosify_map_dscp_value(const char *val, uint8_t *dscp_val)
  348. {
  349. unsigned long dscp;
  350. bool fallback = false;
  351. char *err;
  352. if (*val == '+') {
  353. fallback = true;
  354. val++;
  355. }
  356. dscp = strtoul(val, &err, 0);
  357. if (err && *err)
  358. dscp = qosify_map_codepoint(val);
  359. if (dscp >= 64)
  360. return -1;
  361. *dscp_val = dscp | (fallback << 6);
  362. return 0;
  363. }
  364. static int
  365. qosify_map_check_class(const char *val, uint8_t *dscp_val)
  366. {
  367. int i;
  368. for (i = 0; i < ARRAY_SIZE(map_class); i++) {
  369. if (map_class[i] && !strcmp(val, map_class[i]->name)) {
  370. *dscp_val = i | QOSIFY_DSCP_CLASS_FLAG;
  371. return 0;
  372. }
  373. }
  374. return -1;
  375. }
  376. int qosify_map_dscp_value(const char *val, uint8_t *dscp_val)
  377. {
  378. uint8_t fallback = 0;
  379. if (*val == '+') {
  380. fallback = QOSIFY_DSCP_FALLBACK_FLAG;
  381. val++;
  382. }
  383. if (qosify_map_check_class(val, dscp_val) &&
  384. __qosify_map_dscp_value(val, dscp_val))
  385. return -1;
  386. *dscp_val |= fallback;
  387. return 0;
  388. }
  389. static void
  390. qosify_map_dscp_codepoint_str(char *dest, int len, uint8_t dscp)
  391. {
  392. int i;
  393. if (dscp & QOSIFY_DSCP_FALLBACK_FLAG) {
  394. *(dest++) = '+';
  395. len--;
  396. dscp &= ~QOSIFY_DSCP_FALLBACK_FLAG;
  397. }
  398. for (i = 0; i < ARRAY_SIZE(codepoints); i++) {
  399. if (codepoints[i].val != dscp)
  400. continue;
  401. snprintf(dest, len, "%s", codepoints[i].name);
  402. return;
  403. }
  404. snprintf(dest, len, "0x%x", dscp);
  405. }
  406. static void
  407. qosify_map_parse_line(char *str)
  408. {
  409. const char *key, *value;
  410. uint8_t dscp;
  411. str = str_skip(str, true);
  412. key = str;
  413. str = str_skip(str, false);
  414. if (!*str)
  415. return;
  416. *(str++) = 0;
  417. str = str_skip(str, true);
  418. value = str;
  419. if (qosify_map_dscp_value(value, &dscp))
  420. return;
  421. if (!strncmp(key, "dns:", 4))
  422. qosify_map_set_entry(CL_MAP_DNS, true, key + 4, dscp);
  423. if (!strncmp(key, "dns_q:", 6) || !strncmp(key, "dns_c:", 6))
  424. qosify_map_set_entry(CL_MAP_DNS, true, key + 6, dscp);
  425. if (!strncmp(key, "tcp:", 4))
  426. qosify_map_set_entry(CL_MAP_TCP_PORTS, true, key + 4, dscp);
  427. else if (!strncmp(key, "udp:", 4))
  428. qosify_map_set_entry(CL_MAP_UDP_PORTS, true, key + 4, dscp);
  429. else if (strchr(key, ':'))
  430. qosify_map_set_entry(CL_MAP_IPV6_ADDR, true, key, dscp);
  431. else if (strchr(key, '.'))
  432. qosify_map_set_entry(CL_MAP_IPV4_ADDR, true, key, dscp);
  433. }
  434. static void
  435. __qosify_map_load_file_data(FILE *f)
  436. {
  437. char line[1024];
  438. char *cur;
  439. while (fgets(line, sizeof(line), f)) {
  440. cur = strchr(line, '#');
  441. if (cur)
  442. *cur = 0;
  443. cur = line + strlen(line);
  444. if (cur == line)
  445. continue;
  446. while (cur > line && isspace(cur[-1]))
  447. cur--;
  448. *cur = 0;
  449. qosify_map_parse_line(line);
  450. }
  451. }
  452. static int
  453. __qosify_map_load_file(const char *file)
  454. {
  455. glob_t gl;
  456. FILE *f;
  457. int i;
  458. if (!file)
  459. return 0;
  460. glob(file, 0, NULL, &gl);
  461. for (i = 0; i < gl.gl_pathc; i++) {
  462. f = fopen(file, "r");
  463. if (!f)
  464. continue;
  465. __qosify_map_load_file_data(f);
  466. fclose(f);
  467. }
  468. globfree(&gl);
  469. return 0;
  470. }
  471. int qosify_map_load_file(const char *file)
  472. {
  473. struct qosify_map_file *f;
  474. if (!file)
  475. return 0;
  476. f = calloc(1, sizeof(*f) + strlen(file) + 1);
  477. strcpy(f->filename, file);
  478. list_add_tail(&f->list, &map_files);
  479. return __qosify_map_load_file(file);
  480. }
  481. static void qosify_map_reset_file_entries(void)
  482. {
  483. struct qosify_map_entry *e;
  484. map_dns_seq = 0;
  485. avl_for_each_element(&map_data, e, avl)
  486. e->data.file = false;
  487. }
  488. void qosify_map_clear_files(void)
  489. {
  490. struct qosify_map_file *f, *tmp;
  491. qosify_map_reset_file_entries();
  492. list_for_each_entry_safe(f, tmp, &map_files, list) {
  493. list_del(&f->list);
  494. free(f);
  495. }
  496. }
  497. void qosify_map_reset_config(void)
  498. {
  499. qosify_map_clear_files();
  500. qosify_map_set_dscp_default(CL_MAP_TCP_PORTS, 0);
  501. qosify_map_set_dscp_default(CL_MAP_UDP_PORTS, 0);
  502. qosify_map_timeout = 3600;
  503. qosify_active_timeout = 300;
  504. memset(&config, 0, sizeof(config));
  505. flow_config.dscp_prio = 0xff;
  506. flow_config.dscp_bulk = 0xff;
  507. config.dscp_icmp = 0xff;
  508. }
  509. void qosify_map_reload(void)
  510. {
  511. struct qosify_map_file *f;
  512. qosify_map_reset_file_entries();
  513. list_for_each_entry(f, &map_files, list)
  514. __qosify_map_load_file(f->filename);
  515. qosify_map_gc();
  516. }
  517. static void qosify_map_free_entry(struct qosify_map_entry *e)
  518. {
  519. int fd = qosify_map_fds[e->data.id];
  520. avl_delete(&map_data, &e->avl);
  521. if (e->data.id < CL_MAP_DNS)
  522. bpf_map_delete_elem(fd, &e->data.addr);
  523. free(e);
  524. }
  525. static bool
  526. qosify_map_entry_refresh_timeout(struct qosify_map_entry *e)
  527. {
  528. struct qosify_ip_map_val val;
  529. int fd = qosify_map_fds[e->data.id];
  530. if (e->data.id != CL_MAP_IPV4_ADDR &&
  531. e->data.id != CL_MAP_IPV6_ADDR)
  532. return false;
  533. if (bpf_map_lookup_elem(fd, &e->data.addr, &val))
  534. return false;
  535. if (!val.seen)
  536. return false;
  537. e->timeout = qosify_gettime() + qosify_active_timeout;
  538. val.seen = 0;
  539. bpf_map_update_elem(fd, &e->data.addr, &val, BPF_ANY);
  540. return true;
  541. }
  542. void qosify_map_gc(void)
  543. {
  544. struct qosify_map_entry *e, *tmp;
  545. int32_t timeout = 0;
  546. uint32_t cur_time = qosify_gettime();
  547. next_timeout = 0;
  548. avl_for_each_element_safe(&map_data, e, avl, tmp) {
  549. int32_t cur_timeout;
  550. if (e->data.user && e->timeout != ~0) {
  551. cur_timeout = e->timeout - cur_time;
  552. if (cur_timeout <= 0 &&
  553. qosify_map_entry_refresh_timeout(e))
  554. cur_timeout = e->timeout - cur_time;
  555. if (cur_timeout <= 0) {
  556. e->data.user = false;
  557. e->data.dscp = e->data.file_dscp;
  558. } else if (!timeout || cur_timeout < timeout) {
  559. timeout = cur_timeout;
  560. next_timeout = e->timeout;
  561. }
  562. }
  563. if (e->data.file || e->data.user)
  564. continue;
  565. qosify_map_free_entry(e);
  566. }
  567. if (!timeout)
  568. return;
  569. uloop_timeout_set(&qosify_map_timer, timeout * 1000);
  570. }
  571. int qosify_map_lookup_dns_entry(char *host, bool cname, uint8_t *dscp, uint32_t *seq)
  572. {
  573. struct qosify_map_data data = {
  574. .id = CL_MAP_DNS,
  575. .addr.dns.pattern = "",
  576. };
  577. struct qosify_map_entry *e;
  578. bool ret = -1;
  579. char *c;
  580. e = avl_find_ge_element(&map_data, &data, e, avl);
  581. if (!e)
  582. return -1;
  583. for (c = host; *c; c++)
  584. *c = tolower(*c);
  585. avl_for_element_to_last(&map_data, e, e, avl) {
  586. regex_t *regex = &e->data.addr.dns.regex;
  587. if (e->data.id != CL_MAP_DNS)
  588. break;
  589. if (!cname && e->data.addr.dns.only_cname)
  590. continue;
  591. if (e->data.addr.dns.pattern[0] == '/') {
  592. if (regexec(regex, host, 0, NULL, 0) != 0)
  593. continue;
  594. } else {
  595. if (fnmatch(e->data.addr.dns.pattern, host, 0))
  596. continue;
  597. }
  598. if (*dscp == 0xff || e->data.addr.dns.seq < *seq) {
  599. *dscp = e->data.dscp;
  600. *seq = e->data.addr.dns.seq;
  601. }
  602. ret = 0;
  603. }
  604. return ret;
  605. }
  606. int qosify_map_add_dns_host(char *host, const char *addr, const char *type, int ttl)
  607. {
  608. struct qosify_map_data data = {
  609. .dscp = 0xff
  610. };
  611. int prev_timeout = qosify_map_timeout;
  612. uint32_t lookup_seq = 0;
  613. if (qosify_map_lookup_dns_entry(host, false, &data.dscp, &lookup_seq))
  614. return 0;
  615. data.user = true;
  616. if (!strcmp(type, "A"))
  617. data.id = CL_MAP_IPV4_ADDR;
  618. else if (!strcmp(type, "AAAA"))
  619. data.id = CL_MAP_IPV6_ADDR;
  620. else
  621. return 0;
  622. if (qosify_map_fill_ip(&data, addr))
  623. return -1;
  624. if (ttl)
  625. qosify_map_timeout = ttl;
  626. __qosify_map_set_entry(&data);
  627. qosify_map_timeout = prev_timeout;
  628. return 0;
  629. }
  630. static void
  631. blobmsg_add_dscp(struct blob_buf *b, const char *name, uint8_t dscp)
  632. {
  633. int buf_len = 8;
  634. char *buf;
  635. if (dscp & QOSIFY_DSCP_CLASS_FLAG) {
  636. const char *val;
  637. int idx;
  638. idx = dscp & QOSIFY_DSCP_VALUE_MASK;
  639. if (map_class[idx])
  640. val = map_class[idx]->name;
  641. else
  642. val = "<invalid>";
  643. blobmsg_printf(b, name, "%s%s",
  644. (dscp & QOSIFY_DSCP_FALLBACK_FLAG) ? "+" : "", val);
  645. return;
  646. }
  647. buf = blobmsg_alloc_string_buffer(b, name, buf_len);
  648. qosify_map_dscp_codepoint_str(buf, buf_len, dscp);
  649. blobmsg_add_string_buffer(b);
  650. }
  651. void qosify_map_dump(struct blob_buf *b)
  652. {
  653. struct qosify_map_entry *e;
  654. uint32_t cur_time = qosify_gettime();
  655. int buf_len = INET6_ADDRSTRLEN + 1;
  656. char *buf;
  657. void *a;
  658. int af;
  659. a = blobmsg_open_array(b, "entries");
  660. avl_for_each_element(&map_data, e, avl) {
  661. void *c;
  662. if (!e->data.file && !e->data.user)
  663. continue;
  664. c = blobmsg_open_table(b, NULL);
  665. if (e->data.user && e->timeout != ~0) {
  666. int32_t cur_timeout = e->timeout - cur_time;
  667. if (cur_timeout < 0)
  668. cur_timeout = 0;
  669. blobmsg_add_u32(b, "timeout", cur_timeout);
  670. }
  671. blobmsg_add_u8(b, "file", e->data.file);
  672. blobmsg_add_u8(b, "user", e->data.user);
  673. blobmsg_add_dscp(b, "dscp", e->data.dscp);
  674. blobmsg_add_string(b, "type", qosify_map_info[e->data.id].type_name);
  675. switch (e->data.id) {
  676. case CL_MAP_TCP_PORTS:
  677. case CL_MAP_UDP_PORTS:
  678. blobmsg_printf(b, "addr", "%d", ntohs(e->data.addr.port));
  679. break;
  680. case CL_MAP_IPV4_ADDR:
  681. case CL_MAP_IPV6_ADDR:
  682. buf = blobmsg_alloc_string_buffer(b, "addr", buf_len);
  683. af = e->data.id == CL_MAP_IPV6_ADDR ? AF_INET6 : AF_INET;
  684. inet_ntop(af, &e->data.addr, buf, buf_len);
  685. blobmsg_add_string_buffer(b);
  686. break;
  687. case CL_MAP_DNS:
  688. blobmsg_add_string(b, "addr", e->data.addr.dns.pattern);
  689. break;
  690. default:
  691. break;
  692. }
  693. blobmsg_close_table(b, c);
  694. }
  695. blobmsg_close_array(b, a);
  696. }
  697. static int32_t
  698. qosify_map_get_class_id(const char *name)
  699. {
  700. int i;
  701. for (i = 0; i < ARRAY_SIZE(map_class); i++)
  702. if (map_class[i] && !strcmp(map_class[i]->name, name))
  703. return i;
  704. for (i = 0; i < ARRAY_SIZE(map_class); i++)
  705. if (!map_class[i])
  706. return i;
  707. for (i = 0; i < ARRAY_SIZE(map_class); i++) {
  708. if (!(map_class[i]->data.flags & QOSIFY_CLASS_FLAG_PRESENT)) {
  709. free(map_class[i]);
  710. map_class[i] = NULL;
  711. return i;
  712. }
  713. }
  714. return -1;
  715. }
  716. int map_fill_dscp_value(uint8_t *dest, struct blob_attr *attr, bool reset)
  717. {
  718. if (reset)
  719. *dest = 0xff;
  720. if (!attr)
  721. return 0;
  722. if (qosify_map_dscp_value(blobmsg_get_string(attr), dest))
  723. return -1;
  724. return 0;
  725. }
  726. int map_parse_flow_config(struct qosify_flow_config *cfg, struct blob_attr *attr,
  727. bool reset)
  728. {
  729. enum {
  730. CL_CONFIG_DSCP_PRIO,
  731. CL_CONFIG_DSCP_BULK,
  732. CL_CONFIG_BULK_TIMEOUT,
  733. CL_CONFIG_BULK_PPS,
  734. CL_CONFIG_PRIO_PKT_LEN,
  735. __CL_CONFIG_MAX
  736. };
  737. static const struct blobmsg_policy policy[__CL_CONFIG_MAX] = {
  738. [CL_CONFIG_DSCP_PRIO] = { "dscp_prio", BLOBMSG_TYPE_STRING },
  739. [CL_CONFIG_DSCP_BULK] = { "dscp_bulk", BLOBMSG_TYPE_STRING },
  740. [CL_CONFIG_BULK_TIMEOUT] = { "bulk_trigger_timeout", BLOBMSG_TYPE_INT32 },
  741. [CL_CONFIG_BULK_PPS] = { "bulk_trigger_pps", BLOBMSG_TYPE_INT32 },
  742. [CL_CONFIG_PRIO_PKT_LEN] = { "prio_max_avg_pkt_len", BLOBMSG_TYPE_INT32 },
  743. };
  744. struct blob_attr *tb[__CL_CONFIG_MAX];
  745. struct blob_attr *cur;
  746. if (reset)
  747. memset(cfg, 0, sizeof(*cfg));
  748. blobmsg_parse(policy, __CL_CONFIG_MAX, tb, blobmsg_data(attr), blobmsg_len(attr));
  749. if (map_fill_dscp_value(&cfg->dscp_prio, tb[CL_CONFIG_DSCP_PRIO], reset) ||
  750. map_fill_dscp_value(&cfg->dscp_bulk, tb[CL_CONFIG_DSCP_BULK], reset))
  751. return -1;
  752. if ((cur = tb[CL_CONFIG_BULK_TIMEOUT]) != NULL)
  753. cfg->bulk_trigger_timeout = blobmsg_get_u32(cur);
  754. if ((cur = tb[CL_CONFIG_BULK_PPS]) != NULL)
  755. cfg->bulk_trigger_pps = blobmsg_get_u32(cur);
  756. if ((cur = tb[CL_CONFIG_PRIO_PKT_LEN]) != NULL)
  757. cfg->prio_max_avg_pkt_len = blobmsg_get_u32(cur);
  758. return 0;
  759. }
  760. static int
  761. qosify_map_create_class(struct blob_attr *attr)
  762. {
  763. struct qosify_map_class *class;
  764. enum {
  765. MAP_CLASS_INGRESS,
  766. MAP_CLASS_EGRESS,
  767. __MAP_CLASS_MAX
  768. };
  769. static const struct blobmsg_policy policy[__MAP_CLASS_MAX] = {
  770. [MAP_CLASS_INGRESS] = { "ingress", BLOBMSG_TYPE_STRING },
  771. [MAP_CLASS_EGRESS] = { "egress", BLOBMSG_TYPE_STRING },
  772. };
  773. struct blob_attr *tb[__MAP_CLASS_MAX];
  774. const char *name;
  775. char *name_buf;
  776. int32_t slot;
  777. blobmsg_parse(policy, __MAP_CLASS_MAX, tb,
  778. blobmsg_data(attr), blobmsg_len(attr));
  779. if (!tb[MAP_CLASS_INGRESS] || !tb[MAP_CLASS_EGRESS])
  780. return -1;
  781. name = blobmsg_name(attr);
  782. slot = qosify_map_get_class_id(name);
  783. if (slot < 0)
  784. return -1;
  785. class = map_class[slot];
  786. if (!class) {
  787. class = calloc_a(sizeof(*class), &name_buf, strlen(name) + 1);
  788. class->name = strcpy(name_buf, name);
  789. map_class[slot] = class;
  790. }
  791. class->data.flags |= QOSIFY_CLASS_FLAG_PRESENT;
  792. if (__qosify_map_dscp_value(blobmsg_get_string(tb[MAP_CLASS_INGRESS]),
  793. &class->data.val.ingress) ||
  794. __qosify_map_dscp_value(blobmsg_get_string(tb[MAP_CLASS_EGRESS]),
  795. &class->data.val.egress)) {
  796. map_class[slot] = NULL;
  797. free(class);
  798. return -1;
  799. }
  800. return 0;
  801. }
  802. void qosify_map_set_classes(struct blob_attr *val)
  803. {
  804. int fd = qosify_map_fds[CL_MAP_CLASS];
  805. struct qosify_class empty_data = {};
  806. struct blob_attr *cur;
  807. int32_t i;
  808. int rem;
  809. for (i = 0; i < ARRAY_SIZE(map_class); i++)
  810. if (map_class[i])
  811. map_class[i]->data.flags &= ~QOSIFY_CLASS_FLAG_PRESENT;
  812. blobmsg_for_each_attr(cur, val, rem)
  813. qosify_map_create_class(cur);
  814. for (i = 0; i < ARRAY_SIZE(map_class); i++) {
  815. if (map_class[i] &&
  816. (map_class[i]->data.flags & QOSIFY_CLASS_FLAG_PRESENT))
  817. continue;
  818. free(map_class[i]);
  819. map_class[i] = NULL;
  820. }
  821. blobmsg_for_each_attr(cur, val, rem) {
  822. i = qosify_map_get_class_id(blobmsg_name(cur));
  823. if (i < 0 || !map_class[i])
  824. continue;
  825. map_parse_flow_config(&map_class[i]->data.config, cur, true);
  826. }
  827. for (i = 0; i < ARRAY_SIZE(map_class); i++) {
  828. struct qosify_class *data;
  829. data = map_class[i] ? &map_class[i]->data : &empty_data;
  830. bpf_map_update_elem(fd, &i, data, BPF_ANY);
  831. }
  832. }
  833. void qosify_map_update_config(void)
  834. {
  835. int fd = qosify_map_fds[CL_MAP_CONFIG];
  836. uint32_t key = 0;
  837. bpf_map_update_elem(fd, &key, &config, BPF_ANY);
  838. }