• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/qosify/interface.c

  1 // SPDX-License-Identifier: GPL-2.0+
  2 /*
  3  * Copyright (C) 2021 Felix Fietkau <nbd@nbd.name>
  4  */
  5 #define _GNU_SOURCE
  6 #include <sys/types.h>
  7 #include <sys/socket.h>
  8 #include <sys/ioctl.h>
  9 #include <net/if_arp.h>
 10 #include <net/if.h>
 11 #include <netinet/if_ether.h>
 12 
 13 #include <unistd.h>
 14 #include <errno.h>
 15 
 16 #include <netlink/msg.h>
 17 #include <netlink/attr.h>
 18 #include <netlink/socket.h>
 19 
 20 #include <linux/rtnetlink.h>
 21 #include <linux/pkt_cls.h>
 22 
 23 #include <libubox/vlist.h>
 24 #include <libubox/avl-cmp.h>
 25 #include <libubox/uloop.h>
 26 
 27 #include "qosify.h"
 28 
 29 static void interface_update_cb(struct vlist_tree *tree,
 30                                 struct vlist_node *node_new,
 31                                 struct vlist_node *node_old);
 32 
 33 static VLIST_TREE(devices, avl_strcmp, interface_update_cb, true, false);
 34 static VLIST_TREE(interfaces, avl_strcmp, interface_update_cb, true, false);
 35 static int socket_fd;
 36 static struct nl_sock *rtnl_sock;
 37 
 38 #define APPEND(_buf, _ofs, _format, ...) _ofs += snprintf(_buf + _ofs, sizeof(_buf) - _ofs, _format, ##__VA_ARGS__)
 39 
 40 struct qosify_iface_config {
 41         struct blob_attr *data;
 42 
 43         bool ingress;
 44         bool egress;
 45         bool nat;
 46         bool host_isolate;
 47         bool autorate_ingress;
 48 
 49         const char *bandwidth_up;
 50         const char *bandwidth_down;
 51         const char *mode;
 52         const char *common_opts;
 53         const char *ingress_opts;
 54         const char *egress_opts;
 55 };
 56 
 57 
 58 struct qosify_iface {
 59         struct vlist_node node;
 60 
 61         char ifname[IFNAMSIZ];
 62         bool active;
 63 
 64         bool device;
 65         struct blob_attr *config_data;
 66         struct qosify_iface_config config;
 67 };
 68 
 69 enum {
 70         IFACE_ATTR_BW_UP,
 71         IFACE_ATTR_BW_DOWN,
 72         IFACE_ATTR_INGRESS,
 73         IFACE_ATTR_EGRESS,
 74         IFACE_ATTR_MODE,
 75         IFACE_ATTR_NAT,
 76         IFACE_ATTR_HOST_ISOLATE,
 77         IFACE_ATTR_AUTORATE_IN,
 78         IFACE_ATTR_INGRESS_OPTS,
 79         IFACE_ATTR_EGRESS_OPTS,
 80         IFACE_ATTR_OPTS,
 81         __IFACE_ATTR_MAX
 82 };
 83 
 84 static inline const char *qosify_iface_name(struct qosify_iface *iface)
 85 {
 86         return iface->node.avl.key;
 87 }
 88 
 89 static void
 90 iface_config_parse(struct blob_attr *attr, struct blob_attr **tb)
 91 {
 92         static const struct blobmsg_policy policy[__IFACE_ATTR_MAX] = {
 93                 [IFACE_ATTR_BW_UP] = { "bandwidth_up", BLOBMSG_TYPE_STRING },
 94                 [IFACE_ATTR_BW_DOWN] = { "bandwidth_down", BLOBMSG_TYPE_STRING },
 95                 [IFACE_ATTR_INGRESS] = { "ingress", BLOBMSG_TYPE_BOOL },
 96                 [IFACE_ATTR_EGRESS] = { "egress", BLOBMSG_TYPE_BOOL },
 97                 [IFACE_ATTR_MODE] = { "mode", BLOBMSG_TYPE_STRING },
 98                 [IFACE_ATTR_NAT] = { "nat", BLOBMSG_TYPE_BOOL },
 99                 [IFACE_ATTR_HOST_ISOLATE] = { "host_isolate", BLOBMSG_TYPE_BOOL },
100                 [IFACE_ATTR_AUTORATE_IN] = { "autorate_ingress", BLOBMSG_TYPE_BOOL },
101                 [IFACE_ATTR_INGRESS_OPTS] = { "ingress_options", BLOBMSG_TYPE_STRING },
102                 [IFACE_ATTR_EGRESS_OPTS] = { "egress_options", BLOBMSG_TYPE_STRING },
103                 [IFACE_ATTR_OPTS] = { "options", BLOBMSG_TYPE_STRING },
104         };
105 
106         blobmsg_parse(policy, __IFACE_ATTR_MAX, tb, blobmsg_data(attr), blobmsg_len(attr));
107 }
108 
109 static bool
110 iface_config_equal(struct qosify_iface *if1, struct qosify_iface *if2)
111 {
112         struct blob_attr *tb1[__IFACE_ATTR_MAX], *tb2[__IFACE_ATTR_MAX];
113         int i;
114 
115         iface_config_parse(if1->config_data, tb1);
116         iface_config_parse(if2->config_data, tb2);
117 
118         for (i = 0; i < __IFACE_ATTR_MAX; i++) {
119                 if (!!tb1[i] != !!tb2[i])
120                         return false;
121 
122                 if (!tb1[i])
123                         continue;
124 
125                 if (blob_raw_len(tb1[i]) != blob_raw_len(tb2[i]))
126                         return false;
127 
128                 if (memcmp(tb1[i], tb2[i], blob_raw_len(tb1[i])) != 0)
129                         return false;
130         }
131 
132         return true;
133 }
134 
135 static const char *check_str(struct blob_attr *attr)
136 {
137         const char *str = blobmsg_get_string(attr);
138 
139         if (strchr(str, '\''))
140                 return NULL;
141 
142         return str;
143 }
144 
145 static void
146 iface_config_set(struct qosify_iface *iface, struct blob_attr *attr)
147 {
148         struct qosify_iface_config *cfg = &iface->config;
149         struct blob_attr *tb[__IFACE_ATTR_MAX];
150         struct blob_attr *cur;
151 
152         iface_config_parse(attr, tb);
153 
154         memset(cfg, 0, sizeof(*cfg));
155 
156         /* defaults */
157         cfg->mode = "diffserv4";
158         cfg->ingress = true;
159         cfg->egress = true;
160         cfg->host_isolate = true;
161         cfg->autorate_ingress = false;
162         cfg->nat = !iface->device;
163 
164         if ((cur = tb[IFACE_ATTR_BW_UP]) != NULL)
165                 cfg->bandwidth_up = check_str(cur);
166         if ((cur = tb[IFACE_ATTR_BW_DOWN]) != NULL)
167                 cfg->bandwidth_down = check_str(cur);
168         if ((cur = tb[IFACE_ATTR_MODE]) != NULL)
169                 cfg->mode = check_str(cur);
170         if ((cur = tb[IFACE_ATTR_OPTS]) != NULL)
171                 cfg->common_opts = check_str(cur);
172         if ((cur = tb[IFACE_ATTR_EGRESS_OPTS]) != NULL)
173                 cfg->egress_opts = check_str(cur);
174         if ((cur = tb[IFACE_ATTR_INGRESS_OPTS]) != NULL)
175                 cfg->ingress_opts = check_str(cur);
176         if ((cur = tb[IFACE_ATTR_INGRESS]) != NULL)
177                 cfg->ingress = blobmsg_get_bool(cur);
178         if ((cur = tb[IFACE_ATTR_EGRESS]) != NULL)
179                 cfg->egress = blobmsg_get_bool(cur);
180         if ((cur = tb[IFACE_ATTR_NAT]) != NULL)
181                 cfg->nat = blobmsg_get_bool(cur);
182         if ((cur = tb[IFACE_ATTR_HOST_ISOLATE]) != NULL)
183                 cfg->host_isolate = blobmsg_get_bool(cur);
184         if ((cur = tb[IFACE_ATTR_AUTORATE_IN]) != NULL)
185                 cfg->autorate_ingress = blobmsg_get_bool(cur);
186 }
187 
188 static const char *
189 interface_ifb_name(struct qosify_iface *iface)
190 {
191         static char ifname[IFNAMSIZ + 1] = "ifb-";
192         int len = strlen(iface->ifname);
193 
194         if (len + 4 < IFNAMSIZ) {
195                 snprintf(ifname + 4, IFNAMSIZ - 4, "%s", iface->ifname);
196 
197                 return ifname;
198         }
199 
200         ifname[4] = iface->ifname[0];
201         ifname[5] = iface->ifname[1];
202         snprintf(ifname + 6, IFNAMSIZ - 6, "%s", iface->ifname + len - (IFNAMSIZ + 6) - 1);
203 
204         return ifname;
205 }
206 
207 static int
208 prepare_qdisc_cmd(char *buf, int len, const char *dev, bool add, const char *type)
209 {
210         return snprintf(buf, len, "tc qdisc %s dev '%s' %s",
211                         add ? "add" : "del", dev, type);
212 }
213 
214 static int
215 prepare_filter_cmd(char *buf, int len, const char *dev, int prio, bool add, bool egress)
216 {
217         return snprintf(buf, len, "tc filter %s dev '%s' %sgress prio %d",
218                         add ? "add" : "del", dev, egress ? "e" : "in", prio);
219 }
220 
221 static int
222 cmd_add_bpf_filter(const char *ifname, int prio, bool egress, bool eth)
223 {
224         struct tcmsg tcmsg = {
225                 .tcm_family = AF_UNSPEC,
226                 .tcm_ifindex = if_nametoindex(ifname),
227         };
228         struct nl_msg *msg;
229         struct nlattr *opts;
230         const char *suffix;
231         int prog_fd = -1;
232         char name[32];
233 
234         suffix = qosify_get_program(!egress * QOSIFY_INGRESS + !eth * QOSIFY_IP_ONLY, &prog_fd);
235         if (!suffix)
236                 return -1;
237 
238         snprintf(name, sizeof(name), "qosify_%s", suffix);
239 
240         if (egress)
241                 tcmsg.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_EGRESS);
242         else
243                 tcmsg.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS);
244 
245         tcmsg.tcm_info = TC_H_MAKE(prio << 16, htons(ETH_P_ALL));
246 
247         msg = nlmsg_alloc_simple(RTM_NEWTFILTER, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL);
248         nlmsg_append(msg, &tcmsg, sizeof(tcmsg), NLMSG_ALIGNTO);
249         nla_put_string(msg, TCA_KIND, "bpf");
250 
251         opts = nla_nest_start(msg, TCA_OPTIONS);
252         nla_put_u32(msg, TCA_BPF_FD, prog_fd);
253         nla_put_string(msg, TCA_BPF_NAME, name);
254         nla_put_u32(msg, TCA_BPF_FLAGS, TCA_BPF_FLAG_ACT_DIRECT);
255         nla_put_u32(msg, TCA_BPF_FLAGS_GEN, TCA_CLS_FLAGS_SKIP_HW);
256         nla_nest_end(msg, opts);
257 
258         nl_send_auto_complete(rtnl_sock, msg);
259         nlmsg_free(msg);
260 
261         return nl_wait_for_ack(rtnl_sock);
262 }
263 
264 static int
265 cmd_add_qdisc(struct qosify_iface *iface, const char *ifname, bool egress, bool eth)
266 {
267         struct qosify_iface_config *cfg = &iface->config;
268         const char *bw = egress ? cfg->bandwidth_up : cfg->bandwidth_down;
269         const char *dir_opts = egress ? cfg->egress_opts : cfg->ingress_opts;
270         char buf[512];
271         int ofs;
272 
273         ofs = prepare_qdisc_cmd(buf, sizeof(buf), ifname, true, "clsact");
274         qosify_run_cmd(buf, true);
275 
276         ofs = prepare_qdisc_cmd(buf, sizeof(buf), ifname, true, "root cake");
277         if (bw)
278                 APPEND(buf, ofs, " bandwidth %s", bw);
279 
280         APPEND(buf, ofs, " %s %sgress", cfg->mode, egress ? "e" : "in");
281         if (!egress && cfg->autorate_ingress)
282                 APPEND(buf, ofs, " autorate-ingress");
283 
284         if (cfg->host_isolate)
285                 APPEND(buf, ofs, " %snat dual-%shost",
286                         cfg->nat ? "" : "no",
287                         egress ? "src" : "dst");
288         else
289                 APPEND(buf, ofs, " flows");
290 
291         APPEND(buf, ofs, " %s %s",
292                cfg->common_opts ? cfg->common_opts : "",
293                dir_opts ? dir_opts : "");
294 
295         return qosify_run_cmd(buf, false);
296 }
297 
298 static int
299 cmd_add_ingress(struct qosify_iface *iface, bool eth)
300 {
301         const char *ifbdev = interface_ifb_name(iface);
302         char buf[256];
303         int prio = QOSIFY_PRIO_BASE;
304         int ofs;
305 
306         cmd_add_bpf_filter(iface->ifname, prio++, false, eth);
307 
308         ofs = prepare_filter_cmd(buf, sizeof(buf), iface->ifname, prio++, true, false);
309         APPEND(buf, ofs, " protocol ip u32 match ip sport 53 0xffff "
310                          "flowid 1:1 action mirred egress redirect dev " QOSIFY_DNS_IFNAME);
311         qosify_run_cmd(buf, false);
312 
313         ofs = prepare_filter_cmd(buf, sizeof(buf), iface->ifname, prio++, true, false);
314         APPEND(buf, ofs, " protocol 802.1Q u32 offset plus 4 match ip sport 53 0xffff "
315                          "flowid 1:1 action mirred egress redirect dev " QOSIFY_DNS_IFNAME);
316         qosify_run_cmd(buf, false);
317 
318         ofs = prepare_filter_cmd(buf, sizeof(buf), iface->ifname, prio++, true, false);
319         APPEND(buf, ofs, " protocol ipv6 u32 match ip6 sport 53 0xffff "
320                          "flowid 1:1 action mirred egress redirect dev " QOSIFY_DNS_IFNAME);
321         qosify_run_cmd(buf, false);
322 
323         ofs = prepare_filter_cmd(buf, sizeof(buf), iface->ifname, prio++, true, false);
324         APPEND(buf, ofs, " protocol ipv6 u32 offset plus 4 match ip6 sport 53 0xffff "
325                          "flowid 1:1 action mirred egress redirect dev " QOSIFY_DNS_IFNAME);
326         qosify_run_cmd(buf, false);
327 
328 
329         if (!iface->config.ingress)
330                 return 0;
331 
332         snprintf(buf, sizeof(buf), "ip link add '%s' type ifb", ifbdev);
333         qosify_run_cmd(buf, false);
334 
335         cmd_add_qdisc(iface, ifbdev, false, eth);
336 
337         snprintf(buf, sizeof(buf), "ip link set dev '%s' up", ifbdev);
338         qosify_run_cmd(buf, false);
339 
340         ofs = prepare_filter_cmd(buf, sizeof(buf), iface->ifname, prio++, true, false);
341         APPEND(buf, ofs, " protocol all u32 match u32 0 0 flowid 1:1"
342                          " action mirred egress redirect dev '%s'", ifbdev);
343         return qosify_run_cmd(buf, false);
344 }
345 
346 static int cmd_add_egress(struct qosify_iface *iface, bool eth)
347 {
348         if (!iface->config.egress)
349                 return 0;
350 
351         cmd_add_qdisc(iface, iface->ifname, true, eth);
352 
353         return cmd_add_bpf_filter(iface->ifname, QOSIFY_PRIO_BASE, true, eth);
354 }
355 
356 static void
357 interface_clear_qdisc(struct qosify_iface *iface)
358 {
359         char buf[64];
360         int i;
361 
362         prepare_qdisc_cmd(buf, sizeof(buf), iface->ifname, false, "root");
363         qosify_run_cmd(buf, true);
364 
365         for (i = 0; i < 6; i++) {
366                 prepare_filter_cmd(buf, sizeof(buf), iface->ifname, QOSIFY_PRIO_BASE + i, false, false);
367                 qosify_run_cmd(buf, true);
368         }
369 
370         prepare_filter_cmd(buf, sizeof(buf), iface->ifname, QOSIFY_PRIO_BASE, false, true);
371         qosify_run_cmd(buf, true);
372 
373         snprintf(buf, sizeof(buf), "ip link del '%s'", interface_ifb_name(iface));
374         qosify_run_cmd(buf, true);
375 }
376 
377 static void
378 interface_start(struct qosify_iface *iface)
379 {
380         struct ifreq ifr = {};
381         bool eth;
382 
383         if (!iface->ifname[0] || iface->active)
384                 return;
385 
386         ULOG_INFO("start interface %s\n", iface->ifname);
387 
388         strncpy(ifr.ifr_name, iface->ifname, sizeof(ifr.ifr_name));
389         if (ioctl(socket_fd, SIOCGIFHWADDR, &ifr) < 0) {
390                 ULOG_ERR("ioctl(SIOCGIFHWADDR, %s) failed: %s\n", iface->ifname, strerror(errno));
391                 return;
392         }
393 
394         eth = ifr.ifr_hwaddr.sa_family == ARPHRD_ETHER;
395 
396         interface_clear_qdisc(iface);
397         cmd_add_egress(iface, eth);
398         cmd_add_ingress(iface, eth);
399 
400         iface->active = true;
401 }
402 
403 static void
404 interface_stop(struct qosify_iface *iface)
405 {
406         if (!iface->ifname[0] || !iface->active)
407                 return;
408 
409         ULOG_INFO("stop interface %s\n", iface->ifname);
410         iface->active = false;
411 
412         interface_clear_qdisc(iface);
413 }
414 
415 static void
416 interface_set_config(struct qosify_iface *iface, struct blob_attr *config)
417 {
418         iface->config_data = blob_memdup(config);
419         iface_config_set(iface, iface->config_data);
420         interface_start(iface);
421 }
422 
423 static void
424 interface_update_cb(struct vlist_tree *tree,
425                     struct vlist_node *node_new, struct vlist_node *node_old)
426 {
427         struct qosify_iface *if_new = NULL, *if_old = NULL;
428 
429         if (node_new)
430                 if_new = container_of(node_new, struct qosify_iface, node);
431         if (node_old)
432                 if_old = container_of(node_old, struct qosify_iface, node);
433 
434         if (if_new && if_old) {
435                 if (!iface_config_equal(if_old, if_new)) {
436                         interface_stop(if_old);
437                         free(if_old->config_data);
438                         interface_set_config(if_old, if_new->config_data);
439                 }
440 
441                 free(if_new);
442                 return;
443         }
444 
445         if (if_old) {
446                 interface_stop(if_old);
447                 free(if_old->config_data);
448                 free(if_old);
449         }
450 
451         if (if_new)
452                 interface_set_config(if_new, if_new->config_data);
453 }
454 
455 static void
456 interface_create(struct blob_attr *attr, bool device)
457 {
458         struct qosify_iface *iface;
459         const char *name = blobmsg_name(attr);
460         int name_len = strlen(name);
461         char *name_buf;
462 
463         if (strchr(name, '\''))
464                 return;
465 
466         if (name_len >= IFNAMSIZ)
467                 return;
468 
469         if (blobmsg_type(attr) != BLOBMSG_TYPE_TABLE)
470                 return;
471 
472         iface = calloc_a(sizeof(*iface), &name_buf, name_len + 1);
473         strcpy(name_buf, blobmsg_name(attr));
474         iface->config_data = attr;
475         iface->device = device;
476         vlist_add(device ? &devices : &interfaces, &iface->node, name_buf);
477 }
478 
479 void qosify_iface_config_update(struct blob_attr *ifaces, struct blob_attr *devs)
480 {
481         struct blob_attr *cur;
482         int rem;
483 
484         vlist_update(&devices);
485         blobmsg_for_each_attr(cur, devs, rem)
486                 interface_create(cur, true);
487         vlist_flush(&devices);
488 
489         vlist_update(&interfaces);
490         blobmsg_for_each_attr(cur, ifaces, rem)
491                 interface_create(cur, false);
492         vlist_flush(&interfaces);
493 }
494 
495 static void
496 qosify_iface_check_device(struct qosify_iface *iface)
497 {
498         const char *name = qosify_iface_name(iface);
499         int ifindex;
500 
501         ifindex = if_nametoindex(name);
502         if (!ifindex) {
503                 interface_stop(iface);
504                 iface->ifname[0] = 0;
505         } else {
506                 snprintf(iface->ifname, sizeof(iface->ifname), "%s", name);
507                 interface_start(iface);
508         }
509 }
510 
511 static void
512 qosify_iface_check_interface(struct qosify_iface *iface)
513 {
514         const char *name = qosify_iface_name(iface);
515         char ifname[IFNAMSIZ];
516 
517         if (qosify_ubus_check_interface(name, ifname, sizeof(ifname)) == 0) {
518                 snprintf(iface->ifname, sizeof(iface->ifname), "%s", ifname);
519                 interface_start(iface);
520         } else {
521                 interface_stop(iface);
522                 iface->ifname[0] = 0;
523         }
524 }
525 
526 static void qos_iface_check_cb(struct uloop_timeout *t)
527 {
528         struct qosify_iface *iface;
529 
530         vlist_for_each_element(&devices, iface, node)
531                 qosify_iface_check_device(iface);
532         vlist_for_each_element(&interfaces, iface, node)
533                 qosify_iface_check_interface(iface);
534         qosify_ubus_update_bridger(false);
535 }
536 
537 void qosify_iface_check(void)
538 {
539         static struct uloop_timeout timer = {
540                 .cb = qos_iface_check_cb,
541         };
542 
543         uloop_timeout_set(&timer, 10);
544 }
545 
546 static void
547 __qosify_iface_status(struct blob_buf *b, struct qosify_iface *iface)
548 {
549         void *c;
550 
551         c = blobmsg_open_table(b, qosify_iface_name(iface));
552         blobmsg_add_u8(b, "active", iface->active);
553         if (iface->ifname[0])
554                 blobmsg_add_string(b, "ifname", iface->ifname);
555         blobmsg_add_u8(b, "egress", iface->config.egress);
556         blobmsg_add_u8(b, "ingress", iface->config.ingress);
557         blobmsg_close_table(b, c);
558 
559 }
560 
561 void qosify_iface_status(struct blob_buf *b)
562 {
563         struct qosify_iface *iface;
564         void *c;
565 
566         c = blobmsg_open_table(b, "devices");
567         vlist_for_each_element(&devices, iface, node)
568                 __qosify_iface_status(b, iface);
569         blobmsg_close_table(b, c);
570 
571         c = blobmsg_open_table(b, "interfaces");
572         vlist_for_each_element(&interfaces, iface, node)
573                 __qosify_iface_status(b, iface);
574         blobmsg_close_table(b, c);
575 }
576 
577 static int
578 qosify_nl_error_cb(struct sockaddr_nl *nla, struct nlmsgerr *err,
579                    void *arg)
580 {
581         struct nlmsghdr *nlh = (struct nlmsghdr *) err - 1;
582         struct nlattr *tb[NLMSGERR_ATTR_MAX + 1];
583         struct nlattr *attrs;
584         int ack_len = sizeof(*nlh) + sizeof(int) + sizeof(*nlh);
585         int len = nlh->nlmsg_len;
586         const char *errstr = "(unknown)";
587 
588         if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
589                 return NL_STOP;
590 
591         if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
592                 ack_len += err->msg.nlmsg_len - sizeof(*nlh);
593 
594         attrs = (void *) ((unsigned char *) nlh + ack_len);
595         len -= ack_len;
596 
597         nla_parse(tb, NLMSGERR_ATTR_MAX, attrs, len, NULL);
598         if (tb[NLMSGERR_ATTR_MSG])
599                 errstr = nla_data(tb[NLMSGERR_ATTR_MSG]);
600 
601         ULOG_ERR("Netlink error(%d): %s\n", err->error, errstr);
602 
603         return NL_STOP;
604 }
605 
606 static void
607 __qosify_iface_get_device(struct blob_buf *b, struct qosify_iface *iface)
608 {
609         if (!iface->ifname[0] || !iface->active)
610                 return;
611 
612         blobmsg_add_string(b, NULL, iface->ifname);
613 }
614 
615 void qosify_iface_get_devices(struct blob_buf *b)
616 {
617         struct qosify_iface *iface;
618 
619         vlist_for_each_element(&devices, iface, node)
620                 __qosify_iface_get_device(b, iface);
621         vlist_for_each_element(&interfaces, iface, node)
622                 __qosify_iface_get_device(b, iface);
623 }
624 
625 int qosify_iface_init(void)
626 {
627         int fd, opt;
628 
629         socket_fd = socket(AF_UNIX, SOCK_DGRAM, 0);
630         if (socket < 0)
631                 return -1;
632 
633         rtnl_sock = nl_socket_alloc();
634         if (!rtnl_sock)
635                 return -1;
636 
637         if (nl_connect(rtnl_sock, NETLINK_ROUTE))
638                 return -1;
639 
640         nl_cb_err(nl_socket_get_cb(rtnl_sock), NL_CB_CUSTOM,
641                   qosify_nl_error_cb, NULL);
642 
643         fd = nl_socket_get_fd(rtnl_sock);
644         opt = 1;
645         setsockopt(fd, SOL_NETLINK, NETLINK_EXT_ACK, &opt, sizeof(opt));
646 
647         opt = 1;
648         setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, &opt, sizeof(opt));
649 
650         return 0;
651 }
652 
653 void qosify_iface_stop(void)
654 {
655         struct qosify_iface *iface;
656 
657         vlist_for_each_element(&interfaces, iface, node)
658                 interface_stop(iface);
659         vlist_for_each_element(&devices, iface, node)
660                 interface_stop(iface);
661 
662         nl_socket_free(rtnl_sock);
663 }
664 
665 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt