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