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