aboutsummaryrefslogtreecommitdiffstats
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
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.
-rw-r--r--src/firejail/dhcp.c16
-rw-r--r--src/fnet/fnet.h1
-rw-r--r--src/fnet/interface.c122
-rw-r--r--src/fnet/main.c4
4 files changed, 143 insertions, 0 deletions
diff --git a/src/firejail/dhcp.c b/src/firejail/dhcp.c
index c9bbb4d8f..7ce9a2b18 100644
--- a/src/firejail/dhcp.c
+++ b/src/firejail/dhcp.c
@@ -117,6 +117,21 @@ static void dhcp_start_dhclient(const Dhclient *client) {
117 *(client->pid) = dhcp_read_pidfile(client); 117 *(client->pid) = dhcp_read_pidfile(client);
118} 118}
119 119
120static void dhcp_waitll(const char *ifname) {
121 sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 3, PATH_FNET, "waitll", ifname);
122}
123
124static void dhcp_waitll_all() {
125 if (cfg.bridge0.arg_ip6_dhcp)
126 dhcp_waitll(cfg.bridge0.devsandbox);
127 if (cfg.bridge1.arg_ip6_dhcp)
128 dhcp_waitll(cfg.bridge1.devsandbox);
129 if (cfg.bridge2.arg_ip6_dhcp)
130 dhcp_waitll(cfg.bridge2.devsandbox);
131 if (cfg.bridge3.arg_ip6_dhcp)
132 dhcp_waitll(cfg.bridge3.devsandbox);
133}
134
120void dhcp_start(void) { 135void dhcp_start(void) {
121 if (!any_dhcp()) 136 if (!any_dhcp())
122 return; 137 return;
@@ -131,6 +146,7 @@ void dhcp_start(void) {
131 printf("Running dhclient -4 in the background as pid %ld\n", (long) dhclient4_pid); 146 printf("Running dhclient -4 in the background as pid %ld\n", (long) dhclient4_pid);
132 } 147 }
133 if (any_ip6_dhcp()) { 148 if (any_ip6_dhcp()) {
149 dhcp_waitll_all();
134 dhcp_start_dhclient(&dhclient6); 150 dhcp_start_dhclient(&dhclient6);
135 if (arg_debug) 151 if (arg_debug)
136 printf("Running dhclient -6 in the background as pid %ld\n", (long) dhclient6_pid); 152 printf("Running dhclient -6 in the background as pid %ld\n", (long) dhclient6_pid);
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;