1 /* 2 * Copyright (c) 2026 Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7 #include <sys/param.h> 8 #include <sys/ioctl.h> 9 #include <sys/socket.h> 10 11 #ifndef WITHOUT_NETLINK 12 #include <netlink/netlink.h> 13 #include <netlink/netlink_route.h> 14 #include <netlink/netlink_snl.h> 15 #include <netlink/netlink_snl_route.h> 16 #include <netlink/netlink_snl_route_compat.h> 17 #include <netlink/netlink_snl_route_parsers.h> 18 #endif 19 20 #include "dhcpd.h" 21 22 #ifndef WITHOUT_NETLINK 23 /* 24 * Check if the interface has a routable IPv6 address, if true 25 * assume it has IPv6 connectivity. 26 * Returns true if a unicast IPv6 address with non-link-local scope 27 * is found, false otherwise. 28 */ 29 static bool 30 check_ipv6_address(struct snl_state *ss, uint16_t ifindex) 31 { 32 struct snl_writer nw; 33 struct snl_errmsg_data e = {}; 34 struct snl_parsed_addr addr = {}; 35 struct nlmsghdr *hdr, *rx_hdr; 36 struct ifaddrmsg *ifahdr; 37 38 snl_init_writer(ss, &nw); 39 hdr = snl_create_msg_request(&nw, RTM_GETADDR); 40 hdr->nlmsg_flags |= NLM_F_DUMP; 41 ifahdr = snl_reserve_msg_object(&nw, struct ifaddrmsg); 42 ifahdr->ifa_family = AF_INET6; 43 ifahdr->ifa_index = ifindex; 44 45 if ((hdr = snl_finalize_msg(&nw)) == NULL || !snl_send_message(ss, hdr)) 46 return (false); 47 48 while ((rx_hdr = snl_read_reply_multi(ss, hdr->nlmsg_seq, &e)) != NULL) { 49 struct sockaddr_in6 *sin6; 50 51 if (!snl_parse_nlmsg(ss, rx_hdr, &snl_rtm_addr_parser, &addr)) 52 continue; 53 54 if (addr.ifa_address != NULL) { 55 sin6 = (struct sockaddr_in6 *)addr.ifa_address; 56 if (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) 57 return (true); 58 } 59 } 60 return (false); 61 } 62 63 /* 64 * If a default route is found, assume IPv6 connectivity. 65 * Returns true if found, false otherwise. 66 */ 67 static bool 68 check_ipv6_defaultroute(struct snl_state *ss) 69 { 70 struct snl_writer nw; 71 struct snl_parsed_route r = {}; 72 struct in6_addr in6 = IN6ADDR_ANY_INIT; 73 struct nlmsghdr *hdr, *rx_hdr; 74 struct rtmsg *rtmsg; 75 76 snl_init_writer(ss, &nw); 77 hdr = snl_create_msg_request(&nw, RTM_GETROUTE); 78 rtmsg = snl_reserve_msg_object(&nw, struct rtmsg); 79 rtmsg->rtm_family = AF_INET6; 80 rtmsg->rtm_dst_len = 0; 81 rtmsg->rtm_type = RTN_UNICAST; 82 rtmsg->rtm_flags = RTM_F_PREFIX; 83 snl_add_msg_attr_ip6(&nw, RTA_DST, &in6); 84 85 if ((hdr = snl_finalize_msg(&nw)) == NULL || !snl_send_message(ss, hdr)) 86 return (false); 87 88 rx_hdr = snl_read_reply(ss, hdr->nlmsg_seq); 89 if (rx_hdr == NULL || rx_hdr->nlmsg_type != NL_RTM_NEWROUTE) 90 return (false); 91 92 if (!snl_parse_nlmsg(ss, rx_hdr, &snl_rtm_route_parser, &r)) 93 return (false); 94 95 if (r.rta_gw == NULL) 96 return (false); 97 98 return (true); 99 } 100 #endif 101 102 bool 103 check_ipv6_connectivity(uint16_t ifindex __unused) 104 { 105 bool ret = false; 106 107 #ifndef WITHOUT_NETLINK 108 /* Return true if the interface has a routable ipv6 address */ 109 ret = check_ipv6_address(&nl_ss, ifindex); 110 111 /* A default route using a link-local address may exist. */ 112 if (!ret) 113 ret = check_ipv6_defaultroute(&nl_ss); 114 snl_clear_lb(&nl_ss); 115 #endif 116 117 return (ret); 118 } 119