]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-netlink/local-addresses.c
tree-wide: use typesafe_qsort()
[thirdparty/systemd.git] / src / libsystemd / sd-netlink / local-addresses.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
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"
8041b5ba 9
93bab288
YW
10static int address_compare(const struct local_address *a, const struct local_address *b) {
11 int r;
5502f0d9
LP
12
13 /* Order lowest scope first, IPv4 before IPv6, lowest interface index first */
14
e9140aff
LP
15 if (a->family == AF_INET && b->family == AF_INET6)
16 return -1;
17 if (a->family == AF_INET6 && b->family == AF_INET)
18 return 1;
19
93bab288
YW
20 r = CMP(a->scope, b->scope);
21 if (r != 0)
22 return r;
5502f0d9 23
93bab288
YW
24 r = CMP(a->metric, b->metric);
25 if (r != 0)
26 return r;
5502f0d9 27
93bab288
YW
28 r = CMP(a->ifindex, b->ifindex);
29 if (r != 0)
30 return r;
5502f0d9 31
00d75e57 32 return memcmp(&a->address, &b->address, FAMILY_ADDRESS_SIZE(a->family));
5502f0d9
LP
33}
34
1c4baffc 35int local_addresses(sd_netlink *context, int ifindex, int af, struct local_address **ret) {
4afd3348
LP
36 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
37 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
e80af1bd 38 _cleanup_free_ struct local_address *list = NULL;
5502f0d9 39 size_t n_list = 0, n_allocated = 0;
1c4baffc 40 sd_netlink_message *m;
d1ca51b1 41 int r;
d73c3269 42
e80af1bd
LP
43 assert(ret);
44
ee8c4568 45 if (context)
1c4baffc 46 rtnl = sd_netlink_ref(context);
ee8c4568 47 else {
1c4baffc 48 r = sd_netlink_open(&rtnl);
ee8c4568
LP
49 if (r < 0)
50 return r;
51 }
8041b5ba 52
1d050e1e 53 r = sd_rtnl_message_new_addr(rtnl, &req, RTM_GETADDR, 0, af);
d1ca51b1
TG
54 if (r < 0)
55 return r;
8041b5ba 56
1c4baffc 57 r = sd_netlink_call(rtnl, req, 0, &reply);
d1ca51b1
TG
58 if (r < 0)
59 return r;
d1ca51b1 60
1c4baffc 61 for (m = reply; m; m = sd_netlink_message_next(m)) {
e80af1bd 62 struct local_address *a;
d1ca51b1 63 unsigned char flags;
5502f0d9 64 uint16_t type;
1d050e1e 65 int ifi, family;
d1ca51b1 66
1c4baffc 67 r = sd_netlink_message_get_errno(m);
d1ca51b1
TG
68 if (r < 0)
69 return r;
70
1c4baffc 71 r = sd_netlink_message_get_type(m, &type);
d1ca51b1
TG
72 if (r < 0)
73 return r;
d1ca51b1 74 if (type != RTM_NEWADDR)
8041b5ba
LP
75 continue;
76
ee8c4568
LP
77 r = sd_rtnl_message_addr_get_ifindex(m, &ifi);
78 if (r < 0)
79 return r;
1d050e1e
LP
80 if (ifindex > 0 && ifi != ifindex)
81 continue;
ee8c4568 82
1d050e1e
LP
83 r = sd_rtnl_message_addr_get_family(m, &family);
84 if (r < 0)
85 return r;
86 if (af != AF_UNSPEC && af != family)
ee8c4568
LP
87 continue;
88
5502f0d9 89 r = sd_rtnl_message_addr_get_flags(m, &flags);
d1ca51b1
TG
90 if (r < 0)
91 return r;
5502f0d9 92 if (flags & IFA_F_DEPRECATED)
d73c3269 93 continue;
8041b5ba 94
e9140aff 95 if (!GREEDY_REALLOC0(list, n_allocated, n_list+1))
5502f0d9
LP
96 return -ENOMEM;
97
98 a = list + n_list;
99
100 r = sd_rtnl_message_addr_get_scope(m, &a->scope);
d1ca51b1
TG
101 if (r < 0)
102 return r;
8041b5ba 103
945c2931 104 if (ifindex == 0 && IN_SET(a->scope, RT_SCOPE_HOST, RT_SCOPE_NOWHERE))
d73c3269 105 continue;
8041b5ba 106
1d050e1e 107 switch (family) {
5502f0d9 108
d1ca51b1 109 case AF_INET:
1c4baffc 110 r = sd_netlink_message_read_in_addr(m, IFA_LOCAL, &a->address.in);
d1ca51b1 111 if (r < 0) {
1c4baffc 112 r = sd_netlink_message_read_in_addr(m, IFA_ADDRESS, &a->address.in);
d1ca51b1
TG
113 if (r < 0)
114 continue;
115 }
116 break;
5502f0d9 117
d1ca51b1 118 case AF_INET6:
1c4baffc 119 r = sd_netlink_message_read_in6_addr(m, IFA_LOCAL, &a->address.in6);
d1ca51b1 120 if (r < 0) {
1c4baffc 121 r = sd_netlink_message_read_in6_addr(m, IFA_ADDRESS, &a->address.in6);
d1ca51b1
TG
122 if (r < 0)
123 continue;
124 }
125 break;
5502f0d9 126
d1ca51b1 127 default:
d73c3269 128 continue;
d73c3269 129 }
8041b5ba 130
ee8c4568 131 a->ifindex = ifi;
1d050e1e 132 a->family = family;
8041b5ba 133
d1ca51b1 134 n_list++;
5502f0d9 135 };
8041b5ba 136
93bab288 137 typesafe_qsort(list, n_list, address_compare);
e9140aff 138
1cc6c93a 139 *ret = TAKE_PTR(list);
e9140aff
LP
140
141 return (int) n_list;
142}
143
1c4baffc 144int local_gateways(sd_netlink *context, int ifindex, int af, struct local_address **ret) {
4afd3348
LP
145 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
146 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
e9140aff 147 _cleanup_free_ struct local_address *list = NULL;
1c4baffc 148 sd_netlink_message *m = NULL;
e9140aff
LP
149 size_t n_list = 0, n_allocated = 0;
150 int r;
151
152 assert(ret);
153
154 if (context)
1c4baffc 155 rtnl = sd_netlink_ref(context);
e9140aff 156 else {
1c4baffc 157 r = sd_netlink_open(&rtnl);
e9140aff
LP
158 if (r < 0)
159 return r;
160 }
161
1d050e1e 162 r = sd_rtnl_message_new_route(rtnl, &req, RTM_GETROUTE, af, RTPROT_UNSPEC);
e9140aff
LP
163 if (r < 0)
164 return r;
165
1c4baffc 166 r = sd_netlink_message_request_dump(req, true);
e9140aff
LP
167 if (r < 0)
168 return r;
169
1c4baffc 170 r = sd_netlink_call(rtnl, req, 0, &reply);
e9140aff
LP
171 if (r < 0)
172 return r;
173
1c4baffc 174 for (m = reply; m; m = sd_netlink_message_next(m)) {
e9140aff
LP
175 struct local_address *a;
176 uint16_t type;
a98433c0 177 unsigned char dst_len, src_len;
e9140aff 178 uint32_t ifi;
1d050e1e 179 int family;
e9140aff 180
1c4baffc 181 r = sd_netlink_message_get_errno(m);
e9140aff
LP
182 if (r < 0)
183 return r;
184
1c4baffc 185 r = sd_netlink_message_get_type(m, &type);
e9140aff
LP
186 if (r < 0)
187 return r;
e9140aff
LP
188 if (type != RTM_NEWROUTE)
189 continue;
190
a98433c0 191 /* We only care for default routes */
584d0d2a 192 r = sd_rtnl_message_route_get_dst_prefixlen(m, &dst_len);
e9140aff
LP
193 if (r < 0)
194 return r;
e9140aff
LP
195 if (dst_len != 0)
196 continue;
197
584d0d2a 198 r = sd_rtnl_message_route_get_src_prefixlen(m, &src_len);
a98433c0
LP
199 if (r < 0)
200 return r;
201 if (src_len != 0)
202 continue;
203
1c4baffc 204 r = sd_netlink_message_read_u32(m, RTA_OIF, &ifi);
568fc5c3
LP
205 if (r == -ENODATA) /* Not all routes have an RTA_OIF attribute (for example nexthop ones) */
206 continue;
e9140aff
LP
207 if (r < 0)
208 return r;
e9140aff
LP
209 if (ifindex > 0 && (int) ifi != ifindex)
210 continue;
211
1d050e1e
LP
212 r = sd_rtnl_message_route_get_family(m, &family);
213 if (r < 0)
214 return r;
215 if (af != AF_UNSPEC && af != family)
216 continue;
217
e9140aff
LP
218 if (!GREEDY_REALLOC0(list, n_allocated, n_list + 1))
219 return -ENOMEM;
220
221 a = list + n_list;
222
1d050e1e 223 switch (family) {
e9140aff 224 case AF_INET:
1c4baffc 225 r = sd_netlink_message_read_in_addr(m, RTA_GATEWAY, &a->address.in);
e9140aff
LP
226 if (r < 0)
227 continue;
228
229 break;
230 case AF_INET6:
1c4baffc 231 r = sd_netlink_message_read_in6_addr(m, RTA_GATEWAY, &a->address.in6);
e9140aff
LP
232 if (r < 0)
233 continue;
234
235 break;
236 default:
237 continue;
238 }
239
1c4baffc 240 sd_netlink_message_read_u32(m, RTA_PRIORITY, &a->metric);
e9140aff
LP
241
242 a->ifindex = ifi;
1d050e1e 243 a->family = family;
e9140aff 244
1d050e1e 245 n_list++;
e9140aff
LP
246 }
247
93bab288 248 typesafe_qsort(list, n_list, address_compare);
d73c3269 249
1cc6c93a 250 *ret = TAKE_PTR(list);
d73c3269 251
e80af1bd 252 return (int) n_list;
8041b5ba 253}