]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-netdev.c
networkd: don't check for ifindex in answer to RTM_NEWLINK
[thirdparty/systemd.git] / src / network / networkd-netdev.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 static const char* const netdev_kind_table[] = {
30 [NETDEV_KIND_BRIDGE] = "bridge",
31 [NETDEV_KIND_BOND] = "bond",
32 [NETDEV_KIND_VLAN] = "vlan",
33 };
34
35 DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetdevKind);
36 DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetdevKind, "Failed to parse netdev kind");
37
38 void netdev_free(Netdev *netdev) {
39 netdev_enslave_callback *callback;
40
41 if (!netdev)
42 return;
43
44 while ((callback = netdev->callbacks)) {
45 LIST_REMOVE(callbacks, netdev->callbacks, callback);
46 free(callback);
47 }
48
49 if (netdev->name)
50 hashmap_remove(netdev->manager->netdevs, netdev->name);
51
52 free(netdev->filename);
53
54 free(netdev->description);
55 free(netdev->name);
56
57 free(netdev);
58 }
59
60 int netdev_get(Manager *manager, const char *name, Netdev **ret) {
61 Netdev *netdev;
62
63 assert(manager);
64 assert(name);
65 assert(ret);
66
67 netdev = hashmap_get(manager->netdevs, name);
68 if (!netdev) {
69 *ret = NULL;
70 return -ENOENT;
71 }
72
73 *ret = netdev;
74
75 return 0;
76 }
77
78 static int netdev_enter_failed(Netdev *netdev) {
79 netdev->state = NETDEV_STATE_FAILED;
80
81 return 0;
82 }
83
84 static int netdev_enslave_ready(Netdev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
85 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
86 int r;
87
88 assert(netdev);
89 assert(netdev->state == NETDEV_STATE_READY);
90 assert(link);
91 assert(callback);
92
93 r = sd_rtnl_message_link_new(RTM_SETLINK, link->ifindex, &req);
94 if (r < 0) {
95 log_error_netdev(netdev,
96 "Could not allocate RTM_SETLINK message: %s",
97 strerror(-r));
98 return r;
99 }
100
101 r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
102 if (r < 0) {
103 log_error_netdev(netdev,
104 "Could not append IFLA_MASTER attribute: %s",
105 strerror(-r));
106 return r;
107 }
108
109 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
110 if (r < 0) {
111 log_error_netdev(netdev,
112 "Could not send rtnetlink message: %s",
113 strerror(-r));
114 return r;
115 }
116
117 log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
118
119 return 0;
120 }
121
122 static int netdev_enter_ready(Netdev *netdev) {
123 netdev_enslave_callback *callback;
124
125 assert(netdev);
126 assert(netdev->name);
127
128 netdev->state = NETDEV_STATE_READY;
129
130 log_info_netdev(netdev, "netdev ready");
131
132 LIST_FOREACH(callbacks, callback, netdev->callbacks) {
133 /* enslave the links that were attempted to be enslaved befor the
134 * link was ready */
135 netdev_enslave_ready(netdev, callback->link, callback->callback);
136 }
137
138 return 0;
139 }
140
141 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
142 Netdev *netdev = userdata;
143 int r, ifindex;
144
145 assert(netdev->state != _NETDEV_STATE_INVALID);
146
147 r = sd_rtnl_message_get_errno(m);
148 if (r < 0) {
149 log_warning_netdev(netdev, "netdev failed: %s", strerror(-r));
150 netdev_enter_failed(netdev);
151
152 return 1;
153 }
154
155 return 1;
156 }
157
158 static int netdev_create(Netdev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
159 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
160 const char *kind;
161 int r;
162
163 assert(netdev);
164 assert(!(netdev->kind == NETDEV_KIND_VLAN) || (link && callback && netdev->vlanid >= 0));
165 assert(netdev->name);
166 assert(netdev->manager);
167 assert(netdev->manager->rtnl);
168
169 r = sd_rtnl_message_link_new(RTM_NEWLINK, 0, &req);
170 if (r < 0) {
171 log_error_netdev(netdev,
172 "Could not allocate RTM_NEWLINK message: %s",
173 strerror(-r));
174 return r;
175 }
176
177 if (link) {
178 r = sd_rtnl_message_append_u32(req, IFLA_LINK, link->ifindex);
179 if (r < 0) {
180 log_error_netdev(netdev,
181 "Could not append IFLA_LINK attribute: %s",
182 strerror(-r));
183 return r;
184 }
185 }
186
187 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
188 if (r < 0) {
189 log_error_netdev(netdev,
190 "Could not append IFLA_IFNAME attribute: %s",
191 strerror(-r));
192 return r;
193 }
194
195 r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
196 if (r < 0) {
197 log_error_netdev(netdev,
198 "Could not open IFLA_LINKINFO container: %s",
199 strerror(-r));
200 return r;
201 }
202
203 kind = netdev_kind_to_string(netdev->kind);
204 if (!kind) {
205 log_error_netdev(netdev, "Invalid kind");
206 return -EINVAL;
207 }
208
209 r = sd_rtnl_message_append_string(req, IFLA_INFO_KIND, kind);
210 if (r < 0) {
211 log_error_netdev(netdev,
212 "Could not append IFLA_INFO_KIND attribute: %s",
213 strerror(-r));
214 return r;
215 }
216
217 if (netdev->vlanid >= 0) {
218 r = sd_rtnl_message_open_container(req, IFLA_INFO_DATA);
219 if (r < 0) {
220 log_error_netdev(netdev,
221 "Could not open IFLA_INFO_DATA container: %s",
222 strerror(-r));
223 return r;
224 }
225
226 r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid);
227 if (r < 0) {
228 log_error_netdev(netdev,
229 "Could not append IFLA_VLAN_ID attribute: %s",
230 strerror(-r));
231 return r;
232 }
233
234 r = sd_rtnl_message_close_container(req);
235 if (r < 0) {
236 log_error_netdev(netdev,
237 "Could not close IFLA_INFO_DATA container %s",
238 strerror(-r));
239 return r;
240 }
241 }
242
243 r = sd_rtnl_message_close_container(req);
244 if (r < 0) {
245 log_error_netdev(netdev,
246 "Could not close IFLA_LINKINFO container %s",
247 strerror(-r));
248 return r;
249 }
250
251 if (link)
252 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
253 else
254 r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
255 if (r < 0) {
256 log_error_netdev(netdev,
257 "Could not send rtnetlink message: %s", strerror(-r));
258 return r;
259 }
260
261 log_debug_netdev(netdev, "creating netdev");
262
263 netdev->state = NETDEV_STATE_CREATING;
264
265 return 0;
266 }
267
268 int netdev_enslave(Netdev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
269 if (netdev->kind == NETDEV_KIND_VLAN)
270 return netdev_create(netdev, link, callback);
271
272 if (netdev->state == NETDEV_STATE_READY) {
273 netdev_enslave_ready(netdev, link, callback);
274 } else {
275 /* the netdev is not yet read, save this request for when it is*/
276 netdev_enslave_callback *cb;
277
278 cb = new0(netdev_enslave_callback, 1);
279 if (!cb)
280 return log_oom();
281
282 cb->callback = callback;
283 cb->link = link;
284
285 LIST_PREPEND(callbacks, netdev->callbacks, cb);
286 }
287
288 return 0;
289 }
290
291 int netdev_set_ifindex(Netdev *netdev, int ifindex) {
292 assert(netdev);
293 assert(ifindex > 0);
294
295 if (netdev->ifindex > 0) {
296 if (netdev->ifindex == ifindex)
297 return 0;
298 else
299 return -EEXIST;
300 }
301
302 netdev->ifindex = ifindex;
303
304 netdev_enter_ready(netdev);
305
306 return 0;
307 }
308
309 static int netdev_load_one(Manager *manager, const char *filename) {
310 _cleanup_netdev_free_ Netdev *netdev = NULL;
311 _cleanup_fclose_ FILE *file = NULL;
312 int r;
313
314 assert(manager);
315 assert(filename);
316
317 file = fopen(filename, "re");
318 if (!file) {
319 if (errno == ENOENT)
320 return 0;
321 else
322 return errno;
323 }
324
325 netdev = new0(Netdev, 1);
326 if (!netdev)
327 return log_oom();
328
329 netdev->manager = manager;
330 netdev->state = _NETDEV_STATE_INVALID;
331 netdev->kind = _NETDEV_KIND_INVALID;
332 netdev->vlanid = -1;
333
334 r = config_parse(NULL, filename, file, "Netdev\0VLAN\0", config_item_perf_lookup,
335 (void*) network_gperf_lookup, false, false, netdev);
336 if (r < 0) {
337 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
338 return r;
339 }
340
341 if (netdev->kind == _NETDEV_KIND_INVALID) {
342 log_warning("Netdev without Kind configured in %s. Ignoring", filename);
343 return 0;
344 }
345
346 if (!netdev->name) {
347 log_warning("Netdev without Name configured in %s. Ignoring", filename);
348 return 0;
349 }
350
351 if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid < 0) {
352 log_warning("VLAN without Id configured in %s. Ignoring", filename);
353 return 0;
354 }
355
356 netdev->filename = strdup(filename);
357 if (!netdev->filename)
358 return log_oom();
359
360 r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
361 if (r < 0)
362 return r;
363
364 LIST_HEAD_INIT(netdev->callbacks);
365
366 if (netdev->kind != NETDEV_KIND_VLAN) {
367 r = netdev_create(netdev, NULL, NULL);
368 if (r < 0)
369 return r;
370 }
371
372 netdev = NULL;
373
374 return 0;
375 }
376
377 int netdev_load(Manager *manager) {
378 Netdev *netdev;
379 char **files, **f;
380 int r;
381
382 assert(manager);
383
384 while ((netdev = hashmap_first(manager->netdevs)))
385 netdev_free(netdev);
386
387 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
388 if (r < 0) {
389 log_error("Failed to enumerate netdev files: %s", strerror(-r));
390 return r;
391 }
392
393 STRV_FOREACH_BACKWARDS(f, files) {
394 r = netdev_load_one(manager, *f);
395 if (r < 0)
396 return r;
397 }
398
399 strv_free(files);
400
401 return 0;
402 }