diff options
-rw-r--r-- | src/firejail/dhcp.c | 16 | ||||
-rw-r--r-- | src/fnet/fnet.h | 1 | ||||
-rw-r--r-- | src/fnet/interface.c | 122 | ||||
-rw-r--r-- | src/fnet/main.c | 4 |
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 | ||
120 | static void dhcp_waitll(const char *ifname) { | ||
121 | sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 3, PATH_FNET, "waitll", ifname); | ||
122 | } | ||
123 | |||
124 | static 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 | |||
120 | void dhcp_start(void) { | 135 | void 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]); | |||
47 | void net_if_ip(const char *ifname, uint32_t ip, uint32_t mask, int mtu); | 47 | void net_if_ip(const char *ifname, uint32_t ip, uint32_t mask, int mtu); |
48 | int net_if_mac(const char *ifname, const unsigned char mac[6]); | 48 | int net_if_mac(const char *ifname, const unsigned char mac[6]); |
49 | void net_if_ip6(const char *ifname, const char *addr6); | 49 | void net_if_ip6(const char *ifname, const char *addr6); |
50 | void 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 | ||
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 | } | ||
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 | ||
52 | int main(int argc, char **argv) { | 53 | int 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; |