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