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