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