chiark / gitweb /
3ab99420a375aec78823986362849e7a364ca30a
[elogind.git] / src / libsystemd / sd-rtnl / 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-rtnl.h"
24 #include "rtnl-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 0;
54 }
55
56 int local_addresses(sd_rtnl *context, int ifindex, struct local_address **ret) {
57         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
58         _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
59         _cleanup_free_ struct local_address *list = NULL;
60         size_t n_list = 0, n_allocated = 0;
61         sd_rtnl_message *m;
62         int r;
63
64         assert(ret);
65
66         if (context)
67                 rtnl = sd_rtnl_ref(context);
68         else {
69                 r = sd_rtnl_open(&rtnl, 0);
70                 if (r < 0)
71                         return r;
72         }
73
74         r = sd_rtnl_message_new_addr(rtnl, &req, RTM_GETADDR, 0, AF_UNSPEC);
75         if (r < 0)
76                 return r;
77
78         r = sd_rtnl_call(rtnl, req, 0, &reply);
79         if (r < 0)
80                 return r;
81
82         for (m = reply; m; m = sd_rtnl_message_next(m)) {
83                 struct local_address *a;
84                 unsigned char flags;
85                 uint16_t type;
86                 int ifi;
87
88                 r = sd_rtnl_message_get_errno(m);
89                 if (r < 0)
90                         return r;
91
92                 r = sd_rtnl_message_get_type(m, &type);
93                 if (r < 0)
94                         return r;
95
96                 if (type != RTM_NEWADDR)
97                         continue;
98
99                 r = sd_rtnl_message_addr_get_ifindex(m, &ifi);
100                 if (r < 0)
101                         return r;
102
103                 if (ifindex != 0 && ifi != ifindex)
104                         continue;
105
106                 r = sd_rtnl_message_addr_get_flags(m, &flags);
107                 if (r < 0)
108                         return r;
109
110                 if (flags & IFA_F_DEPRECATED)
111                         continue;
112
113                 if (!GREEDY_REALLOC0(list, n_allocated, n_list+1))
114                         return -ENOMEM;
115
116                 a = list + n_list;
117
118                 r = sd_rtnl_message_addr_get_scope(m, &a->scope);
119                 if (r < 0)
120                         return r;
121
122                 if (ifindex == 0 && (a->scope == RT_SCOPE_HOST || a->scope == RT_SCOPE_NOWHERE))
123                         continue;
124
125                 r = sd_rtnl_message_addr_get_family(m, &a->family);
126                 if (r < 0)
127                         return r;
128
129                 switch (a->family) {
130
131                 case AF_INET:
132                         r = sd_rtnl_message_read_in_addr(m, IFA_LOCAL, &a->address.in);
133                         if (r < 0) {
134                                 r = sd_rtnl_message_read_in_addr(m, IFA_ADDRESS, &a->address.in);
135                                 if (r < 0)
136                                         continue;
137                         }
138                         break;
139
140                 case AF_INET6:
141                         r = sd_rtnl_message_read_in6_addr(m, IFA_LOCAL, &a->address.in6);
142                         if (r < 0) {
143                                 r = sd_rtnl_message_read_in6_addr(m, IFA_ADDRESS, &a->address.in6);
144                                 if (r < 0)
145                                         continue;
146                         }
147                         break;
148
149                 default:
150                         continue;
151                 }
152
153                 a->ifindex = ifi;
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_rtnl *context, int ifindex, struct local_address **ret) {
168         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
169         _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
170         _cleanup_free_ struct local_address *list = NULL;
171         sd_rtnl_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_rtnl_ref(context);
179         else {
180                 r = sd_rtnl_open(&rtnl, 0);
181                 if (r < 0)
182                         return r;
183         }
184
185         r = sd_rtnl_message_new_route(rtnl, &req, RTM_GETROUTE, AF_UNSPEC, RTPROT_UNSPEC);
186         if (r < 0)
187                 return r;
188
189         r = sd_rtnl_message_request_dump(req, true);
190         if (r < 0)
191                 return r;
192
193         r = sd_rtnl_call(rtnl, req, 0, &reply);
194         if (r < 0)
195                 return r;
196
197         for (m = reply; m; m = sd_rtnl_message_next(m)) {
198                 struct local_address *a;
199                 uint16_t type;
200                 unsigned char dst_len;
201                 uint32_t ifi;
202
203                 r = sd_rtnl_message_get_errno(m);
204                 if (r < 0)
205                         return r;
206
207                 r = sd_rtnl_message_get_type(m, &type);
208                 if (r < 0)
209                         return r;
210
211                 if (type != RTM_NEWROUTE)
212                         continue;
213
214                 r = sd_rtnl_message_route_get_dst_len(m, &dst_len);
215                 if (r < 0)
216                         return r;
217
218                 /* We only care for default routes */
219                 if (dst_len != 0)
220                         continue;
221
222                 r = sd_rtnl_message_read_u32(m, RTA_OIF, &ifi);
223                 if (r < 0)
224                         return r;
225
226                 if (ifindex > 0 && (int) ifi != ifindex)
227                         continue;
228
229                 if (!GREEDY_REALLOC0(list, n_allocated, n_list + 1))
230                         return -ENOMEM;
231
232                 a = list + n_list;
233
234                 r = sd_rtnl_message_route_get_family(m, &a->family);
235                 if (r < 0)
236                         return r;
237
238                 switch (a->family) {
239                 case AF_INET:
240                         r = sd_rtnl_message_read_in_addr(m, RTA_GATEWAY, &a->address.in);
241                         if (r < 0)
242                                 continue;
243
244                         break;
245                 case AF_INET6:
246                         r = sd_rtnl_message_read_in6_addr(m, RTA_GATEWAY, &a->address.in6);
247                         if (r < 0)
248                                 continue;
249
250                         break;
251                 default:
252                         continue;
253                 }
254
255                 sd_rtnl_message_read_u32(m, RTA_PRIORITY, &a->metric);
256
257                 a->ifindex = ifi;
258                 n_list++;
259
260         }
261
262         if (n_list > 0)
263                 qsort(list, n_list, sizeof(struct local_address), address_compare);
264
265         *ret = list;
266         list = NULL;
267
268         return (int) n_list;
269 }