diff options
Diffstat (limited to 'src/fnet/interface.c')
-rw-r--r-- | src/fnet/interface.c | 130 |
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 | ||
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,129 @@ 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 | 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 | |||
397 | static 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 | ||
458 | void 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 | } | ||