Line data Source code
1 : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2 :
3 : /***
4 : This file is part of systemd.
5 :
6 : Copyright 2008-2011 Lennart Poettering
7 : Copyright 2014 Tom Gundersen
8 :
9 : systemd is free software; you can redistribute it and/or modify it
10 : under the terms of the GNU Lesser General Public License as published by
11 : the Free Software Foundation; either version 2.1 of the License, or
12 : (at your option) any later version.
13 :
14 : systemd is distributed in the hope that it will be useful, but
15 : WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 : Lesser General Public License for more details.
18 :
19 : You should have received a copy of the GNU Lesser General Public License
20 : along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 : ***/
22 :
23 : #include "sd-netlink.h"
24 : #include "netlink-util.h"
25 : #include "macro.h"
26 : #include "local-addresses.h"
27 :
28 4 : static int address_compare(const void *_a, const void *_b) {
29 4 : const struct local_address *a = _a, *b = _b;
30 :
31 : /* Order lowest scope first, IPv4 before IPv6, lowest interface index first */
32 :
33 4 : if (a->family == AF_INET && b->family == AF_INET6)
34 0 : return -1;
35 4 : if (a->family == AF_INET6 && b->family == AF_INET)
36 2 : return 1;
37 :
38 2 : if (a->scope < b->scope)
39 0 : return -1;
40 2 : if (a->scope > b->scope)
41 1 : return 1;
42 :
43 1 : if (a->metric < b->metric)
44 0 : return -1;
45 1 : if (a->metric > b->metric)
46 0 : return 1;
47 :
48 1 : if (a->ifindex < b->ifindex)
49 0 : return -1;
50 1 : if (a->ifindex > b->ifindex)
51 1 : return 1;
52 :
53 0 : return memcmp(&a->address, &b->address, FAMILY_ADDRESS_SIZE(a->family));
54 : }
55 :
56 1 : int local_addresses(sd_netlink *context, int ifindex, int af, struct local_address **ret) {
57 2 : _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
58 2 : _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
59 2 : _cleanup_free_ struct local_address *list = NULL;
60 1 : size_t n_list = 0, n_allocated = 0;
61 : sd_netlink_message *m;
62 : int r;
63 :
64 1 : assert(ret);
65 :
66 1 : if (context)
67 0 : rtnl = sd_netlink_ref(context);
68 : else {
69 1 : r = sd_netlink_open(&rtnl);
70 1 : if (r < 0)
71 0 : return r;
72 : }
73 :
74 1 : r = sd_rtnl_message_new_addr(rtnl, &req, RTM_GETADDR, 0, af);
75 1 : if (r < 0)
76 0 : return r;
77 :
78 1 : r = sd_netlink_call(rtnl, req, 0, &reply);
79 1 : if (r < 0)
80 0 : return r;
81 :
82 7 : for (m = reply; m; m = sd_netlink_message_next(m)) {
83 : struct local_address *a;
84 : unsigned char flags;
85 : uint16_t type;
86 : int ifi, family;
87 :
88 6 : r = sd_netlink_message_get_errno(m);
89 6 : if (r < 0)
90 0 : return r;
91 :
92 6 : r = sd_netlink_message_get_type(m, &type);
93 6 : if (r < 0)
94 0 : return r;
95 6 : if (type != RTM_NEWADDR)
96 2 : continue;
97 :
98 6 : r = sd_rtnl_message_addr_get_ifindex(m, &ifi);
99 6 : if (r < 0)
100 0 : return r;
101 6 : if (ifindex > 0 && ifi != ifindex)
102 0 : continue;
103 :
104 6 : r = sd_rtnl_message_addr_get_family(m, &family);
105 6 : if (r < 0)
106 0 : return r;
107 6 : if (af != AF_UNSPEC && af != family)
108 0 : continue;
109 :
110 6 : r = sd_rtnl_message_addr_get_flags(m, &flags);
111 6 : if (r < 0)
112 0 : return r;
113 6 : if (flags & IFA_F_DEPRECATED)
114 0 : continue;
115 :
116 6 : if (!GREEDY_REALLOC0(list, n_allocated, n_list+1))
117 0 : return -ENOMEM;
118 :
119 6 : a = list + n_list;
120 :
121 6 : r = sd_rtnl_message_addr_get_scope(m, &a->scope);
122 6 : if (r < 0)
123 0 : return r;
124 :
125 6 : if (ifindex == 0 && (a->scope == RT_SCOPE_HOST || a->scope == RT_SCOPE_NOWHERE))
126 2 : continue;
127 :
128 4 : switch (family) {
129 :
130 : case AF_INET:
131 2 : r = sd_netlink_message_read_in_addr(m, IFA_LOCAL, &a->address.in);
132 2 : if (r < 0) {
133 0 : r = sd_netlink_message_read_in_addr(m, IFA_ADDRESS, &a->address.in);
134 0 : if (r < 0)
135 0 : continue;
136 : }
137 2 : break;
138 :
139 : case AF_INET6:
140 2 : r = sd_netlink_message_read_in6_addr(m, IFA_LOCAL, &a->address.in6);
141 2 : if (r < 0) {
142 2 : r = sd_netlink_message_read_in6_addr(m, IFA_ADDRESS, &a->address.in6);
143 2 : if (r < 0)
144 0 : continue;
145 : }
146 2 : break;
147 :
148 : default:
149 0 : continue;
150 : }
151 :
152 4 : a->ifindex = ifi;
153 4 : a->family = family;
154 :
155 4 : n_list++;
156 : };
157 :
158 1 : if (n_list > 0)
159 1 : qsort(list, n_list, sizeof(struct local_address), address_compare);
160 :
161 1 : *ret = list;
162 1 : list = NULL;
163 :
164 1 : return (int) n_list;
165 : }
166 :
167 1 : int local_gateways(sd_netlink *context, int ifindex, int af, struct local_address **ret) {
168 2 : _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
169 2 : _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
170 2 : _cleanup_free_ struct local_address *list = NULL;
171 1 : sd_netlink_message *m = NULL;
172 1 : size_t n_list = 0, n_allocated = 0;
173 : int r;
174 :
175 1 : assert(ret);
176 :
177 1 : if (context)
178 0 : rtnl = sd_netlink_ref(context);
179 : else {
180 1 : r = sd_netlink_open(&rtnl);
181 1 : if (r < 0)
182 0 : return r;
183 : }
184 :
185 1 : r = sd_rtnl_message_new_route(rtnl, &req, RTM_GETROUTE, af, RTPROT_UNSPEC);
186 1 : if (r < 0)
187 0 : return r;
188 :
189 1 : r = sd_netlink_message_request_dump(req, true);
190 1 : if (r < 0)
191 0 : return r;
192 :
193 1 : r = sd_netlink_call(rtnl, req, 0, &reply);
194 1 : if (r < 0)
195 0 : return r;
196 :
197 23 : for (m = reply; m; m = sd_netlink_message_next(m)) {
198 : struct local_address *a;
199 : uint16_t type;
200 : unsigned char dst_len, src_len;
201 : uint32_t ifi;
202 : int family;
203 :
204 22 : r = sd_netlink_message_get_errno(m);
205 22 : if (r < 0)
206 0 : return r;
207 :
208 22 : r = sd_netlink_message_get_type(m, &type);
209 22 : if (r < 0)
210 0 : return r;
211 22 : if (type != RTM_NEWROUTE)
212 21 : continue;
213 :
214 : /* We only care for default routes */
215 22 : r = sd_rtnl_message_route_get_dst_prefixlen(m, &dst_len);
216 22 : if (r < 0)
217 0 : return r;
218 22 : if (dst_len != 0)
219 19 : continue;
220 :
221 3 : r = sd_rtnl_message_route_get_src_prefixlen(m, &src_len);
222 3 : if (r < 0)
223 0 : return r;
224 3 : if (src_len != 0)
225 0 : continue;
226 :
227 3 : r = sd_netlink_message_read_u32(m, RTA_OIF, &ifi);
228 3 : if (r < 0)
229 0 : return r;
230 3 : if (ifindex > 0 && (int) ifi != ifindex)
231 0 : continue;
232 :
233 3 : r = sd_rtnl_message_route_get_family(m, &family);
234 3 : if (r < 0)
235 0 : return r;
236 3 : if (af != AF_UNSPEC && af != family)
237 0 : continue;
238 :
239 3 : if (!GREEDY_REALLOC0(list, n_allocated, n_list + 1))
240 0 : return -ENOMEM;
241 :
242 3 : a = list + n_list;
243 :
244 3 : switch (family) {
245 : case AF_INET:
246 1 : r = sd_netlink_message_read_in_addr(m, RTA_GATEWAY, &a->address.in);
247 1 : if (r < 0)
248 0 : continue;
249 :
250 1 : break;
251 : case AF_INET6:
252 2 : r = sd_netlink_message_read_in6_addr(m, RTA_GATEWAY, &a->address.in6);
253 2 : if (r < 0)
254 2 : continue;
255 :
256 0 : break;
257 : default:
258 0 : continue;
259 : }
260 :
261 1 : sd_netlink_message_read_u32(m, RTA_PRIORITY, &a->metric);
262 :
263 1 : a->ifindex = ifi;
264 1 : a->family = family;
265 :
266 1 : n_list++;
267 : }
268 :
269 1 : if (n_list > 0)
270 1 : qsort(list, n_list, sizeof(struct local_address), address_compare);
271 :
272 1 : *ret = list;
273 1 : list = NULL;
274 :
275 1 : return (int) n_list;
276 : }
|