mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-10-31 20:23:36 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			2282 lines
		
	
	
		
			62 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2282 lines
		
	
	
		
			62 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* Router advertisement
 | |
|  * Copyright (C) 2016 Cumulus Networks
 | |
|  * Copyright (C) 2005 6WIND <jean-mickael.guerin@6wind.com>
 | |
|  * Copyright (C) 1999 Kunihiro Ishiguro
 | |
|  *
 | |
|  * This file is part of GNU Zebra.
 | |
|  *
 | |
|  * GNU Zebra is free software; you can redistribute it and/or modify it
 | |
|  * under the terms of the GNU General Public License as published by the
 | |
|  * Free Software Foundation; either version 2, or (at your option) any
 | |
|  * later version.
 | |
|  *
 | |
|  * GNU Zebra is distributed in the hope that it will be useful, but
 | |
|  * WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|  * General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License along
 | |
|  * with this program; see the file COPYING; if not, write to the Free Software
 | |
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 | |
|  */
 | |
| 
 | |
| #include <zebra.h>
 | |
| 
 | |
| #include "memory.h"
 | |
| #include "zebra_memory.h"
 | |
| #include "sockopt.h"
 | |
| #include "thread.h"
 | |
| #include "if.h"
 | |
| #include "stream.h"
 | |
| #include "log.h"
 | |
| #include "prefix.h"
 | |
| #include "linklist.h"
 | |
| #include "command.h"
 | |
| #include "privs.h"
 | |
| #include "vrf.h"
 | |
| #include "ns.h"
 | |
| #include "lib_errors.h"
 | |
| 
 | |
| #include "zebra/interface.h"
 | |
| #include "zebra/rtadv.h"
 | |
| #include "zebra/debug.h"
 | |
| #include "zebra/rib.h"
 | |
| #include "zebra/zapi_msg.h"
 | |
| #include "zebra/zebra_vrf.h"
 | |
| #include "zebra/zebra_errors.h"
 | |
| #include "zebra/zebra_router.h"
 | |
| 
 | |
| extern struct zebra_privs_t zserv_privs;
 | |
| 
 | |
| #if defined(HAVE_RTADV)
 | |
| 
 | |
| DEFINE_MTYPE_STATIC(ZEBRA, RTADV_PREFIX, "Router Advertisement Prefix")
 | |
| 
 | |
| #ifdef OPEN_BSD
 | |
| #include <netinet/icmp6.h>
 | |
| #endif
 | |
| 
 | |
| /* If RFC2133 definition is used. */
 | |
| #ifndef IPV6_JOIN_GROUP
 | |
| #define IPV6_JOIN_GROUP  IPV6_ADD_MEMBERSHIP
 | |
| #endif
 | |
| #ifndef IPV6_LEAVE_GROUP
 | |
| #define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP
 | |
| #endif
 | |
| 
 | |
| #define ALLNODE   "ff02::1"
 | |
| #define ALLROUTER "ff02::2"
 | |
| 
 | |
| DEFINE_MTYPE_STATIC(ZEBRA, RTADV_RDNSS, "Router Advertisement RDNSS")
 | |
| DEFINE_MTYPE_STATIC(ZEBRA, RTADV_DNSSL, "Router Advertisement DNSSL")
 | |
| 
 | |
| /* Order is intentional.  Matches RFC4191.  This array is also used for
 | |
|    command matching, so only modify with care. */
 | |
| const char *rtadv_pref_strs[] = {"medium", "high", "INVALID", "low", 0};
 | |
| 
 | |
| enum rtadv_event {
 | |
| 	RTADV_START,
 | |
| 	RTADV_STOP,
 | |
| 	RTADV_TIMER,
 | |
| 	RTADV_TIMER_MSEC,
 | |
| 	RTADV_READ
 | |
| };
 | |
| 
 | |
| static void rtadv_event(struct zebra_vrf *, enum rtadv_event, int);
 | |
| 
 | |
| static int if_join_all_router(int, struct interface *);
 | |
| static int if_leave_all_router(int, struct interface *);
 | |
| 
 | |
| static int rtadv_get_socket(struct zebra_vrf *zvrf)
 | |
| {
 | |
| 	if (zvrf->rtadv.sock > 0)
 | |
| 		return zvrf->rtadv.sock;
 | |
| 	return zrouter.rtadv_sock;
 | |
| }
 | |
| 
 | |
| static int rtadv_increment_received(struct zebra_vrf *zvrf, ifindex_t *ifindex)
 | |
| {
 | |
| 	int ret = -1;
 | |
| 	struct interface *iface;
 | |
| 	struct zebra_if *zif;
 | |
| 
 | |
| 	iface = if_lookup_by_index(*ifindex, zvrf->vrf->vrf_id);
 | |
| 	if (iface && iface->info) {
 | |
| 		zif = iface->info;
 | |
| 		zif->ra_rcvd++;
 | |
| 		ret = 0;
 | |
| 	}
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int rtadv_recv_packet(struct zebra_vrf *zvrf, int sock, uint8_t *buf,
 | |
| 			     int buflen, struct sockaddr_in6 *from,
 | |
| 			     ifindex_t *ifindex, int *hoplimit)
 | |
| {
 | |
| 	int ret;
 | |
| 	struct msghdr msg;
 | |
| 	struct iovec iov;
 | |
| 	struct cmsghdr *cmsgptr;
 | |
| 	struct in6_addr dst;
 | |
| 
 | |
| 	char adata[1024];
 | |
| 
 | |
| 	/* Fill in message and iovec. */
 | |
| 	memset(&msg, 0, sizeof(msg));
 | |
| 	msg.msg_name = (void *)from;
 | |
| 	msg.msg_namelen = sizeof(struct sockaddr_in6);
 | |
| 	msg.msg_iov = &iov;
 | |
| 	msg.msg_iovlen = 1;
 | |
| 	msg.msg_control = (void *)adata;
 | |
| 	msg.msg_controllen = sizeof adata;
 | |
| 	iov.iov_base = buf;
 | |
| 	iov.iov_len = buflen;
 | |
| 
 | |
| 	/* If recvmsg fail return minus value. */
 | |
| 	ret = recvmsg(sock, &msg, 0);
 | |
| 	if (ret < 0)
 | |
| 		return ret;
 | |
| 
 | |
| 	for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL;
 | |
| 	     cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
 | |
| 		/* I want interface index which this packet comes from. */
 | |
| 		if (cmsgptr->cmsg_level == IPPROTO_IPV6
 | |
| 		    && cmsgptr->cmsg_type == IPV6_PKTINFO) {
 | |
| 			struct in6_pktinfo *ptr;
 | |
| 
 | |
| 			ptr = (struct in6_pktinfo *)CMSG_DATA(cmsgptr);
 | |
| 			*ifindex = ptr->ipi6_ifindex;
 | |
| 			memcpy(&dst, &ptr->ipi6_addr, sizeof(ptr->ipi6_addr));
 | |
| 		}
 | |
| 
 | |
| 		/* Incoming packet's hop limit. */
 | |
| 		if (cmsgptr->cmsg_level == IPPROTO_IPV6
 | |
| 		    && cmsgptr->cmsg_type == IPV6_HOPLIMIT) {
 | |
| 			int *hoptr = (int *)CMSG_DATA(cmsgptr);
 | |
| 			*hoplimit = *hoptr;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	rtadv_increment_received(zvrf, ifindex);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| #define RTADV_MSG_SIZE 4096
 | |
| 
 | |
| /* Send router advertisement packet. */
 | |
| static void rtadv_send_packet(int sock, struct interface *ifp)
 | |
| {
 | |
| 	struct msghdr msg;
 | |
| 	struct iovec iov;
 | |
| 	struct cmsghdr *cmsgptr;
 | |
| 	struct in6_pktinfo *pkt;
 | |
| 	struct sockaddr_in6 addr;
 | |
| 	static void *adata = NULL;
 | |
| 	unsigned char buf[RTADV_MSG_SIZE];
 | |
| 	struct nd_router_advert *rtadv;
 | |
| 	int ret;
 | |
| 	int len = 0;
 | |
| 	struct zebra_if *zif;
 | |
| 	struct rtadv_prefix *rprefix;
 | |
| 	uint8_t all_nodes_addr[] = {0xff, 0x02, 0, 0, 0, 0, 0, 0,
 | |
| 				    0,    0,    0, 0, 0, 0, 0, 1};
 | |
| 	struct listnode *node;
 | |
| 	uint16_t pkt_RouterLifetime;
 | |
| 
 | |
| 	/*
 | |
| 	 * Allocate control message bufffer.  This is dynamic because
 | |
| 	 * CMSG_SPACE is not guaranteed not to call a function.  Note that
 | |
| 	 * the size will be different on different architectures due to
 | |
| 	 * differing alignment rules.
 | |
| 	 */
 | |
| 	if (adata == NULL) {
 | |
| 		/* XXX Free on shutdown. */
 | |
| 		adata = calloc(1, CMSG_SPACE(sizeof(struct in6_pktinfo)));
 | |
| 
 | |
| 		if (adata == NULL) {
 | |
| 			zlog_debug(
 | |
| 				"rtadv_send_packet: can't malloc control data");
 | |
| 			exit(-1);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* Logging of packet. */
 | |
| 	if (IS_ZEBRA_DEBUG_PACKET)
 | |
| 		zlog_debug("%s(%u): Tx RA, socket %u", ifp->name, ifp->ifindex,
 | |
| 			   sock);
 | |
| 
 | |
| 	/* Fill in sockaddr_in6. */
 | |
| 	memset(&addr, 0, sizeof(struct sockaddr_in6));
 | |
| 	addr.sin6_family = AF_INET6;
 | |
| #ifdef SIN6_LEN
 | |
| 	addr.sin6_len = sizeof(struct sockaddr_in6);
 | |
| #endif /* SIN6_LEN */
 | |
| 	addr.sin6_port = htons(IPPROTO_ICMPV6);
 | |
| 	IPV6_ADDR_COPY(&addr.sin6_addr, all_nodes_addr);
 | |
| 
 | |
| 	/* Fetch interface information. */
 | |
| 	zif = ifp->info;
 | |
| 
 | |
| 	/* Make router advertisement message. */
 | |
| 	rtadv = (struct nd_router_advert *)buf;
 | |
| 
 | |
| 	rtadv->nd_ra_type = ND_ROUTER_ADVERT;
 | |
| 	rtadv->nd_ra_code = 0;
 | |
| 	rtadv->nd_ra_cksum = 0;
 | |
| 
 | |
| 	rtadv->nd_ra_curhoplimit = 64;
 | |
| 
 | |
| 	/* RFC4191: Default Router Preference is 0 if Router Lifetime is 0. */
 | |
| 	rtadv->nd_ra_flags_reserved = zif->rtadv.AdvDefaultLifetime == 0
 | |
| 					      ? 0
 | |
| 					      : zif->rtadv.DefaultPreference;
 | |
| 	rtadv->nd_ra_flags_reserved <<= 3;
 | |
| 
 | |
| 	if (zif->rtadv.AdvManagedFlag)
 | |
| 		rtadv->nd_ra_flags_reserved |= ND_RA_FLAG_MANAGED;
 | |
| 	if (zif->rtadv.AdvOtherConfigFlag)
 | |
| 		rtadv->nd_ra_flags_reserved |= ND_RA_FLAG_OTHER;
 | |
| 	if (zif->rtadv.AdvHomeAgentFlag)
 | |
| 		rtadv->nd_ra_flags_reserved |= ND_RA_FLAG_HOME_AGENT;
 | |
| 	/* Note that according to Neighbor Discovery (RFC 4861 [18]),
 | |
| 	 * AdvDefaultLifetime is by default based on the value of
 | |
| 	 * MaxRtrAdvInterval.  AdvDefaultLifetime is used in the Router Lifetime
 | |
| 	 * field of Router Advertisements.  Given that this field is expressed
 | |
| 	 * in seconds, a small MaxRtrAdvInterval value can result in a zero
 | |
| 	 * value for this field.  To prevent this, routers SHOULD keep
 | |
| 	 * AdvDefaultLifetime in at least one second, even if the use of
 | |
| 	 * MaxRtrAdvInterval would result in a smaller value. -- RFC6275, 7.5 */
 | |
| 	pkt_RouterLifetime =
 | |
| 		zif->rtadv.AdvDefaultLifetime != -1
 | |
| 			? zif->rtadv.AdvDefaultLifetime
 | |
| 			: MAX(1, 0.003 * zif->rtadv.MaxRtrAdvInterval);
 | |
| 	rtadv->nd_ra_router_lifetime = htons(pkt_RouterLifetime);
 | |
| 	rtadv->nd_ra_reachable = htonl(zif->rtadv.AdvReachableTime);
 | |
| 	rtadv->nd_ra_retransmit = htonl(0);
 | |
| 
 | |
| 	len = sizeof(struct nd_router_advert);
 | |
| 
 | |
| 	/* If both the Home Agent Preference and Home Agent Lifetime are set to
 | |
| 	 * their default values specified above, this option SHOULD NOT be
 | |
| 	 * included in the Router Advertisement messages sent by this home
 | |
| 	 * agent. -- RFC6275, 7.4 */
 | |
| 	if (zif->rtadv.AdvHomeAgentFlag
 | |
| 	    && (zif->rtadv.HomeAgentPreference
 | |
| 		|| zif->rtadv.HomeAgentLifetime != -1)) {
 | |
| 		struct nd_opt_homeagent_info *ndopt_hai =
 | |
| 			(struct nd_opt_homeagent_info *)(buf + len);
 | |
| 		ndopt_hai->nd_opt_hai_type = ND_OPT_HA_INFORMATION;
 | |
| 		ndopt_hai->nd_opt_hai_len = 1;
 | |
| 		ndopt_hai->nd_opt_hai_reserved = 0;
 | |
| 		ndopt_hai->nd_opt_hai_preference =
 | |
| 			htons(zif->rtadv.HomeAgentPreference);
 | |
| 		/* 16-bit unsigned integer.  The lifetime associated with the
 | |
| 		 * home
 | |
| 		 * agent in units of seconds.  The default value is the same as
 | |
| 		 * the
 | |
| 		 * Router Lifetime, as specified in the main body of the Router
 | |
| 		 * Advertisement.  The maximum value corresponds to 18.2 hours.
 | |
| 		 * A
 | |
| 		 * value of 0 MUST NOT be used. -- RFC6275, 7.5 */
 | |
| 		ndopt_hai->nd_opt_hai_lifetime =
 | |
| 			htons(zif->rtadv.HomeAgentLifetime != -1
 | |
| 				      ? zif->rtadv.HomeAgentLifetime
 | |
| 				      : MAX(1, pkt_RouterLifetime) /* 0 is OK
 | |
| 								      for RL,
 | |
| 								      but not
 | |
| 								      for HAL*/
 | |
| 			      );
 | |
| 		len += sizeof(struct nd_opt_homeagent_info);
 | |
| 	}
 | |
| 
 | |
| 	if (zif->rtadv.AdvIntervalOption) {
 | |
| 		struct nd_opt_adv_interval *ndopt_adv =
 | |
| 			(struct nd_opt_adv_interval *)(buf + len);
 | |
| 		ndopt_adv->nd_opt_ai_type = ND_OPT_ADV_INTERVAL;
 | |
| 		ndopt_adv->nd_opt_ai_len = 1;
 | |
| 		ndopt_adv->nd_opt_ai_reserved = 0;
 | |
| 		ndopt_adv->nd_opt_ai_interval =
 | |
| 			htonl(zif->rtadv.MaxRtrAdvInterval);
 | |
| 		len += sizeof(struct nd_opt_adv_interval);
 | |
| 	}
 | |
| 
 | |
| 	/* Fill in prefix. */
 | |
| 	for (ALL_LIST_ELEMENTS_RO(zif->rtadv.AdvPrefixList, node, rprefix)) {
 | |
| 		struct nd_opt_prefix_info *pinfo;
 | |
| 
 | |
| 		pinfo = (struct nd_opt_prefix_info *)(buf + len);
 | |
| 
 | |
| 		pinfo->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
 | |
| 		pinfo->nd_opt_pi_len = 4;
 | |
| 		pinfo->nd_opt_pi_prefix_len = rprefix->prefix.prefixlen;
 | |
| 
 | |
| 		pinfo->nd_opt_pi_flags_reserved = 0;
 | |
| 		if (rprefix->AdvOnLinkFlag)
 | |
| 			pinfo->nd_opt_pi_flags_reserved |=
 | |
| 				ND_OPT_PI_FLAG_ONLINK;
 | |
| 		if (rprefix->AdvAutonomousFlag)
 | |
| 			pinfo->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_AUTO;
 | |
| 		if (rprefix->AdvRouterAddressFlag)
 | |
| 			pinfo->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_RADDR;
 | |
| 
 | |
| 		pinfo->nd_opt_pi_valid_time = htonl(rprefix->AdvValidLifetime);
 | |
| 		pinfo->nd_opt_pi_preferred_time =
 | |
| 			htonl(rprefix->AdvPreferredLifetime);
 | |
| 		pinfo->nd_opt_pi_reserved2 = 0;
 | |
| 
 | |
| 		IPV6_ADDR_COPY(&pinfo->nd_opt_pi_prefix,
 | |
| 			       &rprefix->prefix.prefix);
 | |
| 
 | |
| #ifdef DEBUG
 | |
| 		{
 | |
| 			uint8_t buf[INET6_ADDRSTRLEN];
 | |
| 
 | |
| 			zlog_debug("DEBUG %s",
 | |
| 				   inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
 | |
| 					     buf, INET6_ADDRSTRLEN));
 | |
| 		}
 | |
| #endif /* DEBUG */
 | |
| 
 | |
| 		len += sizeof(struct nd_opt_prefix_info);
 | |
| 	}
 | |
| 
 | |
| 	/* Hardware address. */
 | |
| 	if (ifp->hw_addr_len != 0) {
 | |
| 		buf[len++] = ND_OPT_SOURCE_LINKADDR;
 | |
| 
 | |
| 		/* Option length should be rounded up to next octet if
 | |
| 		   the link address does not end on an octet boundary. */
 | |
| 		buf[len++] = (ifp->hw_addr_len + 9) >> 3;
 | |
| 
 | |
| 		memcpy(buf + len, ifp->hw_addr, ifp->hw_addr_len);
 | |
| 		len += ifp->hw_addr_len;
 | |
| 
 | |
| 		/* Pad option to end on an octet boundary. */
 | |
| 		memset(buf + len, 0, -(ifp->hw_addr_len + 2) & 0x7);
 | |
| 		len += -(ifp->hw_addr_len + 2) & 0x7;
 | |
| 	}
 | |
| 
 | |
| 	/* MTU */
 | |
| 	if (zif->rtadv.AdvLinkMTU) {
 | |
| 		struct nd_opt_mtu *opt = (struct nd_opt_mtu *)(buf + len);
 | |
| 		opt->nd_opt_mtu_type = ND_OPT_MTU;
 | |
| 		opt->nd_opt_mtu_len = 1;
 | |
| 		opt->nd_opt_mtu_reserved = 0;
 | |
| 		opt->nd_opt_mtu_mtu = htonl(zif->rtadv.AdvLinkMTU);
 | |
| 		len += sizeof(struct nd_opt_mtu);
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * There is no limit on the number of configurable recursive DNS
 | |
| 	 * servers or search list entries. We don't want the RA message
 | |
| 	 * to exceed the link's MTU (risking fragmentation) or even
 | |
| 	 * blow the stack buffer allocated for it.
 | |
| 	 */
 | |
| 	size_t max_len = MIN(ifp->mtu6 - 40, sizeof(buf));
 | |
| 
 | |
| 	/* Recursive DNS servers */
 | |
| 	struct rtadv_rdnss *rdnss;
 | |
| 
 | |
| 	for (ALL_LIST_ELEMENTS_RO(zif->rtadv.AdvRDNSSList, node, rdnss)) {
 | |
| 		size_t opt_len =
 | |
| 			sizeof(struct nd_opt_rdnss) + sizeof(struct in6_addr);
 | |
| 
 | |
| 		if (len + opt_len > max_len) {
 | |
| 			zlog_warn(
 | |
| 				"%s(%u): Tx RA: RDNSS option would exceed MTU, omitting it",
 | |
| 				ifp->name, ifp->ifindex);
 | |
| 			goto no_more_opts;
 | |
| 		}
 | |
| 		struct nd_opt_rdnss *opt = (struct nd_opt_rdnss *)(buf + len);
 | |
| 
 | |
| 		opt->nd_opt_rdnss_type = ND_OPT_RDNSS;
 | |
| 		opt->nd_opt_rdnss_len = opt_len / 8;
 | |
| 		opt->nd_opt_rdnss_reserved = 0;
 | |
| 		opt->nd_opt_rdnss_lifetime = htonl(
 | |
| 			rdnss->lifetime_set
 | |
| 				? rdnss->lifetime
 | |
| 				: MAX(1, 0.003 * zif->rtadv.MaxRtrAdvInterval));
 | |
| 
 | |
| 		len += sizeof(struct nd_opt_rdnss);
 | |
| 
 | |
| 		IPV6_ADDR_COPY(buf + len, &rdnss->addr);
 | |
| 		len += sizeof(struct in6_addr);
 | |
| 	}
 | |
| 
 | |
| 	/* DNS search list */
 | |
| 	struct rtadv_dnssl *dnssl;
 | |
| 
 | |
| 	for (ALL_LIST_ELEMENTS_RO(zif->rtadv.AdvDNSSLList, node, dnssl)) {
 | |
| 		size_t opt_len = sizeof(struct nd_opt_dnssl)
 | |
| 				 + ((dnssl->encoded_len + 7) & ~7);
 | |
| 
 | |
| 		if (len + opt_len > max_len) {
 | |
| 			zlog_warn(
 | |
| 				"%s(%u): Tx RA: DNSSL option would exceed MTU, omitting it",
 | |
| 				ifp->name, ifp->ifindex);
 | |
| 			goto no_more_opts;
 | |
| 		}
 | |
| 		struct nd_opt_dnssl *opt = (struct nd_opt_dnssl *)(buf + len);
 | |
| 
 | |
| 		opt->nd_opt_dnssl_type = ND_OPT_DNSSL;
 | |
| 		opt->nd_opt_dnssl_len = opt_len / 8;
 | |
| 		opt->nd_opt_dnssl_reserved = 0;
 | |
| 		opt->nd_opt_dnssl_lifetime = htonl(
 | |
| 			dnssl->lifetime_set
 | |
| 				? dnssl->lifetime
 | |
| 				: MAX(1, 0.003 * zif->rtadv.MaxRtrAdvInterval));
 | |
| 
 | |
| 		len += sizeof(struct nd_opt_dnssl);
 | |
| 
 | |
| 		memcpy(buf + len, dnssl->encoded_name, dnssl->encoded_len);
 | |
| 		len += dnssl->encoded_len;
 | |
| 
 | |
| 		/* Zero-pad to 8-octet boundary */
 | |
| 		while (len % 8)
 | |
| 			buf[len++] = '\0';
 | |
| 	}
 | |
| 
 | |
| no_more_opts:
 | |
| 
 | |
| 	msg.msg_name = (void *)&addr;
 | |
| 	msg.msg_namelen = sizeof(struct sockaddr_in6);
 | |
| 	msg.msg_iov = &iov;
 | |
| 	msg.msg_iovlen = 1;
 | |
| 	msg.msg_control = (void *)adata;
 | |
| 	msg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
 | |
| 	msg.msg_flags = 0;
 | |
| 	iov.iov_base = buf;
 | |
| 	iov.iov_len = len;
 | |
| 
 | |
| 	cmsgptr = CMSG_FIRSTHDR(&msg);
 | |
| 	cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
 | |
| 	cmsgptr->cmsg_level = IPPROTO_IPV6;
 | |
| 	cmsgptr->cmsg_type = IPV6_PKTINFO;
 | |
| 
 | |
| 	pkt = (struct in6_pktinfo *)CMSG_DATA(cmsgptr);
 | |
| 	memset(&pkt->ipi6_addr, 0, sizeof(struct in6_addr));
 | |
| 	pkt->ipi6_ifindex = ifp->ifindex;
 | |
| 
 | |
| 	ret = sendmsg(sock, &msg, 0);
 | |
| 	if (ret < 0) {
 | |
| 		flog_err_sys(EC_LIB_SOCKET,
 | |
| 			     "%s(%u): Tx RA failed, socket %u error %d (%s)",
 | |
| 			     ifp->name, ifp->ifindex, sock, errno,
 | |
| 			     safe_strerror(errno));
 | |
| 	} else
 | |
| 		zif->ra_sent++;
 | |
| }
 | |
| 
 | |
| static int rtadv_timer(struct thread *thread)
 | |
| {
 | |
| 	struct zebra_vrf *zvrf = THREAD_ARG(thread);
 | |
| 	struct vrf *vrf;
 | |
| 	struct interface *ifp;
 | |
| 	struct zebra_if *zif;
 | |
| 	int period;
 | |
| 
 | |
| 	zvrf->rtadv.ra_timer = NULL;
 | |
| 	if (zvrf->rtadv.adv_msec_if_count == 0) {
 | |
| 		period = 1000; /* 1 s */
 | |
| 		rtadv_event(zvrf, RTADV_TIMER, 1 /* 1 s */);
 | |
| 	} else {
 | |
| 		period = 10; /* 10 ms */
 | |
| 		rtadv_event(zvrf, RTADV_TIMER_MSEC, 10 /* 10 ms */);
 | |
| 	}
 | |
| 
 | |
| 	RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id)
 | |
| 		FOR_ALL_INTERFACES (vrf, ifp) {
 | |
| 			if (if_is_loopback(ifp)
 | |
| 			    || CHECK_FLAG(ifp->status,
 | |
| 					  ZEBRA_INTERFACE_VRF_LOOPBACK)
 | |
| 			    || !if_is_operative(ifp))
 | |
| 				continue;
 | |
| 
 | |
| 			zif = ifp->info;
 | |
| 
 | |
| 			if (zif->rtadv.AdvSendAdvertisements) {
 | |
| 				if (zif->rtadv.inFastRexmit) {
 | |
| 					/* We assume we fast rexmit every sec so
 | |
| 					 * no
 | |
| 					 * additional vars */
 | |
| 					if (--zif->rtadv.NumFastReXmitsRemain
 | |
| 					    <= 0)
 | |
| 						zif->rtadv.inFastRexmit = 0;
 | |
| 
 | |
| 					if (IS_ZEBRA_DEBUG_SEND)
 | |
| 						zlog_debug(
 | |
| 							"Fast RA Rexmit on interface %s",
 | |
| 							ifp->name);
 | |
| 
 | |
| 					rtadv_send_packet(rtadv_get_socket(zvrf),
 | |
| 							  ifp);
 | |
| 				} else {
 | |
| 					zif->rtadv.AdvIntervalTimer -= period;
 | |
| 					if (zif->rtadv.AdvIntervalTimer <= 0) {
 | |
| 						/* FIXME: using
 | |
| 						   MaxRtrAdvInterval each
 | |
| 						   time isn't what section
 | |
| 						   6.2.4 of RFC4861 tells to do.
 | |
| 						   */
 | |
| 						zif->rtadv.AdvIntervalTimer =
 | |
| 							zif->rtadv
 | |
| 								.MaxRtrAdvInterval;
 | |
| 						rtadv_send_packet(
 | |
| 							  rtadv_get_socket(zvrf),
 | |
| 							  ifp);
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void rtadv_process_solicit(struct interface *ifp)
 | |
| {
 | |
| 	struct zebra_vrf *zvrf = vrf_info_lookup(ifp->vrf_id);
 | |
| 
 | |
| 	assert(zvrf);
 | |
| 	rtadv_send_packet(rtadv_get_socket(zvrf), ifp);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * This function processes optional attributes off of
 | |
|  * end of a RA packet received.  At this point in
 | |
|  * time we only care about this in one situation
 | |
|  * which is when a interface does not have a LL
 | |
|  * v6 address.  We still need to be able to install
 | |
|  * the mac address for v4 to v6 resolution
 | |
|  */
 | |
| static void rtadv_process_optional(uint8_t *optional, unsigned int len,
 | |
| 				   struct interface *ifp,
 | |
| 				   struct sockaddr_in6 *addr)
 | |
| {
 | |
| 	char *mac;
 | |
| 
 | |
| 	while (len > 0) {
 | |
| 		struct nd_opt_hdr *opt_hdr = (struct nd_opt_hdr *)optional;
 | |
| 
 | |
| 		switch(opt_hdr->nd_opt_type) {
 | |
| 		case ND_OPT_SOURCE_LINKADDR:
 | |
| 			mac = (char *)(optional+2);
 | |
| 			if_nbr_mac_to_ipv4ll_neigh_update(ifp, mac,
 | |
| 							  &addr->sin6_addr, 1);
 | |
| 			break;
 | |
| 		default:
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		len -= 8 * opt_hdr->nd_opt_len;
 | |
| 		optional += 8 * opt_hdr->nd_opt_len;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void rtadv_process_advert(uint8_t *msg, unsigned int len,
 | |
| 				 struct interface *ifp,
 | |
| 				 struct sockaddr_in6 *addr)
 | |
| {
 | |
| 	struct nd_router_advert *radvert;
 | |
| 	char addr_str[INET6_ADDRSTRLEN];
 | |
| 	struct zebra_if *zif;
 | |
| 	struct prefix p;
 | |
| 
 | |
| 	zif = ifp->info;
 | |
| 
 | |
| 	inet_ntop(AF_INET6, &addr->sin6_addr, addr_str, INET6_ADDRSTRLEN);
 | |
| 
 | |
| 	if (len < sizeof(struct nd_router_advert)) {
 | |
| 		if (IS_ZEBRA_DEBUG_PACKET)
 | |
| 			zlog_debug("%s(%u): Rx RA with invalid length %d from %s",
 | |
| 				   ifp->name, ifp->ifindex, len, addr_str);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (!IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr)) {
 | |
| 		rtadv_process_optional(msg + sizeof(struct nd_router_advert),
 | |
| 				       len - sizeof(struct nd_router_advert),
 | |
| 				       ifp, addr);
 | |
| 		if (IS_ZEBRA_DEBUG_PACKET)
 | |
| 			zlog_debug("%s(%u): Rx RA with non-linklocal source address from %s",
 | |
| 				   ifp->name, ifp->ifindex, addr_str);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	radvert = (struct nd_router_advert *)msg;
 | |
| 
 | |
| 	if ((radvert->nd_ra_curhoplimit && zif->rtadv.AdvCurHopLimit)
 | |
| 	    && (radvert->nd_ra_curhoplimit != zif->rtadv.AdvCurHopLimit)) {
 | |
| 		flog_warn(
 | |
| 			EC_ZEBRA_RA_PARAM_MISMATCH,
 | |
| 			"%s(%u): Rx RA - our AdvCurHopLimit doesn't agree with %s",
 | |
| 			ifp->name, ifp->ifindex, addr_str);
 | |
| 	}
 | |
| 
 | |
| 	if ((radvert->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED)
 | |
| 	    && !zif->rtadv.AdvManagedFlag) {
 | |
| 		flog_warn(
 | |
| 			EC_ZEBRA_RA_PARAM_MISMATCH,
 | |
| 			"%s(%u): Rx RA - our AdvManagedFlag doesn't agree with %s",
 | |
| 			ifp->name, ifp->ifindex, addr_str);
 | |
| 	}
 | |
| 
 | |
| 	if ((radvert->nd_ra_flags_reserved & ND_RA_FLAG_OTHER)
 | |
| 	    && !zif->rtadv.AdvOtherConfigFlag) {
 | |
| 		flog_warn(
 | |
| 			EC_ZEBRA_RA_PARAM_MISMATCH,
 | |
| 			"%s(%u): Rx RA - our AdvOtherConfigFlag doesn't agree with %s",
 | |
| 			ifp->name, ifp->ifindex, addr_str);
 | |
| 	}
 | |
| 
 | |
| 	if ((radvert->nd_ra_reachable && zif->rtadv.AdvReachableTime)
 | |
| 	    && (ntohl(radvert->nd_ra_reachable)
 | |
| 		!= zif->rtadv.AdvReachableTime)) {
 | |
| 		flog_warn(
 | |
| 			EC_ZEBRA_RA_PARAM_MISMATCH,
 | |
| 			"%s(%u): Rx RA - our AdvReachableTime doesn't agree with %s",
 | |
| 			ifp->name, ifp->ifindex, addr_str);
 | |
| 	}
 | |
| 
 | |
| 	if ((radvert->nd_ra_retransmit && zif->rtadv.AdvRetransTimer)
 | |
| 	    && (ntohl(radvert->nd_ra_retransmit)
 | |
| 		!= (unsigned int)zif->rtadv.AdvRetransTimer)) {
 | |
| 		flog_warn(
 | |
| 			EC_ZEBRA_RA_PARAM_MISMATCH,
 | |
| 			"%s(%u): Rx RA - our AdvRetransTimer doesn't agree with %s",
 | |
| 			ifp->name, ifp->ifindex, addr_str);
 | |
| 	}
 | |
| 
 | |
| 	/* Create entry for neighbor if not known. */
 | |
| 	p.family = AF_INET6;
 | |
| 	IPV6_ADDR_COPY(&p.u.prefix6, &addr->sin6_addr);
 | |
| 	p.prefixlen = IPV6_MAX_PREFIXLEN;
 | |
| 
 | |
| 	if (!nbr_connected_check(ifp, &p))
 | |
| 		nbr_connected_add_ipv6(ifp, &addr->sin6_addr);
 | |
| }
 | |
| 
 | |
| 
 | |
| static void rtadv_process_packet(uint8_t *buf, unsigned int len,
 | |
| 				 ifindex_t ifindex, int hoplimit,
 | |
| 				 struct sockaddr_in6 *from,
 | |
| 				 struct zebra_vrf *zvrf)
 | |
| {
 | |
| 	struct icmp6_hdr *icmph;
 | |
| 	struct interface *ifp;
 | |
| 	struct zebra_if *zif;
 | |
| 	char addr_str[INET6_ADDRSTRLEN];
 | |
| 
 | |
| 	inet_ntop(AF_INET6, &from->sin6_addr, addr_str, INET6_ADDRSTRLEN);
 | |
| 
 | |
| 	/* Interface search. */
 | |
| 	ifp = if_lookup_by_index(ifindex, zvrf->vrf->vrf_id);
 | |
| 	if (ifp == NULL) {
 | |
| 		flog_warn(EC_ZEBRA_UNKNOWN_INTERFACE,
 | |
| 			  "RA/RS received on unknown IF %u from %s", ifindex,
 | |
| 			  addr_str);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (IS_ZEBRA_DEBUG_PACKET)
 | |
| 		zlog_debug("%s(%u): Rx RA/RS len %d from %s", ifp->name,
 | |
| 			   ifp->ifindex, len, addr_str);
 | |
| 
 | |
| 	if (if_is_loopback(ifp)
 | |
| 	    || CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK))
 | |
| 		return;
 | |
| 
 | |
| 	/* Check interface configuration. */
 | |
| 	zif = ifp->info;
 | |
| 	if (!zif->rtadv.AdvSendAdvertisements)
 | |
| 		return;
 | |
| 
 | |
| 	/* ICMP message length check. */
 | |
| 	if (len < sizeof(struct icmp6_hdr)) {
 | |
| 		zlog_debug("%s(%u): Rx RA with Invalid ICMPV6 packet length %d",
 | |
| 			   ifp->name, ifp->ifindex, len);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	icmph = (struct icmp6_hdr *)buf;
 | |
| 
 | |
| 	/* ICMP message type check. */
 | |
| 	if (icmph->icmp6_type != ND_ROUTER_SOLICIT
 | |
| 	    && icmph->icmp6_type != ND_ROUTER_ADVERT) {
 | |
| 		zlog_debug("%s(%u): Rx RA - Unwanted ICMPV6 message type %d",
 | |
| 			   ifp->name, ifp->ifindex, icmph->icmp6_type);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/* Hoplimit check. */
 | |
| 	if (hoplimit >= 0 && hoplimit != 255) {
 | |
| 		zlog_debug("%s(%u): Rx RA - Invalid hoplimit %d", ifp->name,
 | |
| 			   ifp->ifindex, hoplimit);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/* Check ICMP message type. */
 | |
| 	if (icmph->icmp6_type == ND_ROUTER_SOLICIT)
 | |
| 		rtadv_process_solicit(ifp);
 | |
| 	else if (icmph->icmp6_type == ND_ROUTER_ADVERT)
 | |
| 		rtadv_process_advert(buf, len, ifp, from);
 | |
| 
 | |
| 	return;
 | |
| }
 | |
| 
 | |
| static int rtadv_read(struct thread *thread)
 | |
| {
 | |
| 	int sock;
 | |
| 	int len;
 | |
| 	uint8_t buf[RTADV_MSG_SIZE];
 | |
| 	struct sockaddr_in6 from;
 | |
| 	ifindex_t ifindex = 0;
 | |
| 	int hoplimit = -1;
 | |
| 	struct zebra_vrf *zvrf = THREAD_ARG(thread);
 | |
| 
 | |
| 	sock = THREAD_FD(thread);
 | |
| 	zvrf->rtadv.ra_read = NULL;
 | |
| 
 | |
| 	/* Register myself. */
 | |
| 	rtadv_event(zvrf, RTADV_READ, sock);
 | |
| 
 | |
| 	len = rtadv_recv_packet(zvrf, sock, buf, sizeof(buf), &from, &ifindex,
 | |
| 				&hoplimit);
 | |
| 
 | |
| 	if (len < 0) {
 | |
| 		flog_err_sys(EC_LIB_SOCKET,
 | |
| 			     "RA/RS recv failed, socket %u error %s", sock,
 | |
| 			     safe_strerror(errno));
 | |
| 		return len;
 | |
| 	}
 | |
| 
 | |
| 	rtadv_process_packet(buf, (unsigned)len, ifindex, hoplimit, &from, zvrf);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rtadv_make_socket(ns_id_t ns_id)
 | |
| {
 | |
| 	int sock = -1;
 | |
| 	int ret = 0;
 | |
| 	struct icmp6_filter filter;
 | |
| 
 | |
| 	frr_with_privs(&zserv_privs) {
 | |
| 
 | |
| 		sock = ns_socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6, ns_id);
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	if (sock < 0) {
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	ret = setsockopt_ipv6_pktinfo(sock, 1);
 | |
| 	if (ret < 0) {
 | |
| 		close(sock);
 | |
| 		return ret;
 | |
| 	}
 | |
| 	ret = setsockopt_ipv6_multicast_loop(sock, 0);
 | |
| 	if (ret < 0) {
 | |
| 		close(sock);
 | |
| 		return ret;
 | |
| 	}
 | |
| 	ret = setsockopt_ipv6_unicast_hops(sock, 255);
 | |
| 	if (ret < 0) {
 | |
| 		close(sock);
 | |
| 		return ret;
 | |
| 	}
 | |
| 	ret = setsockopt_ipv6_multicast_hops(sock, 255);
 | |
| 	if (ret < 0) {
 | |
| 		close(sock);
 | |
| 		return ret;
 | |
| 	}
 | |
| 	ret = setsockopt_ipv6_hoplimit(sock, 1);
 | |
| 	if (ret < 0) {
 | |
| 		close(sock);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	ICMP6_FILTER_SETBLOCKALL(&filter);
 | |
| 	ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
 | |
| 	ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
 | |
| 
 | |
| 	ret = setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filter,
 | |
| 			 sizeof(struct icmp6_filter));
 | |
| 	if (ret < 0) {
 | |
| 		zlog_info("ICMP6_FILTER set fail: %s", safe_strerror(errno));
 | |
| 		close(sock);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	return sock;
 | |
| }
 | |
| 
 | |
| static struct rtadv_prefix *rtadv_prefix_new(void)
 | |
| {
 | |
| 	return XCALLOC(MTYPE_RTADV_PREFIX, sizeof(struct rtadv_prefix));
 | |
| }
 | |
| 
 | |
| static void rtadv_prefix_free(struct rtadv_prefix *rtadv_prefix)
 | |
| {
 | |
| 	XFREE(MTYPE_RTADV_PREFIX, rtadv_prefix);
 | |
| }
 | |
| 
 | |
| static struct rtadv_prefix *rtadv_prefix_lookup(struct list *rplist,
 | |
| 						struct prefix_ipv6 *p)
 | |
| {
 | |
| 	struct listnode *node;
 | |
| 	struct rtadv_prefix *rprefix;
 | |
| 
 | |
| 	for (ALL_LIST_ELEMENTS_RO(rplist, node, rprefix))
 | |
| 		if (prefix_same((struct prefix *)&rprefix->prefix,
 | |
| 				(struct prefix *)p))
 | |
| 			return rprefix;
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| static struct rtadv_prefix *rtadv_prefix_get(struct list *rplist,
 | |
| 					     struct prefix_ipv6 *p)
 | |
| {
 | |
| 	struct rtadv_prefix *rprefix;
 | |
| 
 | |
| 	rprefix = rtadv_prefix_lookup(rplist, p);
 | |
| 	if (rprefix)
 | |
| 		return rprefix;
 | |
| 
 | |
| 	rprefix = rtadv_prefix_new();
 | |
| 	memcpy(&rprefix->prefix, p, sizeof(struct prefix_ipv6));
 | |
| 	listnode_add(rplist, rprefix);
 | |
| 
 | |
| 	return rprefix;
 | |
| }
 | |
| 
 | |
| static void rtadv_prefix_set(struct zebra_if *zif, struct rtadv_prefix *rp)
 | |
| {
 | |
| 	struct rtadv_prefix *rprefix;
 | |
| 
 | |
| 	rprefix = rtadv_prefix_get(zif->rtadv.AdvPrefixList, &rp->prefix);
 | |
| 
 | |
| 	/* Set parameters. */
 | |
| 	rprefix->AdvValidLifetime = rp->AdvValidLifetime;
 | |
| 	rprefix->AdvPreferredLifetime = rp->AdvPreferredLifetime;
 | |
| 	rprefix->AdvOnLinkFlag = rp->AdvOnLinkFlag;
 | |
| 	rprefix->AdvAutonomousFlag = rp->AdvAutonomousFlag;
 | |
| 	rprefix->AdvRouterAddressFlag = rp->AdvRouterAddressFlag;
 | |
| }
 | |
| 
 | |
| static int rtadv_prefix_reset(struct zebra_if *zif, struct rtadv_prefix *rp)
 | |
| {
 | |
| 	struct rtadv_prefix *rprefix;
 | |
| 
 | |
| 	rprefix = rtadv_prefix_lookup(zif->rtadv.AdvPrefixList, &rp->prefix);
 | |
| 	if (rprefix != NULL) {
 | |
| 		listnode_delete(zif->rtadv.AdvPrefixList, (void *)rprefix);
 | |
| 		rtadv_prefix_free(rprefix);
 | |
| 		return 1;
 | |
| 	} else
 | |
| 		return 0;
 | |
| }
 | |
| 
 | |
| static void ipv6_nd_suppress_ra_set(struct interface *ifp,
 | |
| 				    ipv6_nd_suppress_ra_status status)
 | |
| {
 | |
| 	struct zebra_if *zif;
 | |
| 	struct zebra_vrf *zvrf;
 | |
| 
 | |
| 	zif = ifp->info;
 | |
| 	zvrf = vrf_info_lookup(ifp->vrf_id);
 | |
| 
 | |
| 	if (status == RA_SUPPRESS) {
 | |
| 		/* RA is currently enabled */
 | |
| 		if (zif->rtadv.AdvSendAdvertisements) {
 | |
| 			zif->rtadv.AdvSendAdvertisements = 0;
 | |
| 			zif->rtadv.AdvIntervalTimer = 0;
 | |
| 			zvrf->rtadv.adv_if_count--;
 | |
| 
 | |
| 			if_leave_all_router(rtadv_get_socket(zvrf), ifp);
 | |
| 
 | |
| 			if (zvrf->rtadv.adv_if_count == 0)
 | |
| 				rtadv_event(zvrf, RTADV_STOP, 0);
 | |
| 		}
 | |
| 	} else {
 | |
| 		if (!zif->rtadv.AdvSendAdvertisements) {
 | |
| 			zif->rtadv.AdvSendAdvertisements = 1;
 | |
| 			zif->rtadv.AdvIntervalTimer = 0;
 | |
| 			zvrf->rtadv.adv_if_count++;
 | |
| 
 | |
| 			if (zif->rtadv.MaxRtrAdvInterval >= 1000) {
 | |
| 				/* Enable Fast RA only when RA interval is in
 | |
| 				 * secs */
 | |
| 				zif->rtadv.inFastRexmit = 1;
 | |
| 				zif->rtadv.NumFastReXmitsRemain =
 | |
| 					RTADV_NUM_FAST_REXMITS;
 | |
| 			}
 | |
| 
 | |
| 			if_join_all_router(rtadv_get_socket(zvrf), ifp);
 | |
| 
 | |
| 			if (zvrf->rtadv.adv_if_count == 1)
 | |
| 				rtadv_event(zvrf, RTADV_START,
 | |
| 					    rtadv_get_socket(zvrf));
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Handle client (BGP) message to enable or disable IPv6 RA on an interface.
 | |
|  * Note that while the client could request RA on an interface on which the
 | |
|  * operator has not enabled RA, RA won't be disabled upon client request
 | |
|  * if the operator has explicitly enabled RA. The enable request can also
 | |
|  * specify a RA interval (in seconds).
 | |
|  */
 | |
| static void zebra_interface_radv_set(ZAPI_HANDLER_ARGS, int enable)
 | |
| {
 | |
| 	struct stream *s;
 | |
| 	ifindex_t ifindex;
 | |
| 	struct interface *ifp;
 | |
| 	struct zebra_if *zif;
 | |
| 	int ra_interval;
 | |
| 
 | |
| 	s = msg;
 | |
| 
 | |
| 	/* Get interface index and RA interval. */
 | |
| 	STREAM_GETL(s, ifindex);
 | |
| 	STREAM_GETL(s, ra_interval);
 | |
| 
 | |
| 	if (IS_ZEBRA_DEBUG_EVENT)
 | |
| 		zlog_debug("%u: IF %u RA %s from client %s, interval %ds",
 | |
| 			   zvrf_id(zvrf), ifindex,
 | |
| 			   enable ? "enable" : "disable",
 | |
| 			   zebra_route_string(client->proto), ra_interval);
 | |
| 
 | |
| 	/* Locate interface and check VRF match. */
 | |
| 	ifp = if_lookup_by_index(ifindex, zvrf->vrf->vrf_id);
 | |
| 	if (!ifp) {
 | |
| 		flog_warn(EC_ZEBRA_UNKNOWN_INTERFACE,
 | |
| 			  "%u: IF %u RA %s client %s - interface unknown",
 | |
| 			  zvrf_id(zvrf), ifindex, enable ? "enable" : "disable",
 | |
| 			  zebra_route_string(client->proto));
 | |
| 		return;
 | |
| 	}
 | |
| 	if (ifp->vrf_id != zvrf_id(zvrf)) {
 | |
| 		zlog_debug(
 | |
| 			"%u: IF %u RA %s client %s - VRF mismatch, IF VRF %u",
 | |
| 			zvrf_id(zvrf), ifindex, enable ? "enable" : "disable",
 | |
| 			zebra_route_string(client->proto), ifp->vrf_id);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	zif = ifp->info;
 | |
| 	if (enable) {
 | |
| 		SET_FLAG(zif->rtadv.ra_configured, BGP_RA_CONFIGURED);
 | |
| 		ipv6_nd_suppress_ra_set(ifp, RA_ENABLE);
 | |
| 		if (ra_interval
 | |
| 		    && (ra_interval * 1000) < zif->rtadv.MaxRtrAdvInterval
 | |
| 		    && !CHECK_FLAG(zif->rtadv.ra_configured,
 | |
| 				   VTY_RA_INTERVAL_CONFIGURED))
 | |
| 			zif->rtadv.MaxRtrAdvInterval = ra_interval * 1000;
 | |
| 	} else {
 | |
| 		UNSET_FLAG(zif->rtadv.ra_configured, BGP_RA_CONFIGURED);
 | |
| 		if (!CHECK_FLAG(zif->rtadv.ra_configured,
 | |
| 				VTY_RA_INTERVAL_CONFIGURED))
 | |
| 			zif->rtadv.MaxRtrAdvInterval =
 | |
| 				RTADV_MAX_RTR_ADV_INTERVAL;
 | |
| 		if (!CHECK_FLAG(zif->rtadv.ra_configured, VTY_RA_CONFIGURED))
 | |
| 			ipv6_nd_suppress_ra_set(ifp, RA_SUPPRESS);
 | |
| 	}
 | |
| stream_failure:
 | |
| 	return;
 | |
| }
 | |
| 
 | |
| void zebra_interface_radv_disable(ZAPI_HANDLER_ARGS)
 | |
| {
 | |
| 	zebra_interface_radv_set(client, hdr, msg, zvrf, 0);
 | |
| }
 | |
| void zebra_interface_radv_enable(ZAPI_HANDLER_ARGS)
 | |
| {
 | |
| 	zebra_interface_radv_set(client, hdr, msg, zvrf, 1);
 | |
| }
 | |
| 
 | |
| DEFUN (ipv6_nd_suppress_ra,
 | |
|        ipv6_nd_suppress_ra_cmd,
 | |
|        "ipv6 nd suppress-ra",
 | |
|        "Interface IPv6 config commands\n"
 | |
|        "Neighbor discovery\n"
 | |
|        "Suppress Router Advertisement\n")
 | |
| {
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 
 | |
| 	if (if_is_loopback(ifp)
 | |
| 	    || CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK)) {
 | |
| 		vty_out(vty,
 | |
| 			"Cannot configure IPv6 Router Advertisements on this  interface\n");
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 
 | |
| 	if (!CHECK_FLAG(zif->rtadv.ra_configured, BGP_RA_CONFIGURED))
 | |
| 		ipv6_nd_suppress_ra_set(ifp, RA_SUPPRESS);
 | |
| 
 | |
| 	UNSET_FLAG(zif->rtadv.ra_configured, VTY_RA_CONFIGURED);
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (no_ipv6_nd_suppress_ra,
 | |
|        no_ipv6_nd_suppress_ra_cmd,
 | |
|        "no ipv6 nd suppress-ra",
 | |
|        NO_STR
 | |
|        "Interface IPv6 config commands\n"
 | |
|        "Neighbor discovery\n"
 | |
|        "Suppress Router Advertisement\n")
 | |
| {
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 
 | |
| 	if (if_is_loopback(ifp)
 | |
| 	    || CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK)) {
 | |
| 		vty_out(vty,
 | |
| 			"Cannot configure IPv6 Router Advertisements on this interface\n");
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 
 | |
| 	ipv6_nd_suppress_ra_set(ifp, RA_ENABLE);
 | |
| 	SET_FLAG(zif->rtadv.ra_configured, VTY_RA_CONFIGURED);
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (ipv6_nd_ra_interval_msec,
 | |
|        ipv6_nd_ra_interval_msec_cmd,
 | |
|        "ipv6 nd ra-interval msec (70-1800000)",
 | |
|        "Interface IPv6 config commands\n"
 | |
|        "Neighbor discovery\n"
 | |
|        "Router Advertisement interval\n"
 | |
|        "Router Advertisement interval in milliseconds\n"
 | |
|        "Router Advertisement interval in milliseconds\n")
 | |
| {
 | |
| 	int idx_number = 4;
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	unsigned interval;
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 	struct zebra_vrf *zvrf;
 | |
| 
 | |
| 	zvrf = vrf_info_lookup(ifp->vrf_id);
 | |
| 
 | |
| 	interval = strtoul(argv[idx_number]->arg, NULL, 10);
 | |
| 	if ((zif->rtadv.AdvDefaultLifetime != -1
 | |
| 	     && interval > (unsigned)zif->rtadv.AdvDefaultLifetime * 1000)) {
 | |
| 		vty_out(vty,
 | |
| 			"This ra-interval would conflict with configured ra-lifetime!\n");
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 
 | |
| 	if (zif->rtadv.MaxRtrAdvInterval % 1000)
 | |
| 		zvrf->rtadv.adv_msec_if_count--;
 | |
| 
 | |
| 	if (interval % 1000)
 | |
| 		zvrf->rtadv.adv_msec_if_count++;
 | |
| 
 | |
| 	SET_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED);
 | |
| 	zif->rtadv.MaxRtrAdvInterval = interval;
 | |
| 	zif->rtadv.MinRtrAdvInterval = 0.33 * interval;
 | |
| 	zif->rtadv.AdvIntervalTimer = 0;
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (ipv6_nd_ra_interval,
 | |
|        ipv6_nd_ra_interval_cmd,
 | |
|        "ipv6 nd ra-interval (1-1800)",
 | |
|        "Interface IPv6 config commands\n"
 | |
|        "Neighbor discovery\n"
 | |
|        "Router Advertisement interval\n"
 | |
|        "Router Advertisement interval in seconds\n")
 | |
| {
 | |
| 	int idx_number = 3;
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	unsigned interval;
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 	struct zebra_vrf *zvrf;
 | |
| 
 | |
| 	zvrf = vrf_info_lookup(ifp->vrf_id);
 | |
| 
 | |
| 	interval = strtoul(argv[idx_number]->arg, NULL, 10);
 | |
| 	if ((zif->rtadv.AdvDefaultLifetime != -1
 | |
| 	     && interval > (unsigned)zif->rtadv.AdvDefaultLifetime)) {
 | |
| 		vty_out(vty,
 | |
| 			"This ra-interval would conflict with configured ra-lifetime!\n");
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 
 | |
| 	if (zif->rtadv.MaxRtrAdvInterval % 1000)
 | |
| 		zvrf->rtadv.adv_msec_if_count--;
 | |
| 
 | |
| 	/* convert to milliseconds */
 | |
| 	interval = interval * 1000;
 | |
| 
 | |
| 	SET_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED);
 | |
| 	zif->rtadv.MaxRtrAdvInterval = interval;
 | |
| 	zif->rtadv.MinRtrAdvInterval = 0.33 * interval;
 | |
| 	zif->rtadv.AdvIntervalTimer = 0;
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (no_ipv6_nd_ra_interval,
 | |
|        no_ipv6_nd_ra_interval_cmd,
 | |
|        "no ipv6 nd ra-interval [<(1-1800)|msec (1-1800000)>]",
 | |
|        NO_STR
 | |
|        "Interface IPv6 config commands\n"
 | |
|        "Neighbor discovery\n"
 | |
|        "Router Advertisement interval\n"
 | |
|        "Router Advertisement interval in seconds\n"
 | |
|        "Specify millisecond router advertisement interval\n"
 | |
|        "Router Advertisement interval in milliseconds\n")
 | |
| {
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 	struct zebra_vrf *zvrf = NULL;
 | |
| 
 | |
| 	zvrf = vrf_info_lookup(ifp->vrf_id);
 | |
| 
 | |
| 	if (zif->rtadv.MaxRtrAdvInterval % 1000)
 | |
| 		zvrf->rtadv.adv_msec_if_count--;
 | |
| 
 | |
| 	UNSET_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED);
 | |
| 
 | |
| 	if (CHECK_FLAG(zif->rtadv.ra_configured, BGP_RA_CONFIGURED))
 | |
| 		zif->rtadv.MaxRtrAdvInterval = 10000;
 | |
| 	else
 | |
| 		zif->rtadv.MaxRtrAdvInterval = RTADV_MAX_RTR_ADV_INTERVAL;
 | |
| 
 | |
| 	zif->rtadv.AdvIntervalTimer = zif->rtadv.MaxRtrAdvInterval;
 | |
| 	zif->rtadv.MinRtrAdvInterval = RTADV_MIN_RTR_ADV_INTERVAL;
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (ipv6_nd_ra_lifetime,
 | |
|        ipv6_nd_ra_lifetime_cmd,
 | |
|        "ipv6 nd ra-lifetime (0-9000)",
 | |
|        "Interface IPv6 config commands\n"
 | |
|        "Neighbor discovery\n"
 | |
|        "Router lifetime\n"
 | |
|        "Router lifetime in seconds (0 stands for a non-default gw)\n")
 | |
| {
 | |
| 	int idx_number = 3;
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 	int lifetime;
 | |
| 
 | |
| 	lifetime = strtoul(argv[idx_number]->arg, NULL, 10);
 | |
| 
 | |
| 	/* The value to be placed in the Router Lifetime field
 | |
| 	 * of Router Advertisements sent from the interface,
 | |
| 	 * in seconds.  MUST be either zero or between
 | |
| 	 * MaxRtrAdvInterval and 9000 seconds. -- RFC4861, 6.2.1 */
 | |
| 	if ((lifetime != 0 && lifetime * 1000 < zif->rtadv.MaxRtrAdvInterval)) {
 | |
| 		vty_out(vty,
 | |
| 			"This ra-lifetime would conflict with configured ra-interval\n");
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 
 | |
| 	zif->rtadv.AdvDefaultLifetime = lifetime;
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (no_ipv6_nd_ra_lifetime,
 | |
|        no_ipv6_nd_ra_lifetime_cmd,
 | |
|        "no ipv6 nd ra-lifetime [(0-9000)]",
 | |
|        NO_STR
 | |
|        "Interface IPv6 config commands\n"
 | |
|        "Neighbor discovery\n"
 | |
|        "Router lifetime\n"
 | |
|        "Router lifetime in seconds (0 stands for a non-default gw)\n")
 | |
| {
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 
 | |
| 	zif->rtadv.AdvDefaultLifetime = -1;
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (ipv6_nd_reachable_time,
 | |
|        ipv6_nd_reachable_time_cmd,
 | |
|        "ipv6 nd reachable-time (1-3600000)",
 | |
|        "Interface IPv6 config commands\n"
 | |
|        "Neighbor discovery\n"
 | |
|        "Reachable time\n"
 | |
|        "Reachable time in milliseconds\n")
 | |
| {
 | |
| 	int idx_number = 3;
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 	zif->rtadv.AdvReachableTime = strtoul(argv[idx_number]->arg, NULL, 10);
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (no_ipv6_nd_reachable_time,
 | |
|        no_ipv6_nd_reachable_time_cmd,
 | |
|        "no ipv6 nd reachable-time [(1-3600000)]",
 | |
|        NO_STR
 | |
|        "Interface IPv6 config commands\n"
 | |
|        "Neighbor discovery\n"
 | |
|        "Reachable time\n"
 | |
|        "Reachable time in milliseconds\n")
 | |
| {
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 
 | |
| 	zif->rtadv.AdvReachableTime = 0;
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (ipv6_nd_homeagent_preference,
 | |
|        ipv6_nd_homeagent_preference_cmd,
 | |
|        "ipv6 nd home-agent-preference (0-65535)",
 | |
|        "Interface IPv6 config commands\n"
 | |
|        "Neighbor discovery\n"
 | |
|        "Home Agent preference\n"
 | |
|        "preference value (default is 0, least preferred)\n")
 | |
| {
 | |
| 	int idx_number = 3;
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 	zif->rtadv.HomeAgentPreference =
 | |
| 		strtoul(argv[idx_number]->arg, NULL, 10);
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (no_ipv6_nd_homeagent_preference,
 | |
|        no_ipv6_nd_homeagent_preference_cmd,
 | |
|        "no ipv6 nd home-agent-preference [(0-65535)]",
 | |
|        NO_STR
 | |
|        "Interface IPv6 config commands\n"
 | |
|        "Neighbor discovery\n"
 | |
|        "Home Agent preference\n"
 | |
|        "preference value (default is 0, least preferred)\n")
 | |
| {
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 
 | |
| 	zif->rtadv.HomeAgentPreference = 0;
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (ipv6_nd_homeagent_lifetime,
 | |
|        ipv6_nd_homeagent_lifetime_cmd,
 | |
|        "ipv6 nd home-agent-lifetime (0-65520)",
 | |
|        "Interface IPv6 config commands\n"
 | |
|        "Neighbor discovery\n"
 | |
|        "Home Agent lifetime\n"
 | |
|        "Home Agent lifetime in seconds (0 to track ra-lifetime)\n")
 | |
| {
 | |
| 	int idx_number = 3;
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 	zif->rtadv.HomeAgentLifetime = strtoul(argv[idx_number]->arg, NULL, 10);
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (no_ipv6_nd_homeagent_lifetime,
 | |
|        no_ipv6_nd_homeagent_lifetime_cmd,
 | |
|        "no ipv6 nd home-agent-lifetime [(0-65520)]",
 | |
|        NO_STR
 | |
|        "Interface IPv6 config commands\n"
 | |
|        "Neighbor discovery\n"
 | |
|        "Home Agent lifetime\n"
 | |
|        "Home Agent lifetime in seconds (0 to track ra-lifetime)\n")
 | |
| {
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 
 | |
| 	zif->rtadv.HomeAgentLifetime = -1;
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (ipv6_nd_managed_config_flag,
 | |
|        ipv6_nd_managed_config_flag_cmd,
 | |
|        "ipv6 nd managed-config-flag",
 | |
|        "Interface IPv6 config commands\n"
 | |
|        "Neighbor discovery\n"
 | |
|        "Managed address configuration flag\n")
 | |
| {
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 
 | |
| 	zif->rtadv.AdvManagedFlag = 1;
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (no_ipv6_nd_managed_config_flag,
 | |
|        no_ipv6_nd_managed_config_flag_cmd,
 | |
|        "no ipv6 nd managed-config-flag",
 | |
|        NO_STR
 | |
|        "Interface IPv6 config commands\n"
 | |
|        "Neighbor discovery\n"
 | |
|        "Managed address configuration flag\n")
 | |
| {
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 
 | |
| 	zif->rtadv.AdvManagedFlag = 0;
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (ipv6_nd_homeagent_config_flag,
 | |
|        ipv6_nd_homeagent_config_flag_cmd,
 | |
|        "ipv6 nd home-agent-config-flag",
 | |
|        "Interface IPv6 config commands\n"
 | |
|        "Neighbor discovery\n"
 | |
|        "Home Agent configuration flag\n")
 | |
| {
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 
 | |
| 	zif->rtadv.AdvHomeAgentFlag = 1;
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (no_ipv6_nd_homeagent_config_flag,
 | |
|        no_ipv6_nd_homeagent_config_flag_cmd,
 | |
|        "no ipv6 nd home-agent-config-flag",
 | |
|        NO_STR
 | |
|        "Interface IPv6 config commands\n"
 | |
|        "Neighbor discovery\n"
 | |
|        "Home Agent configuration flag\n")
 | |
| {
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 
 | |
| 	zif->rtadv.AdvHomeAgentFlag = 0;
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (ipv6_nd_adv_interval_config_option,
 | |
|        ipv6_nd_adv_interval_config_option_cmd,
 | |
|        "ipv6 nd adv-interval-option",
 | |
|        "Interface IPv6 config commands\n"
 | |
|        "Neighbor discovery\n"
 | |
|        "Advertisement Interval Option\n")
 | |
| {
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 
 | |
| 	zif->rtadv.AdvIntervalOption = 1;
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (no_ipv6_nd_adv_interval_config_option,
 | |
|        no_ipv6_nd_adv_interval_config_option_cmd,
 | |
|        "no ipv6 nd adv-interval-option",
 | |
|        NO_STR
 | |
|        "Interface IPv6 config commands\n"
 | |
|        "Neighbor discovery\n"
 | |
|        "Advertisement Interval Option\n")
 | |
| {
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 
 | |
| 	zif->rtadv.AdvIntervalOption = 0;
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (ipv6_nd_other_config_flag,
 | |
|        ipv6_nd_other_config_flag_cmd,
 | |
|        "ipv6 nd other-config-flag",
 | |
|        "Interface IPv6 config commands\n"
 | |
|        "Neighbor discovery\n"
 | |
|        "Other statefull configuration flag\n")
 | |
| {
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 
 | |
| 	zif->rtadv.AdvOtherConfigFlag = 1;
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (no_ipv6_nd_other_config_flag,
 | |
|        no_ipv6_nd_other_config_flag_cmd,
 | |
|        "no ipv6 nd other-config-flag",
 | |
|        NO_STR
 | |
|        "Interface IPv6 config commands\n"
 | |
|        "Neighbor discovery\n"
 | |
|        "Other statefull configuration flag\n")
 | |
| {
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 
 | |
| 	zif->rtadv.AdvOtherConfigFlag = 0;
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (ipv6_nd_prefix,
 | |
|        ipv6_nd_prefix_cmd,
 | |
|        "ipv6 nd prefix X:X::X:X/M [<(0-4294967295)|infinite> <(0-4294967295)|infinite>] [<router-address|off-link [no-autoconfig]|no-autoconfig [off-link]>]",
 | |
|        "Interface IPv6 config commands\n"
 | |
|        "Neighbor discovery\n"
 | |
|        "Prefix information\n"
 | |
|        "IPv6 prefix\n"
 | |
|        "Valid lifetime in seconds\n"
 | |
|        "Infinite valid lifetime\n"
 | |
|        "Preferred lifetime in seconds\n"
 | |
|        "Infinite preferred lifetime\n"
 | |
|        "Set Router Address flag\n"
 | |
|        "Do not use prefix for onlink determination\n"
 | |
|        "Do not use prefix for autoconfiguration\n"
 | |
|        "Do not use prefix for autoconfiguration\n"
 | |
|        "Do not use prefix for onlink determination\n")
 | |
| {
 | |
| 	/* prelude */
 | |
| 	char *prefix = argv[3]->arg;
 | |
| 	int lifetimes = (argc > 4) && (argv[4]->type == RANGE_TKN
 | |
| 				       || strmatch(argv[4]->text, "infinite"));
 | |
| 	int routeropts = lifetimes ? argc > 6 : argc > 4;
 | |
| 
 | |
| 	int idx_routeropts = routeropts ? (lifetimes ? 6 : 4) : 0;
 | |
| 
 | |
| 	char *lifetime = NULL, *preflifetime = NULL;
 | |
| 	int routeraddr = 0, offlink = 0, noautoconf = 0;
 | |
| 	if (lifetimes) {
 | |
| 		lifetime = argv[4]->type == RANGE_TKN ? argv[4]->arg
 | |
| 						      : argv[4]->text;
 | |
| 		preflifetime = argv[5]->type == RANGE_TKN ? argv[5]->arg
 | |
| 							  : argv[5]->text;
 | |
| 	}
 | |
| 	if (routeropts) {
 | |
| 		routeraddr =
 | |
| 			strmatch(argv[idx_routeropts]->text, "router-address");
 | |
| 		if (!routeraddr) {
 | |
| 			offlink = (argc > idx_routeropts + 1
 | |
| 				   || strmatch(argv[idx_routeropts]->text,
 | |
| 					       "off-link"));
 | |
| 			noautoconf = (argc > idx_routeropts + 1
 | |
| 				      || strmatch(argv[idx_routeropts]->text,
 | |
| 						  "no-autoconfig"));
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* business */
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zebra_if = ifp->info;
 | |
| 	int ret;
 | |
| 	struct rtadv_prefix rp;
 | |
| 
 | |
| 	ret = str2prefix_ipv6(prefix, &rp.prefix);
 | |
| 	if (!ret) {
 | |
| 		vty_out(vty, "Malformed IPv6 prefix\n");
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 	apply_mask_ipv6(&rp.prefix); /* RFC4861 4.6.2 */
 | |
| 	rp.AdvOnLinkFlag = !offlink;
 | |
| 	rp.AdvAutonomousFlag = !noautoconf;
 | |
| 	rp.AdvRouterAddressFlag = routeraddr;
 | |
| 	rp.AdvValidLifetime = RTADV_VALID_LIFETIME;
 | |
| 	rp.AdvPreferredLifetime = RTADV_PREFERRED_LIFETIME;
 | |
| 
 | |
| 	if (lifetimes) {
 | |
| 		rp.AdvValidLifetime = strmatch(lifetime, "infinite")
 | |
| 					      ? UINT32_MAX
 | |
| 					      : strtoll(lifetime, NULL, 10);
 | |
| 		rp.AdvPreferredLifetime =
 | |
| 			strmatch(preflifetime, "infinite")
 | |
| 				? UINT32_MAX
 | |
| 				: strtoll(preflifetime, NULL, 10);
 | |
| 		if (rp.AdvPreferredLifetime > rp.AdvValidLifetime) {
 | |
| 			vty_out(vty, "Invalid preferred lifetime\n");
 | |
| 			return CMD_WARNING_CONFIG_FAILED;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	rtadv_prefix_set(zebra_if, &rp);
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (no_ipv6_nd_prefix,
 | |
|        no_ipv6_nd_prefix_cmd,
 | |
|        "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]>]",
 | |
|         NO_STR
 | |
|        "Interface IPv6 config commands\n"
 | |
|        "Neighbor discovery\n"
 | |
|        "Prefix information\n"
 | |
|        "IPv6 prefix\n"
 | |
|        "Valid lifetime in seconds\n"
 | |
|        "Infinite valid lifetime\n"
 | |
|        "Preferred lifetime in seconds\n"
 | |
|        "Infinite preferred lifetime\n"
 | |
|        "Set Router Address flag\n"
 | |
|        "Do not use prefix for onlink determination\n"
 | |
|        "Do not use prefix for autoconfiguration\n"
 | |
|        "Do not use prefix for autoconfiguration\n"
 | |
|        "Do not use prefix for onlink determination\n")
 | |
| {
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zebra_if = ifp->info;
 | |
| 	int ret;
 | |
| 	struct rtadv_prefix rp;
 | |
| 	char *prefix = argv[4]->arg;
 | |
| 
 | |
| 	ret = str2prefix_ipv6(prefix, &rp.prefix);
 | |
| 	if (!ret) {
 | |
| 		vty_out(vty, "Malformed IPv6 prefix\n");
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 	apply_mask_ipv6(&rp.prefix); /* RFC4861 4.6.2 */
 | |
| 
 | |
| 	ret = rtadv_prefix_reset(zebra_if, &rp);
 | |
| 	if (!ret) {
 | |
| 		vty_out(vty, "Non-existant IPv6 prefix\n");
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (ipv6_nd_router_preference,
 | |
|        ipv6_nd_router_preference_cmd,
 | |
|        "ipv6 nd router-preference <high|medium|low>",
 | |
|        "Interface IPv6 config commands\n"
 | |
|        "Neighbor discovery\n"
 | |
|        "Default router preference\n"
 | |
|        "High default router preference\n"
 | |
|        "Medium default router preference (default)\n"
 | |
|        "Low default router preference\n")
 | |
| {
 | |
| 	int idx_high_medium_low = 3;
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 	int i = 0;
 | |
| 
 | |
| 	while (0 != rtadv_pref_strs[i]) {
 | |
| 		if (strncmp(argv[idx_high_medium_low]->arg, rtadv_pref_strs[i],
 | |
| 			    1)
 | |
| 		    == 0) {
 | |
| 			zif->rtadv.DefaultPreference = i;
 | |
| 			return CMD_SUCCESS;
 | |
| 		}
 | |
| 		i++;
 | |
| 	}
 | |
| 
 | |
| 	return CMD_ERR_NO_MATCH;
 | |
| }
 | |
| 
 | |
| DEFUN (no_ipv6_nd_router_preference,
 | |
|        no_ipv6_nd_router_preference_cmd,
 | |
|        "no ipv6 nd router-preference [<high|medium|low>]",
 | |
|        NO_STR
 | |
|        "Interface IPv6 config commands\n"
 | |
|        "Neighbor discovery\n"
 | |
|        "Default router preference\n"
 | |
|        "High default router preference\n"
 | |
|        "Medium default router preference (default)\n"
 | |
|        "Low default router preference\n")
 | |
| {
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 
 | |
| 	zif->rtadv.DefaultPreference =
 | |
| 		RTADV_PREF_MEDIUM; /* Default per RFC4191. */
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (ipv6_nd_mtu,
 | |
|        ipv6_nd_mtu_cmd,
 | |
|        "ipv6 nd mtu (1-65535)",
 | |
|        "Interface IPv6 config commands\n"
 | |
|        "Neighbor discovery\n"
 | |
|        "Advertised MTU\n"
 | |
|        "MTU in bytes\n")
 | |
| {
 | |
| 	int idx_number = 3;
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 	zif->rtadv.AdvLinkMTU = strtoul(argv[idx_number]->arg, NULL, 10);
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (no_ipv6_nd_mtu,
 | |
|        no_ipv6_nd_mtu_cmd,
 | |
|        "no ipv6 nd mtu [(1-65535)]",
 | |
|        NO_STR
 | |
|        "Interface IPv6 config commands\n"
 | |
|        "Neighbor discovery\n"
 | |
|        "Advertised MTU\n"
 | |
|        "MTU in bytes\n")
 | |
| {
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 	zif->rtadv.AdvLinkMTU = 0;
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| static struct rtadv_rdnss *rtadv_rdnss_new(void)
 | |
| {
 | |
| 	return XCALLOC(MTYPE_RTADV_RDNSS, sizeof(struct rtadv_rdnss));
 | |
| }
 | |
| 
 | |
| static void rtadv_rdnss_free(struct rtadv_rdnss *rdnss)
 | |
| {
 | |
| 	XFREE(MTYPE_RTADV_RDNSS, rdnss);
 | |
| }
 | |
| 
 | |
| static struct rtadv_rdnss *rtadv_rdnss_lookup(struct list *list,
 | |
| 					      struct rtadv_rdnss *rdnss)
 | |
| {
 | |
| 	struct listnode *node;
 | |
| 	struct rtadv_rdnss *p;
 | |
| 
 | |
| 	for (ALL_LIST_ELEMENTS_RO(list, node, p))
 | |
| 		if (IPV6_ADDR_SAME(&p->addr, &rdnss->addr))
 | |
| 			return p;
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| static struct rtadv_rdnss *rtadv_rdnss_get(struct list *list,
 | |
| 					   struct rtadv_rdnss *rdnss)
 | |
| {
 | |
| 	struct rtadv_rdnss *p;
 | |
| 
 | |
| 	p = rtadv_rdnss_lookup(list, rdnss);
 | |
| 	if (p)
 | |
| 		return p;
 | |
| 
 | |
| 	p = rtadv_rdnss_new();
 | |
| 	memcpy(p, rdnss, sizeof(struct rtadv_rdnss));
 | |
| 	listnode_add(list, p);
 | |
| 
 | |
| 	return p;
 | |
| }
 | |
| 
 | |
| static void rtadv_rdnss_set(struct zebra_if *zif, struct rtadv_rdnss *rdnss)
 | |
| {
 | |
| 	struct rtadv_rdnss *p;
 | |
| 
 | |
| 	p = rtadv_rdnss_get(zif->rtadv.AdvRDNSSList, rdnss);
 | |
| 	p->lifetime = rdnss->lifetime;
 | |
| 	p->lifetime_set = rdnss->lifetime_set;
 | |
| }
 | |
| 
 | |
| static int rtadv_rdnss_reset(struct zebra_if *zif, struct rtadv_rdnss *rdnss)
 | |
| {
 | |
| 	struct rtadv_rdnss *p;
 | |
| 
 | |
| 	p = rtadv_rdnss_lookup(zif->rtadv.AdvRDNSSList, rdnss);
 | |
| 	if (p) {
 | |
| 		listnode_delete(zif->rtadv.AdvRDNSSList, p);
 | |
| 		rtadv_rdnss_free(p);
 | |
| 		return 1;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static struct rtadv_dnssl *rtadv_dnssl_new(void)
 | |
| {
 | |
| 	return XCALLOC(MTYPE_RTADV_DNSSL, sizeof(struct rtadv_dnssl));
 | |
| }
 | |
| 
 | |
| static void rtadv_dnssl_free(struct rtadv_dnssl *dnssl)
 | |
| {
 | |
| 	XFREE(MTYPE_RTADV_DNSSL, dnssl);
 | |
| }
 | |
| 
 | |
| static struct rtadv_dnssl *rtadv_dnssl_lookup(struct list *list,
 | |
| 					      struct rtadv_dnssl *dnssl)
 | |
| {
 | |
| 	struct listnode *node;
 | |
| 	struct rtadv_dnssl *p;
 | |
| 
 | |
| 	for (ALL_LIST_ELEMENTS_RO(list, node, p))
 | |
| 		if (!strcasecmp(p->name, dnssl->name))
 | |
| 			return p;
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| static struct rtadv_dnssl *rtadv_dnssl_get(struct list *list,
 | |
| 					   struct rtadv_dnssl *dnssl)
 | |
| {
 | |
| 	struct rtadv_dnssl *p;
 | |
| 
 | |
| 	p = rtadv_dnssl_lookup(list, dnssl);
 | |
| 	if (p)
 | |
| 		return p;
 | |
| 
 | |
| 	p = rtadv_dnssl_new();
 | |
| 	memcpy(p, dnssl, sizeof(struct rtadv_dnssl));
 | |
| 	listnode_add(list, p);
 | |
| 
 | |
| 	return p;
 | |
| }
 | |
| 
 | |
| static void rtadv_dnssl_set(struct zebra_if *zif, struct rtadv_dnssl *dnssl)
 | |
| {
 | |
| 	struct rtadv_dnssl *p;
 | |
| 
 | |
| 	p = rtadv_dnssl_get(zif->rtadv.AdvDNSSLList, dnssl);
 | |
| 	memcpy(p, dnssl, sizeof(struct rtadv_dnssl));
 | |
| }
 | |
| 
 | |
| static int rtadv_dnssl_reset(struct zebra_if *zif, struct rtadv_dnssl *dnssl)
 | |
| {
 | |
| 	struct rtadv_dnssl *p;
 | |
| 
 | |
| 	p = rtadv_dnssl_lookup(zif->rtadv.AdvDNSSLList, dnssl);
 | |
| 	if (p) {
 | |
| 		listnode_delete(zif->rtadv.AdvDNSSLList, p);
 | |
| 		rtadv_dnssl_free(p);
 | |
| 		return 1;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Convert dotted domain name (with or without trailing root zone dot) to
 | |
|  * sequence of length-prefixed labels, as described in [RFC1035 3.1]. Write up
 | |
|  * to strlen(in) + 2 octets to out.
 | |
|  *
 | |
|  * Returns the number of octets written to out or -1 if in does not constitute
 | |
|  * a valid domain name.
 | |
|  */
 | |
| static int rtadv_dnssl_encode(uint8_t *out, const char *in)
 | |
| {
 | |
| 	const char *label_start, *label_end;
 | |
| 	size_t outp;
 | |
| 
 | |
| 	outp = 0;
 | |
| 	label_start = in;
 | |
| 
 | |
| 	while (*label_start) {
 | |
| 		size_t label_len;
 | |
| 
 | |
| 		label_end = strchr(label_start, '.');
 | |
| 		if (label_end == NULL)
 | |
| 			label_end = label_start + strlen(label_start);
 | |
| 
 | |
| 		label_len = label_end - label_start;
 | |
| 		if (label_len >= 64)
 | |
| 			return -1; /* labels must be 63 octets or less */
 | |
| 
 | |
| 		out[outp++] = (uint8_t)label_len;
 | |
| 		memcpy(out + outp, label_start, label_len);
 | |
| 		outp += label_len;
 | |
| 		label_start += label_len;
 | |
| 		if (*label_start == '.')
 | |
| 			label_start++;
 | |
| 	}
 | |
| 
 | |
| 	out[outp++] = '\0';
 | |
| 	return outp;
 | |
| }
 | |
| 
 | |
| DEFUN(ipv6_nd_rdnss,
 | |
|       ipv6_nd_rdnss_cmd,
 | |
|       "ipv6 nd rdnss X:X::X:X [<(0-4294967295)|infinite>]",
 | |
|       "Interface IPv6 config commands\n"
 | |
|       "Neighbor discovery\n"
 | |
|       "Recursive DNS server information\n"
 | |
|       "IPv6 address\n"
 | |
|       "Valid lifetime in seconds\n"
 | |
|       "Infinite valid lifetime\n")
 | |
| {
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 	struct rtadv_rdnss rdnss = {};
 | |
| 
 | |
| 	if (inet_pton(AF_INET6, argv[3]->arg, &rdnss.addr) != 1) {
 | |
| 		vty_out(vty, "Malformed IPv6 address\n");
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 	if (argc > 4) {
 | |
| 		char *lifetime = argv[4]->type == RANGE_TKN ? argv[4]->arg
 | |
| 							    : argv[4]->text;
 | |
| 		rdnss.lifetime = strmatch(lifetime, "infinite")
 | |
| 					 ? UINT32_MAX
 | |
| 					 : strtoll(lifetime, NULL, 10);
 | |
| 		rdnss.lifetime_set = 1;
 | |
| 	}
 | |
| 
 | |
| 	rtadv_rdnss_set(zif, &rdnss);
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN(no_ipv6_nd_rdnss,
 | |
|       no_ipv6_nd_rdnss_cmd,
 | |
|       "no ipv6 nd rdnss X:X::X:X [<(0-4294967295)|infinite>]",
 | |
|       NO_STR
 | |
|       "Interface IPv6 config commands\n"
 | |
|       "Neighbor discovery\n"
 | |
|       "Recursive DNS server information\n"
 | |
|       "IPv6 address\n"
 | |
|       "Valid lifetime in seconds\n"
 | |
|       "Infinite valid lifetime\n")
 | |
| {
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 	struct rtadv_rdnss rdnss = {};
 | |
| 
 | |
| 	if (inet_pton(AF_INET6, argv[4]->arg, &rdnss.addr) != 1) {
 | |
| 		vty_out(vty, "Malformed IPv6 address\n");
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 	if (rtadv_rdnss_reset(zif, &rdnss) != 1) {
 | |
| 		vty_out(vty, "Non-existant RDNSS address\n");
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN(ipv6_nd_dnssl,
 | |
|       ipv6_nd_dnssl_cmd,
 | |
|       "ipv6 nd dnssl SUFFIX [<(0-4294967295)|infinite>]",
 | |
|       "Interface IPv6 config commands\n"
 | |
|       "Neighbor discovery\n"
 | |
|       "DNS search list information\n"
 | |
|       "Domain name suffix\n"
 | |
|       "Valid lifetime in seconds\n"
 | |
|       "Infinite valid lifetime\n")
 | |
| {
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 	struct rtadv_dnssl dnssl = {};
 | |
| 	size_t len;
 | |
| 	int ret;
 | |
| 
 | |
| 	len = strlcpy(dnssl.name, argv[3]->arg, sizeof(dnssl.name));
 | |
| 	if (len == 0 || len >= sizeof(dnssl.name)) {
 | |
| 		vty_out(vty, "Malformed DNS search domain\n");
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 	if (dnssl.name[len - 1] == '.') {
 | |
| 		/*
 | |
| 		 * Allow, but don't require, a trailing dot signifying the root
 | |
| 		 * zone. Canonicalize by cutting it off if present.
 | |
| 		 */
 | |
| 		dnssl.name[len - 1] = '\0';
 | |
| 		len--;
 | |
| 	}
 | |
| 	if (argc > 4) {
 | |
| 		char *lifetime = argv[4]->type == RANGE_TKN ? argv[4]->arg
 | |
| 							    : argv[4]->text;
 | |
| 		dnssl.lifetime = strmatch(lifetime, "infinite")
 | |
| 					 ? UINT32_MAX
 | |
| 					 : strtoll(lifetime, NULL, 10);
 | |
| 		dnssl.lifetime_set = 1;
 | |
| 	}
 | |
| 
 | |
| 	ret = rtadv_dnssl_encode(dnssl.encoded_name, dnssl.name);
 | |
| 	if (ret < 0) {
 | |
| 		vty_out(vty, "Malformed DNS search domain\n");
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 	dnssl.encoded_len = ret;
 | |
| 	rtadv_dnssl_set(zif, &dnssl);
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN(no_ipv6_nd_dnssl,
 | |
|       no_ipv6_nd_dnssl_cmd,
 | |
|       "no ipv6 nd dnssl SUFFIX [<(0-4294967295)|infinite>]",
 | |
|       NO_STR
 | |
|       "Interface IPv6 config commands\n"
 | |
|       "Neighbor discovery\n"
 | |
|       "DNS search list information\n"
 | |
|       "Domain name suffix\n"
 | |
|       "Valid lifetime in seconds\n"
 | |
|       "Infinite valid lifetime\n")
 | |
| {
 | |
| 	VTY_DECLVAR_CONTEXT(interface, ifp);
 | |
| 	struct zebra_if *zif = ifp->info;
 | |
| 	struct rtadv_dnssl dnssl = {};
 | |
| 	size_t len;
 | |
| 
 | |
| 	len = strlcpy(dnssl.name, argv[4]->arg, sizeof(dnssl.name));
 | |
| 	if (len == 0 || len >= sizeof(dnssl.name)) {
 | |
| 		vty_out(vty, "Malformed DNS search domain\n");
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 	if (dnssl.name[len - 1] == '.') {
 | |
| 		dnssl.name[len - 1] = '\0';
 | |
| 		len--;
 | |
| 	}
 | |
| 	if (rtadv_dnssl_reset(zif, &dnssl) != 1) {
 | |
| 		vty_out(vty, "Non-existant DNS search domain\n");
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Dump interface ND information to vty. */
 | |
| static int nd_dump_vty(struct vty *vty, struct interface *ifp)
 | |
| {
 | |
| 	struct zebra_if *zif;
 | |
| 	struct rtadvconf *rtadv;
 | |
| 	int interval;
 | |
| 
 | |
| 	zif = (struct zebra_if *)ifp->info;
 | |
| 	rtadv = &zif->rtadv;
 | |
| 
 | |
| 	if (rtadv->AdvSendAdvertisements) {
 | |
| 		vty_out(vty,
 | |
| 			"  ND advertised reachable time is %d milliseconds\n",
 | |
| 			rtadv->AdvReachableTime);
 | |
| 		vty_out(vty,
 | |
| 			"  ND advertised retransmit interval is %d milliseconds\n",
 | |
| 			rtadv->AdvRetransTimer);
 | |
| 		vty_out(vty, "  ND router advertisements sent: %d rcvd: %d\n",
 | |
| 			zif->ra_sent, zif->ra_rcvd);
 | |
| 		interval = rtadv->MaxRtrAdvInterval;
 | |
| 		if (interval % 1000)
 | |
| 			vty_out(vty,
 | |
| 				"  ND router advertisements are sent every "
 | |
| 				"%d milliseconds\n",
 | |
| 				interval);
 | |
| 		else
 | |
| 			vty_out(vty,
 | |
| 				"  ND router advertisements are sent every "
 | |
| 				"%d seconds\n",
 | |
| 				interval / 1000);
 | |
| 		if (rtadv->AdvDefaultLifetime != -1)
 | |
| 			vty_out(vty,
 | |
| 				"  ND router advertisements live for %d seconds\n",
 | |
| 				rtadv->AdvDefaultLifetime);
 | |
| 		else
 | |
| 			vty_out(vty,
 | |
| 				"  ND router advertisements lifetime tracks ra-interval\n");
 | |
| 		vty_out(vty,
 | |
| 			"  ND router advertisement default router preference is "
 | |
| 			"%s\n",
 | |
| 			rtadv_pref_strs[rtadv->DefaultPreference]);
 | |
| 		if (rtadv->AdvManagedFlag)
 | |
| 			vty_out(vty,
 | |
| 				"  Hosts use DHCP to obtain routable addresses.\n");
 | |
| 		else
 | |
| 			vty_out(vty,
 | |
| 				"  Hosts use stateless autoconfig for addresses.\n");
 | |
| 		if (rtadv->AdvHomeAgentFlag) {
 | |
| 			vty_out(vty,
 | |
| 				"  ND router advertisements with Home Agent flag bit set.\n");
 | |
| 			if (rtadv->HomeAgentLifetime != -1)
 | |
| 				vty_out(vty,
 | |
| 					"  Home Agent lifetime is %u seconds\n",
 | |
| 					rtadv->HomeAgentLifetime);
 | |
| 			else
 | |
| 				vty_out(vty,
 | |
| 					"  Home Agent lifetime tracks ra-lifetime\n");
 | |
| 			vty_out(vty, "  Home Agent preference is %u\n",
 | |
| 				rtadv->HomeAgentPreference);
 | |
| 		}
 | |
| 		if (rtadv->AdvIntervalOption)
 | |
| 			vty_out(vty,
 | |
| 				"  ND router advertisements with Adv. Interval option.\n");
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Write configuration about router advertisement. */
 | |
| static int rtadv_config_write(struct vty *vty, struct interface *ifp)
 | |
| {
 | |
| 	struct zebra_if *zif;
 | |
| 	struct listnode *node;
 | |
| 	struct rtadv_prefix *rprefix;
 | |
| 	struct rtadv_rdnss *rdnss;
 | |
| 	struct rtadv_dnssl *dnssl;
 | |
| 	char buf[PREFIX_STRLEN];
 | |
| 	int interval;
 | |
| 
 | |
| 	zif = ifp->info;
 | |
| 
 | |
| 	if (!(if_is_loopback(ifp)
 | |
| 	      || CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK))) {
 | |
| 		if (zif->rtadv.AdvSendAdvertisements
 | |
| 		    && CHECK_FLAG(zif->rtadv.ra_configured, VTY_RA_CONFIGURED))
 | |
| 			vty_out(vty, " no ipv6 nd suppress-ra\n");
 | |
| 	}
 | |
| 
 | |
| 	interval = zif->rtadv.MaxRtrAdvInterval;
 | |
| 	if (CHECK_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED)) {
 | |
| 		if (interval % 1000)
 | |
| 			vty_out(vty, " ipv6 nd ra-interval msec %d\n",
 | |
| 				interval);
 | |
| 		else if (interval != RTADV_MAX_RTR_ADV_INTERVAL)
 | |
| 			vty_out(vty, " ipv6 nd ra-interval %d\n",
 | |
| 				interval / 1000);
 | |
| 	}
 | |
| 
 | |
| 	if (zif->rtadv.AdvIntervalOption)
 | |
| 		vty_out(vty, " ipv6 nd adv-interval-option\n");
 | |
| 
 | |
| 	if (zif->rtadv.AdvDefaultLifetime != -1)
 | |
| 		vty_out(vty, " ipv6 nd ra-lifetime %d\n",
 | |
| 			zif->rtadv.AdvDefaultLifetime);
 | |
| 
 | |
| 	if (zif->rtadv.HomeAgentPreference)
 | |
| 		vty_out(vty, " ipv6 nd home-agent-preference %u\n",
 | |
| 			zif->rtadv.HomeAgentPreference);
 | |
| 
 | |
| 	if (zif->rtadv.HomeAgentLifetime != -1)
 | |
| 		vty_out(vty, " ipv6 nd home-agent-lifetime %u\n",
 | |
| 			zif->rtadv.HomeAgentLifetime);
 | |
| 
 | |
| 	if (zif->rtadv.AdvHomeAgentFlag)
 | |
| 		vty_out(vty, " ipv6 nd home-agent-config-flag\n");
 | |
| 
 | |
| 	if (zif->rtadv.AdvReachableTime)
 | |
| 		vty_out(vty, " ipv6 nd reachable-time %d\n",
 | |
| 			zif->rtadv.AdvReachableTime);
 | |
| 
 | |
| 	if (zif->rtadv.AdvManagedFlag)
 | |
| 		vty_out(vty, " ipv6 nd managed-config-flag\n");
 | |
| 
 | |
| 	if (zif->rtadv.AdvOtherConfigFlag)
 | |
| 		vty_out(vty, " ipv6 nd other-config-flag\n");
 | |
| 
 | |
| 	if (zif->rtadv.DefaultPreference != RTADV_PREF_MEDIUM)
 | |
| 		vty_out(vty, " ipv6 nd router-preference %s\n",
 | |
| 			rtadv_pref_strs[zif->rtadv.DefaultPreference]);
 | |
| 
 | |
| 	if (zif->rtadv.AdvLinkMTU)
 | |
| 		vty_out(vty, " ipv6 nd mtu %d\n", zif->rtadv.AdvLinkMTU);
 | |
| 
 | |
| 	for (ALL_LIST_ELEMENTS_RO(zif->rtadv.AdvPrefixList, node, rprefix)) {
 | |
| 		vty_out(vty, " ipv6 nd prefix %s",
 | |
| 			prefix2str(&rprefix->prefix, buf, sizeof(buf)));
 | |
| 		if ((rprefix->AdvValidLifetime != RTADV_VALID_LIFETIME)
 | |
| 		    || (rprefix->AdvPreferredLifetime
 | |
| 			!= RTADV_PREFERRED_LIFETIME)) {
 | |
| 			if (rprefix->AdvValidLifetime == UINT32_MAX)
 | |
| 				vty_out(vty, " infinite");
 | |
| 			else
 | |
| 				vty_out(vty, " %u", rprefix->AdvValidLifetime);
 | |
| 			if (rprefix->AdvPreferredLifetime == UINT32_MAX)
 | |
| 				vty_out(vty, " infinite");
 | |
| 			else
 | |
| 				vty_out(vty, " %u",
 | |
| 					rprefix->AdvPreferredLifetime);
 | |
| 		}
 | |
| 		if (!rprefix->AdvOnLinkFlag)
 | |
| 			vty_out(vty, " off-link");
 | |
| 		if (!rprefix->AdvAutonomousFlag)
 | |
| 			vty_out(vty, " no-autoconfig");
 | |
| 		if (rprefix->AdvRouterAddressFlag)
 | |
| 			vty_out(vty, " router-address");
 | |
| 		vty_out(vty, "\n");
 | |
| 	}
 | |
| 	for (ALL_LIST_ELEMENTS_RO(zif->rtadv.AdvRDNSSList, node, rdnss)) {
 | |
| 		char buf[INET6_ADDRSTRLEN];
 | |
| 
 | |
| 		vty_out(vty, " ipv6 nd rdnss %s",
 | |
| 			inet_ntop(AF_INET6, &rdnss->addr, buf, sizeof(buf)));
 | |
| 		if (rdnss->lifetime_set) {
 | |
| 			if (rdnss->lifetime == UINT32_MAX)
 | |
| 				vty_out(vty, " infinite");
 | |
| 			else
 | |
| 				vty_out(vty, " %u", rdnss->lifetime);
 | |
| 		}
 | |
| 		vty_out(vty, "\n");
 | |
| 	}
 | |
| 	for (ALL_LIST_ELEMENTS_RO(zif->rtadv.AdvDNSSLList, node, dnssl)) {
 | |
| 		vty_out(vty, " ipv6 nd dnssl %s", dnssl->name);
 | |
| 		if (dnssl->lifetime_set) {
 | |
| 			if (dnssl->lifetime == UINT32_MAX)
 | |
| 				vty_out(vty, " infinite");
 | |
| 			else
 | |
| 				vty_out(vty, " %u", dnssl->lifetime);
 | |
| 		}
 | |
| 		vty_out(vty, "\n");
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void rtadv_event(struct zebra_vrf *zvrf, enum rtadv_event event, int val)
 | |
| {
 | |
| 	struct rtadv *rtadv = &zvrf->rtadv;
 | |
| 
 | |
| 	switch (event) {
 | |
| 	case RTADV_START:
 | |
| 		thread_add_read(zrouter.master, rtadv_read, zvrf, val,
 | |
| 				&rtadv->ra_read);
 | |
| 		thread_add_event(zrouter.master, rtadv_timer, zvrf, 0,
 | |
| 				 &rtadv->ra_timer);
 | |
| 		break;
 | |
| 	case RTADV_STOP:
 | |
| 		if (rtadv->ra_timer) {
 | |
| 			thread_cancel(rtadv->ra_timer);
 | |
| 			rtadv->ra_timer = NULL;
 | |
| 		}
 | |
| 		if (rtadv->ra_read) {
 | |
| 			thread_cancel(rtadv->ra_read);
 | |
| 			rtadv->ra_read = NULL;
 | |
| 		}
 | |
| 		break;
 | |
| 	case RTADV_TIMER:
 | |
| 		thread_add_timer(zrouter.master, rtadv_timer, zvrf, val,
 | |
| 				 &rtadv->ra_timer);
 | |
| 		break;
 | |
| 	case RTADV_TIMER_MSEC:
 | |
| 		thread_add_timer_msec(zrouter.master, rtadv_timer, zvrf, val,
 | |
| 				      &rtadv->ra_timer);
 | |
| 		break;
 | |
| 	case RTADV_READ:
 | |
| 		thread_add_read(zrouter.master, rtadv_read, zvrf, val,
 | |
| 				&rtadv->ra_read);
 | |
| 		break;
 | |
| 	default:
 | |
| 		break;
 | |
| 	}
 | |
| 	return;
 | |
| }
 | |
| 
 | |
| void rtadv_init(struct zebra_vrf *zvrf)
 | |
| {
 | |
| 	if (vrf_is_backend_netns()) {
 | |
| 		zvrf->rtadv.sock = rtadv_make_socket(zvrf->zns->ns_id);
 | |
| 		zrouter.rtadv_sock = -1;
 | |
| 	} else if (!zrouter.rtadv_sock) {
 | |
| 		zvrf->rtadv.sock = -1;
 | |
| 		if (!zrouter.rtadv_sock)
 | |
| 			zrouter.rtadv_sock = rtadv_make_socket(zvrf->zns->ns_id);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void rtadv_terminate(struct zebra_vrf *zvrf)
 | |
| {
 | |
| 	rtadv_event(zvrf, RTADV_STOP, 0);
 | |
| 	if (zvrf->rtadv.sock >= 0) {
 | |
| 		close(zvrf->rtadv.sock);
 | |
| 		zvrf->rtadv.sock = -1;
 | |
| 	} else if (zrouter.rtadv_sock >= 0) {
 | |
| 		close(zrouter.rtadv_sock);
 | |
| 		zrouter.rtadv_sock = -1;
 | |
| 	}
 | |
| 	zvrf->rtadv.adv_if_count = 0;
 | |
| 	zvrf->rtadv.adv_msec_if_count = 0;
 | |
| }
 | |
| 
 | |
| void rtadv_cmd_init(void)
 | |
| {
 | |
| 	hook_register(zebra_if_extra_info, nd_dump_vty);
 | |
| 	hook_register(zebra_if_config_wr, rtadv_config_write);
 | |
| 
 | |
| 	install_element(INTERFACE_NODE, &ipv6_nd_suppress_ra_cmd);
 | |
| 	install_element(INTERFACE_NODE, &no_ipv6_nd_suppress_ra_cmd);
 | |
| 	install_element(INTERFACE_NODE, &ipv6_nd_ra_interval_cmd);
 | |
| 	install_element(INTERFACE_NODE, &ipv6_nd_ra_interval_msec_cmd);
 | |
| 	install_element(INTERFACE_NODE, &no_ipv6_nd_ra_interval_cmd);
 | |
| 	install_element(INTERFACE_NODE, &ipv6_nd_ra_lifetime_cmd);
 | |
| 	install_element(INTERFACE_NODE, &no_ipv6_nd_ra_lifetime_cmd);
 | |
| 	install_element(INTERFACE_NODE, &ipv6_nd_reachable_time_cmd);
 | |
| 	install_element(INTERFACE_NODE, &no_ipv6_nd_reachable_time_cmd);
 | |
| 	install_element(INTERFACE_NODE, &ipv6_nd_managed_config_flag_cmd);
 | |
| 	install_element(INTERFACE_NODE, &no_ipv6_nd_managed_config_flag_cmd);
 | |
| 	install_element(INTERFACE_NODE, &ipv6_nd_other_config_flag_cmd);
 | |
| 	install_element(INTERFACE_NODE, &no_ipv6_nd_other_config_flag_cmd);
 | |
| 	install_element(INTERFACE_NODE, &ipv6_nd_homeagent_config_flag_cmd);
 | |
| 	install_element(INTERFACE_NODE, &no_ipv6_nd_homeagent_config_flag_cmd);
 | |
| 	install_element(INTERFACE_NODE, &ipv6_nd_homeagent_preference_cmd);
 | |
| 	install_element(INTERFACE_NODE, &no_ipv6_nd_homeagent_preference_cmd);
 | |
| 	install_element(INTERFACE_NODE, &ipv6_nd_homeagent_lifetime_cmd);
 | |
| 	install_element(INTERFACE_NODE, &no_ipv6_nd_homeagent_lifetime_cmd);
 | |
| 	install_element(INTERFACE_NODE,
 | |
| 			&ipv6_nd_adv_interval_config_option_cmd);
 | |
| 	install_element(INTERFACE_NODE,
 | |
| 			&no_ipv6_nd_adv_interval_config_option_cmd);
 | |
| 	install_element(INTERFACE_NODE, &ipv6_nd_prefix_cmd);
 | |
| 	install_element(INTERFACE_NODE, &no_ipv6_nd_prefix_cmd);
 | |
| 	install_element(INTERFACE_NODE, &ipv6_nd_router_preference_cmd);
 | |
| 	install_element(INTERFACE_NODE, &no_ipv6_nd_router_preference_cmd);
 | |
| 	install_element(INTERFACE_NODE, &ipv6_nd_mtu_cmd);
 | |
| 	install_element(INTERFACE_NODE, &no_ipv6_nd_mtu_cmd);
 | |
| 	install_element(INTERFACE_NODE, &ipv6_nd_rdnss_cmd);
 | |
| 	install_element(INTERFACE_NODE, &no_ipv6_nd_rdnss_cmd);
 | |
| 	install_element(INTERFACE_NODE, &ipv6_nd_dnssl_cmd);
 | |
| 	install_element(INTERFACE_NODE, &no_ipv6_nd_dnssl_cmd);
 | |
| }
 | |
| 
 | |
| static int if_join_all_router(int sock, struct interface *ifp)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	struct ipv6_mreq mreq;
 | |
| 
 | |
| 	memset(&mreq, 0, sizeof(struct ipv6_mreq));
 | |
| 	inet_pton(AF_INET6, ALLROUTER, &mreq.ipv6mr_multiaddr);
 | |
| 	mreq.ipv6mr_interface = ifp->ifindex;
 | |
| 
 | |
| 	ret = setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *)&mreq,
 | |
| 			 sizeof mreq);
 | |
| 	if (ret < 0)
 | |
| 		flog_err_sys(EC_LIB_SOCKET,
 | |
| 			     "%s(%u): Failed to join group, socket %u error %s",
 | |
| 			     ifp->name, ifp->ifindex, sock,
 | |
| 			     safe_strerror(errno));
 | |
| 
 | |
| 	if (IS_ZEBRA_DEBUG_EVENT)
 | |
| 		zlog_debug(
 | |
| 			"%s(%u): Join All-Routers multicast group, socket %u",
 | |
| 			ifp->name, ifp->ifindex, sock);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int if_leave_all_router(int sock, struct interface *ifp)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	struct ipv6_mreq mreq;
 | |
| 
 | |
| 	memset(&mreq, 0, sizeof(struct ipv6_mreq));
 | |
| 	inet_pton(AF_INET6, ALLROUTER, &mreq.ipv6mr_multiaddr);
 | |
| 	mreq.ipv6mr_interface = ifp->ifindex;
 | |
| 
 | |
| 	ret = setsockopt(sock, IPPROTO_IPV6, IPV6_LEAVE_GROUP, (char *)&mreq,
 | |
| 			 sizeof mreq);
 | |
| 	if (ret < 0)
 | |
| 		flog_err_sys(
 | |
| 			EC_LIB_SOCKET,
 | |
| 			"%s(%u): Failed to leave group, socket %u error %s",
 | |
| 			ifp->name, ifp->ifindex, sock, safe_strerror(errno));
 | |
| 
 | |
| 	if (IS_ZEBRA_DEBUG_EVENT)
 | |
| 		zlog_debug(
 | |
| 			"%s(%u): Leave All-Routers multicast group, socket %u",
 | |
| 			ifp->name, ifp->ifindex, sock);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| #else
 | |
| void rtadv_init(struct zebra_vrf *zvrf)
 | |
| {
 | |
| 	/* Empty.*/;
 | |
| }
 | |
| void rtadv_terminate(struct zebra_vrf *zvrf)
 | |
| {
 | |
| 	/* Empty.*/;
 | |
| }
 | |
| void rtadv_cmd_init(void)
 | |
| {
 | |
| 	/* Empty.*/;
 | |
| }
 | |
| #endif /* HAVE_RTADV */
 | 
