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