diff options
author | Kristóf Marussy <kris7topher@gmail.com> | 2019-12-30 20:56:03 +0100 |
---|---|---|
committer | Kristóf Marussy <kris7topher@gmail.com> | 2020-01-01 03:40:19 +0100 |
commit | c082d90be6396149404704e127f10ec7c9aa79ad (patch) | |
tree | 2301e5c31f352d7d1465dcb7d268cb3c55e4a879 /src/fnet/interface.c | |
parent | Run dhclient inside the sandbox (diff) | |
download | firejail-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/interface.c')
-rw-r--r-- | src/fnet/interface.c | 122 |
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 | ||
32 | static void check_if_name(const char *ifname) { | 34 | static 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 | |||
376 | static 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 | |||
395 | static 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 | ||
456 | void 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 | } | ||