]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/local-addresses.c
Merge pull request #19124 from takaswie/topic/fw-audio-entries
[thirdparty/systemd.git] / src / shared / local-addresses.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
8041b5ba 2
1c4baffc 3#include "sd-netlink.h"
b5efdb8a
LP
4
5#include "alloc-util.h"
e80af1bd 6#include "local-addresses.h"
cf0fbc49
TA
7#include "macro.h"
8#include "netlink-util.h"
760877e9 9#include "sort-util.h"
8041b5ba 10
93bab288
YW
11static int address_compare(const struct local_address *a, const struct local_address *b) {
12 int r;
5502f0d9
LP
13
14 /* Order lowest scope first, IPv4 before IPv6, lowest interface index first */
15
e9140aff
LP
16 if (a->family == AF_INET && b->family == AF_INET6)
17 return -1;
18 if (a->family == AF_INET6 && b->family == AF_INET)
19 return 1;
20
93bab288
YW
21 r = CMP(a->scope, b->scope);
22 if (r != 0)
23 return r;
5502f0d9 24
93bab288
YW
25 r = CMP(a->metric, b->metric);
26 if (r != 0)
27 return r;
5502f0d9 28
93bab288
YW
29 r = CMP(a->ifindex, b->ifindex);
30 if (r != 0)
31 return r;
5502f0d9 32
00d75e57 33 return memcmp(&a->address, &b->address, FAMILY_ADDRESS_SIZE(a->family));
5502f0d9
LP
34}
35
1c4baffc 36int local_addresses(sd_netlink *context, int ifindex, int af, struct local_address **ret) {
4afd3348
LP
37 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
38 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
e80af1bd 39 _cleanup_free_ struct local_address *list = NULL;
5502f0d9 40 size_t n_list = 0, n_allocated = 0;
1c4baffc 41 sd_netlink_message *m;
d1ca51b1 42 int r;
d73c3269 43
ee8c4568 44 if (context)
1c4baffc 45 rtnl = sd_netlink_ref(context);
ee8c4568 46 else {
1c4baffc 47 r = sd_netlink_open(&rtnl);
ee8c4568
LP
48 if (r < 0)
49 return r;
50 }
8041b5ba 51
1d050e1e 52 r = sd_rtnl_message_new_addr(rtnl, &req, RTM_GETADDR, 0, af);
d1ca51b1
TG
53 if (r < 0)
54 return r;
8041b5ba 55
1c4baffc 56 r = sd_netlink_call(rtnl, req, 0, &reply);
d1ca51b1
TG
57 if (r < 0)
58 return r;
d1ca51b1 59
1c4baffc 60 for (m = reply; m; m = sd_netlink_message_next(m)) {
e80af1bd 61 struct local_address *a;
d1ca51b1 62 unsigned char flags;
5502f0d9 63 uint16_t type;
1d050e1e 64 int ifi, family;
d1ca51b1 65
1c4baffc 66 r = sd_netlink_message_get_errno(m);
d1ca51b1
TG
67 if (r < 0)
68 return r;
69
1c4baffc 70 r = sd_netlink_message_get_type(m, &type);
d1ca51b1
TG
71 if (r < 0)
72 return r;
d1ca51b1 73 if (type != RTM_NEWADDR)
8041b5ba
LP
74 continue;
75
ee8c4568
LP
76 r = sd_rtnl_message_addr_get_ifindex(m, &ifi);
77 if (r < 0)
78 return r;
1d050e1e
LP
79 if (ifindex > 0 && ifi != ifindex)
80 continue;
ee8c4568 81
1d050e1e
LP
82 r = sd_rtnl_message_addr_get_family(m, &family);
83 if (r < 0)
84 return r;
85 if (af != AF_UNSPEC && af != family)
ee8c4568
LP
86 continue;
87
5502f0d9 88 r = sd_rtnl_message_addr_get_flags(m, &flags);
d1ca51b1
TG
89 if (r < 0)
90 return r;
5502f0d9 91 if (flags & IFA_F_DEPRECATED)
d73c3269 92 continue;
8041b5ba 93
e9140aff 94 if (!GREEDY_REALLOC0(list, n_allocated, n_list+1))
5502f0d9
LP
95 return -ENOMEM;
96
97 a = list + n_list;
98
99 r = sd_rtnl_message_addr_get_scope(m, &a->scope);
d1ca51b1
TG
100 if (r < 0)
101 return r;
8041b5ba 102
945c2931 103 if (ifindex == 0 && IN_SET(a->scope, RT_SCOPE_HOST, RT_SCOPE_NOWHERE))
d73c3269 104 continue;
8041b5ba 105
1d050e1e 106 switch (family) {
5502f0d9 107
d1ca51b1 108 case AF_INET:
1c4baffc 109 r = sd_netlink_message_read_in_addr(m, IFA_LOCAL, &a->address.in);
d1ca51b1 110 if (r < 0) {
1c4baffc 111 r = sd_netlink_message_read_in_addr(m, IFA_ADDRESS, &a->address.in);
d1ca51b1
TG
112 if (r < 0)
113 continue;
114 }
115 break;
5502f0d9 116
d1ca51b1 117 case AF_INET6:
1c4baffc 118 r = sd_netlink_message_read_in6_addr(m, IFA_LOCAL, &a->address.in6);
d1ca51b1 119 if (r < 0) {
1c4baffc 120 r = sd_netlink_message_read_in6_addr(m, IFA_ADDRESS, &a->address.in6);
d1ca51b1
TG
121 if (r < 0)
122 continue;
123 }
124 break;
5502f0d9 125
d1ca51b1 126 default:
d73c3269 127 continue;
d73c3269 128 }
8041b5ba 129
ee8c4568 130 a->ifindex = ifi;
1d050e1e 131 a->family = family;
8041b5ba 132
d1ca51b1 133 n_list++;
5502f0d9 134 };
8041b5ba 135
c3a8c6aa
LP
136 if (ret) {
137 typesafe_qsort(list, n_list, address_compare);
138 *ret = TAKE_PTR(list);
139 }
e9140aff
LP
140
141 return (int) n_list;
142}
143
bff94a84
YW
144static int add_local_gateway(
145 struct local_address **list,
146 size_t *n_list,
147 size_t *n_allocated,
148 int af,
149 int ifindex,
150 uint32_t metric,
151 const RouteVia *via) {
152
153 assert(list);
154 assert(n_list);
155 assert(n_allocated);
156 assert(via);
157
158 if (af != AF_UNSPEC && af != via->family)
159 return 0;
160
161 if (!GREEDY_REALLOC(*list, *n_allocated, *n_list + 1))
162 return -ENOMEM;
163
164 (*list)[(*n_list)++] = (struct local_address) {
165 .ifindex = ifindex,
166 .metric = metric,
167 .family = via->family,
168 .address = via->address,
169 };
170
171 return 0;
172}
173
1c4baffc 174int local_gateways(sd_netlink *context, int ifindex, int af, struct local_address **ret) {
4afd3348
LP
175 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
176 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
e9140aff 177 _cleanup_free_ struct local_address *list = NULL;
e9140aff
LP
178 size_t n_list = 0, n_allocated = 0;
179 int r;
180
e9140aff 181 if (context)
1c4baffc 182 rtnl = sd_netlink_ref(context);
e9140aff 183 else {
1c4baffc 184 r = sd_netlink_open(&rtnl);
e9140aff
LP
185 if (r < 0)
186 return r;
187 }
188
1d050e1e 189 r = sd_rtnl_message_new_route(rtnl, &req, RTM_GETROUTE, af, RTPROT_UNSPEC);
e9140aff
LP
190 if (r < 0)
191 return r;
192
1c4baffc 193 r = sd_netlink_message_request_dump(req, true);
e9140aff
LP
194 if (r < 0)
195 return r;
196
1c4baffc 197 r = sd_netlink_call(rtnl, req, 0, &reply);
e9140aff
LP
198 if (r < 0)
199 return r;
200
bff94a84
YW
201 for (sd_netlink_message *m = reply; m; m = sd_netlink_message_next(m)) {
202 _cleanup_ordered_set_free_free_ OrderedSet *multipath_routes = NULL;
203 _cleanup_free_ void *rta_multipath = NULL;
204 union in_addr_union gateway;
e9140aff 205 uint16_t type;
d1b014df 206 unsigned char dst_len, src_len, table;
d2f4a948 207 uint32_t ifi = 0, metric = 0;
bff94a84 208 size_t rta_len;
1d050e1e 209 int family;
bff94a84 210 RouteVia via;
e9140aff 211
1c4baffc 212 r = sd_netlink_message_get_errno(m);
e9140aff
LP
213 if (r < 0)
214 return r;
215
1c4baffc 216 r = sd_netlink_message_get_type(m, &type);
e9140aff
LP
217 if (r < 0)
218 return r;
e9140aff
LP
219 if (type != RTM_NEWROUTE)
220 continue;
221
a98433c0 222 /* We only care for default routes */
584d0d2a 223 r = sd_rtnl_message_route_get_dst_prefixlen(m, &dst_len);
e9140aff
LP
224 if (r < 0)
225 return r;
e9140aff
LP
226 if (dst_len != 0)
227 continue;
228
584d0d2a 229 r = sd_rtnl_message_route_get_src_prefixlen(m, &src_len);
a98433c0
LP
230 if (r < 0)
231 return r;
232 if (src_len != 0)
233 continue;
234
d1b014df
LP
235 r = sd_rtnl_message_route_get_table(m, &table);
236 if (r < 0)
237 return r;
238 if (table != RT_TABLE_MAIN)
239 continue;
240
bff94a84
YW
241 r = sd_netlink_message_read_u32(m, RTA_PRIORITY, &metric);
242 if (r < 0 && r != -ENODATA)
e9140aff 243 return r;
e9140aff 244
1d050e1e
LP
245 r = sd_rtnl_message_route_get_family(m, &family);
246 if (r < 0)
247 return r;
bff94a84 248 if (!IN_SET(family, AF_INET, AF_INET6))
1d050e1e
LP
249 continue;
250
bff94a84
YW
251 r = sd_netlink_message_read_u32(m, RTA_OIF, &ifi);
252 if (r < 0 && r != -ENODATA)
253 return r;
254 if (r >= 0) {
255 if (ifi <= 0)
256 return -EINVAL;
257 if (ifindex > 0 && (int) ifi != ifindex)
258 continue;
e9140aff 259
bff94a84
YW
260 r = netlink_message_read_in_addr_union(m, RTA_GATEWAY, family, &gateway);
261 if (r < 0 && r != -ENODATA)
262 return r;
263 if (r >= 0) {
264 via.family = family;
265 via.address = gateway;
266 r = add_local_gateway(&list, &n_list, &n_allocated, af, ifi, metric, &via);
267 if (r < 0)
268 return r;
e9140aff 269
e9140aff 270 continue;
bff94a84 271 }
e9140aff 272
bff94a84 273 if (family != AF_INET)
e9140aff
LP
274 continue;
275
bff94a84
YW
276 r = sd_netlink_message_read(m, RTA_VIA, sizeof(via), &via);
277 if (r < 0 && r != -ENODATA)
278 return r;
279 if (r >= 0) {
280 r = add_local_gateway(&list, &n_list, &n_allocated, af, ifi, metric, &via);
281 if (r < 0)
282 return r;
283
284 continue;
285 }
e9140aff
LP
286 }
287
bff94a84
YW
288 r = sd_netlink_message_read_data(m, RTA_MULTIPATH, &rta_len, &rta_multipath);
289 if (r < 0 && r != -ENODATA)
290 return r;
291 if (r >= 0) {
292 MultipathRoute *mr;
e9140aff 293
bff94a84
YW
294 r = rtattr_read_nexthop(rta_multipath, rta_len, family, &multipath_routes);
295 if (r < 0)
296 return r;
e9140aff 297
bff94a84
YW
298 ORDERED_SET_FOREACH(mr, multipath_routes) {
299 if (ifindex > 0 && mr->ifindex != ifindex)
300 continue;
301
302 r = add_local_gateway(&list, &n_list, &n_allocated, af, ifi, metric, &mr->gateway);
303 if (r < 0)
304 return r;
305 }
306 }
e9140aff
LP
307 }
308
c3a8c6aa
LP
309 if (ret) {
310 typesafe_qsort(list, n_list, address_compare);
311 *ret = TAKE_PTR(list);
312 }
d73c3269 313
e80af1bd 314 return (int) n_list;
8041b5ba 315}