chiark / gitweb /
networkd: avoid segfault
[elogind.git] / src / network / networkd-link.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 Tom Gundersen <teg@jklm.no>
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <netinet/ether.h>
23 #include <linux/if.h>
24
25 #include "networkd.h"
26 #include "libudev-private.h"
27 #include "util.h"
28
29 int link_new(Manager *manager, struct udev_device *device, Link **ret) {
30         _cleanup_link_free_ Link *link = NULL;
31         const char *mac;
32         struct ether_addr *mac_addr;
33         int r;
34
35         assert(device);
36         assert(ret);
37
38         link = new0(Link, 1);
39         if (!link)
40                 return -ENOMEM;
41
42         link->ifindex = udev_device_get_ifindex(device);
43         if (link->ifindex <= 0)
44                 return -EINVAL;
45
46         mac = udev_device_get_sysattr_value(device, "address");
47         if (!mac)
48                 return -EINVAL;
49
50         mac_addr = ether_aton(mac);
51         if (!mac_addr)
52                 return -EINVAL;
53
54         memcpy(&link->mac, mac_addr, sizeof(struct ether_addr));
55
56         link->manager = manager;
57         link->state = _LINK_STATE_INVALID;
58
59         r = hashmap_put(manager->links, &link->ifindex, link);
60         if (r < 0)
61                 return r;
62
63         *ret = link;
64         link = NULL;
65
66         return 0;
67 }
68
69 void link_free(Link *link) {
70         if (!link)
71                 return;
72
73         assert(link->manager);
74
75         hashmap_remove(link->manager->links, &link->ifindex);
76
77         free(link);
78 }
79
80 int link_add(Manager *m, struct udev_device *device) {
81         Link *link;
82         Network *network;
83         int r;
84         uint64_t ifindex;
85
86         assert(m);
87         assert(device);
88
89         ifindex = udev_device_get_ifindex(device);
90         link = hashmap_get(m->links, &ifindex);
91         if (link)
92                 return 0;
93
94         r = link_new(m, device, &link);
95         if (r < 0) {
96                 log_error("could not create link: %s", strerror(-r));
97                 return r;
98         }
99
100         r = network_get(m, device, &network);
101         if (r < 0)
102                 return r == -ENOENT ? 0 : r;
103
104         r = network_apply(m, network, link);
105         if (r < 0)
106                 return r;
107
108         return 0;
109 }
110
111 static int link_enter_configured(Link *link) {
112         log_info("Link configured successfully.");
113
114         link->state = LINK_STATE_CONFIGURED;
115
116         return 0;
117 }
118
119 static int link_enter_failed(Link *link) {
120         log_warning("Could not configure link.");
121
122         link->state = LINK_STATE_FAILED;
123
124         return 0;
125 }
126
127 static bool link_is_up(Link *link) {
128         return link->flags & IFF_UP;
129 }
130
131 static int link_enter_routes_set(Link *link) {
132         log_info("Routes set for link %u", (unsigned)link->ifindex);
133
134         if (link_is_up(link))
135                 return link_enter_configured(link);
136
137         link->state = LINK_STATE_ROUTES_SET;
138
139         return 0;
140 }
141
142 static int route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
143         Link *link = userdata;
144         int r;
145
146         assert(link->rtnl_messages > 0);
147         assert(link->state == LINK_STATE_SET_ROUTES || link->state == LINK_STATE_FAILED);
148
149         link->rtnl_messages --;
150
151         if (link->state == LINK_STATE_FAILED)
152                 return 1;
153
154         r = sd_rtnl_message_get_errno(m);
155         if (r < 0 && r != -EEXIST) {
156                 log_warning("Could not set route on interface %u: %s",
157                             (unsigned)link->ifindex, strerror(-r));
158                 return link_enter_failed(link);
159         }
160
161         if (link->rtnl_messages == 0)
162                 return link_enter_routes_set(link);
163
164         return 1;
165 }
166
167 static int link_enter_set_routes(Link *link) {
168         Route *route;
169         int r;
170
171         assert(link);
172         assert(link->network);
173         assert(link->rtnl_messages == 0);
174         assert(link->state == LINK_STATE_ADDRESSES_SET);
175
176         link->state = LINK_STATE_SET_ROUTES;
177
178         if (!link->network->routes)
179                 return link_enter_routes_set(link);
180
181         LIST_FOREACH(routes, route, link->network->routes) {
182                 r = route_configure(route, link, &route_handler);
183                 if (r < 0)
184                         link_enter_failed(link);
185         }
186
187         return 0;
188 }
189
190 static int link_enter_addresses_set(Link *link) {
191         log_info("Addresses set for link %u", (unsigned)link->ifindex);
192
193         link->state = LINK_STATE_ADDRESSES_SET;
194
195         return link_enter_set_routes(link);
196 }
197
198 static int address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
199         Link *link = userdata;
200         int r;
201
202         assert(link->rtnl_messages > 0);
203         assert(link->state == LINK_STATE_SET_ADDRESSES || link->state == LINK_STATE_FAILED);
204
205         link->rtnl_messages --;
206
207         if (link->state == LINK_STATE_FAILED)
208                 return 1;
209
210         r = sd_rtnl_message_get_errno(m);
211         if (r < 0 && r != -EEXIST) {
212                 log_warning("Could not set address on interface %u: %s",
213                             (unsigned)link->ifindex, strerror(-r));
214                 link_enter_failed(link);
215         }
216
217         if (link->rtnl_messages == 0)
218                 link_enter_addresses_set(link);
219
220         return 1;
221 }
222
223 static int link_enter_set_addresses(Link *link) {
224         Address *address;
225         int r;
226
227         assert(link);
228         assert(link->network);
229         assert(link->rtnl_messages == 0);
230
231         if (!link->network->addresses)
232                 return link_enter_addresses_set(link);
233
234         link->state = LINK_STATE_SET_ADDRESSES;
235
236         LIST_FOREACH(addresses, address, link->network->addresses) {
237                 r = address_configure(address, link, &address_handler);
238                 if (r < 0)
239                         link_enter_failed(link);
240         }
241
242         return 0;
243 }
244
245 static int link_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
246         Link *link = userdata;
247         int r;
248
249         r = sd_rtnl_message_get_errno(m);
250         if (r < 0) {
251                 log_warning("Could not bring up interface %u: %s",
252                             (unsigned)link->ifindex, strerror(-r));
253                 return link_enter_failed(link);
254         }
255
256         link->flags |= IFF_UP;
257
258         log_info("Link is UP.");
259
260         if (link->state == LINK_STATE_ROUTES_SET)
261                 return link_enter_configured(link);
262
263         return 1;
264 }
265
266 static int link_up(Link *link) {
267         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
268         int r;
269
270         assert(link);
271         assert(link->manager);
272         assert(link->manager->rtnl);
273
274         r = sd_rtnl_message_link_new(RTM_NEWLINK, link->ifindex, 0, IFF_UP, &req);
275         if (r < 0) {
276                 log_error("Could not allocate RTM_NEWLINK message");
277                 return r;
278         }
279
280         r = sd_rtnl_call_async(link->manager->rtnl, req, link_handler, link, 0, NULL);
281         if (r < 0) {
282                 log_error("Could not send rtnetlink message: %s", strerror(-r));
283                 return r;
284         }
285
286         return 0;
287 }
288
289 int link_configure(Link *link) {
290         int r;
291
292         r = link_up(link);
293         if (r < 0)
294                 return link_enter_failed(link);
295
296         r = link_enter_set_addresses(link);
297         if (r < 0)
298                 return link_enter_failed(link);
299
300         return 0;
301 }