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