chiark / gitweb /
sd-bus: avoid a null dereference
[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, src_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                 /* We only care for default routes */
215                 r = sd_rtnl_message_route_get_dst_len(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_len(m, &src_len);
222                 if (r < 0)
223                         return r;
224                 if (src_len != 0)
225                         continue;
226
227                 r = sd_rtnl_message_read_u32(m, RTA_OIF, &ifi);
228                 if (r < 0)
229                         return r;
230
231                 if (ifindex > 0 && (int) ifi != ifindex)
232                         continue;
233
234                 if (!GREEDY_REALLOC0(list, n_allocated, n_list + 1))
235                         return -ENOMEM;
236
237                 a = list + n_list;
238
239                 r = sd_rtnl_message_route_get_family(m, &a->family);
240                 if (r < 0)
241                         return r;
242
243                 switch (a->family) {
244                 case AF_INET:
245                         r = sd_rtnl_message_read_in_addr(m, RTA_GATEWAY, &a->address.in);
246                         if (r < 0)
247                                 continue;
248
249                         break;
250                 case AF_INET6:
251                         r = sd_rtnl_message_read_in6_addr(m, RTA_GATEWAY, &a->address.in6);
252                         if (r < 0)
253                                 continue;
254
255                         break;
256                 default:
257                         continue;
258                 }
259
260                 sd_rtnl_message_read_u32(m, RTA_PRIORITY, &a->metric);
261
262                 a->ifindex = ifi;
263                 n_list++;
264
265         }
266
267         if (n_list > 0)
268                 qsort(list, n_list, sizeof(struct local_address), address_compare);
269
270         *ret = list;
271         list = NULL;
272
273         return (int) n_list;
274 }