2 This file is part of systemd.
4 Copyright (C) 2014 Intel Corporation. All rights reserved.
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 #include <netinet/ether.h>
21 #include <netinet/icmp6.h>
26 #include "networkd-link.h"
28 static int ndisc_netlink_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, void *userdata
) {
29 _cleanup_link_unref_ Link
*link
= userdata
;
33 assert(link
->ndisc_messages
> 0);
35 link
->ndisc_messages
--;
37 r
= sd_netlink_message_get_errno(m
);
38 if (r
< 0 && r
!= -EEXIST
) {
39 log_link_error_errno(link
, r
, "Could not set NDisc route or address: %m");
40 link_enter_failed(link
);
43 if (link
->ndisc_messages
== 0) {
44 link
->ndisc_configured
= true;
45 link_check_ready(link
);
51 static void ndisc_prefix_autonomous_handler(sd_ndisc
*nd
, const struct in6_addr
*prefix
, unsigned prefixlen
,
52 unsigned lifetime_preferred
, unsigned lifetime_valid
, void *userdata
) {
53 _cleanup_address_free_ Address
*address
= NULL
;
54 Link
*link
= userdata
;
60 assert(link
->network
);
62 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
65 r
= address_new(&address
);
67 log_link_error_errno(link
, r
, "Could not allocate address: %m");
71 assert_se(sd_event_now(link
->manager
->event
, clock_boottime_or_monotonic(), &time_now
) >= 0);
73 address
->family
= AF_INET6
;
74 address
->in_addr
.in6
= *prefix
;
75 if (in_addr_is_null(AF_INET6
, (const union in_addr_union
*) &link
->network
->ipv6_token
) == 0)
76 memcpy(((char *)&address
->in_addr
.in6
) + 8, ((char *)&link
->network
->ipv6_token
) + 8, 8);
78 /* see RFC4291 section 2.5.1 */
79 address
->in_addr
.in6
.__in6_u
.__u6_addr8
[8] = link
->mac
.ether_addr_octet
[0];
80 address
->in_addr
.in6
.__in6_u
.__u6_addr8
[8] ^= 1 << 1;
81 address
->in_addr
.in6
.__in6_u
.__u6_addr8
[9] = link
->mac
.ether_addr_octet
[1];
82 address
->in_addr
.in6
.__in6_u
.__u6_addr8
[10] = link
->mac
.ether_addr_octet
[2];
83 address
->in_addr
.in6
.__in6_u
.__u6_addr8
[11] = 0xff;
84 address
->in_addr
.in6
.__in6_u
.__u6_addr8
[12] = 0xfe;
85 address
->in_addr
.in6
.__in6_u
.__u6_addr8
[13] = link
->mac
.ether_addr_octet
[3];
86 address
->in_addr
.in6
.__in6_u
.__u6_addr8
[14] = link
->mac
.ether_addr_octet
[4];
87 address
->in_addr
.in6
.__in6_u
.__u6_addr8
[15] = link
->mac
.ether_addr_octet
[5];
89 address
->prefixlen
= prefixlen
;
90 address
->flags
= IFA_F_NOPREFIXROUTE
|IFA_F_MANAGETEMPADDR
;
91 address
->cinfo
.ifa_prefered
= lifetime_preferred
;
92 address
->cinfo
.ifa_valid
= lifetime_valid
;
94 r
= address_configure(address
, link
, ndisc_netlink_handler
, true);
96 log_link_warning_errno(link
, r
, "Could not set SLAAC address: %m");
97 link_enter_failed(link
);
101 link
->ndisc_messages
++;
104 static void ndisc_prefix_onlink_handler(sd_ndisc
*nd
, const struct in6_addr
*prefix
, unsigned prefixlen
, unsigned lifetime
, void *userdata
) {
105 _cleanup_route_free_ Route
*route
= NULL
;
106 Link
*link
= userdata
;
113 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
116 r
= route_new(&route
);
118 log_link_error_errno(link
, r
, "Could not allocate route: %m");
122 assert_se(sd_event_now(link
->manager
->event
, clock_boottime_or_monotonic(), &time_now
) >= 0);
124 route
->family
= AF_INET6
;
125 route
->table
= RT_TABLE_MAIN
;
126 route
->protocol
= RTPROT_RA
;
127 route
->flags
= RTM_F_PREFIX
;
128 route
->dst
.in6
= *prefix
;
129 route
->dst_prefixlen
= prefixlen
;
130 route
->lifetime
= time_now
+ lifetime
* USEC_PER_SEC
;
132 r
= route_configure(route
, link
, ndisc_netlink_handler
);
134 log_link_warning_errno(link
, r
, "Could not set prefix route: %m");
135 link_enter_failed(link
);
139 link
->ndisc_messages
++;
142 static void ndisc_router_handler(sd_ndisc
*nd
, uint8_t flags
, const struct in6_addr
*gateway
, unsigned lifetime
, int pref
, void *userdata
) {
143 _cleanup_route_free_ Route
*route
= NULL
;
144 Link
*link
= userdata
;
149 assert(link
->network
);
150 assert(link
->manager
);
152 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
155 if (flags
& (ND_RA_FLAG_MANAGED
| ND_RA_FLAG_OTHER
)) {
156 if (flags
& ND_RA_FLAG_MANAGED
)
157 dhcp6_request_address(link
);
159 r
= sd_dhcp6_client_start(link
->dhcp6_client
);
160 if (r
< 0 && r
!= -EBUSY
)
161 log_link_warning_errno(link
, r
, "Starting DHCPv6 client on NDisc request failed: %m");
167 r
= route_new(&route
);
169 log_link_error_errno(link
, r
, "Could not allocate route: %m");
173 assert_se(sd_event_now(link
->manager
->event
, clock_boottime_or_monotonic(), &time_now
) >= 0);
175 route
->family
= AF_INET6
;
176 route
->table
= RT_TABLE_MAIN
;
177 route
->protocol
= RTPROT_RA
;
179 route
->gw
.in6
= *gateway
;
180 route
->lifetime
= time_now
+ lifetime
* USEC_PER_SEC
;
182 r
= route_configure(route
, link
, ndisc_netlink_handler
);
184 log_link_warning_errno(link
, r
, "Could not set default route: %m");
185 link_enter_failed(link
);
189 link
->ndisc_messages
++;
192 static void ndisc_handler(sd_ndisc
*nd
, int event
, void *userdata
) {
193 Link
*link
= userdata
;
198 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
202 case SD_NDISC_EVENT_TIMEOUT
:
203 dhcp6_request_address(link
);
205 r
= sd_dhcp6_client_start(link
->dhcp6_client
);
206 if (r
< 0 && r
!= -EBUSY
)
207 log_link_warning_errno(link
, r
, "Starting DHCPv6 client after NDisc timeout failed: %m");
209 link
->ndisc_configured
= true;
210 link_check_ready(link
);
213 case SD_NDISC_EVENT_STOP
:
216 log_link_warning(link
, "IPv6 Neighbor Discovery unknown event: %d", event
);
220 int ndisc_configure(Link
*link
) {
223 assert_return(link
, -EINVAL
);
225 r
= sd_ndisc_new(&link
->ndisc_router_discovery
);
229 r
= sd_ndisc_attach_event(link
->ndisc_router_discovery
, NULL
, 0);
233 r
= sd_ndisc_set_mac(link
->ndisc_router_discovery
, &link
->mac
);
237 r
= sd_ndisc_set_index(link
->ndisc_router_discovery
, link
->ifindex
);
241 r
= sd_ndisc_set_callback(link
->ndisc_router_discovery
,
242 ndisc_router_handler
,
243 ndisc_prefix_onlink_handler
,
244 ndisc_prefix_autonomous_handler
,