]>
Commit | Line | Data |
---|---|---|
718e3744 | 1 | /* Router advertisement |
34ccea1e | 2 | * Copyright (C) 2016 Cumulus Networks |
7cee1bb1 | 3 | * Copyright (C) 2005 6WIND <jean-mickael.guerin@6wind.com> |
718e3744 | 4 | * Copyright (C) 1999 Kunihiro Ishiguro |
5 | * | |
6 | * This file is part of GNU Zebra. | |
7 | * | |
8 | * GNU Zebra is free software; you can redistribute it and/or modify it | |
9 | * under the terms of the GNU General Public License as published by the | |
10 | * Free Software Foundation; either version 2, or (at your option) any | |
11 | * later version. | |
12 | * | |
13 | * GNU Zebra 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 | * General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with GNU Zebra; see the file COPYING. If not, write to the Free | |
20 | * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | |
21 | * 02111-1307, USA. | |
22 | */ | |
23 | ||
24 | #include <zebra.h> | |
25 | ||
26 | #include "memory.h" | |
4a1ab8e4 | 27 | #include "zebra_memory.h" |
718e3744 | 28 | #include "sockopt.h" |
29 | #include "thread.h" | |
30 | #include "if.h" | |
4a04e5f7 | 31 | #include "stream.h" |
718e3744 | 32 | #include "log.h" |
33 | #include "prefix.h" | |
34 | #include "linklist.h" | |
35 | #include "command.h" | |
edd7c245 | 36 | #include "privs.h" |
cd80d74f | 37 | #include "vrf.h" |
718e3744 | 38 | |
39 | #include "zebra/interface.h" | |
40 | #include "zebra/rtadv.h" | |
41 | #include "zebra/debug.h" | |
537d8ea9 | 42 | #include "zebra/rib.h" |
4b5e1359 | 43 | #include "zebra/zserv.h" |
fe18ee2d | 44 | #include "zebra/zebra_ns.h" |
7c551956 | 45 | #include "zebra/zebra_vrf.h" |
718e3744 | 46 | |
edd7c245 | 47 | extern struct zebra_privs_t zserv_privs; |
48 | ||
56c1f7d8 | 49 | #if defined (HAVE_RTADV) |
718e3744 | 50 | |
fa2b17e3 | 51 | #ifdef OPEN_BSD |
52 | #include <netinet/icmp6.h> | |
53 | #endif | |
54 | ||
718e3744 | 55 | /* If RFC2133 definition is used. */ |
56 | #ifndef IPV6_JOIN_GROUP | |
57 | #define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP | |
58 | #endif | |
59 | #ifndef IPV6_LEAVE_GROUP | |
60 | #define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP | |
61 | #endif | |
62 | ||
63 | #define ALLNODE "ff02::1" | |
64 | #define ALLROUTER "ff02::2" | |
65 | ||
7cee1bb1 | 66 | enum rtadv_event {RTADV_START, RTADV_STOP, RTADV_TIMER, |
67 | RTADV_TIMER_MSEC, RTADV_READ}; | |
718e3744 | 68 | |
43459b1f | 69 | static void rtadv_event (struct zebra_ns *, enum rtadv_event, int); |
718e3744 | 70 | |
a1ac18c4 | 71 | static int if_join_all_router (int, struct interface *); |
72 | static int if_leave_all_router (int, struct interface *); | |
6b0655a2 | 73 | |
911ad1e2 | 74 | static int |
b892f1dd | 75 | rtadv_increment_received(struct zebra_ns *zns, ifindex_t *ifindex) |
911ad1e2 | 76 | { |
795b5abf QY |
77 | int ret = -1; |
78 | struct interface *iface; | |
79 | struct zebra_if *zif; | |
80 | ||
911ad1e2 | 81 | iface = if_lookup_by_index_per_ns (zns, *ifindex); |
82 | if (iface && iface->info) | |
83 | { | |
84 | zif = iface->info; | |
85 | zif->ra_rcvd++; | |
86 | ret = 0; | |
87 | } | |
795b5abf QY |
88 | return ret; |
89 | } | |
90 | ||
a1ac18c4 | 91 | static int |
911ad1e2 | 92 | rtadv_recv_packet (struct zebra_ns *zns, int sock, u_char *buf, int buflen, |
b892f1dd | 93 | struct sockaddr_in6 *from, ifindex_t *ifindex, |
718e3744 | 94 | int *hoplimit) |
95 | { | |
96 | int ret; | |
97 | struct msghdr msg; | |
98 | struct iovec iov; | |
99 | struct cmsghdr *cmsgptr; | |
100 | struct in6_addr dst; | |
101 | ||
102 | char adata[1024]; | |
103 | ||
104 | /* Fill in message and iovec. */ | |
105 | msg.msg_name = (void *) from; | |
106 | msg.msg_namelen = sizeof (struct sockaddr_in6); | |
107 | msg.msg_iov = &iov; | |
108 | msg.msg_iovlen = 1; | |
109 | msg.msg_control = (void *) adata; | |
110 | msg.msg_controllen = sizeof adata; | |
111 | iov.iov_base = buf; | |
112 | iov.iov_len = buflen; | |
113 | ||
114 | /* If recvmsg fail return minus value. */ | |
115 | ret = recvmsg (sock, &msg, 0); | |
116 | if (ret < 0) | |
117 | return ret; | |
118 | ||
b99760ab | 119 | for (cmsgptr = ZCMSG_FIRSTHDR(&msg); cmsgptr != NULL; |
718e3744 | 120 | cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) |
121 | { | |
122 | /* I want interface index which this packet comes from. */ | |
123 | if (cmsgptr->cmsg_level == IPPROTO_IPV6 && | |
124 | cmsgptr->cmsg_type == IPV6_PKTINFO) | |
125 | { | |
126 | struct in6_pktinfo *ptr; | |
127 | ||
128 | ptr = (struct in6_pktinfo *) CMSG_DATA (cmsgptr); | |
129 | *ifindex = ptr->ipi6_ifindex; | |
130 | memcpy(&dst, &ptr->ipi6_addr, sizeof(ptr->ipi6_addr)); | |
131 | } | |
132 | ||
133 | /* Incoming packet's hop limit. */ | |
134 | if (cmsgptr->cmsg_level == IPPROTO_IPV6 && | |
135 | cmsgptr->cmsg_type == IPV6_HOPLIMIT) | |
b0b709ab SH |
136 | { |
137 | int *hoptr = (int *) CMSG_DATA (cmsgptr); | |
138 | *hoplimit = *hoptr; | |
139 | } | |
718e3744 | 140 | } |
795b5abf | 141 | |
911ad1e2 | 142 | rtadv_increment_received(zns, ifindex); |
718e3744 | 143 | return ret; |
144 | } | |
145 | ||
146 | #define RTADV_MSG_SIZE 4096 | |
147 | ||
148 | /* Send router advertisement packet. */ | |
a1ac18c4 | 149 | static void |
718e3744 | 150 | rtadv_send_packet (int sock, struct interface *ifp) |
151 | { | |
152 | struct msghdr msg; | |
153 | struct iovec iov; | |
154 | struct cmsghdr *cmsgptr; | |
155 | struct in6_pktinfo *pkt; | |
156 | struct sockaddr_in6 addr; | |
57492d56 | 157 | static void *adata = NULL; |
718e3744 | 158 | unsigned char buf[RTADV_MSG_SIZE]; |
159 | struct nd_router_advert *rtadv; | |
160 | int ret; | |
161 | int len = 0; | |
162 | struct zebra_if *zif; | |
1eb8ef25 | 163 | struct rtadv_prefix *rprefix; |
718e3744 | 164 | u_char all_nodes_addr[] = {0xff,0x02,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; |
52dc7ee6 | 165 | struct listnode *node; |
d660f698 | 166 | u_int16_t pkt_RouterLifetime; |
718e3744 | 167 | |
57492d56 | 168 | /* |
169 | * Allocate control message bufffer. This is dynamic because | |
170 | * CMSG_SPACE is not guaranteed not to call a function. Note that | |
171 | * the size will be different on different architectures due to | |
172 | * differing alignment rules. | |
173 | */ | |
174 | if (adata == NULL) | |
175 | { | |
176 | /* XXX Free on shutdown. */ | |
177 | adata = malloc(CMSG_SPACE(sizeof(struct in6_pktinfo))); | |
178 | ||
179 | if (adata == NULL) | |
f360eac0 | 180 | zlog_err("rtadv_send_packet: can't malloc control data"); |
57492d56 | 181 | } |
182 | ||
718e3744 | 183 | /* Logging of packet. */ |
184 | if (IS_ZEBRA_DEBUG_PACKET) | |
f360eac0 | 185 | zlog_debug ("%s(%u): Tx RA, socket %u", |
186 | ifp->name, ifp->ifindex, sock); | |
718e3744 | 187 | |
188 | /* Fill in sockaddr_in6. */ | |
189 | memset (&addr, 0, sizeof (struct sockaddr_in6)); | |
190 | addr.sin6_family = AF_INET6; | |
191 | #ifdef SIN6_LEN | |
192 | addr.sin6_len = sizeof (struct sockaddr_in6); | |
193 | #endif /* SIN6_LEN */ | |
194 | addr.sin6_port = htons (IPPROTO_ICMPV6); | |
aca43b65 | 195 | IPV6_ADDR_COPY (&addr.sin6_addr, all_nodes_addr); |
718e3744 | 196 | |
197 | /* Fetch interface information. */ | |
198 | zif = ifp->info; | |
199 | ||
200 | /* Make router advertisement message. */ | |
201 | rtadv = (struct nd_router_advert *) buf; | |
202 | ||
203 | rtadv->nd_ra_type = ND_ROUTER_ADVERT; | |
204 | rtadv->nd_ra_code = 0; | |
205 | rtadv->nd_ra_cksum = 0; | |
206 | ||
207 | rtadv->nd_ra_curhoplimit = 64; | |
b60668d0 CC |
208 | |
209 | /* RFC4191: Default Router Preference is 0 if Router Lifetime is 0. */ | |
210 | rtadv->nd_ra_flags_reserved = | |
211 | zif->rtadv.AdvDefaultLifetime == 0 ? 0 : zif->rtadv.DefaultPreference; | |
212 | rtadv->nd_ra_flags_reserved <<= 3; | |
213 | ||
718e3744 | 214 | if (zif->rtadv.AdvManagedFlag) |
215 | rtadv->nd_ra_flags_reserved |= ND_RA_FLAG_MANAGED; | |
216 | if (zif->rtadv.AdvOtherConfigFlag) | |
217 | rtadv->nd_ra_flags_reserved |= ND_RA_FLAG_OTHER; | |
7cee1bb1 | 218 | if (zif->rtadv.AdvHomeAgentFlag) |
219 | rtadv->nd_ra_flags_reserved |= ND_RA_FLAG_HOME_AGENT; | |
d660f698 DO |
220 | /* Note that according to Neighbor Discovery (RFC 4861 [18]), |
221 | * AdvDefaultLifetime is by default based on the value of | |
222 | * MaxRtrAdvInterval. AdvDefaultLifetime is used in the Router Lifetime | |
223 | * field of Router Advertisements. Given that this field is expressed | |
224 | * in seconds, a small MaxRtrAdvInterval value can result in a zero | |
225 | * value for this field. To prevent this, routers SHOULD keep | |
226 | * AdvDefaultLifetime in at least one second, even if the use of | |
227 | * MaxRtrAdvInterval would result in a smaller value. -- RFC6275, 7.5 */ | |
228 | pkt_RouterLifetime = zif->rtadv.AdvDefaultLifetime != -1 ? | |
229 | zif->rtadv.AdvDefaultLifetime : | |
230 | MAX (1, 0.003 * zif->rtadv.MaxRtrAdvInterval); | |
231 | rtadv->nd_ra_router_lifetime = htons (pkt_RouterLifetime); | |
718e3744 | 232 | rtadv->nd_ra_reachable = htonl (zif->rtadv.AdvReachableTime); |
233 | rtadv->nd_ra_retransmit = htonl (0); | |
234 | ||
235 | len = sizeof (struct nd_router_advert); | |
236 | ||
d660f698 DO |
237 | /* If both the Home Agent Preference and Home Agent Lifetime are set to |
238 | * their default values specified above, this option SHOULD NOT be | |
239 | * included in the Router Advertisement messages sent by this home | |
240 | * agent. -- RFC6275, 7.4 */ | |
241 | if | |
242 | ( | |
243 | zif->rtadv.AdvHomeAgentFlag && | |
244 | (zif->rtadv.HomeAgentPreference || zif->rtadv.HomeAgentLifetime != -1) | |
245 | ) | |
7cee1bb1 | 246 | { |
247 | struct nd_opt_homeagent_info *ndopt_hai = | |
248 | (struct nd_opt_homeagent_info *)(buf + len); | |
249 | ndopt_hai->nd_opt_hai_type = ND_OPT_HA_INFORMATION; | |
250 | ndopt_hai->nd_opt_hai_len = 1; | |
251 | ndopt_hai->nd_opt_hai_reserved = 0; | |
252 | ndopt_hai->nd_opt_hai_preference = htons(zif->rtadv.HomeAgentPreference); | |
d660f698 DO |
253 | /* 16-bit unsigned integer. The lifetime associated with the home |
254 | * agent in units of seconds. The default value is the same as the | |
255 | * Router Lifetime, as specified in the main body of the Router | |
256 | * Advertisement. The maximum value corresponds to 18.2 hours. A | |
257 | * value of 0 MUST NOT be used. -- RFC6275, 7.5 */ | |
258 | ndopt_hai->nd_opt_hai_lifetime = htons | |
259 | ( | |
260 | zif->rtadv.HomeAgentLifetime != -1 ? | |
261 | zif->rtadv.HomeAgentLifetime : | |
262 | MAX (1, pkt_RouterLifetime) /* 0 is OK for RL, but not for HAL*/ | |
263 | ); | |
7cee1bb1 | 264 | len += sizeof(struct nd_opt_homeagent_info); |
265 | } | |
266 | ||
267 | if (zif->rtadv.AdvIntervalOption) | |
268 | { | |
269 | struct nd_opt_adv_interval *ndopt_adv = | |
270 | (struct nd_opt_adv_interval *)(buf + len); | |
271 | ndopt_adv->nd_opt_ai_type = ND_OPT_ADV_INTERVAL; | |
272 | ndopt_adv->nd_opt_ai_len = 1; | |
273 | ndopt_adv->nd_opt_ai_reserved = 0; | |
274 | ndopt_adv->nd_opt_ai_interval = htonl(zif->rtadv.MaxRtrAdvInterval); | |
275 | len += sizeof(struct nd_opt_adv_interval); | |
276 | } | |
277 | ||
718e3744 | 278 | /* Fill in prefix. */ |
1eb8ef25 | 279 | for (ALL_LIST_ELEMENTS_RO (zif->rtadv.AdvPrefixList, node, rprefix)) |
718e3744 | 280 | { |
281 | struct nd_opt_prefix_info *pinfo; | |
718e3744 | 282 | |
283 | pinfo = (struct nd_opt_prefix_info *) (buf + len); | |
284 | ||
285 | pinfo->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION; | |
286 | pinfo->nd_opt_pi_len = 4; | |
287 | pinfo->nd_opt_pi_prefix_len = rprefix->prefix.prefixlen; | |
288 | ||
289 | pinfo->nd_opt_pi_flags_reserved = 0; | |
290 | if (rprefix->AdvOnLinkFlag) | |
291 | pinfo->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_ONLINK; | |
292 | if (rprefix->AdvAutonomousFlag) | |
293 | pinfo->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_AUTO; | |
7cee1bb1 | 294 | if (rprefix->AdvRouterAddressFlag) |
295 | pinfo->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_RADDR; | |
718e3744 | 296 | |
297 | pinfo->nd_opt_pi_valid_time = htonl (rprefix->AdvValidLifetime); | |
298 | pinfo->nd_opt_pi_preferred_time = htonl (rprefix->AdvPreferredLifetime); | |
299 | pinfo->nd_opt_pi_reserved2 = 0; | |
300 | ||
aca43b65 | 301 | IPV6_ADDR_COPY (&pinfo->nd_opt_pi_prefix, &rprefix->prefix.prefix); |
718e3744 | 302 | |
303 | #ifdef DEBUG | |
304 | { | |
305 | u_char buf[INET6_ADDRSTRLEN]; | |
306 | ||
b6178002 | 307 | zlog_debug ("DEBUG %s", inet_ntop (AF_INET6, &pinfo->nd_opt_pi_prefix, |
3e31cded | 308 | buf, INET6_ADDRSTRLEN)); |
718e3744 | 309 | |
310 | } | |
311 | #endif /* DEBUG */ | |
312 | ||
313 | len += sizeof (struct nd_opt_prefix_info); | |
314 | } | |
315 | ||
316 | /* Hardware address. */ | |
718e3744 | 317 | if (ifp->hw_addr_len != 0) |
318 | { | |
319 | buf[len++] = ND_OPT_SOURCE_LINKADDR; | |
50adf783 DW |
320 | |
321 | /* Option length should be rounded up to next octet if | |
322 | the link address does not end on an octet boundary. */ | |
323 | buf[len++] = (ifp->hw_addr_len + 9) >> 3; | |
718e3744 | 324 | |
325 | memcpy (buf + len, ifp->hw_addr, ifp->hw_addr_len); | |
326 | len += ifp->hw_addr_len; | |
50adf783 DW |
327 | |
328 | /* Pad option to end on an octet boundary. */ | |
329 | memset (buf + len, 0, -(ifp->hw_addr_len + 2) & 0x7); | |
330 | len += -(ifp->hw_addr_len + 2) & 0x7; | |
718e3744 | 331 | } |
718e3744 | 332 | |
6ae93c05 DO |
333 | /* MTU */ |
334 | if (zif->rtadv.AdvLinkMTU) | |
335 | { | |
336 | struct nd_opt_mtu * opt = (struct nd_opt_mtu *) (buf + len); | |
337 | opt->nd_opt_mtu_type = ND_OPT_MTU; | |
338 | opt->nd_opt_mtu_len = 1; | |
339 | opt->nd_opt_mtu_reserved = 0; | |
340 | opt->nd_opt_mtu_mtu = htonl (zif->rtadv.AdvLinkMTU); | |
341 | len += sizeof (struct nd_opt_mtu); | |
342 | } | |
343 | ||
718e3744 | 344 | msg.msg_name = (void *) &addr; |
345 | msg.msg_namelen = sizeof (struct sockaddr_in6); | |
346 | msg.msg_iov = &iov; | |
347 | msg.msg_iovlen = 1; | |
348 | msg.msg_control = (void *) adata; | |
f841e02e | 349 | msg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); |
57492d56 | 350 | msg.msg_flags = 0; |
718e3744 | 351 | iov.iov_base = buf; |
352 | iov.iov_len = len; | |
353 | ||
b99760ab | 354 | cmsgptr = ZCMSG_FIRSTHDR(&msg); |
57492d56 | 355 | cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); |
718e3744 | 356 | cmsgptr->cmsg_level = IPPROTO_IPV6; |
357 | cmsgptr->cmsg_type = IPV6_PKTINFO; | |
80893817 | 358 | |
718e3744 | 359 | pkt = (struct in6_pktinfo *) CMSG_DATA (cmsgptr); |
360 | memset (&pkt->ipi6_addr, 0, sizeof (struct in6_addr)); | |
361 | pkt->ipi6_ifindex = ifp->ifindex; | |
362 | ||
363 | ret = sendmsg (sock, &msg, 0); | |
9ccabd1c | 364 | if (ret < 0) |
365 | { | |
f360eac0 | 366 | zlog_err ("%s(%u): Tx RA failed, socket %u error %d (%s)", |
367 | ifp->name, ifp->ifindex, sock, errno, safe_strerror(errno)); | |
9ccabd1c | 368 | } |
795b5abf QY |
369 | else |
370 | zif->ra_sent++; | |
718e3744 | 371 | } |
372 | ||
a1ac18c4 | 373 | static int |
718e3744 | 374 | rtadv_timer (struct thread *thread) |
375 | { | |
43459b1f | 376 | struct zebra_ns *zns = THREAD_ARG (thread); |
1a1a7065 | 377 | struct vrf *vrf; |
1eb8ef25 | 378 | struct listnode *node, *nnode; |
718e3744 | 379 | struct interface *ifp; |
380 | struct zebra_if *zif; | |
7cee1bb1 | 381 | int period; |
718e3744 | 382 | |
43459b1f | 383 | zns->rtadv.ra_timer = NULL; |
384 | if (zns->rtadv.adv_msec_if_count == 0) | |
7cee1bb1 | 385 | { |
386 | period = 1000; /* 1 s */ | |
43459b1f | 387 | rtadv_event (zns, RTADV_TIMER, 1 /* 1 s */); |
7cee1bb1 | 388 | } |
389 | else | |
390 | { | |
391 | period = 10; /* 10 ms */ | |
43459b1f | 392 | rtadv_event (zns, RTADV_TIMER_MSEC, 10 /* 10 ms */); |
7cee1bb1 | 393 | } |
718e3744 | 394 | |
1a1a7065 RW |
395 | RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) |
396 | for (ALL_LIST_ELEMENTS (vrf->iflist, node, nnode, ifp)) | |
43459b1f | 397 | { |
c23af4d3 | 398 | if (if_is_loopback (ifp) || |
399 | CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK) || | |
400 | ! if_is_operative (ifp)) | |
43459b1f | 401 | continue; |
402 | ||
403 | zif = ifp->info; | |
404 | ||
405 | if (zif->rtadv.AdvSendAdvertisements) | |
406 | { | |
6c9678b4 | 407 | if (zif->rtadv.inFastRexmit) |
43459b1f | 408 | { |
6c9678b4 DD |
409 | /* We assume we fast rexmit every sec so no additional vars */ |
410 | if (--zif->rtadv.NumFastReXmitsRemain <= 0) | |
411 | zif->rtadv.inFastRexmit = 0; | |
412 | ||
413 | if (IS_ZEBRA_DEBUG_SEND) | |
414 | zlog_debug("Fast RA Rexmit on interface %s", ifp->name); | |
415 | ||
43459b1f | 416 | rtadv_send_packet (zns->rtadv.sock, ifp); |
417 | } | |
6c9678b4 DD |
418 | else |
419 | { | |
420 | zif->rtadv.AdvIntervalTimer -= period; | |
421 | if (zif->rtadv.AdvIntervalTimer <= 0) | |
422 | { | |
423 | /* FIXME: using MaxRtrAdvInterval each time isn't what section | |
424 | 6.2.4 of RFC4861 tells to do. */ | |
425 | zif->rtadv.AdvIntervalTimer = zif->rtadv.MaxRtrAdvInterval; | |
426 | rtadv_send_packet (zns->rtadv.sock, ifp); | |
427 | } | |
428 | } | |
43459b1f | 429 | } |
430 | } | |
718e3744 | 431 | |
718e3744 | 432 | return 0; |
433 | } | |
434 | ||
a1ac18c4 | 435 | static void |
718e3744 | 436 | rtadv_process_solicit (struct interface *ifp) |
437 | { | |
cd80d74f | 438 | struct zebra_vrf *zvrf = vrf_info_lookup (ifp->vrf_id); |
43459b1f | 439 | struct zebra_ns *zns = zvrf->zns; |
718e3744 | 440 | |
43459b1f | 441 | assert (zns); |
442 | rtadv_send_packet (zns->rtadv.sock, ifp); | |
718e3744 | 443 | } |
444 | ||
a1ac18c4 | 445 | static void |
a80beece DS |
446 | rtadv_process_advert (u_char *msg, unsigned int len, struct interface *ifp, |
447 | struct sockaddr_in6 *addr) | |
718e3744 | 448 | { |
a80beece DS |
449 | struct nd_router_advert *radvert; |
450 | char addr_str[INET6_ADDRSTRLEN]; | |
451 | struct zebra_if *zif; | |
1d20ccf3 | 452 | struct prefix p; |
a80beece DS |
453 | |
454 | zif = ifp->info; | |
455 | ||
456 | inet_ntop (AF_INET6, &addr->sin6_addr, addr_str, INET6_ADDRSTRLEN); | |
457 | ||
a80beece | 458 | if (len < sizeof(struct nd_router_advert)) { |
f360eac0 | 459 | zlog_warn("%s(%u): Rx RA with invalid length %d from %s", |
460 | ifp->name, ifp->ifindex, len, addr_str); | |
a80beece DS |
461 | return; |
462 | } | |
463 | if (!IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr)) { | |
f360eac0 | 464 | zlog_warn("%s(%u): Rx RA with non-linklocal source address from %s", |
465 | ifp->name, ifp->ifindex, addr_str); | |
a80beece DS |
466 | return; |
467 | } | |
468 | ||
469 | radvert = (struct nd_router_advert *) msg; | |
470 | ||
471 | if ((radvert->nd_ra_curhoplimit && zif->rtadv.AdvCurHopLimit) && | |
472 | (radvert->nd_ra_curhoplimit != zif->rtadv.AdvCurHopLimit)) | |
473 | { | |
f360eac0 | 474 | zlog_warn("%s(%u): Rx RA - our AdvCurHopLimit doesn't agree with %s", |
475 | ifp->name, ifp->ifindex, addr_str); | |
a80beece DS |
476 | } |
477 | ||
478 | if ((radvert->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) && | |
479 | !zif->rtadv.AdvManagedFlag) | |
480 | { | |
f360eac0 | 481 | zlog_warn("%s(%u): Rx RA - our AdvManagedFlag doesn't agree with %s", |
482 | ifp->name, ifp->ifindex, addr_str); | |
a80beece DS |
483 | } |
484 | ||
485 | if ((radvert->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) && | |
486 | !zif->rtadv.AdvOtherConfigFlag) | |
487 | { | |
f360eac0 | 488 | zlog_warn("%s(%u): Rx RA - our AdvOtherConfigFlag doesn't agree with %s", |
489 | ifp->name, ifp->ifindex, addr_str); | |
a80beece DS |
490 | } |
491 | ||
492 | if ((radvert->nd_ra_reachable && zif->rtadv.AdvReachableTime) && | |
493 | (ntohl(radvert->nd_ra_reachable) != zif->rtadv.AdvReachableTime)) | |
494 | { | |
f360eac0 | 495 | zlog_warn("%s(%u): Rx RA - our AdvReachableTime doesn't agree with %s", |
496 | ifp->name, ifp->ifindex, addr_str); | |
a80beece DS |
497 | } |
498 | ||
499 | if ((radvert->nd_ra_retransmit && zif->rtadv.AdvRetransTimer) && | |
4e3afb14 | 500 | (ntohl(radvert->nd_ra_retransmit) != (unsigned int)zif->rtadv.AdvRetransTimer)) |
a80beece | 501 | { |
f360eac0 | 502 | zlog_warn("%s(%u): Rx RA - our AdvRetransTimer doesn't agree with %s", |
503 | ifp->name, ifp->ifindex, addr_str); | |
a80beece DS |
504 | } |
505 | ||
1d20ccf3 | 506 | /* Create entry for neighbor if not known. */ |
507 | p.family = AF_INET6; | |
508 | IPV6_ADDR_COPY (&p.u.prefix, &addr->sin6_addr); | |
509 | p.prefixlen = IPV6_MAX_PREFIXLEN; | |
a80beece | 510 | |
1d20ccf3 | 511 | if (!nbr_connected_check(ifp, &p)) |
512 | nbr_connected_add_ipv6 (ifp, &addr->sin6_addr); | |
718e3744 | 513 | } |
514 | ||
a80beece | 515 | |
a1ac18c4 | 516 | static void |
b892f1dd | 517 | rtadv_process_packet (u_char *buf, unsigned int len, ifindex_t ifindex, int hoplimit, |
43459b1f | 518 | struct sockaddr_in6 *from, struct zebra_ns *zns) |
718e3744 | 519 | { |
520 | struct icmp6_hdr *icmph; | |
521 | struct interface *ifp; | |
522 | struct zebra_if *zif; | |
f360eac0 | 523 | char addr_str[INET6_ADDRSTRLEN]; |
524 | ||
525 | inet_ntop (AF_INET6, &from->sin6_addr, addr_str, INET6_ADDRSTRLEN); | |
718e3744 | 526 | |
527 | /* Interface search. */ | |
43459b1f | 528 | ifp = if_lookup_by_index_per_ns (zns, ifindex); |
718e3744 | 529 | if (ifp == NULL) |
530 | { | |
f360eac0 | 531 | zlog_warn ("RA/RS received on unknown IF %u from %s", |
532 | ifindex, addr_str); | |
718e3744 | 533 | return; |
534 | } | |
535 | ||
f360eac0 | 536 | if (IS_ZEBRA_DEBUG_PACKET) |
537 | zlog_debug ("%s(%u): Rx RA/RS len %d from %s", | |
538 | ifp->name, ifp->ifindex, len, addr_str); | |
539 | ||
c23af4d3 | 540 | if (if_is_loopback (ifp) || |
541 | CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK)) | |
718e3744 | 542 | return; |
543 | ||
544 | /* Check interface configuration. */ | |
545 | zif = ifp->info; | |
546 | if (! zif->rtadv.AdvSendAdvertisements) | |
547 | return; | |
548 | ||
549 | /* ICMP message length check. */ | |
550 | if (len < sizeof (struct icmp6_hdr)) | |
551 | { | |
f360eac0 | 552 | zlog_warn ("%s(%u): Rx RA with Invalid ICMPV6 packet length %d", |
553 | ifp->name, ifp->ifindex, len); | |
718e3744 | 554 | return; |
555 | } | |
556 | ||
557 | icmph = (struct icmp6_hdr *) buf; | |
558 | ||
559 | /* ICMP message type check. */ | |
560 | if (icmph->icmp6_type != ND_ROUTER_SOLICIT && | |
561 | icmph->icmp6_type != ND_ROUTER_ADVERT) | |
562 | { | |
f360eac0 | 563 | zlog_warn ("%s(%u): Rx RA - Unwanted ICMPV6 message type %d", |
564 | ifp->name, ifp->ifindex, icmph->icmp6_type); | |
718e3744 | 565 | return; |
566 | } | |
567 | ||
568 | /* Hoplimit check. */ | |
569 | if (hoplimit >= 0 && hoplimit != 255) | |
570 | { | |
f360eac0 | 571 | zlog_warn ("%s(%u): Rx RA - Invalid hoplimit %d", |
572 | ifp->name, ifp->ifindex, hoplimit); | |
718e3744 | 573 | return; |
574 | } | |
575 | ||
576 | /* Check ICMP message type. */ | |
577 | if (icmph->icmp6_type == ND_ROUTER_SOLICIT) | |
578 | rtadv_process_solicit (ifp); | |
579 | else if (icmph->icmp6_type == ND_ROUTER_ADVERT) | |
a80beece | 580 | rtadv_process_advert (buf, len, ifp, from); |
718e3744 | 581 | |
582 | return; | |
583 | } | |
584 | ||
a1ac18c4 | 585 | static int |
718e3744 | 586 | rtadv_read (struct thread *thread) |
587 | { | |
588 | int sock; | |
589 | int len; | |
590 | u_char buf[RTADV_MSG_SIZE]; | |
591 | struct sockaddr_in6 from; | |
b892f1dd | 592 | ifindex_t ifindex = 0; |
718e3744 | 593 | int hoplimit = -1; |
43459b1f | 594 | struct zebra_ns *zns = THREAD_ARG (thread); |
718e3744 | 595 | |
596 | sock = THREAD_FD (thread); | |
43459b1f | 597 | zns->rtadv.ra_read = NULL; |
718e3744 | 598 | |
599 | /* Register myself. */ | |
43459b1f | 600 | rtadv_event (zns, RTADV_READ, sock); |
718e3744 | 601 | |
d9ce8324 | 602 | len = rtadv_recv_packet (zns, sock, buf, sizeof (buf), &from, &ifindex, &hoplimit); |
718e3744 | 603 | |
604 | if (len < 0) | |
605 | { | |
f360eac0 | 606 | zlog_warn ("RA/RS recv failed, socket %u error %s", |
607 | sock, safe_strerror (errno)); | |
718e3744 | 608 | return len; |
609 | } | |
610 | ||
43459b1f | 611 | rtadv_process_packet (buf, (unsigned)len, ifindex, hoplimit, &from, zns); |
718e3744 | 612 | |
613 | return 0; | |
614 | } | |
615 | ||
a1ac18c4 | 616 | static int |
43459b1f | 617 | rtadv_make_socket (void) |
718e3744 | 618 | { |
619 | int sock; | |
43459b1f | 620 | int ret = 0; |
718e3744 | 621 | struct icmp6_filter filter; |
622 | ||
edd7c245 | 623 | if ( zserv_privs.change (ZPRIVS_RAISE) ) |
624 | zlog_err ("rtadv_make_socket: could not raise privs, %s", | |
6099b3b5 | 625 | safe_strerror (errno) ); |
edd7c245 | 626 | |
43459b1f | 627 | sock = socket (AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); |
718e3744 | 628 | |
edd7c245 | 629 | if ( zserv_privs.change (ZPRIVS_LOWER) ) |
630 | zlog_err ("rtadv_make_socket: could not lower privs, %s", | |
6099b3b5 | 631 | safe_strerror (errno) ); |
edd7c245 | 632 | |
718e3744 | 633 | if (sock < 0) |
a5c304ab MS |
634 | { |
635 | close (sock); | |
636 | return -1; | |
637 | } | |
718e3744 | 638 | |
639 | ret = setsockopt_ipv6_pktinfo (sock, 1); | |
718e3744 | 640 | if (ret < 0) |
a5c304ab MS |
641 | { |
642 | close (sock); | |
643 | return ret; | |
644 | } | |
718e3744 | 645 | ret = setsockopt_ipv6_multicast_loop (sock, 0); |
646 | if (ret < 0) | |
a5c304ab MS |
647 | { |
648 | close (sock); | |
649 | return ret; | |
650 | } | |
718e3744 | 651 | ret = setsockopt_ipv6_unicast_hops (sock, 255); |
652 | if (ret < 0) | |
a5c304ab MS |
653 | { |
654 | close (sock); | |
655 | return ret; | |
656 | } | |
718e3744 | 657 | ret = setsockopt_ipv6_multicast_hops (sock, 255); |
658 | if (ret < 0) | |
a5c304ab MS |
659 | { |
660 | close (sock); | |
661 | return ret; | |
662 | } | |
718e3744 | 663 | ret = setsockopt_ipv6_hoplimit (sock, 1); |
664 | if (ret < 0) | |
a5c304ab MS |
665 | { |
666 | close (sock); | |
667 | return ret; | |
668 | } | |
718e3744 | 669 | |
670 | ICMP6_FILTER_SETBLOCKALL(&filter); | |
671 | ICMP6_FILTER_SETPASS (ND_ROUTER_SOLICIT, &filter); | |
672 | ICMP6_FILTER_SETPASS (ND_ROUTER_ADVERT, &filter); | |
673 | ||
674 | ret = setsockopt (sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, | |
675 | sizeof (struct icmp6_filter)); | |
676 | if (ret < 0) | |
677 | { | |
6099b3b5 | 678 | zlog_info ("ICMP6_FILTER set fail: %s", safe_strerror (errno)); |
718e3744 | 679 | return ret; |
680 | } | |
681 | ||
682 | return sock; | |
683 | } | |
6b0655a2 | 684 | |
a1ac18c4 | 685 | static struct rtadv_prefix * |
66e5cd87 | 686 | rtadv_prefix_new (void) |
718e3744 | 687 | { |
393deb9b | 688 | return XCALLOC (MTYPE_RTADV_PREFIX, sizeof (struct rtadv_prefix)); |
718e3744 | 689 | } |
690 | ||
a1ac18c4 | 691 | static void |
718e3744 | 692 | rtadv_prefix_free (struct rtadv_prefix *rtadv_prefix) |
693 | { | |
694 | XFREE (MTYPE_RTADV_PREFIX, rtadv_prefix); | |
695 | } | |
696 | ||
a1ac18c4 | 697 | static struct rtadv_prefix * |
aca43b65 | 698 | rtadv_prefix_lookup (struct list *rplist, struct prefix_ipv6 *p) |
718e3744 | 699 | { |
52dc7ee6 | 700 | struct listnode *node; |
718e3744 | 701 | struct rtadv_prefix *rprefix; |
702 | ||
1eb8ef25 | 703 | for (ALL_LIST_ELEMENTS_RO (rplist, node, rprefix)) |
aca43b65 | 704 | if (prefix_same ((struct prefix *) &rprefix->prefix, (struct prefix *) p)) |
1eb8ef25 | 705 | return rprefix; |
718e3744 | 706 | return NULL; |
707 | } | |
708 | ||
a1ac18c4 | 709 | static struct rtadv_prefix * |
aca43b65 | 710 | rtadv_prefix_get (struct list *rplist, struct prefix_ipv6 *p) |
718e3744 | 711 | { |
712 | struct rtadv_prefix *rprefix; | |
713 | ||
714 | rprefix = rtadv_prefix_lookup (rplist, p); | |
715 | if (rprefix) | |
716 | return rprefix; | |
717 | ||
718 | rprefix = rtadv_prefix_new (); | |
aca43b65 | 719 | memcpy (&rprefix->prefix, p, sizeof (struct prefix_ipv6)); |
718e3744 | 720 | listnode_add (rplist, rprefix); |
721 | ||
722 | return rprefix; | |
723 | } | |
724 | ||
a1ac18c4 | 725 | static void |
718e3744 | 726 | rtadv_prefix_set (struct zebra_if *zif, struct rtadv_prefix *rp) |
727 | { | |
728 | struct rtadv_prefix *rprefix; | |
729 | ||
730 | rprefix = rtadv_prefix_get (zif->rtadv.AdvPrefixList, &rp->prefix); | |
731 | ||
732 | /* Set parameters. */ | |
733 | rprefix->AdvValidLifetime = rp->AdvValidLifetime; | |
734 | rprefix->AdvPreferredLifetime = rp->AdvPreferredLifetime; | |
735 | rprefix->AdvOnLinkFlag = rp->AdvOnLinkFlag; | |
736 | rprefix->AdvAutonomousFlag = rp->AdvAutonomousFlag; | |
7cee1bb1 | 737 | rprefix->AdvRouterAddressFlag = rp->AdvRouterAddressFlag; |
718e3744 | 738 | } |
739 | ||
a1ac18c4 | 740 | static int |
718e3744 | 741 | rtadv_prefix_reset (struct zebra_if *zif, struct rtadv_prefix *rp) |
742 | { | |
743 | struct rtadv_prefix *rprefix; | |
744 | ||
745 | rprefix = rtadv_prefix_lookup (zif->rtadv.AdvPrefixList, &rp->prefix); | |
746 | if (rprefix != NULL) | |
747 | { | |
748 | listnode_delete (zif->rtadv.AdvPrefixList, (void *) rprefix); | |
749 | rtadv_prefix_free (rprefix); | |
750 | return 1; | |
751 | } | |
752 | else | |
753 | return 0; | |
754 | } | |
755 | ||
4a04e5f7 | 756 | static void |
b6120505 DW |
757 | ipv6_nd_suppress_ra_set (struct interface *ifp, ipv6_nd_suppress_ra_status status) |
758 | { | |
759 | struct zebra_if *zif; | |
760 | struct zebra_vrf *zvrf; | |
43459b1f | 761 | struct zebra_ns *zns; |
b6120505 DW |
762 | |
763 | zif = ifp->info; | |
764 | zvrf = vrf_info_lookup (ifp->vrf_id); | |
43459b1f | 765 | zns = zvrf->zns; |
b6120505 DW |
766 | |
767 | if (status == RA_SUPPRESS) | |
768 | { | |
769 | /* RA is currently enabled */ | |
770 | if (zif->rtadv.AdvSendAdvertisements) | |
771 | { | |
772 | zif->rtadv.AdvSendAdvertisements = 0; | |
773 | zif->rtadv.AdvIntervalTimer = 0; | |
43459b1f | 774 | zns->rtadv.adv_if_count--; |
b6120505 | 775 | |
43459b1f | 776 | if_leave_all_router (zns->rtadv.sock, ifp); |
b6120505 | 777 | |
43459b1f | 778 | if (zns->rtadv.adv_if_count == 0) |
779 | rtadv_event (zns, RTADV_STOP, 0); | |
b6120505 DW |
780 | } |
781 | } | |
782 | else | |
783 | { | |
784 | if (! zif->rtadv.AdvSendAdvertisements) | |
785 | { | |
786 | zif->rtadv.AdvSendAdvertisements = 1; | |
787 | zif->rtadv.AdvIntervalTimer = 0; | |
43459b1f | 788 | zns->rtadv.adv_if_count++; |
b6120505 | 789 | |
6c9678b4 DD |
790 | if (zif->rtadv.MaxRtrAdvInterval >= 1000) |
791 | { | |
792 | /* Enable Fast RA only when RA interval is in secs */ | |
793 | zif->rtadv.inFastRexmit = 1; | |
794 | zif->rtadv.NumFastReXmitsRemain = RTADV_NUM_FAST_REXMITS; | |
795 | } | |
796 | ||
43459b1f | 797 | if_join_all_router (zns->rtadv.sock, ifp); |
b6120505 | 798 | |
43459b1f | 799 | if (zns->rtadv.adv_if_count == 1) |
800 | rtadv_event (zns, RTADV_START, zns->rtadv.sock); | |
b6120505 DW |
801 | } |
802 | } | |
803 | } | |
804 | ||
4a04e5f7 | 805 | /* |
806 | * Handle client (BGP) message to enable or disable IPv6 RA on an interface. | |
807 | * Note that while the client could request RA on an interface on which the | |
808 | * operator has not enabled RA, RA won't be disabled upon client request | |
5c81b96a | 809 | * if the operator has explicitly enabled RA. The enable request can also |
810 | * specify a RA interval (in seconds). | |
4a04e5f7 | 811 | */ |
812 | void | |
813 | zebra_interface_radv_set (struct zserv *client, int sock, u_short length, | |
814 | struct zebra_vrf *zvrf, int enable) | |
815 | { | |
816 | struct stream *s; | |
817 | unsigned int ifindex; | |
818 | struct interface *ifp; | |
819 | struct zebra_if *zif; | |
5c81b96a | 820 | int ra_interval; |
4a04e5f7 | 821 | |
822 | s = client->ibuf; | |
823 | ||
5c81b96a | 824 | /* Get interface index and RA interval. */ |
4a04e5f7 | 825 | ifindex = stream_getl (s); |
5c81b96a | 826 | ra_interval = stream_getl (s); |
4a04e5f7 | 827 | |
828 | if (IS_ZEBRA_DEBUG_EVENT) | |
5c81b96a | 829 | zlog_debug("%u: IF %u RA %s from client %s, interval %ds", |
661512bf | 830 | zvrf_id (zvrf), ifindex, enable ? "enable" : "disable", |
5c81b96a | 831 | zebra_route_string(client->proto), ra_interval); |
4a04e5f7 | 832 | |
833 | /* Locate interface and check VRF match. */ | |
834 | ifp = if_lookup_by_index_per_ns (zebra_ns_lookup (NS_DEFAULT), ifindex); | |
835 | if (!ifp) | |
836 | { | |
837 | zlog_warn("%u: IF %u RA %s client %s - interface unknown", | |
661512bf | 838 | zvrf_id (zvrf), ifindex, enable ? "enable" : "disable", |
4a04e5f7 | 839 | zebra_route_string(client->proto)); |
840 | return; | |
841 | } | |
661512bf | 842 | if (ifp->vrf_id != zvrf_id (zvrf)) |
4a04e5f7 | 843 | { |
844 | zlog_warn("%u: IF %u RA %s client %s - VRF mismatch, IF VRF %u", | |
661512bf | 845 | zvrf_id (zvrf), ifindex, enable ? "enable" : "disable", |
4a04e5f7 | 846 | zebra_route_string(client->proto), ifp->vrf_id); |
847 | return; | |
848 | } | |
849 | ||
850 | zif = ifp->info; | |
851 | if (enable) | |
5c81b96a | 852 | { |
853 | ipv6_nd_suppress_ra_set (ifp, RA_ENABLE); | |
854 | if (ra_interval && | |
855 | (ra_interval * 1000) < zif->rtadv.MaxRtrAdvInterval) | |
856 | zif->rtadv.MaxRtrAdvInterval = ra_interval * 1000; | |
857 | } | |
4a04e5f7 | 858 | else |
859 | { | |
860 | if (!zif->rtadv.configured) | |
5c81b96a | 861 | { |
862 | zif->rtadv.MaxRtrAdvInterval = RTADV_MAX_RTR_ADV_INTERVAL; | |
863 | ipv6_nd_suppress_ra_set (ifp, RA_SUPPRESS); | |
864 | } | |
4a04e5f7 | 865 | } |
866 | } | |
867 | ||
718e3744 | 868 | DEFUN (ipv6_nd_suppress_ra, |
869 | ipv6_nd_suppress_ra_cmd, | |
870 | "ipv6 nd suppress-ra", | |
3e31cded | 871 | "Interface IPv6 config commands\n" |
718e3744 | 872 | "Neighbor discovery\n" |
873 | "Suppress Router Advertisement\n") | |
874 | { | |
3ddccf18 DL |
875 | VTY_DECLVAR_CONTEXT (interface, ifp); |
876 | struct zebra_if *zif = ifp->info; | |
718e3744 | 877 | |
c23af4d3 | 878 | if (if_is_loopback (ifp) || |
879 | CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK)) | |
718e3744 | 880 | { |
c23af4d3 | 881 | vty_out (vty, "Cannot configure IPv6 Router Advertisements on this interface%s", VTY_NEWLINE); |
718e3744 | 882 | return CMD_WARNING; |
883 | } | |
884 | ||
b6120505 | 885 | ipv6_nd_suppress_ra_set (ifp, RA_SUPPRESS); |
4a04e5f7 | 886 | zif->rtadv.configured = 0; |
718e3744 | 887 | return CMD_SUCCESS; |
888 | } | |
889 | ||
718e3744 | 890 | DEFUN (no_ipv6_nd_suppress_ra, |
891 | no_ipv6_nd_suppress_ra_cmd, | |
892 | "no ipv6 nd suppress-ra", | |
893 | NO_STR | |
3e31cded | 894 | "Interface IPv6 config commands\n" |
718e3744 | 895 | "Neighbor discovery\n" |
896 | "Suppress Router Advertisement\n") | |
897 | { | |
3ddccf18 DL |
898 | VTY_DECLVAR_CONTEXT (interface, ifp); |
899 | struct zebra_if *zif = ifp->info; | |
718e3744 | 900 | |
c23af4d3 | 901 | if (if_is_loopback (ifp) || |
902 | CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK)) | |
718e3744 | 903 | { |
c23af4d3 | 904 | vty_out (vty, "Cannot configure IPv6 Router Advertisements on this interface%s", VTY_NEWLINE); |
718e3744 | 905 | return CMD_WARNING; |
906 | } | |
907 | ||
b6120505 | 908 | ipv6_nd_suppress_ra_set (ifp, RA_ENABLE); |
4a04e5f7 | 909 | zif->rtadv.configured = 1; |
718e3744 | 910 | return CMD_SUCCESS; |
911 | } | |
912 | ||
7cee1bb1 | 913 | DEFUN (ipv6_nd_ra_interval_msec, |
914 | ipv6_nd_ra_interval_msec_cmd, | |
6147e2c6 | 915 | "ipv6 nd ra-interval msec (70-1800000)", |
7cee1bb1 | 916 | "Interface IPv6 config commands\n" |
917 | "Neighbor discovery\n" | |
918 | "Router Advertisement interval\n" | |
3a2d747c | 919 | "Router Advertisement interval in milliseconds\n" |
7cee1bb1 | 920 | "Router Advertisement interval in milliseconds\n") |
921 | { | |
7c022376 | 922 | int idx_number = 4; |
3ddccf18 | 923 | VTY_DECLVAR_CONTEXT (interface, ifp); |
4afa50b3 | 924 | unsigned interval; |
4afa50b3 | 925 | struct zebra_if *zif = ifp->info; |
cd80d74f | 926 | struct zebra_vrf *zvrf = vrf_info_lookup (ifp->vrf_id); |
43459b1f | 927 | struct zebra_ns *zns; |
7cee1bb1 | 928 | |
43459b1f | 929 | zns = zvrf->zns; |
7c022376 | 930 | VTY_GET_INTEGER_RANGE ("router advertisement interval", interval, argv[idx_number]->arg, 70, 1800000); |
4afa50b3 DO |
931 | if ((zif->rtadv.AdvDefaultLifetime != -1 && interval > (unsigned)zif->rtadv.AdvDefaultLifetime * 1000)) |
932 | { | |
933 | vty_out (vty, "This ra-interval would conflict with configured ra-lifetime!%s", VTY_NEWLINE); | |
934 | return CMD_WARNING; | |
935 | } | |
7cee1bb1 | 936 | |
937 | if (zif->rtadv.MaxRtrAdvInterval % 1000) | |
43459b1f | 938 | zns->rtadv.adv_msec_if_count--; |
7cee1bb1 | 939 | |
940 | if (interval % 1000) | |
43459b1f | 941 | zns->rtadv.adv_msec_if_count++; |
7cee1bb1 | 942 | |
943 | zif->rtadv.MaxRtrAdvInterval = interval; | |
944 | zif->rtadv.MinRtrAdvInterval = 0.33 * interval; | |
945 | zif->rtadv.AdvIntervalTimer = 0; | |
946 | ||
947 | return CMD_SUCCESS; | |
948 | } | |
949 | ||
718e3744 | 950 | DEFUN (ipv6_nd_ra_interval, |
951 | ipv6_nd_ra_interval_cmd, | |
6147e2c6 | 952 | "ipv6 nd ra-interval (1-1800)", |
3e31cded | 953 | "Interface IPv6 config commands\n" |
718e3744 | 954 | "Neighbor discovery\n" |
955 | "Router Advertisement interval\n" | |
956 | "Router Advertisement interval in seconds\n") | |
957 | { | |
7c022376 | 958 | int idx_number = 3; |
3ddccf18 | 959 | VTY_DECLVAR_CONTEXT (interface, ifp); |
4afa50b3 | 960 | unsigned interval; |
4afa50b3 | 961 | struct zebra_if *zif = ifp->info; |
cd80d74f | 962 | struct zebra_vrf *zvrf = vrf_info_lookup (ifp->vrf_id); |
43459b1f | 963 | struct zebra_ns *zns; |
718e3744 | 964 | |
43459b1f | 965 | zns = zvrf->zns; |
7c022376 | 966 | VTY_GET_INTEGER_RANGE ("router advertisement interval", interval, argv[idx_number]->arg, 1, 1800); |
4afa50b3 DO |
967 | if ((zif->rtadv.AdvDefaultLifetime != -1 && interval > (unsigned)zif->rtadv.AdvDefaultLifetime)) |
968 | { | |
969 | vty_out (vty, "This ra-interval would conflict with configured ra-lifetime!%s", VTY_NEWLINE); | |
970 | return CMD_WARNING; | |
971 | } | |
718e3744 | 972 | |
7cee1bb1 | 973 | if (zif->rtadv.MaxRtrAdvInterval % 1000) |
43459b1f | 974 | zns->rtadv.adv_msec_if_count--; |
7cee1bb1 | 975 | |
976 | /* convert to milliseconds */ | |
977 | interval = interval * 1000; | |
978 | ||
718e3744 | 979 | zif->rtadv.MaxRtrAdvInterval = interval; |
980 | zif->rtadv.MinRtrAdvInterval = 0.33 * interval; | |
981 | zif->rtadv.AdvIntervalTimer = 0; | |
982 | ||
983 | return CMD_SUCCESS; | |
984 | } | |
985 | ||
986 | DEFUN (no_ipv6_nd_ra_interval, | |
987 | no_ipv6_nd_ra_interval_cmd, | |
34ccea1e | 988 | "no ipv6 nd ra-interval [<(1-1800)|msec (1-1800000)>]", |
718e3744 | 989 | NO_STR |
3e31cded | 990 | "Interface IPv6 config commands\n" |
718e3744 | 991 | "Neighbor discovery\n" |
34ccea1e QY |
992 | "Router Advertisement interval\n" |
993 | "Router Advertisement interval in seconds\n" | |
994 | "Specify millisecond router advertisement interval\n" | |
995 | "Router Advertisement interval in milliseconds\n") | |
718e3744 | 996 | { |
3ddccf18 DL |
997 | VTY_DECLVAR_CONTEXT (interface, ifp); |
998 | struct zebra_if *zif = ifp->info; | |
cd80d74f | 999 | struct zebra_vrf *zvrf; |
43459b1f | 1000 | struct zebra_ns *zns; |
718e3744 | 1001 | |
cd80d74f | 1002 | zvrf = vrf_info_lookup (ifp->vrf_id); |
43459b1f | 1003 | zns = zvrf->zns; |
718e3744 | 1004 | |
7cee1bb1 | 1005 | if (zif->rtadv.MaxRtrAdvInterval % 1000) |
43459b1f | 1006 | zns->rtadv.adv_msec_if_count--; |
7cee1bb1 | 1007 | |
718e3744 | 1008 | zif->rtadv.MaxRtrAdvInterval = RTADV_MAX_RTR_ADV_INTERVAL; |
1009 | zif->rtadv.MinRtrAdvInterval = RTADV_MIN_RTR_ADV_INTERVAL; | |
1010 | zif->rtadv.AdvIntervalTimer = zif->rtadv.MaxRtrAdvInterval; | |
1011 | ||
1012 | return CMD_SUCCESS; | |
1013 | } | |
1014 | ||
1015 | DEFUN (ipv6_nd_ra_lifetime, | |
1016 | ipv6_nd_ra_lifetime_cmd, | |
6147e2c6 | 1017 | "ipv6 nd ra-lifetime (0-9000)", |
3e31cded | 1018 | "Interface IPv6 config commands\n" |
718e3744 | 1019 | "Neighbor discovery\n" |
1020 | "Router lifetime\n" | |
4afa50b3 | 1021 | "Router lifetime in seconds (0 stands for a non-default gw)\n") |
718e3744 | 1022 | { |
7c022376 | 1023 | int idx_number = 3; |
3ddccf18 DL |
1024 | VTY_DECLVAR_CONTEXT (interface, ifp); |
1025 | struct zebra_if *zif = ifp->info; | |
718e3744 | 1026 | int lifetime; |
718e3744 | 1027 | |
7c022376 | 1028 | VTY_GET_INTEGER_RANGE ("router lifetime", lifetime, argv[idx_number]->arg, 0, 9000); |
718e3744 | 1029 | |
d660f698 DO |
1030 | /* The value to be placed in the Router Lifetime field |
1031 | * of Router Advertisements sent from the interface, | |
1032 | * in seconds. MUST be either zero or between | |
1033 | * MaxRtrAdvInterval and 9000 seconds. -- RFC4861, 6.2.1 */ | |
4afa50b3 | 1034 | if ((lifetime != 0 && lifetime * 1000 < zif->rtadv.MaxRtrAdvInterval)) |
718e3744 | 1035 | { |
4afa50b3 | 1036 | vty_out (vty, "This ra-lifetime would conflict with configured ra-interval%s", VTY_NEWLINE); |
718e3744 | 1037 | return CMD_WARNING; |
1038 | } | |
1039 | ||
1040 | zif->rtadv.AdvDefaultLifetime = lifetime; | |
1041 | ||
1042 | return CMD_SUCCESS; | |
1043 | } | |
1044 | ||
1045 | DEFUN (no_ipv6_nd_ra_lifetime, | |
1046 | no_ipv6_nd_ra_lifetime_cmd, | |
34ccea1e | 1047 | "no ipv6 nd ra-lifetime [(0-9000)]", |
718e3744 | 1048 | NO_STR |
3e31cded | 1049 | "Interface IPv6 config commands\n" |
718e3744 | 1050 | "Neighbor discovery\n" |
34ccea1e QY |
1051 | "Router lifetime\n" |
1052 | "Router lifetime in seconds (0 stands for a non-default gw)\n") | |
718e3744 | 1053 | { |
3ddccf18 DL |
1054 | VTY_DECLVAR_CONTEXT (interface, ifp); |
1055 | struct zebra_if *zif = ifp->info; | |
718e3744 | 1056 | |
d660f698 | 1057 | zif->rtadv.AdvDefaultLifetime = -1; |
718e3744 | 1058 | |
1059 | return CMD_SUCCESS; | |
1060 | } | |
1061 | ||
1062 | DEFUN (ipv6_nd_reachable_time, | |
1063 | ipv6_nd_reachable_time_cmd, | |
6147e2c6 | 1064 | "ipv6 nd reachable-time (1-3600000)", |
3e31cded | 1065 | "Interface IPv6 config commands\n" |
718e3744 | 1066 | "Neighbor discovery\n" |
1067 | "Reachable time\n" | |
1068 | "Reachable time in milliseconds\n") | |
1069 | { | |
7c022376 | 1070 | int idx_number = 3; |
3ddccf18 | 1071 | VTY_DECLVAR_CONTEXT (interface, ifp); |
4afa50b3 | 1072 | struct zebra_if *zif = ifp->info; |
7c022376 | 1073 | VTY_GET_INTEGER_RANGE ("reachable time", zif->rtadv.AdvReachableTime, argv[idx_number]->arg, 1, RTADV_MAX_REACHABLE_TIME); |
718e3744 | 1074 | return CMD_SUCCESS; |
1075 | } | |
1076 | ||
1077 | DEFUN (no_ipv6_nd_reachable_time, | |
1078 | no_ipv6_nd_reachable_time_cmd, | |
34ccea1e | 1079 | "no ipv6 nd reachable-time [(1-3600000)]", |
718e3744 | 1080 | NO_STR |
3e31cded | 1081 | "Interface IPv6 config commands\n" |
718e3744 | 1082 | "Neighbor discovery\n" |
34ccea1e QY |
1083 | "Reachable time\n" |
1084 | "Reachable time in milliseconds\n") | |
718e3744 | 1085 | { |
3ddccf18 DL |
1086 | VTY_DECLVAR_CONTEXT (interface, ifp); |
1087 | struct zebra_if *zif = ifp->info; | |
718e3744 | 1088 | |
1089 | zif->rtadv.AdvReachableTime = 0; | |
1090 | ||
1091 | return CMD_SUCCESS; | |
1092 | } | |
1093 | ||
7cee1bb1 | 1094 | DEFUN (ipv6_nd_homeagent_preference, |
1095 | ipv6_nd_homeagent_preference_cmd, | |
6147e2c6 | 1096 | "ipv6 nd home-agent-preference (0-65535)", |
7cee1bb1 | 1097 | "Interface IPv6 config commands\n" |
1098 | "Neighbor discovery\n" | |
1099 | "Home Agent preference\n" | |
4afa50b3 | 1100 | "preference value (default is 0, least preferred)\n") |
7cee1bb1 | 1101 | { |
7c022376 | 1102 | int idx_number = 3; |
3ddccf18 | 1103 | VTY_DECLVAR_CONTEXT (interface, ifp); |
4afa50b3 | 1104 | struct zebra_if *zif = ifp->info; |
7c022376 | 1105 | VTY_GET_INTEGER_RANGE ("home agent preference", zif->rtadv.HomeAgentPreference, argv[idx_number]->arg, 0, 65535); |
7cee1bb1 | 1106 | return CMD_SUCCESS; |
1107 | } | |
1108 | ||
1109 | DEFUN (no_ipv6_nd_homeagent_preference, | |
1110 | no_ipv6_nd_homeagent_preference_cmd, | |
34ccea1e | 1111 | "no ipv6 nd home-agent-preference [(0-65535)]", |
7cee1bb1 | 1112 | NO_STR |
1113 | "Interface IPv6 config commands\n" | |
1114 | "Neighbor discovery\n" | |
34ccea1e QY |
1115 | "Home Agent preference\n" |
1116 | "preference value (default is 0, least preferred)\n") | |
7cee1bb1 | 1117 | { |
3ddccf18 DL |
1118 | VTY_DECLVAR_CONTEXT (interface, ifp); |
1119 | struct zebra_if *zif = ifp->info; | |
7cee1bb1 | 1120 | |
1121 | zif->rtadv.HomeAgentPreference = 0; | |
1122 | ||
1123 | return CMD_SUCCESS; | |
1124 | } | |
1125 | ||
1126 | DEFUN (ipv6_nd_homeagent_lifetime, | |
1127 | ipv6_nd_homeagent_lifetime_cmd, | |
6147e2c6 | 1128 | "ipv6 nd home-agent-lifetime (0-65520)", |
7cee1bb1 | 1129 | "Interface IPv6 config commands\n" |
1130 | "Neighbor discovery\n" | |
1131 | "Home Agent lifetime\n" | |
4afa50b3 | 1132 | "Home Agent lifetime in seconds (0 to track ra-lifetime)\n") |
7cee1bb1 | 1133 | { |
7c022376 | 1134 | int idx_number = 3; |
3ddccf18 | 1135 | VTY_DECLVAR_CONTEXT (interface, ifp); |
4afa50b3 | 1136 | struct zebra_if *zif = ifp->info; |
7c022376 | 1137 | VTY_GET_INTEGER_RANGE ("home agent lifetime", zif->rtadv.HomeAgentLifetime, argv[idx_number]->arg, 0, RTADV_MAX_HALIFETIME); |
7cee1bb1 | 1138 | return CMD_SUCCESS; |
1139 | } | |
1140 | ||
1141 | DEFUN (no_ipv6_nd_homeagent_lifetime, | |
1142 | no_ipv6_nd_homeagent_lifetime_cmd, | |
34ccea1e | 1143 | "no ipv6 nd home-agent-lifetime [(0-65520)]", |
7cee1bb1 | 1144 | NO_STR |
1145 | "Interface IPv6 config commands\n" | |
1146 | "Neighbor discovery\n" | |
34ccea1e QY |
1147 | "Home Agent lifetime\n" |
1148 | "Home Agent lifetime in seconds (0 to track ra-lifetime)\n") | |
7cee1bb1 | 1149 | { |
3ddccf18 DL |
1150 | VTY_DECLVAR_CONTEXT (interface, ifp); |
1151 | struct zebra_if *zif = ifp->info; | |
7cee1bb1 | 1152 | |
d660f698 | 1153 | zif->rtadv.HomeAgentLifetime = -1; |
7cee1bb1 | 1154 | |
1155 | return CMD_SUCCESS; | |
1156 | } | |
1157 | ||
718e3744 | 1158 | DEFUN (ipv6_nd_managed_config_flag, |
1159 | ipv6_nd_managed_config_flag_cmd, | |
1160 | "ipv6 nd managed-config-flag", | |
3e31cded | 1161 | "Interface IPv6 config commands\n" |
718e3744 | 1162 | "Neighbor discovery\n" |
1163 | "Managed address configuration flag\n") | |
1164 | { | |
3ddccf18 DL |
1165 | VTY_DECLVAR_CONTEXT (interface, ifp); |
1166 | struct zebra_if *zif = ifp->info; | |
718e3744 | 1167 | |
1168 | zif->rtadv.AdvManagedFlag = 1; | |
1169 | ||
1170 | return CMD_SUCCESS; | |
1171 | } | |
1172 | ||
1173 | DEFUN (no_ipv6_nd_managed_config_flag, | |
1174 | no_ipv6_nd_managed_config_flag_cmd, | |
1175 | "no ipv6 nd managed-config-flag", | |
1176 | NO_STR | |
3e31cded | 1177 | "Interface IPv6 config commands\n" |
718e3744 | 1178 | "Neighbor discovery\n" |
1179 | "Managed address configuration flag\n") | |
1180 | { | |
3ddccf18 DL |
1181 | VTY_DECLVAR_CONTEXT (interface, ifp); |
1182 | struct zebra_if *zif = ifp->info; | |
718e3744 | 1183 | |
1184 | zif->rtadv.AdvManagedFlag = 0; | |
1185 | ||
1186 | return CMD_SUCCESS; | |
1187 | } | |
1188 | ||
7cee1bb1 | 1189 | DEFUN (ipv6_nd_homeagent_config_flag, |
1190 | ipv6_nd_homeagent_config_flag_cmd, | |
1191 | "ipv6 nd home-agent-config-flag", | |
1192 | "Interface IPv6 config commands\n" | |
1193 | "Neighbor discovery\n" | |
1194 | "Home Agent configuration flag\n") | |
1195 | { | |
3ddccf18 DL |
1196 | VTY_DECLVAR_CONTEXT (interface, ifp); |
1197 | struct zebra_if *zif = ifp->info; | |
7cee1bb1 | 1198 | |
1199 | zif->rtadv.AdvHomeAgentFlag = 1; | |
1200 | ||
1201 | return CMD_SUCCESS; | |
1202 | } | |
1203 | ||
1204 | DEFUN (no_ipv6_nd_homeagent_config_flag, | |
1205 | no_ipv6_nd_homeagent_config_flag_cmd, | |
1206 | "no ipv6 nd home-agent-config-flag", | |
1207 | NO_STR | |
1208 | "Interface IPv6 config commands\n" | |
1209 | "Neighbor discovery\n" | |
1210 | "Home Agent configuration flag\n") | |
1211 | { | |
3ddccf18 DL |
1212 | VTY_DECLVAR_CONTEXT (interface, ifp); |
1213 | struct zebra_if *zif = ifp->info; | |
7cee1bb1 | 1214 | |
1215 | zif->rtadv.AdvHomeAgentFlag = 0; | |
1216 | ||
1217 | return CMD_SUCCESS; | |
1218 | } | |
1219 | ||
1220 | DEFUN (ipv6_nd_adv_interval_config_option, | |
1221 | ipv6_nd_adv_interval_config_option_cmd, | |
1222 | "ipv6 nd adv-interval-option", | |
1223 | "Interface IPv6 config commands\n" | |
1224 | "Neighbor discovery\n" | |
1225 | "Advertisement Interval Option\n") | |
1226 | { | |
3ddccf18 DL |
1227 | VTY_DECLVAR_CONTEXT (interface, ifp); |
1228 | struct zebra_if *zif = ifp->info; | |
7cee1bb1 | 1229 | |
1230 | zif->rtadv.AdvIntervalOption = 1; | |
1231 | ||
1232 | return CMD_SUCCESS; | |
1233 | } | |
1234 | ||
1235 | DEFUN (no_ipv6_nd_adv_interval_config_option, | |
1236 | no_ipv6_nd_adv_interval_config_option_cmd, | |
1237 | "no ipv6 nd adv-interval-option", | |
1238 | NO_STR | |
1239 | "Interface IPv6 config commands\n" | |
1240 | "Neighbor discovery\n" | |
1241 | "Advertisement Interval Option\n") | |
1242 | { | |
3ddccf18 DL |
1243 | VTY_DECLVAR_CONTEXT (interface, ifp); |
1244 | struct zebra_if *zif = ifp->info; | |
7cee1bb1 | 1245 | |
1246 | zif->rtadv.AdvIntervalOption = 0; | |
1247 | ||
1248 | return CMD_SUCCESS; | |
1249 | } | |
1250 | ||
718e3744 | 1251 | DEFUN (ipv6_nd_other_config_flag, |
1252 | ipv6_nd_other_config_flag_cmd, | |
1253 | "ipv6 nd other-config-flag", | |
3e31cded | 1254 | "Interface IPv6 config commands\n" |
718e3744 | 1255 | "Neighbor discovery\n" |
1256 | "Other statefull configuration flag\n") | |
1257 | { | |
3ddccf18 DL |
1258 | VTY_DECLVAR_CONTEXT (interface, ifp); |
1259 | struct zebra_if *zif = ifp->info; | |
718e3744 | 1260 | |
1261 | zif->rtadv.AdvOtherConfigFlag = 1; | |
1262 | ||
1263 | return CMD_SUCCESS; | |
1264 | } | |
1265 | ||
1266 | DEFUN (no_ipv6_nd_other_config_flag, | |
1267 | no_ipv6_nd_other_config_flag_cmd, | |
1268 | "no ipv6 nd other-config-flag", | |
1269 | NO_STR | |
3e31cded | 1270 | "Interface IPv6 config commands\n" |
718e3744 | 1271 | "Neighbor discovery\n" |
1272 | "Other statefull configuration flag\n") | |
1273 | { | |
3ddccf18 DL |
1274 | VTY_DECLVAR_CONTEXT (interface, ifp); |
1275 | struct zebra_if *zif = ifp->info; | |
718e3744 | 1276 | |
1277 | zif->rtadv.AdvOtherConfigFlag = 0; | |
1278 | ||
1279 | return CMD_SUCCESS; | |
1280 | } | |
1281 | ||
3e31cded | 1282 | DEFUN (ipv6_nd_prefix, |
1283 | ipv6_nd_prefix_cmd, | |
34ccea1e | 1284 | "ipv6 nd prefix X:X::X:X/M [<(0-4294967295)|infinite> <(0-4294967295)|infinite>] [<router-address|off-link [no-autoconfig]|no-autoconfig [off-link]>]", |
3e31cded | 1285 | "Interface IPv6 config commands\n" |
718e3744 | 1286 | "Neighbor discovery\n" |
1287 | "Prefix information\n" | |
1288 | "IPv6 prefix\n" | |
1289 | "Valid lifetime in seconds\n" | |
3e31cded | 1290 | "Infinite valid lifetime\n" |
718e3744 | 1291 | "Preferred lifetime in seconds\n" |
3e31cded | 1292 | "Infinite preferred lifetime\n" |
34ccea1e | 1293 | "Set Router Address flag\n" |
3e31cded | 1294 | "Do not use prefix for onlink determination\n" |
7cee1bb1 | 1295 | "Do not use prefix for autoconfiguration\n" |
34ccea1e QY |
1296 | "Do not use prefix for autoconfiguration\n" |
1297 | "Do not use prefix for onlink determination\n") | |
718e3744 | 1298 | { |
34ccea1e QY |
1299 | /* prelude */ |
1300 | char *prefix = argv[3]->arg; | |
1301 | int lifetimes = (argc > 4) && (argv[4]->type == RANGE_TKN || strmatch (argv[4]->text, "infinite")); | |
1302 | int routeropts = lifetimes ? argc > 6 : argc > 4; | |
1303 | ||
1304 | int idx_routeropts = routeropts ? (lifetimes ? 6 : 4) : 0; | |
1305 | ||
1306 | char *lifetime = NULL, *preflifetime = NULL; | |
1307 | int routeraddr = 0, offlink = 0, noautoconf = 0; | |
1308 | if (lifetimes) | |
1309 | { | |
1310 | lifetime = argv[4]->type == RANGE_TKN ? argv[4]->arg : argv[4]->text; | |
1311 | preflifetime = argv[5]->type == RANGE_TKN ? argv[5]->arg : argv[5]->text; | |
1312 | } | |
1313 | if (routeropts) | |
1314 | { | |
1315 | routeraddr = strmatch (argv[idx_routeropts]->text, "router-address"); | |
1316 | if (!routeraddr) | |
1317 | { | |
1318 | offlink = (argc > idx_routeropts + 1 || strmatch (argv[idx_routeropts]->text, "off-link")); | |
1319 | noautoconf = (argc > idx_routeropts + 1 || strmatch (argv[idx_routeropts]->text, "no-autoconfig")); | |
1320 | } | |
1321 | } | |
1322 | ||
1323 | /* business */ | |
3ddccf18 DL |
1324 | VTY_DECLVAR_CONTEXT (interface, ifp); |
1325 | struct zebra_if *zebra_if = ifp->info; | |
718e3744 | 1326 | int ret; |
718e3744 | 1327 | struct rtadv_prefix rp; |
1328 | ||
34ccea1e | 1329 | ret = str2prefix_ipv6 (prefix, &rp.prefix); |
718e3744 | 1330 | if (!ret) |
1331 | { | |
1332 | vty_out (vty, "Malformed IPv6 prefix%s", VTY_NEWLINE); | |
1333 | return CMD_WARNING; | |
1334 | } | |
6bb1273e | 1335 | apply_mask_ipv6 (&rp.prefix); /* RFC4861 4.6.2 */ |
34ccea1e QY |
1336 | rp.AdvOnLinkFlag = !offlink; |
1337 | rp.AdvAutonomousFlag = !noautoconf; | |
1338 | rp.AdvRouterAddressFlag = routeraddr; | |
3e31cded | 1339 | rp.AdvValidLifetime = RTADV_VALID_LIFETIME; |
1340 | rp.AdvPreferredLifetime = RTADV_PREFERRED_LIFETIME; | |
718e3744 | 1341 | |
34ccea1e QY |
1342 | if (lifetimes) |
1343 | { | |
1344 | rp.AdvValidLifetime = strmatch (lifetime, "infinite") ? UINT32_MAX : strtoll (lifetime, NULL, 10); | |
1345 | rp.AdvPreferredLifetime = strmatch (preflifetime, "infinite") ? UINT32_MAX : strtoll (preflifetime, NULL, 10); | |
1346 | if (rp.AdvPreferredLifetime > rp.AdvValidLifetime) | |
1347 | { | |
1348 | vty_out (vty, "Invalid preferred lifetime%s", VTY_NEWLINE); | |
1349 | return CMD_WARNING; | |
1350 | } | |
1351 | } | |
718e3744 | 1352 | |
1353 | rtadv_prefix_set (zebra_if, &rp); | |
1354 | ||
1355 | return CMD_SUCCESS; | |
1356 | } | |
1357 | ||
3e31cded | 1358 | DEFUN (no_ipv6_nd_prefix, |
1359 | no_ipv6_nd_prefix_cmd, | |
34ccea1e QY |
1360 | "no ipv6 nd prefix X:X::X:X/M [<(0-4294967295)|infinite> <(0-4294967295)|infinite>] [<router-address|off-link [no-autoconfig]|no-autoconfig [off-link]>]", |
1361 | NO_STR | |
3e31cded | 1362 | "Interface IPv6 config commands\n" |
718e3744 | 1363 | "Neighbor discovery\n" |
1364 | "Prefix information\n" | |
34ccea1e QY |
1365 | "IPv6 prefix\n" |
1366 | "Valid lifetime in seconds\n" | |
1367 | "Infinite valid lifetime\n" | |
1368 | "Preferred lifetime in seconds\n" | |
1369 | "Infinite preferred lifetime\n" | |
1370 | "Set Router Address flag\n" | |
1371 | "Do not use prefix for onlink determination\n" | |
1372 | "Do not use prefix for autoconfiguration\n" | |
1373 | "Do not use prefix for autoconfiguration\n" | |
1374 | "Do not use prefix for onlink determination\n") | |
718e3744 | 1375 | { |
3ddccf18 DL |
1376 | VTY_DECLVAR_CONTEXT (interface, ifp); |
1377 | struct zebra_if *zebra_if = ifp->info; | |
718e3744 | 1378 | int ret; |
718e3744 | 1379 | struct rtadv_prefix rp; |
34ccea1e QY |
1380 | char *prefix = argv[4]->arg; |
1381 | ||
34ccea1e | 1382 | ret = str2prefix_ipv6 (prefix, &rp.prefix); |
718e3744 | 1383 | if (!ret) |
1384 | { | |
1385 | vty_out (vty, "Malformed IPv6 prefix%s", VTY_NEWLINE); | |
1386 | return CMD_WARNING; | |
1387 | } | |
6bb1273e | 1388 | apply_mask_ipv6 (&rp.prefix); /* RFC4861 4.6.2 */ |
718e3744 | 1389 | |
1390 | ret = rtadv_prefix_reset (zebra_if, &rp); | |
1391 | if (!ret) | |
1392 | { | |
34ccea1e | 1393 | vty_out (vty, "Non-existant IPv6 prefix%s", VTY_NEWLINE); |
718e3744 | 1394 | return CMD_WARNING; |
1395 | } | |
1396 | ||
1397 | return CMD_SUCCESS; | |
1398 | } | |
b60668d0 CC |
1399 | |
1400 | DEFUN (ipv6_nd_router_preference, | |
1401 | ipv6_nd_router_preference_cmd, | |
6147e2c6 | 1402 | "ipv6 nd router-preference <high|medium|low>", |
b60668d0 CC |
1403 | "Interface IPv6 config commands\n" |
1404 | "Neighbor discovery\n" | |
1405 | "Default router preference\n" | |
1406 | "High default router preference\n" | |
1407 | "Low default router preference\n" | |
1408 | "Medium default router preference (default)\n") | |
1409 | { | |
7c022376 | 1410 | int idx_high_medium_low = 3; |
3ddccf18 DL |
1411 | VTY_DECLVAR_CONTEXT (interface, ifp); |
1412 | struct zebra_if *zif = ifp->info; | |
b60668d0 CC |
1413 | int i = 0; |
1414 | ||
b60668d0 CC |
1415 | while (0 != rtadv_pref_strs[i]) |
1416 | { | |
7c022376 | 1417 | if (strncmp (argv[idx_high_medium_low]->arg, rtadv_pref_strs[i], 1) == 0) |
b60668d0 CC |
1418 | { |
1419 | zif->rtadv.DefaultPreference = i; | |
1420 | return CMD_SUCCESS; | |
1421 | } | |
1422 | i++; | |
1423 | } | |
1424 | ||
1425 | return CMD_ERR_NO_MATCH; | |
1426 | } | |
1427 | ||
1428 | DEFUN (no_ipv6_nd_router_preference, | |
1429 | no_ipv6_nd_router_preference_cmd, | |
34ccea1e | 1430 | "no ipv6 nd router-preference [<high|medium|low>]", |
b60668d0 CC |
1431 | NO_STR |
1432 | "Interface IPv6 config commands\n" | |
1433 | "Neighbor discovery\n" | |
34ccea1e QY |
1434 | "Default router preference\n" |
1435 | "High default router preference\n" | |
1436 | "Medium default router preference (default)\n" | |
1437 | "Low default router preference\n") | |
b60668d0 | 1438 | { |
3ddccf18 DL |
1439 | VTY_DECLVAR_CONTEXT (interface, ifp); |
1440 | struct zebra_if *zif = ifp->info; | |
b60668d0 CC |
1441 | |
1442 | zif->rtadv.DefaultPreference = RTADV_PREF_MEDIUM; /* Default per RFC4191. */ | |
1443 | ||
1444 | return CMD_SUCCESS; | |
1445 | } | |
1446 | ||
6ae93c05 DO |
1447 | DEFUN (ipv6_nd_mtu, |
1448 | ipv6_nd_mtu_cmd, | |
6147e2c6 | 1449 | "ipv6 nd mtu (1-65535)", |
6ae93c05 DO |
1450 | "Interface IPv6 config commands\n" |
1451 | "Neighbor discovery\n" | |
1452 | "Advertised MTU\n" | |
1453 | "MTU in bytes\n") | |
1454 | { | |
7c022376 | 1455 | int idx_number = 3; |
3ddccf18 | 1456 | VTY_DECLVAR_CONTEXT (interface, ifp); |
6ae93c05 | 1457 | struct zebra_if *zif = ifp->info; |
7c022376 | 1458 | VTY_GET_INTEGER_RANGE ("MTU", zif->rtadv.AdvLinkMTU, argv[idx_number]->arg, 1, 65535); |
6ae93c05 DO |
1459 | return CMD_SUCCESS; |
1460 | } | |
1461 | ||
1462 | DEFUN (no_ipv6_nd_mtu, | |
1463 | no_ipv6_nd_mtu_cmd, | |
34ccea1e | 1464 | "no ipv6 nd mtu [(1-65535)]", |
6ae93c05 DO |
1465 | NO_STR |
1466 | "Interface IPv6 config commands\n" | |
1467 | "Neighbor discovery\n" | |
34ccea1e QY |
1468 | "Advertised MTU\n" |
1469 | "MTU in bytes\n") | |
6ae93c05 | 1470 | { |
3ddccf18 | 1471 | VTY_DECLVAR_CONTEXT (interface, ifp); |
6ae93c05 DO |
1472 | struct zebra_if *zif = ifp->info; |
1473 | zif->rtadv.AdvLinkMTU = 0; | |
1474 | return CMD_SUCCESS; | |
1475 | } | |
1476 | ||
6ae93c05 | 1477 | |
718e3744 | 1478 | /* Write configuration about router advertisement. */ |
1479 | void | |
1480 | rtadv_config_write (struct vty *vty, struct interface *ifp) | |
1481 | { | |
1482 | struct zebra_if *zif; | |
52dc7ee6 | 1483 | struct listnode *node; |
718e3744 | 1484 | struct rtadv_prefix *rprefix; |
35d921cc | 1485 | char buf[PREFIX_STRLEN]; |
7cee1bb1 | 1486 | int interval; |
718e3744 | 1487 | |
718e3744 | 1488 | zif = ifp->info; |
1489 | ||
c23af4d3 | 1490 | if (!(if_is_loopback (ifp) || |
1491 | CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK))) | |
718e3744 | 1492 | { |
c5b37afc | 1493 | if (zif->rtadv.AdvSendAdvertisements) |
1494 | vty_out (vty, " no ipv6 nd suppress-ra%s", VTY_NEWLINE); | |
718e3744 | 1495 | } |
7cee1bb1 | 1496 | |
1497 | interval = zif->rtadv.MaxRtrAdvInterval; | |
1498 | if (interval % 1000) | |
1499 | vty_out (vty, " ipv6 nd ra-interval msec %d%s", interval, | |
1500 | VTY_NEWLINE); | |
1501 | else | |
1502 | if (interval != RTADV_MAX_RTR_ADV_INTERVAL) | |
1503 | vty_out (vty, " ipv6 nd ra-interval %d%s", interval / 1000, | |
718e3744 | 1504 | VTY_NEWLINE); |
1505 | ||
6134b875 DO |
1506 | if (zif->rtadv.AdvIntervalOption) |
1507 | vty_out (vty, " ipv6 nd adv-interval-option%s", VTY_NEWLINE); | |
1508 | ||
d660f698 | 1509 | if (zif->rtadv.AdvDefaultLifetime != -1) |
718e3744 | 1510 | vty_out (vty, " ipv6 nd ra-lifetime %d%s", zif->rtadv.AdvDefaultLifetime, |
1511 | VTY_NEWLINE); | |
1512 | ||
6134b875 DO |
1513 | if (zif->rtadv.HomeAgentPreference) |
1514 | vty_out (vty, " ipv6 nd home-agent-preference %u%s", | |
1515 | zif->rtadv.HomeAgentPreference, VTY_NEWLINE); | |
1516 | ||
d660f698 | 1517 | if (zif->rtadv.HomeAgentLifetime != -1) |
6134b875 DO |
1518 | vty_out (vty, " ipv6 nd home-agent-lifetime %u%s", |
1519 | zif->rtadv.HomeAgentLifetime, VTY_NEWLINE); | |
1520 | ||
1521 | if (zif->rtadv.AdvHomeAgentFlag) | |
1522 | vty_out (vty, " ipv6 nd home-agent-config-flag%s", VTY_NEWLINE); | |
1523 | ||
718e3744 | 1524 | if (zif->rtadv.AdvReachableTime) |
1525 | vty_out (vty, " ipv6 nd reachable-time %d%s", zif->rtadv.AdvReachableTime, | |
1526 | VTY_NEWLINE); | |
1527 | ||
1528 | if (zif->rtadv.AdvManagedFlag) | |
1529 | vty_out (vty, " ipv6 nd managed-config-flag%s", VTY_NEWLINE); | |
1530 | ||
1531 | if (zif->rtadv.AdvOtherConfigFlag) | |
1532 | vty_out (vty, " ipv6 nd other-config-flag%s", VTY_NEWLINE); | |
1533 | ||
b60668d0 CC |
1534 | if (zif->rtadv.DefaultPreference != RTADV_PREF_MEDIUM) |
1535 | vty_out (vty, " ipv6 nd router-preference %s%s", | |
1536 | rtadv_pref_strs[zif->rtadv.DefaultPreference], | |
1537 | VTY_NEWLINE); | |
1538 | ||
6ae93c05 DO |
1539 | if (zif->rtadv.AdvLinkMTU) |
1540 | vty_out (vty, " ipv6 nd mtu %d%s", zif->rtadv.AdvLinkMTU, VTY_NEWLINE); | |
1541 | ||
1eb8ef25 | 1542 | for (ALL_LIST_ELEMENTS_RO (zif->rtadv.AdvPrefixList, node, rprefix)) |
718e3744 | 1543 | { |
35d921cc TT |
1544 | vty_out (vty, " ipv6 nd prefix %s", |
1545 | prefix2str (&rprefix->prefix, buf, sizeof(buf))); | |
3e31cded | 1546 | if ((rprefix->AdvValidLifetime != RTADV_VALID_LIFETIME) || |
1547 | (rprefix->AdvPreferredLifetime != RTADV_PREFERRED_LIFETIME)) | |
1548 | { | |
1549 | if (rprefix->AdvValidLifetime == UINT32_MAX) | |
1550 | vty_out (vty, " infinite"); | |
1551 | else | |
1552 | vty_out (vty, " %u", rprefix->AdvValidLifetime); | |
1553 | if (rprefix->AdvPreferredLifetime == UINT32_MAX) | |
1554 | vty_out (vty, " infinite"); | |
1555 | else | |
1556 | vty_out (vty, " %u", rprefix->AdvPreferredLifetime); | |
1557 | } | |
1558 | if (!rprefix->AdvOnLinkFlag) | |
1559 | vty_out (vty, " off-link"); | |
1560 | if (!rprefix->AdvAutonomousFlag) | |
1561 | vty_out (vty, " no-autoconfig"); | |
7cee1bb1 | 1562 | if (rprefix->AdvRouterAddressFlag) |
1563 | vty_out (vty, " router-address"); | |
718e3744 | 1564 | vty_out (vty, "%s", VTY_NEWLINE); |
1565 | } | |
1566 | } | |
1567 | ||
718e3744 | 1568 | |
a1ac18c4 | 1569 | static void |
43459b1f | 1570 | rtadv_event (struct zebra_ns *zns, enum rtadv_event event, int val) |
718e3744 | 1571 | { |
43459b1f | 1572 | struct rtadv *rtadv = &zns->rtadv; |
cd80d74f | 1573 | |
718e3744 | 1574 | switch (event) |
1575 | { | |
1576 | case RTADV_START: | |
1577 | if (! rtadv->ra_read) | |
6c9678b4 | 1578 | rtadv->ra_read = thread_add_read (zebrad.master, rtadv_read, zns, val); |
718e3744 | 1579 | if (! rtadv->ra_timer) |
6c9678b4 DD |
1580 | rtadv->ra_timer = thread_add_event (zebrad.master, rtadv_timer, |
1581 | zns, 0); | |
718e3744 | 1582 | break; |
1583 | case RTADV_STOP: | |
1584 | if (rtadv->ra_timer) | |
1585 | { | |
1586 | thread_cancel (rtadv->ra_timer); | |
1587 | rtadv->ra_timer = NULL; | |
1588 | } | |
1589 | if (rtadv->ra_read) | |
1590 | { | |
1591 | thread_cancel (rtadv->ra_read); | |
1592 | rtadv->ra_read = NULL; | |
1593 | } | |
1594 | break; | |
1595 | case RTADV_TIMER: | |
1596 | if (! rtadv->ra_timer) | |
43459b1f | 1597 | rtadv->ra_timer = thread_add_timer (zebrad.master, rtadv_timer, zns, |
3e31cded | 1598 | val); |
718e3744 | 1599 | break; |
7cee1bb1 | 1600 | case RTADV_TIMER_MSEC: |
1601 | if (! rtadv->ra_timer) | |
1602 | rtadv->ra_timer = thread_add_timer_msec (zebrad.master, rtadv_timer, | |
43459b1f | 1603 | zns, val); |
7cee1bb1 | 1604 | break; |
718e3744 | 1605 | case RTADV_READ: |
1606 | if (! rtadv->ra_read) | |
43459b1f | 1607 | rtadv->ra_read = thread_add_read (zebrad.master, rtadv_read, zns, val); |
718e3744 | 1608 | break; |
1609 | default: | |
1610 | break; | |
1611 | } | |
1612 | return; | |
1613 | } | |
1614 | ||
1615 | void | |
43459b1f | 1616 | rtadv_init (struct zebra_ns *zns) |
718e3744 | 1617 | { |
43459b1f | 1618 | zns->rtadv.sock = rtadv_make_socket (); |
cd80d74f | 1619 | } |
718e3744 | 1620 | |
cd80d74f | 1621 | void |
43459b1f | 1622 | rtadv_terminate (struct zebra_ns *zns) |
cd80d74f | 1623 | { |
43459b1f | 1624 | rtadv_event (zns, RTADV_STOP, 0); |
1625 | if (zns->rtadv.sock >= 0) | |
cd80d74f | 1626 | { |
43459b1f | 1627 | close (zns->rtadv.sock); |
1628 | zns->rtadv.sock = -1; | |
cd80d74f | 1629 | } |
718e3744 | 1630 | |
43459b1f | 1631 | zns->rtadv.adv_if_count = 0; |
1632 | zns->rtadv.adv_msec_if_count = 0; | |
cd80d74f | 1633 | } |
718e3744 | 1634 | |
cd80d74f FL |
1635 | void |
1636 | rtadv_cmd_init (void) | |
1637 | { | |
718e3744 | 1638 | install_element (INTERFACE_NODE, &ipv6_nd_suppress_ra_cmd); |
1639 | install_element (INTERFACE_NODE, &no_ipv6_nd_suppress_ra_cmd); | |
718e3744 | 1640 | install_element (INTERFACE_NODE, &ipv6_nd_ra_interval_cmd); |
7cee1bb1 | 1641 | install_element (INTERFACE_NODE, &ipv6_nd_ra_interval_msec_cmd); |
718e3744 | 1642 | install_element (INTERFACE_NODE, &no_ipv6_nd_ra_interval_cmd); |
1643 | install_element (INTERFACE_NODE, &ipv6_nd_ra_lifetime_cmd); | |
1644 | install_element (INTERFACE_NODE, &no_ipv6_nd_ra_lifetime_cmd); | |
1645 | install_element (INTERFACE_NODE, &ipv6_nd_reachable_time_cmd); | |
1646 | install_element (INTERFACE_NODE, &no_ipv6_nd_reachable_time_cmd); | |
1647 | install_element (INTERFACE_NODE, &ipv6_nd_managed_config_flag_cmd); | |
1648 | install_element (INTERFACE_NODE, &no_ipv6_nd_managed_config_flag_cmd); | |
1649 | install_element (INTERFACE_NODE, &ipv6_nd_other_config_flag_cmd); | |
1650 | install_element (INTERFACE_NODE, &no_ipv6_nd_other_config_flag_cmd); | |
7cee1bb1 | 1651 | install_element (INTERFACE_NODE, &ipv6_nd_homeagent_config_flag_cmd); |
1652 | install_element (INTERFACE_NODE, &no_ipv6_nd_homeagent_config_flag_cmd); | |
1653 | install_element (INTERFACE_NODE, &ipv6_nd_homeagent_preference_cmd); | |
1654 | install_element (INTERFACE_NODE, &no_ipv6_nd_homeagent_preference_cmd); | |
1655 | install_element (INTERFACE_NODE, &ipv6_nd_homeagent_lifetime_cmd); | |
1656 | install_element (INTERFACE_NODE, &no_ipv6_nd_homeagent_lifetime_cmd); | |
1657 | install_element (INTERFACE_NODE, &ipv6_nd_adv_interval_config_option_cmd); | |
1658 | install_element (INTERFACE_NODE, &no_ipv6_nd_adv_interval_config_option_cmd); | |
3e31cded | 1659 | install_element (INTERFACE_NODE, &ipv6_nd_prefix_cmd); |
3e31cded | 1660 | install_element (INTERFACE_NODE, &no_ipv6_nd_prefix_cmd); |
b60668d0 CC |
1661 | install_element (INTERFACE_NODE, &ipv6_nd_router_preference_cmd); |
1662 | install_element (INTERFACE_NODE, &no_ipv6_nd_router_preference_cmd); | |
6ae93c05 DO |
1663 | install_element (INTERFACE_NODE, &ipv6_nd_mtu_cmd); |
1664 | install_element (INTERFACE_NODE, &no_ipv6_nd_mtu_cmd); | |
718e3744 | 1665 | } |
1666 | ||
a1ac18c4 | 1667 | static int |
718e3744 | 1668 | if_join_all_router (int sock, struct interface *ifp) |
1669 | { | |
1670 | int ret; | |
1671 | ||
1672 | struct ipv6_mreq mreq; | |
1673 | ||
1674 | memset (&mreq, 0, sizeof (struct ipv6_mreq)); | |
1675 | inet_pton (AF_INET6, ALLROUTER, &mreq.ipv6mr_multiaddr); | |
1676 | mreq.ipv6mr_interface = ifp->ifindex; | |
1677 | ||
1678 | ret = setsockopt (sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, | |
1679 | (char *) &mreq, sizeof mreq); | |
1680 | if (ret < 0) | |
f360eac0 | 1681 | zlog_warn ("%s(%u): Failed to join group, socket %u error %s", |
1682 | ifp->name, ifp->ifindex, sock, safe_strerror (errno)); | |
718e3744 | 1683 | |
f360eac0 | 1684 | if (IS_ZEBRA_DEBUG_EVENT) |
1685 | zlog_debug ("%s(%u): Join All-Routers multicast group, socket %u", | |
1686 | ifp->name, ifp->ifindex, sock); | |
718e3744 | 1687 | |
1688 | return 0; | |
1689 | } | |
1690 | ||
a1ac18c4 | 1691 | static int |
718e3744 | 1692 | if_leave_all_router (int sock, struct interface *ifp) |
1693 | { | |
1694 | int ret; | |
1695 | ||
1696 | struct ipv6_mreq mreq; | |
1697 | ||
1698 | memset (&mreq, 0, sizeof (struct ipv6_mreq)); | |
1699 | inet_pton (AF_INET6, ALLROUTER, &mreq.ipv6mr_multiaddr); | |
1700 | mreq.ipv6mr_interface = ifp->ifindex; | |
1701 | ||
1702 | ret = setsockopt (sock, IPPROTO_IPV6, IPV6_LEAVE_GROUP, | |
1703 | (char *) &mreq, sizeof mreq); | |
1704 | if (ret < 0) | |
f360eac0 | 1705 | zlog_warn ("%s(%u): Failed to leave group, socket %u error %s", |
1706 | ifp->name, ifp->ifindex, sock,safe_strerror (errno)); | |
718e3744 | 1707 | |
f360eac0 | 1708 | if (IS_ZEBRA_DEBUG_EVENT) |
1709 | zlog_debug ("%s(%u): Leave All-Routers multicast group, socket %u", | |
1710 | ifp->name, ifp->ifindex, sock); | |
718e3744 | 1711 | |
1712 | return 0; | |
1713 | } | |
1714 | ||
1715 | #else | |
1716 | void | |
43459b1f | 1717 | rtadv_init (struct zebra_ns *zns) |
cd80d74f FL |
1718 | { |
1719 | /* Empty.*/; | |
1720 | } | |
1721 | void | |
43459b1f | 1722 | rtadv_terminate (struct zebra_ns *zns) |
cd80d74f FL |
1723 | { |
1724 | /* Empty.*/; | |
1725 | } | |
1726 | void | |
1727 | rtadv_cmd_init (void) | |
718e3744 | 1728 | { |
1729 | /* Empty.*/; | |
1730 | } | |
56c1f7d8 | 1731 | #endif /* HAVE_RTADV */ |