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