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