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