]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-link.c
networkd: fix NULL pointer deref
[thirdparty/systemd.git] / src / network / networkd-link.c
CommitLineData
f579559b
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 <netinet/ether.h>
23#include <linux/if.h>
24
25#include "networkd.h"
26#include "libudev-private.h"
27#include "util.h"
28
29int link_new(Manager *manager, struct udev_device *device, Link **ret) {
30 _cleanup_link_free_ Link *link = NULL;
8cd11a0f 31 const char *mac;
602cc437 32 struct ether_addr *mac_addr;
c166a070 33 const char *ifname;
f579559b
TG
34 int r;
35
36 assert(device);
37 assert(ret);
38
39 link = new0(Link, 1);
40 if (!link)
41 return -ENOMEM;
42
5a3eb5a7
TG
43 link->manager = manager;
44 link->state = _LINK_STATE_INVALID;
45
0617ffab
TG
46 link->ifindex = udev_device_get_ifindex(device);
47 if (link->ifindex <= 0)
f579559b
TG
48 return -EINVAL;
49
8cd11a0f 50 mac = udev_device_get_sysattr_value(device, "address");
5a3eb5a7
TG
51 if (mac) {
52 mac_addr = ether_aton(mac);
53 if (mac_addr)
54 memcpy(&link->mac, mac_addr, sizeof(struct ether_addr));
55 }
f579559b 56
c166a070
TG
57 ifname = udev_device_get_sysname(device);
58 link->ifname = strdup(ifname);
59
0617ffab 60 r = hashmap_put(manager->links, &link->ifindex, link);
f579559b
TG
61 if (r < 0)
62 return r;
63
64 *ret = link;
65 link = NULL;
66
67 return 0;
68}
69
70void link_free(Link *link) {
71 if (!link)
72 return;
73
0617ffab 74 assert(link->manager);
f579559b 75
f5be5601
TG
76 if (link->dhcp)
77 sd_dhcp_client_free(link->dhcp);
78
79 route_free(link->dhcp_route);
80 link->dhcp_route = NULL;
81
82 address_free(link->dhcp_address);
83 link->dhcp_address = NULL;
84
0617ffab 85 hashmap_remove(link->manager->links, &link->ifindex);
f579559b 86
c166a070
TG
87 free(link->ifname);
88
f579559b
TG
89 free(link);
90}
91
92int link_add(Manager *m, struct udev_device *device) {
93 Link *link;
94 Network *network;
95 int r;
96 uint64_t ifindex;
02b59d57 97 const char *devtype;
f579559b
TG
98
99 assert(m);
100 assert(device);
101
102 ifindex = udev_device_get_ifindex(device);
103 link = hashmap_get(m->links, &ifindex);
104 if (link)
105 return 0;
106
107 r = link_new(m, device, &link);
108 if (r < 0) {
c166a070 109 log_error("Could not create link: %s", strerror(-r));
f579559b
TG
110 return r;
111 }
112
02b59d57
TG
113 devtype = udev_device_get_devtype(device);
114 if (streq_ptr(devtype, "bridge")) {
115 r = bridge_set_link(m, link);
116 if (r < 0)
117 return r == -ENOENT ? 0 : r;
118 }
119
f579559b
TG
120 r = network_get(m, device, &network);
121 if (r < 0)
122 return r == -ENOENT ? 0 : r;
123
124 r = network_apply(m, network, link);
125 if (r < 0)
126 return r;
127
128 return 0;
129}
130
f882c247 131static int link_enter_configured(Link *link) {
ef1ba606
TG
132 assert(link);
133 assert(link->state == LINK_STATE_SETTING_ROUTES);
134
c166a070 135 log_info("Link '%s' configured", link->ifname);
f882c247
TG
136
137 link->state = LINK_STATE_CONFIGURED;
138
139 return 0;
140}
141
ef1ba606
TG
142static void link_enter_failed(Link *link) {
143 assert(link);
f882c247 144
ef1ba606 145 link->state = LINK_STATE_FAILED;
f882c247
TG
146}
147
f882c247
TG
148static int route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
149 Link *link = userdata;
150 int r;
151
f5be5601
TG
152 assert(link->route_messages > 0);
153 assert(link->state == LINK_STATE_SETTING_ADDRESSES ||
154 link->state == LINK_STATE_SETTING_ROUTES ||
155 link->state == LINK_STATE_FAILED);
f882c247 156
f5be5601 157 link->route_messages --;
f882c247
TG
158
159 if (link->state == LINK_STATE_FAILED)
160 return 1;
161
162 r = sd_rtnl_message_get_errno(m);
c166a070
TG
163 if (r < 0 && r != -EEXIST)
164 log_warning("Could not set route on interface '%s': %s",
165 link->ifname, strerror(-r));
f882c247 166
f5be5601
TG
167 /* we might have received an old reply after moving back to SETTING_ADDRESSES,
168 * ignore it */
169 if (link->route_messages == 0 && link->state == LINK_STATE_SETTING_ROUTES) {
dd3efc09
TG
170 log_info("Routes set for link '%s'", link->ifname);
171 link_enter_configured(link);
172 }
f882c247
TG
173
174 return 1;
175}
176
177static int link_enter_set_routes(Link *link) {
178 Route *route;
179 int r;
180
181 assert(link);
182 assert(link->network);
ef1ba606 183 assert(link->state == LINK_STATE_SETTING_ADDRESSES);
f882c247 184
ef1ba606 185 link->state = LINK_STATE_SETTING_ROUTES;
f882c247 186
f5be5601 187 if (!link->network->static_routes && !link->dhcp_route)
dd3efc09 188 return link_enter_configured(link);
f882c247 189
f048a16b 190 LIST_FOREACH(static_routes, route, link->network->static_routes) {
f882c247 191 r = route_configure(route, link, &route_handler);
dd3efc09
TG
192 if (r < 0) {
193 log_warning("Could not set routes for link '%s'", link->ifname);
ef1ba606
TG
194 link_enter_failed(link);
195 return r;
dd3efc09 196 }
c166a070 197
f5be5601
TG
198 link->route_messages ++;
199 }
200
201 if (link->dhcp_route) {
202 r = route_configure(link->dhcp_route, link, &route_handler);
203 if (r < 0) {
204 log_warning("Could not set routes for link '%s'", link->ifname);
205 link_enter_failed(link);
206 return r;
207 }
208
209 link->route_messages ++;
f882c247
TG
210 }
211
212 return 0;
213}
214
f882c247
TG
215static int address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
216 Link *link = userdata;
217 int r;
218
f5be5601
TG
219 assert(m);
220 assert(link);
221 assert(link->ifname);
222 assert(link->addr_messages > 0);
ef1ba606 223 assert(link->state == LINK_STATE_SETTING_ADDRESSES || link->state == LINK_STATE_FAILED);
f882c247 224
f5be5601 225 link->addr_messages --;
f882c247
TG
226
227 if (link->state == LINK_STATE_FAILED)
228 return 1;
229
230 r = sd_rtnl_message_get_errno(m);
c166a070
TG
231 if (r < 0 && r != -EEXIST)
232 log_warning("Could not set address on interface '%s': %s",
233 link->ifname, strerror(-r));
f882c247 234
f5be5601 235 if (link->addr_messages == 0) {
dd3efc09 236 log_info("Addresses set for link '%s'", link->ifname);
ef1ba606 237 link_enter_set_routes(link);
dd3efc09 238 }
f882c247
TG
239
240 return 1;
241}
242
243static int link_enter_set_addresses(Link *link) {
244 Address *address;
245 int r;
246
247 assert(link);
248 assert(link->network);
f5be5601 249 assert(link->state != _LINK_STATE_INVALID);
f882c247 250
ef1ba606 251 link->state = LINK_STATE_SETTING_ADDRESSES;
f882c247 252
f5be5601 253 if (!link->network->static_addresses && !link->dhcp_address)
ef1ba606 254 return link_enter_set_routes(link);
f882c247 255
f048a16b 256 LIST_FOREACH(static_addresses, address, link->network->static_addresses) {
f882c247 257 r = address_configure(address, link, &address_handler);
dd3efc09
TG
258 if (r < 0) {
259 log_warning("Could not set addresses for link '%s'", link->ifname);
ef1ba606
TG
260 link_enter_failed(link);
261 return r;
dd3efc09 262 }
c166a070 263
f5be5601
TG
264 link->addr_messages ++;
265 }
266
267 if (link->dhcp_address) {
268 r = address_configure(link->dhcp_address, link, &address_handler);
269 if (r < 0) {
270 log_warning("Could not set addresses for link '%s'", link->ifname);
271 link_enter_failed(link);
272 return r;
273 }
274
275 link->addr_messages ++;
f882c247
TG
276 }
277
278 return 0;
279}
280
dd3efc09
TG
281static int link_up_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
282 Link *link = userdata;
283 int r;
284
285 r = sd_rtnl_message_get_errno(m);
286 if (r < 0) {
287 log_warning("Could not bring up interface '%s': %s",
288 link->ifname, strerror(-r));
289 link_enter_failed(link);
290 }
f882c247
TG
291
292 return 1;
293}
294
295static int link_up(Link *link) {
f579559b
TG
296 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
297 int r;
298
f882c247
TG
299 assert(link);
300 assert(link->manager);
301 assert(link->manager->rtnl);
302
0f49a5f7 303 r = sd_rtnl_message_link_new(RTM_SETLINK, link->ifindex, &req);
f579559b 304 if (r < 0) {
0f49a5f7 305 log_error("Could not allocate RTM_SETLINK message");
f579559b
TG
306 return r;
307 }
308
fc25d7f8
TG
309 r = sd_rtnl_message_link_set_flags(req, IFF_UP);
310 if (r < 0) {
311 log_error("Could not set link flags");
312 return r;
313 }
314
dd3efc09 315 r = sd_rtnl_call_async(link->manager->rtnl, req, link_up_handler, link, 0, NULL);
f579559b 316 if (r < 0) {
f882c247 317 log_error("Could not send rtnetlink message: %s", strerror(-r));
f579559b
TG
318 return r;
319 }
320
f882c247
TG
321 return 0;
322}
323
ef1ba606 324static int link_bridge_joined(Link *link) {
f882c247
TG
325 int r;
326
ef1ba606
TG
327 assert(link);
328 assert(link->state == LINK_STATE_JOINING_BRIDGE);
f5be5601 329 assert(link->network);
dd3efc09 330
f882c247 331 r = link_up(link);
ef1ba606
TG
332 if (r < 0) {
333 link_enter_failed(link);
334 return r;
335 }
f882c247 336
f5be5601
TG
337 if (!link->network->dhcp) {
338 r = link_enter_set_addresses(link);
339 if (r < 0)
340 link_enter_failed(link);
341 return r;
ef1ba606
TG
342 }
343
344 return 0;
02b59d57
TG
345}
346
347static int bridge_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
348 Link *link = userdata;
349 int r;
350
ef1ba606
TG
351 assert(link->state == LINK_STATE_JOINING_BRIDGE || link->state == LINK_STATE_FAILED);
352 assert(link->network);
02b59d57
TG
353
354 if (link->state == LINK_STATE_FAILED)
355 return 1;
356
357 r = sd_rtnl_message_get_errno(m);
ef1ba606 358 if (r < 0) {
dd3efc09
TG
359 log_warning("Could not join interface '%s' to bridge '%s': %s",
360 link->ifname, link->network->bridge->name, strerror(-r));
ef1ba606
TG
361 link_enter_failed(link);
362 return 1;
363 } else
dd3efc09
TG
364 log_info("Join interface '%s' to bridge: %s",
365 link->ifname, link->network->bridge->name);
02b59d57 366
ef1ba606 367 link_bridge_joined(link);
02b59d57
TG
368
369 return 1;
370}
371
372static int link_enter_join_bridge(Link *link) {
373 int r;
374
375 assert(link);
376 assert(link->network);
ef1ba606 377 assert(link->state == _LINK_STATE_INVALID);
02b59d57 378
ef1ba606 379 link->state = LINK_STATE_JOINING_BRIDGE;
02b59d57 380
ef1ba606
TG
381 if (!link->network->bridge)
382 return link_bridge_joined(link);
02b59d57
TG
383
384 r = bridge_join(link->network->bridge, link, &bridge_handler);
dd3efc09
TG
385 if (r < 0) {
386 log_warning("Could not join link '%s' to bridge '%s'", link->ifname,
387 link->network->bridge->name);
ef1ba606
TG
388 link_enter_failed(link);
389 return r;
390 }
391
392 return 0;
393}
394
395static int link_get_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
396 Link *link = userdata;
397 int r;
398
399 r = sd_rtnl_message_get_errno(m);
400 if (r < 0) {
401 log_warning("Could not get state of interface '%s': %s",
402 link->ifname, strerror(-r));
403 link_enter_failed(link);
404 }
405
5eb036ca
TG
406 link_update(link, m);
407
ef1ba606
TG
408 return 1;
409}
410
411static int link_get(Link *link) {
412 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
413 int r;
414
415 assert(link);
416 assert(link->manager);
417 assert(link->manager->rtnl);
418
419 r = sd_rtnl_message_link_new(RTM_GETLINK, link->ifindex, &req);
420 if (r < 0) {
421 log_error("Could not allocate RTM_GETLINK message");
422 return r;
423 }
424
425 r = sd_rtnl_call_async(link->manager->rtnl, req, link_get_handler, link, 0, NULL);
426 if (r < 0) {
427 log_error("Could not send rtnetlink message: %s", strerror(-r));
428 return r;
dd3efc09 429 }
02b59d57
TG
430
431 return 0;
432}
433
434int link_configure(Link *link) {
435 int r;
436
ef1ba606
TG
437 assert(link);
438 assert(link->network);
439 assert(link->state == _LINK_STATE_INVALID);
440
dd3efc09 441 r = link_get(link);
ef1ba606
TG
442 if (r < 0) {
443 link_enter_failed(link);
444 return r;
445 }
dd3efc09 446
02b59d57 447 r = link_enter_join_bridge(link);
f882c247 448 if (r < 0)
ef1ba606 449 return r;
f579559b
TG
450
451 return 0;
452}
dd3efc09 453
f5be5601
TG
454static int address_drop_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
455 Link *link = userdata;
456 int r;
457
458 assert(m);
459 assert(link);
460 assert(link->ifname);
461
462 if (link->state == LINK_STATE_FAILED)
463 return 1;
464
465 r = sd_rtnl_message_get_errno(m);
466 if (r < 0 && r != -EEXIST)
467 log_warning("Could not drop address from interface '%s': %s",
468 link->ifname, strerror(-r));
469
470 return 1;
471}
472
473static void dhcp_handler(sd_dhcp_client *client, int event, void *userdata) {
474 Link *link = userdata;
475 struct in_addr address;
476 struct in_addr netmask;
477 struct in_addr gateway;
478 int prefixlen;
479 int r;
480
481 if (link->state == LINK_STATE_FAILED)
482 return;
483
484 if (event < 0) {
485 log_warning("DHCP error: %s", strerror(-event));
486 link_enter_failed(link);
487 return;
488 }
489
490 if (event == DHCP_EVENT_NO_LEASE)
491 log_info("IP address in use.");
492
493 if (event == DHCP_EVENT_IP_CHANGE || event == DHCP_EVENT_EXPIRED ||
494 event == DHCP_EVENT_STOP) {
495 address_drop(link->dhcp_address, link, address_drop_handler);
496
497 address_free(link->dhcp_address);
498 link->dhcp_address = NULL;
499
500 route_free(link->dhcp_route);
501 link->dhcp_route = NULL;
502 }
503
504 r = sd_dhcp_client_get_address(client, &address);
505 if (r < 0) {
506 log_warning("DHCP error: no address");
507 link_enter_failed(link);
508 return;
509 }
510
511 r = sd_dhcp_client_get_netmask(client, &netmask);
512 if (r < 0) {
513 log_warning("DHCP error: no netmask");
514 link_enter_failed(link);
515 return;
516 }
517
518 prefixlen = sd_dhcp_client_prefixlen(&netmask);
519 if (prefixlen < 0) {
520 log_warning("DHCP error: no prefixlen");
521 link_enter_failed(link);
522 return;
523 }
524
525 r = sd_dhcp_client_get_router(client, &gateway);
526 if (r < 0) {
527 log_warning("DHCP error: no router");
528 link_enter_failed(link);
529 return;
530 }
531
532 if (event == DHCP_EVENT_IP_CHANGE || event == DHCP_EVENT_IP_ACQUIRE) {
533 _cleanup_address_free_ Address *addr = NULL;
534 _cleanup_route_free_ Route *rt = NULL;
535
536 log_info("Received config over DHCPv4");
537
538 r = address_new_dynamic(&addr);
539 if (r < 0) {
540 log_error("Could not allocate address");
541 link_enter_failed(link);
542 return;
543 }
544
545 addr->family = AF_INET;
546 addr->in_addr.in = address;
547 addr->prefixlen = prefixlen;
548 addr->netmask = netmask;
549
550 r = route_new_dynamic(&rt);
551 if (r < 0) {
552 log_error("Could not allocate route");
553 link_enter_failed(link);
554 return;
555 }
556
557 rt->family = AF_INET;
558 rt->in_addr.in = gateway;
559
560 link->dhcp_address = addr;
561 link->dhcp_route = rt;
562 addr = NULL;
563 rt = NULL;
564
565 link_enter_set_addresses(link);
566 }
567
568 return;
569}
570
571static int link_acquire_conf(Link *link) {
572 int r;
573
574 assert(link);
575 assert(link->network);
576 assert(link->network->dhcp);
577 assert(link->manager);
578 assert(link->manager->event);
579
580 if (!link->dhcp) {
581 link->dhcp = sd_dhcp_client_new(link->manager->event);
582 if (!link->dhcp)
583 return -ENOMEM;
584
585 r = sd_dhcp_client_set_index(link->dhcp, link->ifindex);
586 if (r < 0)
587 return r;
588
589 r = sd_dhcp_client_set_mac(link->dhcp, &link->mac);
590 if (r < 0)
591 return r;
592
593 r = sd_dhcp_client_set_callback(link->dhcp, dhcp_handler, link);
594 if (r < 0)
595 return r;
596 }
597
598 r = sd_dhcp_client_start(link->dhcp);
599 if (r < 0)
600 return r;
601
602 return 0;
603}
604
22936833
TG
605int link_update(Link *link, sd_rtnl_message *m) {
606 unsigned flags;
607 int r;
608
dd3efc09 609 assert(link);
22936833
TG
610 assert(m);
611
612 r = sd_rtnl_message_link_get_flags(m, &flags);
613 if (r < 0) {
614 log_warning("Could not get link flags of '%s'", link->ifname);
615 return r;
616 }
dd3efc09
TG
617
618 if (link->flags & IFF_UP && !(flags & IFF_UP))
619 log_info("Interface '%s' is down", link->ifname);
620 else if (!(link->flags & IFF_UP) && flags & IFF_UP)
621 log_info("Interface '%s' is up", link->ifname);
622
f5be5601 623 if (link->flags & IFF_LOWER_UP && !(flags & IFF_LOWER_UP)) {
dd3efc09 624 log_info("Interface '%s' is disconnected", link->ifname);
f5be5601
TG
625
626 if (link->network->dhcp) {
627 r = sd_dhcp_client_stop(link->dhcp);
628 if (r < 0) {
629 link_enter_failed(link);
630 return r;
631 }
632 }
633 } else if (!(link->flags & IFF_LOWER_UP) && flags & IFF_LOWER_UP) {
dd3efc09
TG
634 log_info("Interface '%s' is connected", link->ifname);
635
be9326ca 636 if (link->network && link->network->dhcp) {
f5be5601
TG
637 r = link_acquire_conf(link);
638 if (r < 0) {
639 link_enter_failed(link);
640 return r;
641 }
642 }
643 }
644
dd3efc09
TG
645 link->flags = flags;
646
647 return 0;
648}