xref: /freebsd/sbin/dhclient/inet6.c (revision 37fd3fcaaf0ee3e1066ef9d4fb8b9245f7288743)
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
check_ipv6_address(struct snl_state * ss,uint16_t ifindex)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
check_ipv6_defaultroute(struct snl_state * ss)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
check_ipv6_connectivity(uint16_t ifindex __unused)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