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