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