1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "sd-netlink.h"
5 #include "alloc-util.h"
6 #include "local-addresses.h"
8 #include "netlink-util.h"
11 static int address_compare(const struct local_address
*a
, const struct local_address
*b
) {
14 /* Order lowest scope first, IPv4 before IPv6, lowest interface index first */
16 if (a
->family
== AF_INET
&& b
->family
== AF_INET6
)
18 if (a
->family
== AF_INET6
&& b
->family
== AF_INET
)
21 r
= CMP(a
->scope
, b
->scope
);
25 r
= CMP(a
->metric
, b
->metric
);
29 r
= CMP(a
->ifindex
, b
->ifindex
);
33 return memcmp(&a
->address
, &b
->address
, FAMILY_ADDRESS_SIZE(a
->family
));
36 int local_addresses(sd_netlink
*context
, int ifindex
, int af
, struct local_address
**ret
) {
37 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
, *reply
= NULL
;
38 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
39 _cleanup_free_
struct local_address
*list
= NULL
;
40 size_t n_list
= 0, n_allocated
= 0;
41 sd_netlink_message
*m
;
47 rtnl
= sd_netlink_ref(context
);
49 r
= sd_netlink_open(&rtnl
);
54 r
= sd_rtnl_message_new_addr(rtnl
, &req
, RTM_GETADDR
, 0, af
);
58 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
62 for (m
= reply
; m
; m
= sd_netlink_message_next(m
)) {
63 struct local_address
*a
;
68 r
= sd_netlink_message_get_errno(m
);
72 r
= sd_netlink_message_get_type(m
, &type
);
75 if (type
!= RTM_NEWADDR
)
78 r
= sd_rtnl_message_addr_get_ifindex(m
, &ifi
);
81 if (ifindex
> 0 && ifi
!= ifindex
)
84 r
= sd_rtnl_message_addr_get_family(m
, &family
);
87 if (af
!= AF_UNSPEC
&& af
!= family
)
90 r
= sd_rtnl_message_addr_get_flags(m
, &flags
);
93 if (flags
& IFA_F_DEPRECATED
)
96 if (!GREEDY_REALLOC0(list
, n_allocated
, n_list
+1))
101 r
= sd_rtnl_message_addr_get_scope(m
, &a
->scope
);
105 if (ifindex
== 0 && IN_SET(a
->scope
, RT_SCOPE_HOST
, RT_SCOPE_NOWHERE
))
111 r
= sd_netlink_message_read_in_addr(m
, IFA_LOCAL
, &a
->address
.in
);
113 r
= sd_netlink_message_read_in_addr(m
, IFA_ADDRESS
, &a
->address
.in
);
120 r
= sd_netlink_message_read_in6_addr(m
, IFA_LOCAL
, &a
->address
.in6
);
122 r
= sd_netlink_message_read_in6_addr(m
, IFA_ADDRESS
, &a
->address
.in6
);
138 typesafe_qsort(list
, n_list
, address_compare
);
140 *ret
= TAKE_PTR(list
);
145 static int add_local_gateway(
146 struct local_address
**list
,
152 const RouteVia
*via
) {
159 if (af
!= AF_UNSPEC
&& af
!= via
->family
)
162 if (!GREEDY_REALLOC(*list
, *n_allocated
, *n_list
+ 1))
165 (*list
)[(*n_list
)++] = (struct local_address
) {
168 .family
= via
->family
,
169 .address
= via
->address
,
175 int local_gateways(sd_netlink
*context
, int ifindex
, int af
, struct local_address
**ret
) {
176 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
, *reply
= NULL
;
177 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
178 _cleanup_free_
struct local_address
*list
= NULL
;
179 size_t n_list
= 0, n_allocated
= 0;
185 rtnl
= sd_netlink_ref(context
);
187 r
= sd_netlink_open(&rtnl
);
192 r
= sd_rtnl_message_new_route(rtnl
, &req
, RTM_GETROUTE
, af
, RTPROT_UNSPEC
);
196 r
= sd_netlink_message_request_dump(req
, true);
200 r
= sd_netlink_call(rtnl
, req
, 0, &reply
);
204 for (sd_netlink_message
*m
= reply
; m
; m
= sd_netlink_message_next(m
)) {
205 _cleanup_ordered_set_free_free_ OrderedSet
*multipath_routes
= NULL
;
206 _cleanup_free_
void *rta_multipath
= NULL
;
207 union in_addr_union gateway
;
209 unsigned char dst_len
, src_len
, table
;
210 uint32_t ifi
, metric
= 0;
215 r
= sd_netlink_message_get_errno(m
);
219 r
= sd_netlink_message_get_type(m
, &type
);
222 if (type
!= RTM_NEWROUTE
)
225 /* We only care for default routes */
226 r
= sd_rtnl_message_route_get_dst_prefixlen(m
, &dst_len
);
232 r
= sd_rtnl_message_route_get_src_prefixlen(m
, &src_len
);
238 r
= sd_rtnl_message_route_get_table(m
, &table
);
241 if (table
!= RT_TABLE_MAIN
)
244 r
= sd_netlink_message_read_u32(m
, RTA_PRIORITY
, &metric
);
245 if (r
< 0 && r
!= -ENODATA
)
248 r
= sd_rtnl_message_route_get_family(m
, &family
);
251 if (!IN_SET(family
, AF_INET
, AF_INET6
))
254 r
= sd_netlink_message_read_u32(m
, RTA_OIF
, &ifi
);
255 if (r
< 0 && r
!= -ENODATA
)
260 if (ifindex
> 0 && (int) ifi
!= ifindex
)
263 r
= netlink_message_read_in_addr_union(m
, RTA_GATEWAY
, family
, &gateway
);
264 if (r
< 0 && r
!= -ENODATA
)
268 via
.address
= gateway
;
269 r
= add_local_gateway(&list
, &n_list
, &n_allocated
, af
, ifi
, metric
, &via
);
276 if (family
!= AF_INET
)
279 r
= sd_netlink_message_read(m
, RTA_VIA
, sizeof(via
), &via
);
280 if (r
< 0 && r
!= -ENODATA
)
283 r
= add_local_gateway(&list
, &n_list
, &n_allocated
, af
, ifi
, metric
, &via
);
291 r
= sd_netlink_message_read_data(m
, RTA_MULTIPATH
, &rta_len
, &rta_multipath
);
292 if (r
< 0 && r
!= -ENODATA
)
297 r
= rtattr_read_nexthop(rta_multipath
, rta_len
, family
, &multipath_routes
);
301 ORDERED_SET_FOREACH(mr
, multipath_routes
) {
302 if (ifindex
> 0 && mr
->ifindex
!= ifindex
)
305 r
= add_local_gateway(&list
, &n_list
, &n_allocated
, af
, ifi
, metric
, &mr
->gateway
);
312 typesafe_qsort(list
, n_list
, address_compare
);
314 *ret
= TAKE_PTR(list
);