usteer: Fix better candidate not being set in policy
[project/usteer.git] / remote.c
1 /*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License.
5 *
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
10 *
11 * You should have received a copy of the GNU General Public License
12 * along with this program; if not, write to the Free Software
13 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
14 *
15 * Copyright (C) 2020 embedd.ch
16 * Copyright (C) 2020 Felix Fietkau <nbd@nbd.name>
17 * Copyright (C) 2020 John Crispin <john@phrozen.org>
18 */
19
20 #define _GNU_SOURCE
21
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <net/if.h>
26 #include <arpa/inet.h>
27 #include <errno.h>
28 #include <unistd.h>
29
30 #include <libubox/vlist.h>
31 #include <libubox/avl-cmp.h>
32 #include <libubox/usock.h>
33 #include "usteer.h"
34 #include "remote.h"
35 #include "node.h"
36
37 static uint32_t local_id;
38 static struct uloop_fd remote_fd;
39 static struct uloop_timeout remote_timer;
40 static struct uloop_timeout reload_timer;
41
42 static struct blob_buf buf;
43 static uint32_t msg_seq;
44
45 struct interface {
46 struct vlist_node node;
47 int ifindex;
48 };
49
50 static void
51 interfaces_update_cb(struct vlist_tree *tree,
52 struct vlist_node *node_new,
53 struct vlist_node *node_old);
54
55 static int remote_host_cmp(const void *k1, const void *k2, void *ptr)
56 {
57 unsigned long v1 = (unsigned long) k1;
58 unsigned long v2 = (unsigned long) k2;
59
60 return v2 - v1;
61 }
62
63 static VLIST_TREE(interfaces, avl_strcmp, interfaces_update_cb, true, true);
64 LIST_HEAD(remote_nodes);
65 AVL_TREE(remote_hosts, remote_host_cmp, false, NULL);
66
67 static const char *
68 interface_name(struct interface *iface)
69 {
70 return iface->node.avl.key;
71 }
72
73 static void
74 interface_check(struct interface *iface)
75 {
76 iface->ifindex = if_nametoindex(interface_name(iface));
77 uloop_timeout_set(&reload_timer, 1);
78 }
79
80 static void
81 interface_init(struct interface *iface)
82 {
83 interface_check(iface);
84 }
85
86 static void
87 interface_free(struct interface *iface)
88 {
89 avl_delete(&interfaces.avl, &iface->node.avl);
90 free(iface);
91 }
92
93 static void
94 interfaces_update_cb(struct vlist_tree *tree,
95 struct vlist_node *node_new,
96 struct vlist_node *node_old)
97 {
98 struct interface *iface;
99
100 if (node_new && node_old) {
101 iface = container_of(node_new, struct interface, node);
102 free(iface);
103 iface = container_of(node_old, struct interface, node);
104 interface_check(iface);
105 } else if (node_old) {
106 iface = container_of(node_old, struct interface, node);
107 interface_free(iface);
108 } else {
109 iface = container_of(node_new, struct interface, node);
110 interface_init(iface);
111 }
112 }
113
114 void usteer_interface_add(const char *name)
115 {
116 struct interface *iface;
117 char *name_buf;
118
119 iface = calloc_a(sizeof(*iface), &name_buf, strlen(name) + 1);
120 strcpy(name_buf, name);
121 vlist_add(&interfaces, &iface->node, name_buf);
122 }
123
124 void config_set_interfaces(struct blob_attr *data)
125 {
126 struct blob_attr *cur;
127 int rem;
128
129 if (!data)
130 return;
131
132 if (!blobmsg_check_attr_list(data, BLOBMSG_TYPE_STRING))
133 return;
134
135 vlist_update(&interfaces);
136 blobmsg_for_each_attr(cur, data, rem) {
137 usteer_interface_add(blobmsg_data(cur));
138 }
139 vlist_flush(&interfaces);
140 }
141
142 void config_get_interfaces(struct blob_buf *buf)
143 {
144 struct interface *iface;
145 void *c;
146
147 c = blobmsg_open_array(buf, "interfaces");
148 vlist_for_each_element(&interfaces, iface, node) {
149 blobmsg_add_string(buf, NULL, interface_name(iface));
150 }
151 blobmsg_close_array(buf, c);
152 }
153
154 static void
155 interface_add_station(struct usteer_remote_node *node, struct blob_attr *data)
156 {
157 struct sta *sta;
158 struct sta_info *si, *local_si;
159 struct apmsg_sta msg;
160 struct usteer_node *local_node;
161 bool create;
162 bool connect_change;
163
164 if (!parse_apmsg_sta(&msg, data)) {
165 MSG(DEBUG, "Cannot parse station in message\n");
166 return;
167 }
168
169 if (msg.timeout <= 0) {
170 MSG(DEBUG, "Refuse to add an already expired station entry\n");
171 return;
172 }
173
174 sta = usteer_sta_get(msg.addr, true);
175 if (!sta)
176 return;
177
178 si = usteer_sta_info_get(sta, &node->node, &create);
179 if (!si)
180 return;
181
182 connect_change = si->connected != msg.connected;
183 si->connected = msg.connected;
184 si->signal = msg.signal;
185 si->seen = current_time - msg.seen;
186 si->last_connected = current_time - msg.last_connected;
187
188 /* Check if client roamed to this foreign node */
189 if ((connect_change || create) && si->connected == STA_CONNECTED) {
190 for_each_local_node(local_node) {
191 local_si = usteer_sta_info_get(sta, local_node, NULL);
192 if (!local_si)
193 continue;
194
195 if (current_time - local_si->last_connected < config.roam_process_timeout) {
196 node->node.roam_events.target++;
197 break;
198 }
199 }
200 }
201
202 usteer_sta_info_update_timeout(si, msg.timeout);
203 }
204
205 static void
206 remote_node_free(struct usteer_remote_node *node)
207 {
208 struct usteer_remote_host *host = node->host;
209
210 list_del(&node->list);
211 list_del(&node->host_list);
212 usteer_sta_node_cleanup(&node->node);
213 usteer_measurement_report_node_cleanup(&node->node);
214 free(node);
215
216 if (!list_empty(&host->nodes))
217 return;
218
219 avl_delete(&remote_hosts, &host->avl);
220 free(host->addr);
221 free(host);
222 }
223
224 static struct usteer_remote_host *
225 interface_get_host(const char *addr, unsigned long id)
226 {
227 struct usteer_remote_host *host;
228
229 host = avl_find_element(&remote_hosts, (void *)id, host, avl);
230 if (host)
231 goto out;
232
233 host = calloc(1, sizeof(*host));
234 host->avl.key = (void *)id;
235 INIT_LIST_HEAD(&host->nodes);
236 avl_insert(&remote_hosts, &host->avl);
237
238 out:
239 if (host->addr && !strcmp(host->addr, addr))
240 return host;
241
242 free(host->addr);
243 host->addr = strdup(addr);
244
245 return host;
246 }
247
248 static struct usteer_remote_node *
249 interface_get_node(struct usteer_remote_host *host, const char *name)
250 {
251 struct usteer_remote_node *node;
252 int addr_len = strlen(host->addr);
253 char *buf;
254
255 list_for_each_entry(node, &host->nodes, host_list)
256 if (!strcmp(node->name, name))
257 return node;
258
259 node = calloc_a(sizeof(*node), &buf, addr_len + 1 + strlen(name) + 1);
260 node->node.type = NODE_TYPE_REMOTE;
261 node->node.created = current_time;
262
263 sprintf(buf, "%s#%s", host->addr, name);
264 node->node.avl.key = buf;
265 node->name = buf + addr_len + 1;
266 node->host = host;
267 INIT_LIST_HEAD(&node->node.sta_info);
268 INIT_LIST_HEAD(&node->node.measurements);
269
270 list_add_tail(&node->list, &remote_nodes);
271 list_add_tail(&node->host_list, &host->nodes);
272
273 return node;
274 }
275
276 static void
277 interface_add_node(struct usteer_remote_host *host, struct blob_attr *data)
278 {
279 struct usteer_remote_node *node;
280 struct apmsg_node msg;
281 struct blob_attr *cur;
282 int rem;
283
284 if (!parse_apmsg_node(&msg, data)) {
285 MSG(DEBUG, "Cannot parse node in message\n");
286 return;
287 }
288
289 node = interface_get_node(host, msg.name);
290 node->check = 0;
291 node->node.freq = msg.freq;
292 node->node.channel = msg.channel;
293 node->node.op_class = msg.op_class;
294 node->node.n_assoc = msg.n_assoc;
295 node->node.max_assoc = msg.max_assoc;
296 node->node.noise = msg.noise;
297 node->node.load = msg.load;
298
299 memcpy(node->node.bssid, msg.bssid, sizeof(node->node.bssid));
300
301 snprintf(node->node.ssid, sizeof(node->node.ssid), "%s", msg.ssid);
302 usteer_node_set_blob(&node->node.rrm_nr, msg.rrm_nr);
303 usteer_node_set_blob(&node->node.node_info, msg.node_info);
304
305 blob_for_each_attr(cur, msg.stations, rem)
306 interface_add_station(node, cur);
307 }
308
309 static void
310 interface_recv_msg(struct interface *iface, char *addr_str, void *buf, int len)
311 {
312 struct usteer_remote_host *host;
313 struct blob_attr *data = buf;
314 struct apmsg msg;
315 struct blob_attr *cur;
316 int rem;
317
318 if (config.local_mode)
319 return;
320
321 if (blob_pad_len(data) != len) {
322 MSG(DEBUG, "Invalid message length (header: %d, real: %d)\n", blob_pad_len(data), len);
323 return;
324 }
325
326 if (!parse_apmsg(&msg, data)) {
327 MSG(DEBUG, "Missing fields in message\n");
328 return;
329 }
330
331 if (msg.id == local_id)
332 return;
333
334 MSG(NETWORK, "Received message on %s (id=%08x->%08x seq=%d len=%d)\n",
335 interface_name(iface), msg.id, local_id, msg.seq, len);
336
337 host = interface_get_host(addr_str, msg.id);
338 usteer_node_set_blob(&host->host_info, msg.host_info);
339
340 blob_for_each_attr(cur, msg.nodes, rem)
341 interface_add_node(host, cur);
342 }
343
344 static struct interface *
345 interface_find_by_ifindex(int index)
346 {
347 struct interface *iface;
348
349 vlist_for_each_element(&interfaces, iface, node) {
350 if (iface->ifindex == index)
351 return iface;
352 }
353
354 return NULL;
355 }
356
357 static void
358 interface_recv_v4(struct uloop_fd *u, unsigned int events)
359 {
360 static char buf[APMGR_BUFLEN];
361 static char cmsg_buf[( CMSG_SPACE(sizeof(struct in_pktinfo)) + sizeof(int)) + 1];
362 static struct sockaddr_in sin;
363 char addr_str[INET_ADDRSTRLEN];
364 static struct iovec iov = {
365 .iov_base = buf,
366 .iov_len = sizeof(buf)
367 };
368 static struct msghdr msg = {
369 .msg_name = &sin,
370 .msg_namelen = sizeof(sin),
371 .msg_iov = &iov,
372 .msg_iovlen = 1,
373 .msg_control = cmsg_buf,
374 .msg_controllen = sizeof(cmsg_buf),
375 };
376 struct cmsghdr *cmsg;
377 int len;
378
379 do {
380 struct in_pktinfo *pkti = NULL;
381 struct interface *iface;
382
383 len = recvmsg(u->fd, &msg, 0);
384 if (len < 0) {
385 switch (errno) {
386 case EAGAIN:
387 return;
388 case EINTR:
389 continue;
390 default:
391 perror("recvmsg");
392 uloop_fd_delete(u);
393 return;
394 }
395 }
396
397 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
398 if (cmsg->cmsg_type != IP_PKTINFO)
399 continue;
400
401 pkti = (struct in_pktinfo *) CMSG_DATA(cmsg);
402 }
403
404 if (!pkti) {
405 MSG(DEBUG, "Received packet without ifindex\n");
406 continue;
407 }
408
409 iface = interface_find_by_ifindex(pkti->ipi_ifindex);
410 if (!iface) {
411 MSG(DEBUG, "Received packet from unconfigured interface %d\n", pkti->ipi_ifindex);
412 continue;
413 }
414
415 inet_ntop(AF_INET, &sin.sin_addr, addr_str, sizeof(addr_str));
416
417 interface_recv_msg(iface, addr_str, buf, len);
418 } while (1);
419 }
420
421
422 static void interface_recv_v6(struct uloop_fd *u, unsigned int events){
423 static char buf[APMGR_BUFLEN];
424 static char cmsg_buf[( CMSG_SPACE(sizeof(struct in6_pktinfo)) + sizeof(int)) + 1];
425 static struct sockaddr_in6 sin;
426 static struct iovec iov = {
427 .iov_base = buf,
428 .iov_len = sizeof(buf)
429 };
430 static struct msghdr msg = {
431 .msg_name = &sin,
432 .msg_namelen = sizeof(sin),
433 .msg_iov = &iov,
434 .msg_iovlen = 1,
435 .msg_control = cmsg_buf,
436 .msg_controllen = sizeof(cmsg_buf),
437 };
438 struct cmsghdr *cmsg;
439 char addr_str[INET6_ADDRSTRLEN];
440 int len;
441
442 do {
443 struct in6_pktinfo *pkti = NULL;
444 struct interface *iface;
445
446 len = recvmsg(u->fd, &msg, 0);
447 if (len < 0) {
448 switch (errno) {
449 case EAGAIN:
450 return;
451 case EINTR:
452 continue;
453 default:
454 perror("recvmsg");
455 uloop_fd_delete(u);
456 return;
457 }
458 }
459
460 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
461 if (cmsg->cmsg_type != IPV6_PKTINFO)
462 continue;
463
464 pkti = (struct in6_pktinfo *) CMSG_DATA(cmsg);
465 }
466
467 if (!pkti) {
468 MSG(DEBUG, "Received packet without ifindex\n");
469 continue;
470 }
471
472 iface = interface_find_by_ifindex(pkti->ipi6_ifindex);
473 if (!iface) {
474 MSG(DEBUG, "Received packet from unconfigured interface %d\n", pkti->ipi6_ifindex);
475 continue;
476 }
477
478 inet_ntop(AF_INET6, &sin.sin6_addr, addr_str, sizeof(addr_str));
479 if (sin.sin6_addr.s6_addr[0] == 0) {
480 /* IPv4 mapped address. Ignore. */
481 continue;
482 }
483
484 interface_recv_msg(iface, addr_str, buf, len);
485 } while (1);
486 }
487
488 static void interface_send_msg_v4(struct interface *iface, struct blob_attr *data)
489 {
490 static size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_pktinfo)) / sizeof(size_t)) + 1];
491 static struct sockaddr_in a;
492 static struct iovec iov;
493 static struct msghdr m = {
494 .msg_name = (struct sockaddr *) &a,
495 .msg_namelen = sizeof(a),
496 .msg_iov = &iov,
497 .msg_iovlen = 1,
498 .msg_control = cmsg_data,
499 .msg_controllen = CMSG_LEN(sizeof(struct in_pktinfo)),
500 };
501 struct in_pktinfo *pkti;
502 struct cmsghdr *cmsg;
503
504 a.sin_family = AF_INET;
505 a.sin_port = htons(APMGR_PORT);
506 a.sin_addr.s_addr = ~0;
507
508 memset(cmsg_data, 0, sizeof(cmsg_data));
509 cmsg = CMSG_FIRSTHDR(&m);
510 cmsg->cmsg_len = m.msg_controllen;
511 cmsg->cmsg_level = IPPROTO_IP;
512 cmsg->cmsg_type = IP_PKTINFO;
513
514 pkti = (struct in_pktinfo *) CMSG_DATA(cmsg);
515 pkti->ipi_ifindex = iface->ifindex;
516
517 iov.iov_base = data;
518 iov.iov_len = blob_pad_len(data);
519
520 if (sendmsg(remote_fd.fd, &m, 0) < 0)
521 perror("sendmsg");
522 }
523
524
525 static void interface_send_msg_v6(struct interface *iface, struct blob_attr *data) {
526 static struct sockaddr_in6 groupSock = {};
527
528 groupSock.sin6_family = AF_INET6;
529 inet_pton(AF_INET6, APMGR_V6_MCAST_GROUP, &groupSock.sin6_addr);
530 groupSock.sin6_port = htons(APMGR_PORT);
531
532 setsockopt(remote_fd.fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &iface->ifindex, sizeof(iface->ifindex));
533
534 if (sendto(remote_fd.fd, data, blob_pad_len(data), 0, (const struct sockaddr *)&groupSock, sizeof(groupSock)) < 0)
535 perror("sendmsg");
536 }
537
538 static void interface_send_msg(struct interface *iface, struct blob_attr *data){
539 if (config.ipv6) {
540 interface_send_msg_v6(iface, data);
541 } else {
542 interface_send_msg_v4(iface, data);
543 }
544 }
545
546 static void usteer_send_sta_info(struct sta_info *sta)
547 {
548 int seen = current_time - sta->seen;
549 int last_connected = !!sta->connected ? 0 : current_time - sta->last_connected;
550 void *c;
551
552 c = blob_nest_start(&buf, 0);
553 blob_put(&buf, APMSG_STA_ADDR, sta->sta->addr, 6);
554 blob_put_int8(&buf, APMSG_STA_CONNECTED, !!sta->connected);
555 blob_put_int32(&buf, APMSG_STA_SIGNAL, sta->signal);
556 blob_put_int32(&buf, APMSG_STA_SEEN, seen);
557 blob_put_int32(&buf, APMSG_STA_LAST_CONNECTED, last_connected);
558 blob_put_int32(&buf, APMSG_STA_TIMEOUT, config.local_sta_timeout - seen);
559 blob_nest_end(&buf, c);
560 }
561
562 static void usteer_send_node(struct usteer_node *node, struct sta_info *sta)
563 {
564 void *c, *s, *r;
565
566 c = blob_nest_start(&buf, 0);
567
568 blob_put_string(&buf, APMSG_NODE_NAME, usteer_node_name(node));
569 blob_put_string(&buf, APMSG_NODE_SSID, node->ssid);
570 blob_put_int32(&buf, APMSG_NODE_FREQ, node->freq);
571 blob_put_int32(&buf, APMSG_NODE_NOISE, node->noise);
572 blob_put_int32(&buf, APMSG_NODE_LOAD, node->load);
573 blob_put_int32(&buf, APMSG_NODE_N_ASSOC, node->n_assoc);
574 blob_put_int32(&buf, APMSG_NODE_MAX_ASSOC, node->max_assoc);
575 blob_put_int32(&buf, APMSG_NODE_OP_CLASS, node->op_class);
576 blob_put_int32(&buf, APMSG_NODE_CHANNEL, node->channel);
577 blob_put(&buf, APMSG_NODE_BSSID, node->bssid, sizeof(node->bssid));
578 if (node->rrm_nr) {
579 r = blob_nest_start(&buf, APMSG_NODE_RRM_NR);
580 blobmsg_add_field(&buf, BLOBMSG_TYPE_ARRAY, "",
581 blobmsg_data(node->rrm_nr),
582 blobmsg_data_len(node->rrm_nr));
583 blob_nest_end(&buf, r);
584 }
585
586 if (node->node_info)
587 blob_put(&buf, APMSG_NODE_NODE_INFO,
588 blob_data(node->node_info),
589 blob_len(node->node_info));
590
591 s = blob_nest_start(&buf, APMSG_NODE_STATIONS);
592
593 if (sta) {
594 usteer_send_sta_info(sta);
595 } else {
596 list_for_each_entry(sta, &node->sta_info, node_list)
597 usteer_send_sta_info(sta);
598 }
599
600 blob_nest_end(&buf, s);
601
602 blob_nest_end(&buf, c);
603 }
604
605 static void
606 usteer_check_timeout(void)
607 {
608 struct usteer_remote_node *node, *tmp;
609 int timeout = config.remote_node_timeout;
610
611 list_for_each_entry_safe(node, tmp, &remote_nodes, list) {
612 if (config.local_mode || node->check++ > timeout)
613 remote_node_free(node);
614 }
615 }
616
617 static void *
618 usteer_update_init(void)
619 {
620 blob_buf_init(&buf, 0);
621 blob_put_int32(&buf, APMSG_ID, local_id);
622 blob_put_int32(&buf, APMSG_SEQ, ++msg_seq);
623 if (host_info_blob)
624 blob_put(&buf, APMSG_HOST_INFO,
625 blob_data(host_info_blob),
626 blob_len(host_info_blob));
627
628 return blob_nest_start(&buf, APMSG_NODES);
629 }
630
631 static void
632 usteer_update_send(void *c)
633 {
634 struct interface *iface;
635
636 blob_nest_end(&buf, c);
637
638 vlist_for_each_element(&interfaces, iface, node)
639 interface_send_msg(iface, buf.head);
640 }
641
642 void
643 usteer_send_sta_update(struct sta_info *si)
644 {
645 void *c = usteer_update_init();
646 usteer_send_node(si->node, si);
647 usteer_update_send(c);
648 }
649
650 static void
651 usteer_send_update_timer(struct uloop_timeout *t)
652 {
653 struct usteer_node *node;
654 void *c;
655
656 usteer_update_time();
657 uloop_timeout_set(t, config.remote_update_interval);
658
659 if (!config.local_mode &&
660 (!avl_is_empty(&local_nodes) || host_info_blob)) {
661 c = usteer_update_init();
662 for_each_local_node(node)
663 usteer_send_node(node, NULL);
664
665 usteer_update_send(c);
666 }
667 usteer_check_timeout();
668 }
669
670 static int
671 usteer_init_local_id(void)
672 {
673 FILE *f;
674
675 f = fopen("/dev/urandom", "r");
676 if (!f) {
677 perror("fopen(/dev/urandom)");
678 return -1;
679 }
680
681 if (fread(&local_id, sizeof(local_id), 1, f) < 1)
682 return -1;
683
684 fclose(f);
685 return 0;
686 }
687
688 static int usteer_create_v4_socket() {
689 int yes = 1;
690 int fd;
691
692 fd = usock(USOCK_UDP | USOCK_SERVER | USOCK_NONBLOCK |
693 USOCK_NUMERIC | USOCK_IPV4ONLY,
694 "0.0.0.0", APMGR_PORT_STR);
695 if (fd < 0) {
696 perror("usock");
697 return - 1;
698 }
699
700 if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0)
701 perror("setsockopt(IP_PKTINFO)");
702
703 if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes)) < 0)
704 perror("setsockopt(SO_BROADCAST)");
705
706 return fd;
707 }
708
709
710 static int usteer_create_v6_socket() {
711 struct interface *iface;
712 struct ipv6_mreq group;
713 int yes = 1;
714 int fd;
715
716 fd = usock(USOCK_UDP | USOCK_SERVER | USOCK_NONBLOCK |
717 USOCK_NUMERIC | USOCK_IPV6ONLY,
718 "::", APMGR_PORT_STR);
719 if (fd < 0) {
720 perror("usock");
721 return fd;
722 }
723
724 if (!inet_pton(AF_INET6, APMGR_V6_MCAST_GROUP, &group.ipv6mr_multiaddr.s6_addr))
725 perror("inet_pton(AF_INET6)");
726
727 /* Membership has to be added for every interface we listen on. */
728 vlist_for_each_element(&interfaces, iface, node) {
729 group.ipv6mr_interface = iface->ifindex;
730 if(setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&group, sizeof group) < 0)
731 perror("setsockopt(IPV6_ADD_MEMBERSHIP)");
732 }
733
734 if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &yes, sizeof(yes)) < 0)
735 perror("setsockopt(IPV6_RECVPKTINFO)");
736
737 if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes)) < 0)
738 perror("setsockopt(SO_BROADCAST)");
739
740 return fd;
741 }
742
743 static void usteer_reload_timer(struct uloop_timeout *t) {
744 /* Remove uloop descriptor */
745 if (remote_fd.fd && remote_fd.registered) {
746 uloop_fd_delete(&remote_fd);
747 close(remote_fd.fd);
748 }
749
750 if (config.ipv6) {
751 remote_fd.fd = usteer_create_v6_socket();
752 remote_fd.cb = interface_recv_v6;
753 } else {
754 remote_fd.fd = usteer_create_v4_socket();
755 remote_fd.cb = interface_recv_v4;
756 }
757
758 if (remote_fd.fd < 0)
759 return;
760
761 uloop_fd_add(&remote_fd, ULOOP_READ);
762 }
763
764 int usteer_interface_init(void)
765 {
766 if (usteer_init_local_id())
767 return -1;
768
769 remote_timer.cb = usteer_send_update_timer;
770 remote_timer.cb(&remote_timer);
771
772 reload_timer.cb = usteer_reload_timer;
773 reload_timer.cb(&reload_timer);
774
775 return 0;
776 }