]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-link.c
networkd: fix memory leak in error path
[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
449f7554 135 log_info("%s: link 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
449f7554
TG
145 log_warning("%s: failed", link->ifname);
146
ef1ba606 147 link->state = LINK_STATE_FAILED;
f882c247
TG
148}
149
f882c247
TG
150static int route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
151 Link *link = userdata;
152 int r;
153
f5be5601
TG
154 assert(link->route_messages > 0);
155 assert(link->state == LINK_STATE_SETTING_ADDRESSES ||
156 link->state == LINK_STATE_SETTING_ROUTES ||
157 link->state == LINK_STATE_FAILED);
f882c247 158
f5be5601 159 link->route_messages --;
f882c247
TG
160
161 if (link->state == LINK_STATE_FAILED)
162 return 1;
163
164 r = sd_rtnl_message_get_errno(m);
c166a070 165 if (r < 0 && r != -EEXIST)
449f7554 166 log_warning("%s: could not set route: %s",
c166a070 167 link->ifname, strerror(-r));
f882c247 168
f5be5601
TG
169 /* we might have received an old reply after moving back to SETTING_ADDRESSES,
170 * ignore it */
171 if (link->route_messages == 0 && link->state == LINK_STATE_SETTING_ROUTES) {
449f7554 172 log_debug("%s: routes set", link->ifname);
dd3efc09
TG
173 link_enter_configured(link);
174 }
f882c247
TG
175
176 return 1;
177}
178
179static int link_enter_set_routes(Link *link) {
180 Route *route;
181 int r;
182
183 assert(link);
184 assert(link->network);
ef1ba606 185 assert(link->state == LINK_STATE_SETTING_ADDRESSES);
f882c247 186
ef1ba606 187 link->state = LINK_STATE_SETTING_ROUTES;
f882c247 188
f5be5601 189 if (!link->network->static_routes && !link->dhcp_route)
dd3efc09 190 return link_enter_configured(link);
f882c247 191
449f7554
TG
192 log_debug("%s: setting routes", link->ifname);
193
f048a16b 194 LIST_FOREACH(static_routes, route, link->network->static_routes) {
f882c247 195 r = route_configure(route, link, &route_handler);
dd3efc09 196 if (r < 0) {
449f7554 197 log_warning("%s: could not set routes", link->ifname);
ef1ba606
TG
198 link_enter_failed(link);
199 return r;
dd3efc09 200 }
c166a070 201
f5be5601
TG
202 link->route_messages ++;
203 }
204
205 if (link->dhcp_route) {
206 r = route_configure(link->dhcp_route, link, &route_handler);
207 if (r < 0) {
449f7554 208 log_warning("%s: could not set routes", link->ifname);
f5be5601
TG
209 link_enter_failed(link);
210 return r;
211 }
212
213 link->route_messages ++;
f882c247
TG
214 }
215
216 return 0;
217}
218
f882c247
TG
219static int address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
220 Link *link = userdata;
221 int r;
222
f5be5601
TG
223 assert(m);
224 assert(link);
225 assert(link->ifname);
226 assert(link->addr_messages > 0);
ef1ba606 227 assert(link->state == LINK_STATE_SETTING_ADDRESSES || link->state == LINK_STATE_FAILED);
f882c247 228
f5be5601 229 link->addr_messages --;
f882c247
TG
230
231 if (link->state == LINK_STATE_FAILED)
232 return 1;
233
234 r = sd_rtnl_message_get_errno(m);
c166a070 235 if (r < 0 && r != -EEXIST)
449f7554 236 log_warning("%s: could not set address: %s",
c166a070 237 link->ifname, strerror(-r));
f882c247 238
f5be5601 239 if (link->addr_messages == 0) {
449f7554 240 log_debug("%s: addresses set", link->ifname);
ef1ba606 241 link_enter_set_routes(link);
dd3efc09 242 }
f882c247
TG
243
244 return 1;
245}
246
247static int link_enter_set_addresses(Link *link) {
248 Address *address;
249 int r;
250
251 assert(link);
252 assert(link->network);
f5be5601 253 assert(link->state != _LINK_STATE_INVALID);
f882c247 254
ef1ba606 255 link->state = LINK_STATE_SETTING_ADDRESSES;
f882c247 256
f5be5601 257 if (!link->network->static_addresses && !link->dhcp_address)
ef1ba606 258 return link_enter_set_routes(link);
f882c247 259
449f7554
TG
260 log_debug("%s: setting addresses", link->ifname);
261
f048a16b 262 LIST_FOREACH(static_addresses, address, link->network->static_addresses) {
f882c247 263 r = address_configure(address, link, &address_handler);
dd3efc09 264 if (r < 0) {
449f7554 265 log_warning("%s: could not set addresses", link->ifname);
ef1ba606
TG
266 link_enter_failed(link);
267 return r;
dd3efc09 268 }
c166a070 269
f5be5601
TG
270 link->addr_messages ++;
271 }
272
273 if (link->dhcp_address) {
274 r = address_configure(link->dhcp_address, link, &address_handler);
275 if (r < 0) {
449f7554 276 log_warning("%s: could not set addresses", link->ifname);
f5be5601
TG
277 link_enter_failed(link);
278 return r;
279 }
280
281 link->addr_messages ++;
f882c247
TG
282 }
283
284 return 0;
285}
286
dd3efc09
TG
287static int link_up_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
288 Link *link = userdata;
289 int r;
290
1746cf2a
TG
291 assert(link);
292
293 if (link->state == LINK_STATE_FAILED)
294 return 1;
295
dd3efc09
TG
296 r = sd_rtnl_message_get_errno(m);
297 if (r < 0) {
449f7554 298 log_warning("%s: could not bring up interface: %s",
dd3efc09
TG
299 link->ifname, strerror(-r));
300 link_enter_failed(link);
301 }
f882c247 302
1746cf2a
TG
303 log_debug("%s: brought up interface", link->ifname);
304
f882c247
TG
305 return 1;
306}
307
308static int link_up(Link *link) {
f579559b
TG
309 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
310 int r;
311
f882c247
TG
312 assert(link);
313 assert(link->manager);
314 assert(link->manager->rtnl);
315
449f7554
TG
316 log_debug("%s: bringing up link", link->ifname);
317
0f49a5f7 318 r = sd_rtnl_message_link_new(RTM_SETLINK, link->ifindex, &req);
f579559b 319 if (r < 0) {
0f49a5f7 320 log_error("Could not allocate RTM_SETLINK message");
f579559b
TG
321 return r;
322 }
323
fc25d7f8
TG
324 r = sd_rtnl_message_link_set_flags(req, IFF_UP);
325 if (r < 0) {
326 log_error("Could not set link flags");
327 return r;
328 }
329
dd3efc09 330 r = sd_rtnl_call_async(link->manager->rtnl, req, link_up_handler, link, 0, NULL);
f579559b 331 if (r < 0) {
f882c247 332 log_error("Could not send rtnetlink message: %s", strerror(-r));
f579559b
TG
333 return r;
334 }
335
f882c247
TG
336 return 0;
337}
338
ef1ba606 339static int link_bridge_joined(Link *link) {
f882c247
TG
340 int r;
341
ef1ba606
TG
342 assert(link);
343 assert(link->state == LINK_STATE_JOINING_BRIDGE);
f5be5601 344 assert(link->network);
dd3efc09 345
f882c247 346 r = link_up(link);
ef1ba606
TG
347 if (r < 0) {
348 link_enter_failed(link);
349 return r;
350 }
f882c247 351
1746cf2a
TG
352 if (!link->network->dhcp)
353 return link_enter_set_addresses(link);
ef1ba606
TG
354
355 return 0;
02b59d57
TG
356}
357
358static int bridge_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
359 Link *link = userdata;
360 int r;
361
1746cf2a 362 assert(link);
ef1ba606
TG
363 assert(link->state == LINK_STATE_JOINING_BRIDGE || link->state == LINK_STATE_FAILED);
364 assert(link->network);
02b59d57
TG
365
366 if (link->state == LINK_STATE_FAILED)
367 return 1;
368
369 r = sd_rtnl_message_get_errno(m);
ef1ba606 370 if (r < 0) {
449f7554 371 log_warning("%s: could not join bridge '%s': %s",
dd3efc09 372 link->ifname, link->network->bridge->name, strerror(-r));
ef1ba606
TG
373 link_enter_failed(link);
374 return 1;
375 } else
449f7554 376 log_debug("%s: joined bridge '%s'",
dd3efc09 377 link->ifname, link->network->bridge->name);
02b59d57 378
ef1ba606 379 link_bridge_joined(link);
02b59d57
TG
380
381 return 1;
382}
383
384static int link_enter_join_bridge(Link *link) {
385 int r;
386
387 assert(link);
388 assert(link->network);
ef1ba606 389 assert(link->state == _LINK_STATE_INVALID);
02b59d57 390
ef1ba606 391 link->state = LINK_STATE_JOINING_BRIDGE;
02b59d57 392
ef1ba606
TG
393 if (!link->network->bridge)
394 return link_bridge_joined(link);
02b59d57 395
449f7554
TG
396 log_debug("%s: joining bridge", link->ifname);
397
02b59d57 398 r = bridge_join(link->network->bridge, link, &bridge_handler);
dd3efc09 399 if (r < 0) {
449f7554 400 log_warning("%s: could not join bridge '%s'", link->ifname,
dd3efc09 401 link->network->bridge->name);
ef1ba606
TG
402 link_enter_failed(link);
403 return r;
404 }
405
406 return 0;
407}
408
409static int link_get_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
410 Link *link = userdata;
411 int r;
412
1746cf2a
TG
413 assert(link);
414
415 if (link->state == LINK_STATE_FAILED)
416 return 1;
417
ef1ba606
TG
418 r = sd_rtnl_message_get_errno(m);
419 if (r < 0) {
449f7554 420 log_warning("%s: could not get state: %s",
ef1ba606
TG
421 link->ifname, strerror(-r));
422 link_enter_failed(link);
423 }
424
1746cf2a
TG
425 log_debug("%s: got link state", link->ifname);
426
5eb036ca
TG
427 link_update(link, m);
428
ef1ba606
TG
429 return 1;
430}
431
432static int link_get(Link *link) {
433 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
434 int r;
435
436 assert(link);
437 assert(link->manager);
438 assert(link->manager->rtnl);
439
449f7554
TG
440 log_debug("%s: requesting link status", link->ifname);
441
ef1ba606
TG
442 r = sd_rtnl_message_link_new(RTM_GETLINK, link->ifindex, &req);
443 if (r < 0) {
444 log_error("Could not allocate RTM_GETLINK message");
445 return r;
446 }
447
448 r = sd_rtnl_call_async(link->manager->rtnl, req, link_get_handler, link, 0, NULL);
449 if (r < 0) {
450 log_error("Could not send rtnetlink message: %s", strerror(-r));
451 return r;
dd3efc09 452 }
02b59d57
TG
453
454 return 0;
455}
456
457int link_configure(Link *link) {
458 int r;
459
ef1ba606
TG
460 assert(link);
461 assert(link->network);
462 assert(link->state == _LINK_STATE_INVALID);
463
dd3efc09 464 r = link_get(link);
ef1ba606
TG
465 if (r < 0) {
466 link_enter_failed(link);
467 return r;
468 }
dd3efc09 469
1746cf2a 470 return link_enter_join_bridge(link);
f579559b 471}
dd3efc09 472
f5be5601
TG
473static int address_drop_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
474 Link *link = userdata;
475 int r;
476
477 assert(m);
478 assert(link);
479 assert(link->ifname);
480
481 if (link->state == LINK_STATE_FAILED)
482 return 1;
483
484 r = sd_rtnl_message_get_errno(m);
485 if (r < 0 && r != -EEXIST)
449f7554 486 log_warning("%s: could not drop address: %s",
f5be5601
TG
487 link->ifname, strerror(-r));
488
489 return 1;
490}
491
492static void dhcp_handler(sd_dhcp_client *client, int event, void *userdata) {
493 Link *link = userdata;
494 struct in_addr address;
495 struct in_addr netmask;
496 struct in_addr gateway;
497 int prefixlen;
498 int r;
499
1746cf2a
TG
500 assert(link);
501
f5be5601
TG
502 if (link->state == LINK_STATE_FAILED)
503 return;
504
505 if (event < 0) {
449f7554 506 log_warning("%s: DHCP error: %s", link->ifname, strerror(-event));
f5be5601
TG
507 link_enter_failed(link);
508 return;
509 }
510
511 if (event == DHCP_EVENT_NO_LEASE)
449f7554 512 log_debug("%s: IP address in use.", link->ifname);
f5be5601
TG
513
514 if (event == DHCP_EVENT_IP_CHANGE || event == DHCP_EVENT_EXPIRED ||
515 event == DHCP_EVENT_STOP) {
b1b532f5
TG
516 if (link->dhcp_address) {
517 address_drop(link->dhcp_address, link, address_drop_handler);
f5be5601 518
b1b532f5
TG
519 address_free(link->dhcp_address);
520 link->dhcp_address = NULL;
521 }
f5be5601 522
b1b532f5
TG
523 if (link->dhcp_route) {
524 route_free(link->dhcp_route);
525 link->dhcp_route = NULL;
526 }
f5be5601
TG
527 }
528
529 r = sd_dhcp_client_get_address(client, &address);
530 if (r < 0) {
449f7554 531 log_warning("%s: DHCP error: no address", link->ifname);
f5be5601
TG
532 link_enter_failed(link);
533 return;
534 }
535
536 r = sd_dhcp_client_get_netmask(client, &netmask);
537 if (r < 0) {
449f7554 538 log_warning("%s: DHCP error: no netmask", link->ifname);
f5be5601
TG
539 link_enter_failed(link);
540 return;
541 }
542
543 prefixlen = sd_dhcp_client_prefixlen(&netmask);
544 if (prefixlen < 0) {
449f7554 545 log_warning("%s: DHCP error: no prefixlen", link->ifname);
f5be5601
TG
546 link_enter_failed(link);
547 return;
548 }
549
550 r = sd_dhcp_client_get_router(client, &gateway);
551 if (r < 0) {
449f7554 552 log_warning("%s: DHCP error: no router", link->ifname);
f5be5601
TG
553 link_enter_failed(link);
554 return;
555 }
556
557 if (event == DHCP_EVENT_IP_CHANGE || event == DHCP_EVENT_IP_ACQUIRE) {
558 _cleanup_address_free_ Address *addr = NULL;
559 _cleanup_route_free_ Route *rt = NULL;
560
449f7554 561 log_info("%s: received config over DHCPv4", link->ifname);
f5be5601
TG
562
563 r = address_new_dynamic(&addr);
564 if (r < 0) {
565 log_error("Could not allocate address");
566 link_enter_failed(link);
567 return;
568 }
569
570 addr->family = AF_INET;
571 addr->in_addr.in = address;
572 addr->prefixlen = prefixlen;
573 addr->netmask = netmask;
574
575 r = route_new_dynamic(&rt);
576 if (r < 0) {
577 log_error("Could not allocate route");
578 link_enter_failed(link);
579 return;
580 }
581
582 rt->family = AF_INET;
583 rt->in_addr.in = gateway;
584
585 link->dhcp_address = addr;
586 link->dhcp_route = rt;
587 addr = NULL;
588 rt = NULL;
589
590 link_enter_set_addresses(link);
591 }
592
593 return;
594}
595
596static int link_acquire_conf(Link *link) {
597 int r;
598
599 assert(link);
600 assert(link->network);
601 assert(link->network->dhcp);
602 assert(link->manager);
603 assert(link->manager->event);
604
605 if (!link->dhcp) {
606 link->dhcp = sd_dhcp_client_new(link->manager->event);
607 if (!link->dhcp)
608 return -ENOMEM;
609
610 r = sd_dhcp_client_set_index(link->dhcp, link->ifindex);
611 if (r < 0)
612 return r;
613
614 r = sd_dhcp_client_set_mac(link->dhcp, &link->mac);
615 if (r < 0)
616 return r;
617
618 r = sd_dhcp_client_set_callback(link->dhcp, dhcp_handler, link);
619 if (r < 0)
620 return r;
621 }
622
623 r = sd_dhcp_client_start(link->dhcp);
624 if (r < 0)
625 return r;
626
627 return 0;
628}
629
22936833
TG
630int link_update(Link *link, sd_rtnl_message *m) {
631 unsigned flags;
632 int r;
633
dd3efc09 634 assert(link);
06a6e593 635 assert(link->network);
22936833
TG
636 assert(m);
637
1746cf2a
TG
638 if (link->state == LINK_STATE_FAILED)
639 return 0;
640
22936833
TG
641 r = sd_rtnl_message_link_get_flags(m, &flags);
642 if (r < 0) {
449f7554 643 log_warning("%s: could not get link flags", link->ifname);
22936833
TG
644 return r;
645 }
dd3efc09
TG
646
647 if (link->flags & IFF_UP && !(flags & IFF_UP))
449f7554 648 log_info("%s: interface is down", link->ifname);
dd3efc09 649 else if (!(link->flags & IFF_UP) && flags & IFF_UP)
449f7554 650 log_info("%s: interface is up", link->ifname);
dd3efc09 651
f5be5601 652 if (link->flags & IFF_LOWER_UP && !(flags & IFF_LOWER_UP)) {
449f7554 653 log_info("%s: disconnected", link->ifname);
f5be5601
TG
654
655 if (link->network->dhcp) {
656 r = sd_dhcp_client_stop(link->dhcp);
657 if (r < 0) {
658 link_enter_failed(link);
659 return r;
660 }
661 }
662 } else if (!(link->flags & IFF_LOWER_UP) && flags & IFF_LOWER_UP) {
449f7554 663 log_info("%s: connected", link->ifname);
dd3efc09 664
06a6e593 665 if (link->network->dhcp) {
f5be5601
TG
666 r = link_acquire_conf(link);
667 if (r < 0) {
668 link_enter_failed(link);
669 return r;
670 }
671 }
672 }
673
dd3efc09
TG
674 link->flags = flags;
675
1746cf2a 676 log_debug("%s: updated link state", link->ifname);
449f7554 677
dd3efc09
TG
678 return 0;
679}