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