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