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>
22 #include <netinet/in.h>
27 #include "networkd-link.h"
29 static int ndisc_netlink_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, void *userdata
) {
30 _cleanup_link_unref_ Link
*link
= userdata
;
34 assert(link
->ndisc_messages
> 0);
36 link
->ndisc_messages
--;
38 r
= sd_netlink_message_get_errno(m
);
39 if (r
< 0 && r
!= -EEXIST
) {
40 log_link_error_errno(link
, r
, "Could not set NDisc route or address: %m");
41 link_enter_failed(link
);
44 if (link
->ndisc_messages
== 0) {
45 link
->ndisc_configured
= true;
46 link_check_ready(link
);
52 static void ndisc_prefix_autonomous_handler(sd_ndisc
*nd
, const struct in6_addr
*prefix
, unsigned prefixlen
,
53 unsigned lifetime_preferred
, unsigned lifetime_valid
, void *userdata
) {
54 _cleanup_address_free_ Address
*address
= NULL
;
55 Link
*link
= userdata
;
61 assert(link
->network
);
63 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
66 r
= address_new(&address
);
68 log_link_error_errno(link
, r
, "Could not allocate address: %m");
72 assert_se(sd_event_now(link
->manager
->event
, clock_boottime_or_monotonic(), &time_now
) >= 0);
74 address
->family
= AF_INET6
;
75 address
->in_addr
.in6
= *prefix
;
76 if (in_addr_is_null(AF_INET6
, (const union in_addr_union
*) &link
->network
->ipv6_token
) == 0)
77 memcpy(((char *)&address
->in_addr
.in6
) + 8, ((char *)&link
->network
->ipv6_token
) + 8, 8);
79 /* see RFC4291 section 2.5.1 */
80 address
->in_addr
.in6
.s6_addr
[8] = link
->mac
.ether_addr_octet
[0];
81 address
->in_addr
.in6
.s6_addr
[8] ^= 1 << 1;
82 address
->in_addr
.in6
.s6_addr
[9] = link
->mac
.ether_addr_octet
[1];
83 address
->in_addr
.in6
.s6_addr
[10] = link
->mac
.ether_addr_octet
[2];
84 address
->in_addr
.in6
.s6_addr
[11] = 0xff;
85 address
->in_addr
.in6
.s6_addr
[12] = 0xfe;
86 address
->in_addr
.in6
.s6_addr
[13] = link
->mac
.ether_addr_octet
[3];
87 address
->in_addr
.in6
.s6_addr
[14] = link
->mac
.ether_addr_octet
[4];
88 address
->in_addr
.in6
.s6_addr
[15] = link
->mac
.ether_addr_octet
[5];
90 address
->prefixlen
= prefixlen
;
91 address
->flags
= IFA_F_NOPREFIXROUTE
|IFA_F_MANAGETEMPADDR
;
92 address
->cinfo
.ifa_prefered
= lifetime_preferred
;
93 address
->cinfo
.ifa_valid
= lifetime_valid
;
95 r
= address_configure(address
, link
, ndisc_netlink_handler
, true);
97 log_link_warning_errno(link
, r
, "Could not set SLAAC address: %m");
98 link_enter_failed(link
);
102 link
->ndisc_messages
++;
105 static void ndisc_prefix_onlink_handler(sd_ndisc
*nd
, const struct in6_addr
*prefix
, unsigned prefixlen
, unsigned lifetime
, void *userdata
) {
106 _cleanup_route_free_ Route
*route
= NULL
;
107 Link
*link
= userdata
;
114 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
117 r
= route_new(&route
);
119 log_link_error_errno(link
, r
, "Could not allocate route: %m");
123 assert_se(sd_event_now(link
->manager
->event
, clock_boottime_or_monotonic(), &time_now
) >= 0);
125 route
->family
= AF_INET6
;
126 route
->table
= RT_TABLE_MAIN
;
127 route
->protocol
= RTPROT_RA
;
128 route
->flags
= RTM_F_PREFIX
;
129 route
->dst
.in6
= *prefix
;
130 route
->dst_prefixlen
= prefixlen
;
131 route
->lifetime
= time_now
+ lifetime
* USEC_PER_SEC
;
133 r
= route_configure(route
, link
, ndisc_netlink_handler
);
135 log_link_warning_errno(link
, r
, "Could not set prefix route: %m");
136 link_enter_failed(link
);
140 link
->ndisc_messages
++;
143 static void ndisc_router_handler(sd_ndisc
*nd
, uint8_t flags
, const struct in6_addr
*gateway
, unsigned lifetime
, int pref
, void *userdata
) {
144 _cleanup_route_free_ Route
*route
= NULL
;
145 Link
*link
= userdata
;
150 assert(link
->network
);
151 assert(link
->manager
);
153 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
156 if (flags
& (ND_RA_FLAG_MANAGED
| ND_RA_FLAG_OTHER
)) {
157 if (flags
& ND_RA_FLAG_MANAGED
)
158 dhcp6_request_address(link
);
160 r
= sd_dhcp6_client_start(link
->dhcp6_client
);
161 if (r
< 0 && r
!= -EBUSY
)
162 log_link_warning_errno(link
, r
, "Starting DHCPv6 client on NDisc request failed: %m");
168 r
= route_new(&route
);
170 log_link_error_errno(link
, r
, "Could not allocate route: %m");
174 assert_se(sd_event_now(link
->manager
->event
, clock_boottime_or_monotonic(), &time_now
) >= 0);
176 route
->family
= AF_INET6
;
177 route
->table
= RT_TABLE_MAIN
;
178 route
->protocol
= RTPROT_RA
;
180 route
->gw
.in6
= *gateway
;
181 route
->lifetime
= time_now
+ lifetime
* USEC_PER_SEC
;
183 r
= route_configure(route
, link
, ndisc_netlink_handler
);
185 log_link_warning_errno(link
, r
, "Could not set default route: %m");
186 link_enter_failed(link
);
190 link
->ndisc_messages
++;
193 static void ndisc_handler(sd_ndisc
*nd
, int event
, void *userdata
) {
194 Link
*link
= userdata
;
199 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
203 case SD_NDISC_EVENT_TIMEOUT
:
204 dhcp6_request_address(link
);
206 r
= sd_dhcp6_client_start(link
->dhcp6_client
);
207 if (r
< 0 && r
!= -EBUSY
)
208 log_link_warning_errno(link
, r
, "Starting DHCPv6 client after NDisc timeout failed: %m");
210 link
->ndisc_configured
= true;
211 link_check_ready(link
);
214 case SD_NDISC_EVENT_STOP
:
217 log_link_warning(link
, "IPv6 Neighbor Discovery unknown event: %d", event
);
221 int ndisc_configure(Link
*link
) {
224 assert_return(link
, -EINVAL
);
226 r
= sd_ndisc_new(&link
->ndisc_router_discovery
);
230 r
= sd_ndisc_attach_event(link
->ndisc_router_discovery
, NULL
, 0);
234 r
= sd_ndisc_set_mac(link
->ndisc_router_discovery
, &link
->mac
);
238 r
= sd_ndisc_set_index(link
->ndisc_router_discovery
, link
->ifindex
);
242 r
= sd_ndisc_set_callback(link
->ndisc_router_discovery
,
243 ndisc_router_handler
,
244 ndisc_prefix_onlink_handler
,
245 ndisc_prefix_autonomous_handler
,