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