aboutsummaryrefslogtreecommitdiffstats
path: root/src/fnet
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kris7topher@gmail.com>2019-12-30 20:56:03 +0100
committerLibravatar Kristóf Marussy <kris7topher@gmail.com>2020-01-01 03:40:19 +0100
commitc082d90be6396149404704e127f10ec7c9aa79ad (patch)
tree2301e5c31f352d7d1465dcb7d268cb3c55e4a879 /src/fnet
parentRun dhclient inside the sandbox (diff)
downloadfirejail-c082d90be6396149404704e127f10ec7c9aa79ad.tar.gz
firejail-c082d90be6396149404704e127f10ec7c9aa79ad.tar.zst
firejail-c082d90be6396149404704e127f10ec7c9aa79ad.zip
Wait for link-local address for DHCPv6
dhclient -6 fails if the interface to be configures has no link-local address. This is especially problematic when only DHCPv6 is used (e.g., --ip=none --ip6=dhcp), because the wait for a DHCPv4 lease is usually ample time for the LL address to become available on the IPv6 link. The LL address must not be tenative. Therefore, this patch implements waiting for a non-tentative link-local address in fnet for DHCPv6 configured interfaces. The command fnet waitll <if> waits for an LL address on the interface <if>. Currently, the maximum waiting time is 30 seconds, and the kernel is polled through rtnetlink every 500 milliseconds. These values seem sufficient for virtual bridged networks, e.g., libvirt NAT networks.
Diffstat (limited to 'src/fnet')
-rw-r--r--src/fnet/fnet.h1
-rw-r--r--src/fnet/interface.c122
-rw-r--r--src/fnet/main.c4
3 files changed, 127 insertions, 0 deletions
diff --git a/src/fnet/fnet.h b/src/fnet/fnet.h
index 4900967f7..4d0d62b39 100644
--- a/src/fnet/fnet.h
+++ b/src/fnet/fnet.h
@@ -47,6 +47,7 @@ int net_get_mac(const char *ifname, unsigned char mac[6]);
47void net_if_ip(const char *ifname, uint32_t ip, uint32_t mask, int mtu); 47void net_if_ip(const char *ifname, uint32_t ip, uint32_t mask, int mtu);
48int net_if_mac(const char *ifname, const unsigned char mac[6]); 48int net_if_mac(const char *ifname, const unsigned char mac[6]);
49void net_if_ip6(const char *ifname, const char *addr6); 49void net_if_ip6(const char *ifname, const char *addr6);
50void net_if_waitll(const char *ifname);
50 51
51 52
52// arp.c 53// arp.c
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}
diff --git a/src/fnet/main.c b/src/fnet/main.c
index 890f842f6..3ef500b5e 100644
--- a/src/fnet/main.c
+++ b/src/fnet/main.c
@@ -47,6 +47,7 @@ static void usage(void) {
47 printf("\tfnet config mac addr\n"); 47 printf("\tfnet config mac addr\n");
48 printf("\tfnet config ipv6 dev ip\n"); 48 printf("\tfnet config ipv6 dev ip\n");
49 printf("\tfnet ifup dev\n"); 49 printf("\tfnet ifup dev\n");
50 printf("\tfnet waitll dev\n");
50} 51}
51 52
52int main(int argc, char **argv) { 53int main(int argc, char **argv) {
@@ -141,6 +142,9 @@ printf("\n");
141 else if (argc == 5 && strcmp(argv[1], "config") == 0 && strcmp(argv[2], "ipv6") == 0) { 142 else if (argc == 5 && strcmp(argv[1], "config") == 0 && strcmp(argv[2], "ipv6") == 0) {
142 net_if_ip6(argv[3], argv[4]); 143 net_if_ip6(argv[3], argv[4]);
143 } 144 }
145 else if (argc == 3 && strcmp(argv[1], "waitll") == 0) {
146 net_if_waitll(argv[2]);
147 }
144 else { 148 else {
145 fprintf(stderr, "Error fnet: invalid arguments\n"); 149 fprintf(stderr, "Error fnet: invalid arguments\n");
146 return 1; 150 return 1;