chiark / gitweb /
sd-dhcp-client/networkd: add domainname support
[elogind.git] / src / network / networkd-bridge.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 "networkd.h"
23 #include "net-util.h"
24 #include "path-util.h"
25 #include "conf-files.h"
26 #include "conf-parser.h"
27 #include "list.h"
28
29 void bridge_free(Bridge *bridge) {
30         bridge_join_callback *callback;
31
32         if (!bridge)
33                 return;
34
35         while ((callback = bridge->callbacks)) {
36                 LIST_REMOVE(callbacks, bridge->callbacks, callback);
37                 free(callback);
38         }
39
40         if (bridge->name)
41                 hashmap_remove(bridge->manager->bridges, bridge->name);
42
43         free(bridge->filename);
44
45         free(bridge->description);
46         free(bridge->name);
47
48         free(bridge);
49 }
50
51 int bridge_get(Manager *manager, const char *name, Bridge **ret) {
52         Bridge *bridge;
53
54         assert(manager);
55         assert(name);
56         assert(ret);
57
58         bridge = hashmap_get(manager->bridges, name);
59         if (!bridge) {
60                 *ret = NULL;
61                 return -ENOENT;
62         }
63
64         *ret = bridge;
65
66         return 0;
67 }
68
69 static int bridge_enter_failed(Bridge *bridge) {
70         bridge->state = BRIDGE_STATE_FAILED;
71
72         return 0;
73 }
74
75 static int bridge_join_ready(Bridge *bridge, Link* link, sd_rtnl_message_handler_t callback) {
76         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
77         int r;
78
79         assert(bridge);
80         assert(bridge->state == BRIDGE_STATE_READY);
81         assert(link);
82         assert(callback);
83
84         r = sd_rtnl_message_link_new(RTM_SETLINK, link->ifindex, &req);
85         if (r < 0) {
86                 log_error_bridge(bridge,
87                                  "Could not allocate RTM_SETLINK message: %s",
88                                  strerror(-r));
89                 return r;
90         }
91
92         r = sd_rtnl_message_append_u32(req, IFLA_MASTER, bridge->link->ifindex);
93         if (r < 0) {
94                 log_error_bridge(bridge,
95                                  "Could not append IFLA_MASTER attribute: %s",
96                                  strerror(-r));
97                 return r;
98         }
99
100         r = sd_rtnl_call_async(bridge->manager->rtnl, req, callback, link, 0, NULL);
101         if (r < 0) {
102                 log_error_bridge(bridge,
103                                  "Could not send rtnetlink message: %s",
104                                  strerror(-r));
105                 return r;
106         }
107
108         log_debug_bridge(bridge, "joining link %s to bridge", link->ifname);
109
110         return 0;
111 }
112
113 static int bridge_enter_ready(Bridge *bridge) {
114         bridge_join_callback *callback;
115
116         assert(bridge);
117         assert(bridge->name);
118
119         bridge->state = BRIDGE_STATE_READY;
120
121         log_info_bridge(bridge, "bridge ready");
122
123         LIST_FOREACH(callbacks, callback, bridge->callbacks) {
124                 /* join the links that were attempted to be joined befor the
125                  * link was ready */
126                 bridge_join_ready(bridge, callback->link, callback->callback);
127         }
128
129         return 0;
130 }
131
132 static int bridge_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
133         Bridge *bridge = userdata;
134         int r;
135
136         assert(bridge->state != _BRIDGE_STATE_INVALID);
137
138         r = sd_rtnl_message_get_errno(m);
139         if (r < 0) {
140                 log_warning_bridge(bridge, "bridge failed: %s", strerror(-r));
141                 bridge_enter_failed(bridge);
142
143                 return 1;
144         }
145
146         return 1;
147 }
148
149 static int bridge_create(Bridge *bridge) {
150         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
151         int r;
152
153         assert(bridge);
154         assert(bridge->state == _BRIDGE_STATE_INVALID);
155         assert(bridge->name);
156         assert(bridge->manager);
157         assert(bridge->manager->rtnl);
158
159         r = sd_rtnl_message_link_new(RTM_NEWLINK, 0, &req);
160         if (r < 0) {
161                 log_error_bridge(bridge,
162                                  "Could not allocate RTM_NEWLINK message: %s",
163                                  strerror(-r));
164                 return r;
165         }
166
167         r = sd_rtnl_message_append_string(req, IFLA_IFNAME, bridge->name);
168         if (r < 0) {
169                 log_error_bridge(bridge,
170                                  "Could not append IFLA_IFNAME attribute: %s",
171                                  strerror(-r));
172                 return r;
173         }
174
175         r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
176         if (r < 0) {
177                 log_error_bridge(bridge,
178                                  "Could not open IFLA_LINKINFO container: %s",
179                                  strerror(-r));
180                 return r;
181         }
182
183         r = sd_rtnl_message_append_string(req, IFLA_INFO_KIND, "bridge");
184         if (r < 0) {
185                 log_error_bridge(bridge,
186                                  "Could not append IFLA_INFO_KIND attribute: %s",
187                                  strerror(-r));
188                 return r;
189         }
190
191         r = sd_rtnl_message_close_container(req);
192         if (r < 0) {
193                 log_error_bridge(bridge,
194                                  "Could not close IFLA_LINKINFO container %s",
195                                  strerror(-r));
196                 return r;
197         }
198
199         r = sd_rtnl_call_async(bridge->manager->rtnl, req, &bridge_create_handler, bridge, 0, NULL);
200         if (r < 0) {
201                 log_error_bridge(bridge,
202                                  "Could not send rtnetlink message: %s", strerror(-r));
203                 return r;
204         }
205
206         log_debug_bridge(bridge, "creating bridge");
207
208         bridge->state = BRIDGE_STATE_CREATING;
209
210         return 0;
211 }
212
213 int bridge_join(Bridge *bridge, Link *link, sd_rtnl_message_handler_t callback) {
214         if (bridge->state == BRIDGE_STATE_READY) {
215                 bridge_join_ready(bridge, link, callback);
216         } else {
217                 /* the bridge is not yet read, save this request for when it is*/
218                 bridge_join_callback *cb;
219
220                 cb = new0(bridge_join_callback, 1);
221                 if (!cb)
222                         return log_oom();
223
224                 cb->callback = callback;
225                 cb->link = link;
226
227                 LIST_PREPEND(callbacks, bridge->callbacks, cb);
228         }
229
230         return 0;
231 }
232
233 int bridge_set_link(Manager *m, Link *link) {
234         Bridge *bridge;
235         int r;
236
237         r = bridge_get(m, link->ifname, &bridge);
238         if (r < 0)
239                 return r;
240
241         if (bridge->link && bridge->link != link)
242                 return -EEXIST;
243
244         bridge->link = link;
245
246         bridge_enter_ready(bridge);
247
248         return 0;
249 }
250
251 static int bridge_load_one(Manager *manager, const char *filename) {
252         _cleanup_bridge_free_ Bridge *bridge = NULL;
253         _cleanup_fclose_ FILE *file = NULL;
254         int r;
255
256         assert(manager);
257         assert(filename);
258
259         file = fopen(filename, "re");
260         if (!file) {
261                 if (errno == ENOENT)
262                         return 0;
263                 else
264                         return errno;
265         }
266
267         bridge = new0(Bridge, 1);
268         if (!bridge)
269                 return log_oom();
270
271         bridge->manager = manager;
272         bridge->state = _BRIDGE_STATE_INVALID;
273
274         r = config_parse(NULL, filename, file, "Bridge\0", config_item_perf_lookup,
275                         (void*) network_gperf_lookup, false, false, bridge);
276         if (r < 0) {
277                 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
278                 return r;
279         }
280
281         if (!bridge->name) {
282                 log_warning("Bridge without Name configured in %s. Ignoring", filename);
283                 return 0;
284         }
285
286         bridge->filename = strdup(filename);
287         if (!bridge->filename)
288                 return log_oom();
289
290         r = hashmap_put(bridge->manager->bridges, bridge->name, bridge);
291         if (r < 0)
292                 return r;
293
294         LIST_HEAD_INIT(bridge->callbacks);
295
296         r = bridge_create(bridge);
297         if (r < 0)
298                 return r;
299
300         bridge = NULL;
301
302         return 0;
303 }
304
305 int bridge_load(Manager *manager) {
306         Bridge *bridge;
307         char **files, **f;
308         int r;
309
310         assert(manager);
311
312         while ((bridge = hashmap_first(manager->bridges)))
313                 bridge_free(bridge);
314
315         r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
316         if (r < 0) {
317                 log_error("Failed to enumerate netdev files: %s", strerror(-r));
318                 return r;
319         }
320
321         STRV_FOREACH_BACKWARDS(f, files) {
322                 r = bridge_load_one(manager, *f);
323                 if (r < 0)
324                         return r;
325         }
326
327         strv_free(files);
328
329         return 0;
330 }