aboutsummaryrefslogtreecommitdiffstats
path: root/src/fnet/interface.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fnet/interface.c')
-rw-r--r--src/fnet/interface.c122
1 files changed, 122 insertions, 0 deletions
diff --git a/src/fnet/interface.c b/src/fnet/interface.c
index 7e7cceeed..1c07ec8f7 100644
--- a/src/fnet/interface.c
+++ b/src/fnet/interface.c
@@ -28,6 +28,8 @@
28#include <net/if_arp.h> 28#include <net/if_arp.h>
29#include <net/route.h> 29#include <net/route.h>
30#include <linux/if_bridge.h> 30#include <linux/if_bridge.h>
31#include <linux/netlink.h>
32#include <linux/rtnetlink.h>
31 33
32static void check_if_name(const char *ifname) { 34static void check_if_name(const char *ifname) {
33 if (strlen(ifname) > IFNAMSIZ) { 35 if (strlen(ifname) > IFNAMSIZ) {
@@ -370,3 +372,123 @@ void net_if_ip6(const char *ifname, const char *addr6) {
370 372
371 close(sock); 373 close(sock);
372} 374}
375
376static int net_netlink_address_tentative(struct nlmsghdr *current_header) {
377 struct ifaddrmsg *msg = NLMSG_DATA(current_header);
378 struct rtattr *rta = IFA_RTA(msg);
379 size_t msg_len = IFA_PAYLOAD(current_header);
380 int has_flags = 0;
381 while (RTA_OK(rta, msg_len)) {
382 if (rta->rta_type == IFA_FLAGS) {
383 has_flags = 1;
384 uint32_t *flags = RTA_DATA(rta);
385 if (*flags & IFA_F_TENTATIVE)
386 return 1;
387 }
388 rta = RTA_NEXT(rta, msg_len);
389 }
390 // According to <linux/if_addr.h>, if an IFA_FLAGS attribute is present,
391 // the field ifa_flags should be ignored.
392 return !has_flags && (msg->ifa_flags & IFA_F_TENTATIVE);
393}
394
395static int net_netlink_if_has_ll(int sock, int index) {
396 struct {
397 struct nlmsghdr header;
398 struct ifaddrmsg message;
399 } req;
400 memset(&req, 0, sizeof(req));
401 req.header.nlmsg_len = NLMSG_LENGTH(sizeof(req.message));
402 req.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
403 req.header.nlmsg_type = RTM_GETADDR;
404 req.message.ifa_family = AF_INET6;
405 if (send(sock, &req, req.header.nlmsg_len, 0) != req.header.nlmsg_len)
406 errExit("send");
407
408 int found = 0;
409 int all_parts_processed = 0;
410 while (!all_parts_processed) {
411 char buf[16384];
412 ssize_t len = recv(sock, buf, sizeof(buf), 0);
413 if (len < 0)
414 errExit("recv");
415 if (len < sizeof(struct nlmsghdr)) {
416 fprintf(stderr, "Received incomplete netlink message\n");
417 exit(1);
418 }
419
420 struct nlmsghdr *current_header = (struct nlmsghdr *) buf;
421 while (NLMSG_OK(current_header, len)) {
422 switch (current_header->nlmsg_type) {
423 case RTM_NEWADDR: {
424 struct ifaddrmsg *msg = NLMSG_DATA(current_header);
425 if (!found && msg->ifa_index == index && msg->ifa_scope == RT_SCOPE_LINK &&
426 !net_netlink_address_tentative(current_header))
427 found = 1;
428 }
429 break;
430 case NLMSG_NOOP:
431 break;
432 case NLMSG_DONE:
433 all_parts_processed = 1;
434 break;
435 case NLMSG_ERROR: {
436 struct nlmsgerr *err = NLMSG_DATA(current_header);
437 fprintf(stderr, "Netlink error: %d\n", err->error);
438 exit(1);
439 }
440 break;
441 default:
442 fprintf(stderr, "Unknown netlink message type: %u\n", current_header->nlmsg_type);
443 exit(1);
444 break;
445 }
446
447 current_header = NLMSG_NEXT(current_header, len);
448 }
449 }
450
451 return found;
452}
453
454// wait for a link-local IPv6 address for DHCPv6
455// ex: firejail --net=br0 --ip6=dhcp
456void net_if_waitll(const char *ifname) {
457 // find interface index
458 int inet6_sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
459 if (inet6_sock < 0) {
460 fprintf(stderr, "Error fnet: IPv6 is not supported on this system\n");
461 exit(1);
462 }
463 struct ifreq ifr;
464 memset(&ifr, 0, sizeof(ifr));
465 strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1);
466 ifr.ifr_addr.sa_family = AF_INET;
467 if (ioctl(inet6_sock, SIOGIFINDEX, &ifr) < 0) {
468 perror("ioctl SIOGIFINDEX");
469 exit(1);
470 }
471 close(inet6_sock);
472 int index = ifr.ifr_ifindex;
473
474 // poll for link-local address
475 int netlink_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
476 if (netlink_sock < 0)
477 errExit("socket");
478 int tries = 0;
479 int found = 0;
480 while (tries < 60 && !found) {
481 if (tries >= 1)
482 usleep(500000);
483
484 found = net_netlink_if_has_ll(netlink_sock, index);
485
486 tries++;
487 }
488 close(netlink_sock);
489
490 if (!found) {
491 fprintf(stderr, "Waiting for link-local IPv6 address of %s timed out\n", ifname);
492 exit(1);
493 }
494}