1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2014 Intel Corporation. All rights reserved.
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.
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.
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/>.
22 #include <netinet/ether.h>
23 #include <netinet/icmp6.h>
28 #include "networkd-link.h"
30 static int ndisc_netlink_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, void *userdata
) {
31 _cleanup_link_unref_ Link
*link
= userdata
;
35 assert(link
->ndisc_messages
> 0);
37 link
->ndisc_messages
--;
39 r
= sd_netlink_message_get_errno(m
);
40 if (r
< 0 && r
!= -EEXIST
) {
41 log_link_error_errno(link
, r
, "Could not set NDisc route or address: %m");
42 link_enter_failed(link
);
45 if (link
->ndisc_messages
== 0) {
46 link
->ndisc_configured
= true;
47 link_check_ready(link
);
53 static void ndisc_prefix_autonomous_handler(sd_ndisc
*nd
, const struct in6_addr
*prefix
, unsigned prefixlen
,
54 unsigned lifetime_preferred
, unsigned lifetime_valid
, void *userdata
) {
55 _cleanup_address_free_ Address
*address
= NULL
;
56 Link
*link
= userdata
;
62 assert(link
->network
);
64 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
67 r
= address_new(&address
);
69 log_link_error_errno(link
, r
, "Could not allocate address: %m");
73 assert_se(sd_event_now(link
->manager
->event
, clock_boottime_or_monotonic(), &time_now
) >= 0);
75 address
->family
= AF_INET6
;
76 address
->in_addr
.in6
= *prefix
;
77 if (in_addr_is_null(AF_INET6
, (const union in_addr_union
*) &link
->network
->ipv6_token
) == 0)
78 memcpy(((char *)&address
->in_addr
.in6
) + 8, ((char *)&link
->network
->ipv6_token
) + 8, 8);
80 /* see RFC4291 section 2.5.1 */
81 address
->in_addr
.in6
.__in6_u
.__u6_addr8
[8] = link
->mac
.ether_addr_octet
[0];
82 address
->in_addr
.in6
.__in6_u
.__u6_addr8
[8] ^= 1 << 1;
83 address
->in_addr
.in6
.__in6_u
.__u6_addr8
[9] = link
->mac
.ether_addr_octet
[1];
84 address
->in_addr
.in6
.__in6_u
.__u6_addr8
[10] = link
->mac
.ether_addr_octet
[2];
85 address
->in_addr
.in6
.__in6_u
.__u6_addr8
[11] = 0xff;
86 address
->in_addr
.in6
.__in6_u
.__u6_addr8
[12] = 0xfe;
87 address
->in_addr
.in6
.__in6_u
.__u6_addr8
[13] = link
->mac
.ether_addr_octet
[3];
88 address
->in_addr
.in6
.__in6_u
.__u6_addr8
[14] = link
->mac
.ether_addr_octet
[4];
89 address
->in_addr
.in6
.__in6_u
.__u6_addr8
[15] = link
->mac
.ether_addr_octet
[5];
91 address
->prefixlen
= prefixlen
;
92 address
->flags
= IFA_F_NOPREFIXROUTE
;
93 address
->cinfo
.ifa_prefered
= lifetime_preferred
;
94 address
->cinfo
.ifa_valid
= lifetime_valid
;
96 r
= address_configure(address
, link
, ndisc_netlink_handler
, true);
98 log_link_warning_errno(link
, r
, "Could not set SLAAC address: %m");
99 link_enter_failed(link
);
103 link
->ndisc_messages
++;
106 static void ndisc_prefix_onlink_handler(sd_ndisc
*nd
, const struct in6_addr
*prefix
, unsigned prefixlen
, unsigned lifetime
, void *userdata
) {
107 _cleanup_route_free_ Route
*route
= NULL
;
108 Link
*link
= userdata
;
115 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
118 r
= route_new(&route
);
120 log_link_error_errno(link
, r
, "Could not allocate route: %m");
124 assert_se(sd_event_now(link
->manager
->event
, clock_boottime_or_monotonic(), &time_now
) >= 0);
126 route
->family
= AF_INET6
;
127 route
->table
= RT_TABLE_MAIN
;
128 route
->protocol
= RTPROT_RA
;
129 route
->flags
= RTM_F_PREFIX
;
130 route
->dst
.in6
= *prefix
;
131 route
->dst_prefixlen
= prefixlen
;
132 route
->lifetime
= time_now
+ lifetime
* USEC_PER_SEC
;
134 r
= route_configure(route
, link
, ndisc_netlink_handler
);
136 log_link_warning_errno(link
, r
, "Could not set prefix route: %m");
137 link_enter_failed(link
);
141 link
->ndisc_messages
++;
144 static void ndisc_router_handler(sd_ndisc
*nd
, uint8_t flags
, const struct in6_addr
*gateway
, unsigned lifetime
, int pref
, void *userdata
) {
145 _cleanup_route_free_ Route
*route
= NULL
;
146 Link
*link
= userdata
;
151 assert(link
->network
);
152 assert(link
->manager
);
154 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
157 if (flags
& (ND_RA_FLAG_MANAGED
| ND_RA_FLAG_OTHER
)) {
158 if (flags
& ND_RA_FLAG_MANAGED
)
159 dhcp6_request_address(link
);
161 r
= sd_dhcp6_client_start(link
->dhcp6_client
);
162 if (r
< 0 && r
!= -EBUSY
)
163 log_link_warning_errno(link
, r
, "Starting DHCPv6 client on NDisc request failed: %m");
169 r
= route_new(&route
);
171 log_link_error_errno(link
, r
, "Could not allocate route: %m");
175 assert_se(sd_event_now(link
->manager
->event
, clock_boottime_or_monotonic(), &time_now
) >= 0);
177 route
->family
= AF_INET6
;
178 route
->table
= RT_TABLE_MAIN
;
179 route
->protocol
= RTPROT_RA
;
181 route
->gw
.in6
= *gateway
;
182 route
->lifetime
= time_now
+ lifetime
* USEC_PER_SEC
;
184 r
= route_configure(route
, link
, ndisc_netlink_handler
);
186 log_link_warning_errno(link
, r
, "Could not set default route: %m");
187 link_enter_failed(link
);
191 link
->ndisc_messages
++;
194 static void ndisc_handler(sd_ndisc
*nd
, int event
, void *userdata
) {
195 Link
*link
= userdata
;
200 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
204 case SD_NDISC_EVENT_TIMEOUT
:
205 dhcp6_request_address(link
);
207 r
= sd_dhcp6_client_start(link
->dhcp6_client
);
208 if (r
< 0 && r
!= -EBUSY
)
209 log_link_warning_errno(link
, r
, "Starting DHCPv6 client after NDisc timeout failed: %m");
211 link
->ndisc_configured
= true;
212 link_check_ready(link
);
215 case SD_NDISC_EVENT_STOP
:
218 log_link_warning(link
, "IPv6 Neighbor Discovery unknown event: %d", event
);
222 int ndisc_configure(Link
*link
) {
225 assert_return(link
, -EINVAL
);
227 r
= sd_ndisc_new(&link
->ndisc_router_discovery
);
231 r
= sd_ndisc_attach_event(link
->ndisc_router_discovery
, NULL
, 0);
235 r
= sd_ndisc_set_mac(link
->ndisc_router_discovery
, &link
->mac
);
239 r
= sd_ndisc_set_index(link
->ndisc_router_discovery
, link
->ifindex
);
243 r
= sd_ndisc_set_callback(link
->ndisc_router_discovery
,
244 ndisc_router_handler
,
245 ndisc_prefix_onlink_handler
,
246 ndisc_prefix_autonomous_handler
,