chiark / gitweb /
networkd: make sure Network and Link can always be freed
[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->manager = manager;
43         link->state = _LINK_STATE_INVALID;
44
45         link->ifindex = udev_device_get_ifindex(device);
46         if (link->ifindex <= 0)
47                 return -EINVAL;
48
49         mac = udev_device_get_sysattr_value(device, "address");
50         if (mac) {
51                 mac_addr = ether_aton(mac);
52                 if (mac_addr)
53                         memcpy(&link->mac, mac_addr, sizeof(struct ether_addr));
54         }
55
56         r = hashmap_put(manager->links, &link->ifindex, link);
57         if (r < 0)
58                 return r;
59
60         *ret = link;
61         link = NULL;
62
63         return 0;
64 }
65
66 void link_free(Link *link) {
67         if (!link)
68                 return;
69
70         assert(link->manager);
71
72         hashmap_remove(link->manager->links, &link->ifindex);
73
74         free(link);
75 }
76
77 int link_add(Manager *m, struct udev_device *device) {
78         Link *link;
79         Network *network;
80         int r;
81         uint64_t ifindex;
82
83         assert(m);
84         assert(device);
85
86         ifindex = udev_device_get_ifindex(device);
87         link = hashmap_get(m->links, &ifindex);
88         if (link)
89                 return 0;
90
91         r = link_new(m, device, &link);
92         if (r < 0) {
93                 log_error("could not create link: %s", strerror(-r));
94                 return r;
95         }
96
97         r = network_get(m, device, &network);
98         if (r < 0)
99                 return r == -ENOENT ? 0 : r;
100
101         r = network_apply(m, network, link);
102         if (r < 0)
103                 return r;
104
105         return 0;
106 }
107
108 static int link_enter_configured(Link *link) {
109         log_info("Link configured successfully.");
110
111         link->state = LINK_STATE_CONFIGURED;
112
113         return 0;
114 }
115
116 static int link_enter_failed(Link *link) {
117         log_warning("Could not configure link.");
118
119         link->state = LINK_STATE_FAILED;
120
121         return 0;
122 }
123
124 static bool link_is_up(Link *link) {
125         return link->flags & IFF_UP;
126 }
127
128 static int link_enter_routes_set(Link *link) {
129         log_info("Routes set for link %u", (unsigned)link->ifindex);
130
131         if (link_is_up(link))
132                 return link_enter_configured(link);
133
134         link->state = LINK_STATE_ROUTES_SET;
135
136         return 0;
137 }
138
139 static int route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
140         Link *link = userdata;
141         int r;
142
143         assert(link->rtnl_messages > 0);
144         assert(link->state == LINK_STATE_SET_ROUTES || link->state == LINK_STATE_FAILED);
145
146         link->rtnl_messages --;
147
148         if (link->state == LINK_STATE_FAILED)
149                 return 1;
150
151         r = sd_rtnl_message_get_errno(m);
152         if (r < 0 && r != -EEXIST) {
153                 log_warning("Could not set route on interface %u: %s",
154                             (unsigned)link->ifindex, strerror(-r));
155                 return link_enter_failed(link);
156         }
157
158         if (link->rtnl_messages == 0)
159                 return link_enter_routes_set(link);
160
161         return 1;
162 }
163
164 static int link_enter_set_routes(Link *link) {
165         Route *route;
166         int r;
167
168         assert(link);
169         assert(link->network);
170         assert(link->rtnl_messages == 0);
171         assert(link->state == LINK_STATE_ADDRESSES_SET);
172
173         link->state = LINK_STATE_SET_ROUTES;
174
175         if (!link->network->routes)
176                 return link_enter_routes_set(link);
177
178         LIST_FOREACH(routes, route, link->network->routes) {
179                 r = route_configure(route, link, &route_handler);
180                 if (r < 0)
181                         link_enter_failed(link);
182         }
183
184         return 0;
185 }
186
187 static int link_enter_addresses_set(Link *link) {
188         log_info("Addresses set for link %u", (unsigned)link->ifindex);
189
190         link->state = LINK_STATE_ADDRESSES_SET;
191
192         return link_enter_set_routes(link);
193 }
194
195 static int address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
196         Link *link = userdata;
197         int r;
198
199         assert(link->rtnl_messages > 0);
200         assert(link->state == LINK_STATE_SET_ADDRESSES || link->state == LINK_STATE_FAILED);
201
202         link->rtnl_messages --;
203
204         if (link->state == LINK_STATE_FAILED)
205                 return 1;
206
207         r = sd_rtnl_message_get_errno(m);
208         if (r < 0 && r != -EEXIST) {
209                 log_warning("Could not set address on interface %u: %s",
210                             (unsigned)link->ifindex, strerror(-r));
211                 link_enter_failed(link);
212         }
213
214         if (link->rtnl_messages == 0)
215                 link_enter_addresses_set(link);
216
217         return 1;
218 }
219
220 static int link_enter_set_addresses(Link *link) {
221         Address *address;
222         int r;
223
224         assert(link);
225         assert(link->network);
226         assert(link->rtnl_messages == 0);
227
228         if (!link->network->addresses)
229                 return link_enter_addresses_set(link);
230
231         link->state = LINK_STATE_SET_ADDRESSES;
232
233         LIST_FOREACH(addresses, address, link->network->addresses) {
234                 r = address_configure(address, link, &address_handler);
235                 if (r < 0)
236                         link_enter_failed(link);
237         }
238
239         return 0;
240 }
241
242 static int link_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
243         Link *link = userdata;
244         int r;
245
246         r = sd_rtnl_message_get_errno(m);
247         if (r < 0) {
248                 log_warning("Could not bring up interface %u: %s",
249                             (unsigned)link->ifindex, strerror(-r));
250                 return link_enter_failed(link);
251         }
252
253         link->flags |= IFF_UP;
254
255         log_info("Link is UP.");
256
257         if (link->state == LINK_STATE_ROUTES_SET)
258                 return link_enter_configured(link);
259
260         return 1;
261 }
262
263 static int link_up(Link *link) {
264         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
265         int r;
266
267         assert(link);
268         assert(link->manager);
269         assert(link->manager->rtnl);
270
271         r = sd_rtnl_message_link_new(RTM_NEWLINK, link->ifindex, 0, IFF_UP, &req);
272         if (r < 0) {
273                 log_error("Could not allocate RTM_NEWLINK message");
274                 return r;
275         }
276
277         r = sd_rtnl_call_async(link->manager->rtnl, req, link_handler, link, 0, NULL);
278         if (r < 0) {
279                 log_error("Could not send rtnetlink message: %s", strerror(-r));
280                 return r;
281         }
282
283         return 0;
284 }
285
286 int link_configure(Link *link) {
287         int r;
288
289         r = link_up(link);
290         if (r < 0)
291                 return link_enter_failed(link);
292
293         r = link_enter_set_addresses(link);
294         if (r < 0)
295                 return link_enter_failed(link);
296
297         return 0;
298 }