2025-02-05 10:03:58 +01:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
/* Router advertisement
|
|
|
|
* Copyright (C) 2016 Cumulus Networks
|
|
|
|
* Copyright (C) 2005 6WIND <jean-mickael.guerin@6wind.com>
|
|
|
|
* Copyright (C) 1999 Kunihiro Ishiguro
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <zebra.h>
|
|
|
|
#include <netinet/icmp6.h>
|
|
|
|
|
|
|
|
#include "memory.h"
|
|
|
|
#include "sockopt.h"
|
|
|
|
#include "frrevent.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;
|
|
|
|
|
|
|
|
static uint32_t interfaces_configured_for_ra_from_bgp;
|
|
|
|
#define RTADV_ADATA_SIZE 1024
|
|
|
|
|
|
|
|
#if defined(HAVE_RTADV)
|
|
|
|
|
|
|
|
#include "zebra/rtadv_clippy.c"
|
|
|
|
|
|
|
|
DEFINE_MTYPE_STATIC(ZEBRA, RTADV_PREFIX, "Router Advertisement Prefix");
|
|
|
|
DEFINE_MTYPE_STATIC(ZEBRA, ADV_IF, "Advertised Interface");
|
|
|
|
|
|
|
|
#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"
|
|
|
|
|
|
|
|
/* adv list node */
|
|
|
|
struct adv_if {
|
|
|
|
char name[IFNAMSIZ];
|
|
|
|
struct adv_if_list_item list_item;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int adv_if_cmp(const struct adv_if *a, const struct adv_if *b)
|
|
|
|
{
|
|
|
|
return if_cmp_name_func(a->name, b->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
DECLARE_SORTLIST_UNIQ(adv_if_list, struct adv_if, list_item, adv_if_cmp);
|
|
|
|
|
|
|
|
static int rtadv_prefix_cmp(const struct rtadv_prefix *a,
|
|
|
|
const struct rtadv_prefix *b)
|
|
|
|
{
|
|
|
|
return prefix_cmp(&a->prefix, &b->prefix);
|
|
|
|
}
|
|
|
|
|
|
|
|
DECLARE_RBTREE_UNIQ(rtadv_prefixes, struct rtadv_prefix, item,
|
|
|
|
rtadv_prefix_cmp);
|
|
|
|
|
|
|
|
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. */
|
|
|
|
static const char *const 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 struct zebra_vrf *rtadv_interface_get_zvrf(const struct interface *ifp)
|
|
|
|
{
|
|
|
|
/* We use the default vrf for rtadv handling except in netns */
|
|
|
|
if (!vrf_is_backend_netns())
|
|
|
|
return vrf_info_lookup(VRF_DEFAULT);
|
|
|
|
|
|
|
|
return ifp->vrf->info;
|
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
|
|
|
enum ipv6_nd_suppress_ra_status stop)
|
|
|
|
{
|
|
|
|
struct msghdr msg = { 0 };
|
|
|
|
struct iovec iov = { 0 };
|
|
|
|
struct cmsghdr *cmsgptr;
|
|
|
|
struct in6_pktinfo *pkt;
|
|
|
|
struct sockaddr_in6 addr = { 0 };
|
|
|
|
unsigned char buf[RTADV_MSG_SIZE] = { 0 };
|
|
|
|
char adata[RTADV_ADATA_SIZE] = { 0 };
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
/* Logging of packet. */
|
|
|
|
if (IS_ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s(%s:%u): Tx RA, socket %u", ifp->name,
|
|
|
|
ifp->vrf->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 = zif->rtadv.AdvCurHopLimit;
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
|
|
|
/* send RA lifetime of 0 before stopping. rfc4861/6.2.5 */
|
|
|
|
rtadv->nd_ra_router_lifetime =
|
|
|
|
(stop == RA_SUPPRESS) ? htons(0) : htons(pkt_RouterLifetime);
|
|
|
|
rtadv->nd_ra_reachable = htonl(zif->rtadv.AdvReachableTime);
|
|
|
|
rtadv->nd_ra_retransmit = htonl(zif->rtadv.AdvRetransTimer);
|
|
|
|
|
|
|
|
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. */
|
|
|
|
frr_each (rtadv_prefixes, zif->rtadv.prefixes, 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);
|
|
|
|
|
|
|
|
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(%s:%u): Tx RA: RDNSS option would exceed MTU, omitting it",
|
|
|
|
ifp->name, ifp->vrf->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 void rtadv_timer(struct event *thread)
|
|
|
|
{
|
|
|
|
struct zebra_vrf *zvrf = EVENT_ARG(thread);
|
|
|
|
struct vrf *vrf;
|
|
|
|
struct interface *ifp;
|
|
|
|
struct zebra_if *zif;
|
|
|
|
int period;
|
|
|
|
|
|
|
|
zvrf->rtadv.ra_timer = NULL;
|
|
|
|
if (adv_if_list_count(&zvrf->rtadv.adv_msec_if) == 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) || !if_is_operative(ifp) ||
|
|
|
|
IS_ZEBRA_IF_BRIDGE_SLAVE(ifp) ||
|
|
|
|
!connected_get_linklocal(ifp) ||
|
|
|
|
(vrf_is_backend_netns() &&
|
|
|
|
ifp->vrf->vrf_id != zvrf->vrf->vrf_id))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
zif = ifp->info;
|
|
|
|
|
|
|
|
if (zif->rtadv.AdvSendAdvertisements) {
|
|
|
|
if (zif->rtadv.inFastRexmit
|
|
|
|
&& zif->rtadv.UseFastRexmit) {
|
|
|
|
/* 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(%s:%u)",
|
|
|
|
ifp->name,
|
|
|
|
ifp->vrf->name,
|
|
|
|
ifp->ifindex);
|
|
|
|
|
|
|
|
rtadv_send_packet(zvrf->rtadv.sock, ifp,
|
|
|
|
RA_ENABLE);
|
|
|
|
} 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(
|
|
|
|
zvrf->rtadv.sock, ifp,
|
|
|
|
RA_ENABLE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void rtadv_process_solicit(struct interface *ifp)
|
|
|
|
{
|
|
|
|
struct zebra_vrf *zvrf;
|
|
|
|
struct zebra_if *zif;
|
|
|
|
|
|
|
|
zvrf = rtadv_interface_get_zvrf(ifp);
|
|
|
|
assert(zvrf);
|
|
|
|
zif = ifp->info;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If FastRetransmit is enabled, send the RA immediately.
|
|
|
|
* If not enabled but it has been more than MIN_DELAY_BETWEEN_RAS
|
|
|
|
* (3 seconds) since the last RA was sent, send it now and reset
|
|
|
|
* the timer to start at the max (configured) again.
|
|
|
|
* If not enabled and it is less than 3 seconds since the last
|
|
|
|
* RA packet was sent, set the timer for 3 seconds so the next
|
|
|
|
* one will be sent with a minimum of 3 seconds between RAs.
|
|
|
|
* RFC4861 sec 6.2.6
|
|
|
|
*/
|
|
|
|
if ((zif->rtadv.UseFastRexmit)
|
|
|
|
|| (zif->rtadv.AdvIntervalTimer <=
|
|
|
|
(zif->rtadv.MaxRtrAdvInterval - MIN_DELAY_BETWEEN_RAS))) {
|
|
|
|
rtadv_send_packet(zvrf->rtadv.sock, ifp, RA_ENABLE);
|
|
|
|
zif->rtadv.AdvIntervalTimer = zif->rtadv.MaxRtrAdvInterval;
|
|
|
|
} else
|
|
|
|
zif->rtadv.AdvIntervalTimer = MIN_DELAY_BETWEEN_RAS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *rtadv_optionalhdr2str(uint8_t opt_type)
|
|
|
|
{
|
|
|
|
switch (opt_type) {
|
|
|
|
case ND_OPT_SOURCE_LINKADDR:
|
|
|
|
return "Optional Source Link Address";
|
|
|
|
case ND_OPT_TARGET_LINKADDR:
|
|
|
|
return "Optional Target Link Address";
|
|
|
|
case ND_OPT_PREFIX_INFORMATION:
|
|
|
|
return "Optional Prefix Information";
|
|
|
|
case ND_OPT_REDIRECTED_HEADER:
|
|
|
|
return "Optional Redirected Header";
|
|
|
|
case ND_OPT_MTU:
|
|
|
|
return "Optional MTU";
|
|
|
|
case ND_OPT_RTR_ADV_INTERVAL:
|
|
|
|
return "Optional Advertisement Interval";
|
|
|
|
case ND_OPT_HOME_AGENT_INFO:
|
|
|
|
return "Optional Home Agent Information";
|
|
|
|
}
|
|
|
|
|
|
|
|
return "Unknown Optional Type";
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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:
|
|
|
|
if (IS_ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug(
|
|
|
|
"%s:Received Packet with optional Header type %s(%u) that is being ignored",
|
|
|
|
__func__,
|
|
|
|
rtadv_optionalhdr2str(
|
|
|
|
opt_hdr->nd_opt_type),
|
|
|
|
opt_hdr->nd_opt_type);
|
|
|
|
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(%s:%u): Rx RA with invalid length %d from %s",
|
|
|
|
ifp->name, ifp->vrf->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(%s:%u): Rx RA with non-linklocal source address from %s",
|
|
|
|
ifp->name, ifp->vrf->name, ifp->ifindex,
|
|
|
|
addr_str);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
radvert = (struct nd_router_advert *)msg;
|
|
|
|
|
|
|
|
#define SIXHOUR2USEC (int64_t)6 * 60 * 60 * 1000000
|
|
|
|
|
|
|
|
if ((radvert->nd_ra_curhoplimit && zif->rtadv.AdvCurHopLimit) &&
|
|
|
|
(radvert->nd_ra_curhoplimit != zif->rtadv.AdvCurHopLimit) &&
|
|
|
|
(monotime_since(&zif->rtadv.lastadvcurhoplimit, NULL) >
|
|
|
|
SIXHOUR2USEC ||
|
|
|
|
zif->rtadv.lastadvcurhoplimit.tv_sec == 0)) {
|
|
|
|
flog_warn(
|
|
|
|
EC_ZEBRA_RA_PARAM_MISMATCH,
|
|
|
|
"%s(%u): Rx RA - our AdvCurHopLimit (%u) doesn't agree with %s (%u)",
|
|
|
|
ifp->name, ifp->ifindex, zif->rtadv.AdvCurHopLimit,
|
|
|
|
addr_str, radvert->nd_ra_curhoplimit);
|
|
|
|
monotime(&zif->rtadv.lastadvcurhoplimit);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((radvert->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) &&
|
|
|
|
!zif->rtadv.AdvManagedFlag &&
|
|
|
|
(monotime_since(&zif->rtadv.lastadvmanagedflag, NULL) >
|
|
|
|
SIXHOUR2USEC ||
|
|
|
|
zif->rtadv.lastadvmanagedflag.tv_sec == 0)) {
|
|
|
|
flog_warn(
|
|
|
|
EC_ZEBRA_RA_PARAM_MISMATCH,
|
|
|
|
"%s(%u): Rx RA - our AdvManagedFlag (%u) doesn't agree with %s (%u)",
|
|
|
|
ifp->name, ifp->ifindex, zif->rtadv.AdvManagedFlag,
|
|
|
|
addr_str,
|
|
|
|
!!CHECK_FLAG(radvert->nd_ra_flags_reserved,
|
|
|
|
ND_RA_FLAG_MANAGED));
|
|
|
|
monotime(&zif->rtadv.lastadvmanagedflag);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((radvert->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) &&
|
|
|
|
!zif->rtadv.AdvOtherConfigFlag &&
|
|
|
|
(monotime_since(&zif->rtadv.lastadvotherconfigflag, NULL) >
|
|
|
|
SIXHOUR2USEC ||
|
|
|
|
zif->rtadv.lastadvotherconfigflag.tv_sec == 0)) {
|
|
|
|
flog_warn(
|
|
|
|
EC_ZEBRA_RA_PARAM_MISMATCH,
|
|
|
|
"%s(%u): Rx RA - our AdvOtherConfigFlag (%u) doesn't agree with %s (%u)",
|
|
|
|
ifp->name, ifp->ifindex, zif->rtadv.AdvOtherConfigFlag,
|
|
|
|
addr_str,
|
|
|
|
!!CHECK_FLAG(radvert->nd_ra_flags_reserved,
|
|
|
|
ND_RA_FLAG_OTHER));
|
|
|
|
monotime(&zif->rtadv.lastadvotherconfigflag);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((radvert->nd_ra_reachable && zif->rtadv.AdvReachableTime) &&
|
|
|
|
(ntohl(radvert->nd_ra_reachable) != zif->rtadv.AdvReachableTime) &&
|
|
|
|
(monotime_since(&zif->rtadv.lastadvreachabletime, NULL) >
|
|
|
|
SIXHOUR2USEC ||
|
|
|
|
zif->rtadv.lastadvreachabletime.tv_sec == 0)) {
|
|
|
|
flog_warn(
|
|
|
|
EC_ZEBRA_RA_PARAM_MISMATCH,
|
|
|
|
"%s(%u): Rx RA - our AdvReachableTime (%u) doesn't agree with %s (%u)",
|
|
|
|
ifp->name, ifp->ifindex, zif->rtadv.AdvReachableTime,
|
|
|
|
addr_str, ntohl(radvert->nd_ra_reachable));
|
|
|
|
monotime(&zif->rtadv.lastadvreachabletime);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((radvert->nd_ra_retransmit && zif->rtadv.AdvRetransTimer) &&
|
|
|
|
(ntohl(radvert->nd_ra_retransmit) !=
|
|
|
|
(unsigned int)zif->rtadv.AdvRetransTimer) &&
|
|
|
|
(monotime_since(&zif->rtadv.lastadvretranstimer, NULL) >
|
|
|
|
SIXHOUR2USEC ||
|
|
|
|
zif->rtadv.lastadvretranstimer.tv_sec == 0)) {
|
|
|
|
flog_warn(
|
|
|
|
EC_ZEBRA_RA_PARAM_MISMATCH,
|
|
|
|
"%s(%u): Rx RA - our AdvRetransTimer (%u) doesn't agree with %s (%u)",
|
|
|
|
ifp->name, ifp->ifindex, zif->rtadv.AdvRetransTimer,
|
|
|
|
addr_str, ntohl(radvert->nd_ra_retransmit));
|
|
|
|
monotime(&zif->rtadv.lastadvretranstimer);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create entry for neighbor if not known. */
|
|
|
|
p.family = AF_INET6;
|
|
|
|
IPV6_ADDR_COPY(&p.u.prefix6, &addr->sin6_addr);
|
|
|
|
p.prefixlen = IPV6_MAX_BITLEN;
|
|
|
|
|
|
|
|
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(%s:%u): Rx RA/RS len %d from %s", ifp->name,
|
|
|
|
ifp->vrf->name, ifp->ifindex, len, addr_str);
|
|
|
|
|
|
|
|
if (if_is_loopback(ifp))
|
|
|
|
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(%s:%u): Rx RA with Invalid ICMPV6 packet length %d",
|
|
|
|
ifp->name, ifp->vrf->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(%s:%u): Rx RA - Unwanted ICMPV6 message type %d",
|
|
|
|
ifp->name, ifp->vrf->name, ifp->ifindex,
|
|
|
|
icmph->icmp6_type);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Hoplimit check. */
|
|
|
|
if (hoplimit >= 0 && hoplimit != 255) {
|
|
|
|
zlog_debug("%s(%s:%u): Rx RA - Invalid hoplimit %d", ifp->name,
|
|
|
|
ifp->vrf->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 void rtadv_read(struct event *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 = EVENT_ARG(thread);
|
|
|
|
|
|
|
|
sock = EVENT_FD(thread);
|
|
|
|
zvrf->rtadv.ra_read = NULL;
|
|
|
|
|
|
|
|
/* Register myself. */
|
|
|
|
rtadv_event(zvrf, RTADV_READ, 0);
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
rtadv_process_packet(buf, (unsigned)len, ifindex, hoplimit, &from, zvrf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rtadv_make_socket(ns_id_t ns_id)
|
|
|
|
{
|
|
|
|
int sock = -1;
|
|
|
|
int ret = 0;
|
|
|
|
struct icmp6_filter filter;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
frr_with_privs(&zserv_privs) {
|
|
|
|
|
|
|
|
sock = ns_socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6, ns_id);
|
|
|
|
/*
|
|
|
|
* with privs might set errno too if it fails save
|
|
|
|
* to the side
|
|
|
|
*/
|
|
|
|
error = errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sock < 0) {
|
|
|
|
zlog_warn("RTADV socket for ns: %u failure to create: %s(%u)",
|
|
|
|
ns_id, safe_strerror(error), error);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = setsockopt_ipv6_pktinfo(sock, 1);
|
|
|
|
if (ret < 0) {
|
|
|
|
zlog_warn("RTADV failure to set Packet Information");
|
|
|
|
close(sock);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
ret = setsockopt_ipv6_multicast_loop(sock, 0);
|
|
|
|
if (ret < 0) {
|
|
|
|
zlog_warn("RTADV failure to set multicast Loop detection");
|
|
|
|
close(sock);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
ret = setsockopt_ipv6_unicast_hops(sock, 255);
|
|
|
|
if (ret < 0) {
|
|
|
|
zlog_warn("RTADV failure to set maximum unicast hops");
|
|
|
|
close(sock);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
ret = setsockopt_ipv6_multicast_hops(sock, 255);
|
|
|
|
if (ret < 0) {
|
|
|
|
zlog_warn("RTADV failure to set maximum multicast hops");
|
|
|
|
close(sock);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
ret = setsockopt_ipv6_hoplimit(sock, 1);
|
|
|
|
if (ret < 0) {
|
|
|
|
zlog_warn("RTADV failure to set maximum incoming hop limit");
|
|
|
|
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 adv_if *adv_if_new(const char *name)
|
|
|
|
{
|
|
|
|
struct adv_if *new;
|
|
|
|
|
|
|
|
new = XCALLOC(MTYPE_ADV_IF, sizeof(struct adv_if));
|
|
|
|
|
|
|
|
strlcpy(new->name, name, sizeof(new->name));
|
|
|
|
|
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void adv_if_free(struct adv_if *adv_if)
|
|
|
|
{
|
|
|
|
XFREE(MTYPE_ADV_IF, adv_if);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool adv_if_is_empty_internal(const struct adv_if_list_head *adv_if_head)
|
|
|
|
{
|
|
|
|
return adv_if_list_count(adv_if_head) ? false : true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct adv_if *adv_if_add_internal(struct adv_if_list_head *adv_if_head,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
struct adv_if adv_if_lookup = {};
|
|
|
|
struct adv_if *adv_if = NULL;
|
|
|
|
|
|
|
|
strlcpy(adv_if_lookup.name, name, sizeof(adv_if_lookup.name));
|
|
|
|
adv_if = adv_if_list_find(adv_if_head, &adv_if_lookup);
|
|
|
|
|
|
|
|
if (adv_if != NULL)
|
|
|
|
return adv_if;
|
|
|
|
|
|
|
|
adv_if = adv_if_new(adv_if_lookup.name);
|
|
|
|
adv_if_list_add(adv_if_head, adv_if);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct adv_if *adv_if_del_internal(struct adv_if_list_head *adv_if_head,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
struct adv_if adv_if_lookup = {};
|
|
|
|
struct adv_if *adv_if = NULL;
|
|
|
|
|
|
|
|
strlcpy(adv_if_lookup.name, name, sizeof(adv_if_lookup.name));
|
|
|
|
adv_if = adv_if_list_find(adv_if_head, &adv_if_lookup);
|
|
|
|
|
|
|
|
if (adv_if == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
adv_if_list_del(adv_if_head, adv_if);
|
|
|
|
|
|
|
|
return adv_if;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void adv_if_clean_internal(struct adv_if_list_head *adv_if_head)
|
|
|
|
{
|
|
|
|
struct adv_if *node = NULL;
|
|
|
|
|
|
|
|
if (!adv_if_is_empty_internal(adv_if_head)) {
|
|
|
|
frr_each_safe (adv_if_list, adv_if_head, node) {
|
|
|
|
adv_if_list_del(adv_if_head, node);
|
|
|
|
adv_if_free(node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
adv_if_list_fini(adv_if_head);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add to list. On Success, return NULL, otherwise return already existing
|
|
|
|
* adv_if.
|
|
|
|
*/
|
|
|
|
static struct adv_if *adv_if_add(struct zebra_vrf *zvrf, const char *name)
|
|
|
|
{
|
|
|
|
struct adv_if *adv_if = NULL;
|
|
|
|
|
|
|
|
adv_if = adv_if_add_internal(&zvrf->rtadv.adv_if, name);
|
|
|
|
|
|
|
|
if (adv_if != NULL)
|
|
|
|
return adv_if;
|
|
|
|
|
|
|
|
if (IS_ZEBRA_DEBUG_EVENT) {
|
|
|
|
struct vrf *vrf = zvrf->vrf;
|
|
|
|
|
|
|
|
zlog_debug("%s: %s:%u IF %s count: %zu", __func__,
|
|
|
|
VRF_LOGNAME(vrf), zvrf_id(zvrf), name,
|
|
|
|
adv_if_list_count(&zvrf->rtadv.adv_if));
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Del from list. On Success, return the adv_if, otherwise return NULL. Caller
|
|
|
|
* frees.
|
|
|
|
*/
|
|
|
|
static struct adv_if *adv_if_del(struct zebra_vrf *zvrf, const char *name)
|
|
|
|
{
|
|
|
|
struct adv_if *adv_if = NULL;
|
|
|
|
|
|
|
|
adv_if = adv_if_del_internal(&zvrf->rtadv.adv_if, name);
|
|
|
|
|
|
|
|
if (adv_if == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (IS_ZEBRA_DEBUG_EVENT) {
|
|
|
|
struct vrf *vrf = zvrf->vrf;
|
|
|
|
|
|
|
|
zlog_debug("%s: %s:%u IF %s count: %zu", __func__,
|
|
|
|
VRF_LOGNAME(vrf), zvrf_id(zvrf), name,
|
|
|
|
adv_if_list_count(&zvrf->rtadv.adv_if));
|
|
|
|
}
|
|
|
|
|
|
|
|
return adv_if;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add to list. On Success, return NULL, otherwise return already existing
|
|
|
|
* adv_if.
|
|
|
|
*/
|
|
|
|
static struct adv_if *adv_msec_if_add(struct zebra_vrf *zvrf, const char *name)
|
|
|
|
{
|
|
|
|
struct adv_if *adv_if = NULL;
|
|
|
|
|
|
|
|
adv_if = adv_if_add_internal(&zvrf->rtadv.adv_msec_if, name);
|
|
|
|
|
|
|
|
if (adv_if != NULL)
|
|
|
|
return adv_if;
|
|
|
|
|
|
|
|
if (IS_ZEBRA_DEBUG_EVENT) {
|
|
|
|
struct vrf *vrf = zvrf->vrf;
|
|
|
|
|
|
|
|
zlog_debug("%s: %s:%u IF %s count: %zu", __func__,
|
|
|
|
VRF_LOGNAME(vrf), zvrf_id(zvrf), name,
|
|
|
|
adv_if_list_count(&zvrf->rtadv.adv_msec_if));
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Del from list. On Success, return the adv_if, otherwise return NULL. Caller
|
|
|
|
* frees.
|
|
|
|
*/
|
|
|
|
static struct adv_if *adv_msec_if_del(struct zebra_vrf *zvrf, const char *name)
|
|
|
|
{
|
|
|
|
struct adv_if *adv_if = NULL;
|
|
|
|
|
|
|
|
adv_if = adv_if_del_internal(&zvrf->rtadv.adv_msec_if, name);
|
|
|
|
|
|
|
|
if (adv_if == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (IS_ZEBRA_DEBUG_EVENT) {
|
|
|
|
struct vrf *vrf = zvrf->vrf;
|
|
|
|
|
|
|
|
zlog_debug("%s: %s:%u IF %s count: %zu", __func__,
|
|
|
|
VRF_LOGNAME(vrf), zvrf_id(zvrf), name,
|
|
|
|
adv_if_list_count(&zvrf->rtadv.adv_msec_if));
|
|
|
|
}
|
|
|
|
|
|
|
|
return adv_if;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clean adv_if list, called on vrf terminate */
|
|
|
|
static void adv_if_clean(struct zebra_vrf *zvrf)
|
|
|
|
{
|
|
|
|
if (IS_ZEBRA_DEBUG_EVENT) {
|
|
|
|
struct vrf *vrf = zvrf->vrf;
|
|
|
|
|
|
|
|
zlog_debug("%s: %s:%u count: %zu -> 0", __func__,
|
|
|
|
VRF_LOGNAME(vrf), zvrf_id(zvrf),
|
|
|
|
adv_if_list_count(&zvrf->rtadv.adv_if));
|
|
|
|
}
|
|
|
|
|
|
|
|
adv_if_clean_internal(&zvrf->rtadv.adv_if);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clean adv_msec_if list, called on vrf terminate */
|
|
|
|
static void adv_msec_if_clean(struct zebra_vrf *zvrf)
|
|
|
|
{
|
|
|
|
if (IS_ZEBRA_DEBUG_EVENT) {
|
|
|
|
struct vrf *vrf = zvrf->vrf;
|
|
|
|
|
|
|
|
zlog_debug("%s: %s:%u count: %zu -> 0", __func__,
|
|
|
|
VRF_LOGNAME(vrf), zvrf_id(zvrf),
|
|
|
|
adv_if_list_count(&zvrf->rtadv.adv_msec_if));
|
|
|
|
}
|
|
|
|
|
|
|
|
adv_if_clean_internal(&zvrf->rtadv.adv_msec_if);
|
|
|
|
}
|
|
|
|
|
|
|
|
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_get(struct rtadv_prefixes_head *list,
|
|
|
|
struct prefix_ipv6 *p)
|
|
|
|
{
|
|
|
|
struct rtadv_prefix *rprefix, ref;
|
|
|
|
|
|
|
|
ref.prefix = *p;
|
|
|
|
|
|
|
|
rprefix = rtadv_prefixes_find(list, &ref);
|
|
|
|
if (rprefix)
|
|
|
|
return rprefix;
|
|
|
|
|
|
|
|
rprefix = rtadv_prefix_new();
|
|
|
|
memcpy(&rprefix->prefix, p, sizeof(struct prefix_ipv6));
|
|
|
|
rtadv_prefixes_add(list, rprefix);
|
|
|
|
|
|
|
|
return rprefix;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void rtadv_prefix_set_defaults(struct rtadv_prefix *rp)
|
|
|
|
{
|
|
|
|
rp->AdvAutonomousFlag = 1;
|
|
|
|
rp->AdvOnLinkFlag = 1;
|
|
|
|
rp->AdvRouterAddressFlag = 0;
|
|
|
|
rp->AdvPreferredLifetime = RTADV_PREFERRED_LIFETIME;
|
|
|
|
rp->AdvValidLifetime = RTADV_VALID_LIFETIME;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct rtadv_prefix *rtadv_prefix_set(struct zebra_if *zif,
|
|
|
|
struct rtadv_prefix *rp)
|
|
|
|
{
|
|
|
|
struct rtadv_prefix *rprefix;
|
|
|
|
|
|
|
|
rprefix = rtadv_prefix_get(zif->rtadv.prefixes, &rp->prefix);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set parameters based on where the prefix is created.
|
|
|
|
* If auto-created based on kernel address addition, set the
|
|
|
|
* default values. If created from a manual "ipv6 nd prefix"
|
|
|
|
* command, take the parameters from the manual command. Note
|
|
|
|
* that if the manual command exists, the default values will
|
|
|
|
* not overwrite the manual values.
|
|
|
|
*/
|
|
|
|
if (rp->AdvPrefixCreate == PREFIX_SRC_MANUAL) {
|
|
|
|
if (rprefix->AdvPrefixCreate == PREFIX_SRC_AUTO)
|
|
|
|
rprefix->AdvPrefixCreate = PREFIX_SRC_BOTH;
|
|
|
|
else
|
|
|
|
rprefix->AdvPrefixCreate = PREFIX_SRC_MANUAL;
|
|
|
|
|
|
|
|
rprefix->AdvAutonomousFlag = rp->AdvAutonomousFlag;
|
|
|
|
rprefix->AdvOnLinkFlag = rp->AdvOnLinkFlag;
|
|
|
|
rprefix->AdvRouterAddressFlag = rp->AdvRouterAddressFlag;
|
|
|
|
rprefix->AdvPreferredLifetime = rp->AdvPreferredLifetime;
|
|
|
|
rprefix->AdvValidLifetime = rp->AdvValidLifetime;
|
|
|
|
} else if (rp->AdvPrefixCreate == PREFIX_SRC_AUTO) {
|
|
|
|
if (rprefix->AdvPrefixCreate == PREFIX_SRC_MANUAL)
|
|
|
|
rprefix->AdvPrefixCreate = PREFIX_SRC_BOTH;
|
|
|
|
else {
|
|
|
|
rprefix->AdvPrefixCreate = PREFIX_SRC_AUTO;
|
|
|
|
rtadv_prefix_set_defaults(rprefix);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return rprefix;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void rtadv_prefix_reset(struct zebra_if *zif, struct rtadv_prefix *rp,
|
|
|
|
struct rtadv_prefix *rprefix)
|
|
|
|
{
|
|
|
|
if (!rprefix)
|
|
|
|
rprefix = rtadv_prefixes_find(zif->rtadv.prefixes, rp);
|
|
|
|
|
|
|
|
if (rprefix != NULL) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* When deleting an address from the list, need to take care
|
|
|
|
* it wasn't defined both automatically via kernel
|
|
|
|
* address addition as well as manually by vtysh cli. If both,
|
|
|
|
* we don't actually delete but may change the parameters
|
|
|
|
* back to default if a manually defined entry is deleted.
|
|
|
|
*/
|
|
|
|
if (rp->AdvPrefixCreate == PREFIX_SRC_MANUAL) {
|
|
|
|
if (rprefix->AdvPrefixCreate == PREFIX_SRC_BOTH) {
|
|
|
|
rprefix->AdvPrefixCreate = PREFIX_SRC_AUTO;
|
|
|
|
rtadv_prefix_set_defaults(rprefix);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else if (rp->AdvPrefixCreate == PREFIX_SRC_AUTO) {
|
|
|
|
if (rprefix->AdvPrefixCreate == PREFIX_SRC_BOTH) {
|
|
|
|
rprefix->AdvPrefixCreate = PREFIX_SRC_MANUAL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rtadv_prefixes_del(zif->rtadv.prefixes, rprefix);
|
|
|
|
rtadv_prefix_free(rprefix);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct rtadv_prefix *rtadv_add_prefix_manual(struct zebra_if *zif,
|
|
|
|
struct rtadv_prefix *rp)
|
|
|
|
{
|
|
|
|
rp->AdvPrefixCreate = PREFIX_SRC_MANUAL;
|
|
|
|
return rtadv_prefix_set(zif, rp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void rtadv_delete_prefix_manual(struct zebra_if *zif,
|
|
|
|
struct rtadv_prefix *rprefix)
|
|
|
|
{
|
|
|
|
struct rtadv_prefix rp;
|
|
|
|
|
|
|
|
rp.AdvPrefixCreate = PREFIX_SRC_MANUAL;
|
|
|
|
|
|
|
|
rtadv_prefix_reset(zif, &rp, rprefix);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add IPv6 prefixes learned from the kernel to the RA prefix list */
|
|
|
|
void rtadv_add_prefix(struct zebra_if *zif, const struct prefix_ipv6 *p)
|
|
|
|
{
|
|
|
|
struct rtadv_prefix rp;
|
|
|
|
|
|
|
|
rp.prefix = *p;
|
|
|
|
apply_mask_ipv6(&rp.prefix);
|
|
|
|
rp.AdvPrefixCreate = PREFIX_SRC_AUTO;
|
|
|
|
rtadv_prefix_set(zif, &rp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Delete IPv6 prefixes removed by the kernel from the RA prefix list */
|
|
|
|
void rtadv_delete_prefix(struct zebra_if *zif, const struct prefix *p)
|
|
|
|
{
|
|
|
|
struct rtadv_prefix rp;
|
|
|
|
|
|
|
|
rp.prefix = *((struct prefix_ipv6 *)p);
|
|
|
|
apply_mask_ipv6(&rp.prefix);
|
|
|
|
rp.AdvPrefixCreate = PREFIX_SRC_AUTO;
|
|
|
|
rtadv_prefix_reset(zif, &rp, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void rtadv_start_interface_events(struct zebra_vrf *zvrf,
|
|
|
|
struct zebra_if *zif)
|
|
|
|
{
|
|
|
|
struct adv_if *adv_if = NULL;
|
|
|
|
|
|
|
|
if (zif->ifp->ifindex == IFINDEX_INTERNAL) {
|
|
|
|
if (IS_ZEBRA_DEBUG_EVENT)
|
|
|
|
zlog_debug(
|
|
|
|
"%s(%s) has not configured an ifindex yet, delaying until we have one",
|
|
|
|
zif->ifp->name, zvrf->vrf->name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
adv_if = adv_if_add(zvrf, zif->ifp->name);
|
|
|
|
if (adv_if != NULL)
|
|
|
|
return; /* Already added */
|
|
|
|
|
|
|
|
if_join_all_router(zvrf->rtadv.sock, zif->ifp);
|
|
|
|
|
|
|
|
if (adv_if_list_count(&zvrf->rtadv.adv_if) == 1)
|
|
|
|
rtadv_event(zvrf, RTADV_START, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ipv6_nd_suppress_ra_set(struct interface *ifp,
|
|
|
|
enum ipv6_nd_suppress_ra_status status)
|
|
|
|
{
|
|
|
|
struct zebra_if *zif;
|
|
|
|
struct zebra_vrf *zvrf;
|
|
|
|
struct adv_if *adv_if = NULL;
|
|
|
|
|
|
|
|
zif = ifp->info;
|
|
|
|
|
|
|
|
zvrf = rtadv_interface_get_zvrf(ifp);
|
|
|
|
|
|
|
|
if (status == RA_SUPPRESS) {
|
|
|
|
/* RA is currently enabled */
|
|
|
|
if (zif->rtadv.AdvSendAdvertisements) {
|
|
|
|
rtadv_send_packet(zvrf->rtadv.sock, ifp, RA_SUPPRESS);
|
|
|
|
zif->rtadv.AdvSendAdvertisements = 0;
|
|
|
|
zif->rtadv.AdvIntervalTimer = 0;
|
|
|
|
|
|
|
|
adv_if = adv_if_del(zvrf, ifp->name);
|
|
|
|
if (adv_if == NULL)
|
|
|
|
return; /* Nothing to delete */
|
|
|
|
|
|
|
|
adv_if_free(adv_if);
|
|
|
|
|
|
|
|
if_leave_all_router(zvrf->rtadv.sock, ifp);
|
|
|
|
|
|
|
|
if (adv_if_list_count(&zvrf->rtadv.adv_if) == 0)
|
|
|
|
rtadv_event(zvrf, RTADV_STOP, 0);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!zif->rtadv.AdvSendAdvertisements) {
|
|
|
|
zif->rtadv.AdvSendAdvertisements = 1;
|
|
|
|
zif->rtadv.AdvIntervalTimer = 0;
|
|
|
|
if ((zif->rtadv.MaxRtrAdvInterval >= 1000)
|
|
|
|
&& zif->rtadv.UseFastRexmit) {
|
|
|
|
/*
|
|
|
|
* Enable Fast RA only when RA interval is in
|
|
|
|
* secs and Fast RA retransmit is enabled
|
|
|
|
*/
|
|
|
|
zif->rtadv.inFastRexmit = 1;
|
|
|
|
zif->rtadv.NumFastReXmitsRemain =
|
|
|
|
RTADV_NUM_FAST_REXMITS;
|
|
|
|
}
|
|
|
|
|
|
|
|
rtadv_start_interface_events(zvrf, zif);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ipv6_nd_interval_set(struct interface *ifp, uint32_t interval)
|
|
|
|
{
|
|
|
|
struct zebra_if *zif = ifp->info;
|
|
|
|
struct zebra_vrf *zvrf = rtadv_interface_get_zvrf(ifp);
|
|
|
|
struct adv_if *adv_if;
|
|
|
|
|
|
|
|
if (zif->rtadv.MaxRtrAdvInterval % 1000) {
|
|
|
|
adv_if = adv_msec_if_del(zvrf, ifp->name);
|
|
|
|
if (adv_if != NULL)
|
|
|
|
adv_if_free(adv_if);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (interval % 1000)
|
|
|
|
(void)adv_msec_if_add(zvrf, ifp->name);
|
|
|
|
|
|
|
|
zif->rtadv.MaxRtrAdvInterval = interval;
|
|
|
|
zif->rtadv.MinRtrAdvInterval = 0.33 * interval;
|
|
|
|
|
|
|
|
if (interval != RTADV_MAX_RTR_ADV_INTERVAL) {
|
|
|
|
SET_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED);
|
|
|
|
zif->rtadv.AdvIntervalTimer = 0;
|
|
|
|
} else {
|
|
|
|
if (CHECK_FLAG(zif->rtadv.ra_configured, BGP_RA_CONFIGURED))
|
|
|
|
zif->rtadv.MaxRtrAdvInterval = 10000;
|
|
|
|
|
|
|
|
UNSET_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED);
|
|
|
|
zif->rtadv.AdvIntervalTimer = zif->rtadv.MaxRtrAdvInterval;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
uint32_t ra_interval;
|
|
|
|
|
|
|
|
s = msg;
|
|
|
|
|
|
|
|
/* Get interface index and RA interval. */
|
|
|
|
STREAM_GETL(s, ifindex);
|
|
|
|
STREAM_GETL(s, ra_interval);
|
|
|
|
|
|
|
|
if (IS_ZEBRA_DEBUG_EVENT) {
|
|
|
|
struct vrf *vrf = zvrf->vrf;
|
|
|
|
|
|
|
|
zlog_debug("%s:%u: IF %u RA %s from client %s, interval %ums",
|
|
|
|
VRF_LOGNAME(vrf), 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) {
|
|
|
|
struct vrf *vrf = zvrf->vrf;
|
|
|
|
|
|
|
|
flog_warn(EC_ZEBRA_UNKNOWN_INTERFACE,
|
|
|
|
"%s:%u: IF %u RA %s client %s - interface unknown",
|
|
|
|
VRF_LOGNAME(vrf), zvrf_id(zvrf), ifindex,
|
|
|
|
enable ? "enable" : "disable",
|
|
|
|
zebra_route_string(client->proto));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (vrf_is_backend_netns() && ifp->vrf->vrf_id != zvrf_id(zvrf)) {
|
|
|
|
zlog_debug(
|
|
|
|
"%s:%u: IF %u RA %s client %s - VRF mismatch, IF VRF %u",
|
|
|
|
ifp->vrf->name, zvrf_id(zvrf), ifindex,
|
|
|
|
enable ? "enable" : "disable",
|
|
|
|
zebra_route_string(client->proto), ifp->vrf->vrf_id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
zif = ifp->info;
|
|
|
|
if (enable) {
|
|
|
|
if (!CHECK_FLAG(zif->rtadv.ra_configured, BGP_RA_CONFIGURED))
|
|
|
|
interfaces_configured_for_ra_from_bgp++;
|
|
|
|
|
|
|
|
SET_FLAG(zif->rtadv.ra_configured, BGP_RA_CONFIGURED);
|
|
|
|
ipv6_nd_suppress_ra_set(ifp, RA_ENABLE);
|
|
|
|
if (ra_interval
|
|
|
|
&& (ra_interval * 1000) < (unsigned int) zif->rtadv.MaxRtrAdvInterval
|
|
|
|
&& !CHECK_FLAG(zif->rtadv.ra_configured,
|
|
|
|
VTY_RA_INTERVAL_CONFIGURED))
|
|
|
|
zif->rtadv.MaxRtrAdvInterval = ra_interval * 1000;
|
|
|
|
} else {
|
|
|
|
if (CHECK_FLAG(zif->rtadv.ra_configured, BGP_RA_CONFIGURED))
|
|
|
|
interfaces_configured_for_ra_from_bgp--;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* send router lifetime value of zero in RAs on this interface since we're
|
|
|
|
* ceasing to advertise and want to let our neighbors know.
|
|
|
|
* RFC 4861 secion 6.2.5
|
|
|
|
*/
|
|
|
|
void rtadv_stop_ra(struct interface *ifp)
|
|
|
|
{
|
|
|
|
struct zebra_if *zif;
|
|
|
|
struct zebra_vrf *zvrf;
|
|
|
|
|
|
|
|
zif = ifp->info;
|
|
|
|
zvrf = rtadv_interface_get_zvrf(ifp);
|
|
|
|
|
|
|
|
if (zif->rtadv.AdvSendAdvertisements)
|
|
|
|
rtadv_send_packet(zvrf->rtadv.sock, ifp, RA_SUPPRESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Send router lifetime value of zero in RAs on all interfaces since we're
|
|
|
|
* ceasing to advertise globally and want to let all of our neighbors know
|
|
|
|
* RFC 4861 secion 6.2.5
|
|
|
|
*
|
|
|
|
* Delete all ipv6 global prefixes added to the router advertisement prefix
|
|
|
|
* lists prior to ceasing.
|
|
|
|
*/
|
|
|
|
void rtadv_stop_ra_all(void)
|
|
|
|
{
|
|
|
|
struct vrf *vrf;
|
|
|
|
struct interface *ifp;
|
|
|
|
struct zebra_if *zif;
|
|
|
|
struct rtadv_prefix *rprefix;
|
|
|
|
|
|
|
|
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name)
|
|
|
|
FOR_ALL_INTERFACES (vrf, ifp) {
|
|
|
|
zif = ifp->info;
|
|
|
|
|
|
|
|
frr_each_safe (rtadv_prefixes, zif->rtadv.prefixes,
|
|
|
|
rprefix)
|
|
|
|
rtadv_prefix_reset(zif, rprefix, rprefix);
|
|
|
|
|
|
|
|
rtadv_stop_ra(ifp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void show_zvrf_rtadv_adv_if_helper(struct vty *vty,
|
|
|
|
struct adv_if_list_head *adv_if_head)
|
|
|
|
{
|
|
|
|
struct adv_if *node = NULL;
|
|
|
|
|
|
|
|
if (!adv_if_is_empty_internal(adv_if_head)) {
|
|
|
|
frr_each (adv_if_list, adv_if_head, node) {
|
|
|
|
vty_out(vty, " %s\n", node->name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
vty_out(vty, "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void show_zvrf_rtadv_helper(struct vty *vty, struct zebra_vrf *zvrf)
|
|
|
|
{
|
|
|
|
vty_out(vty, "VRF: %s\n", zvrf_name(zvrf));
|
|
|
|
vty_out(vty, " Interfaces:\n");
|
|
|
|
show_zvrf_rtadv_adv_if_helper(vty, &zvrf->rtadv.adv_if);
|
|
|
|
|
|
|
|
vty_out(vty, " Interfaces(msec):\n");
|
|
|
|
show_zvrf_rtadv_adv_if_helper(vty, &zvrf->rtadv.adv_msec_if);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFPY(show_ipv6_nd_ra_if, show_ipv6_nd_ra_if_cmd,
|
|
|
|
"show ipv6 nd ra-interfaces [vrf<NAME$vrf_name|all$vrf_all>]",
|
|
|
|
SHOW_STR IP6_STR
|
|
|
|
"Neighbor discovery\n"
|
|
|
|
"Route Advertisement Interfaces\n" VRF_FULL_CMD_HELP_STR)
|
|
|
|
{
|
|
|
|
struct zebra_vrf *zvrf = NULL;
|
|
|
|
|
|
|
|
if (!vrf_is_backend_netns() && (vrf_name || vrf_all)) {
|
|
|
|
vty_out(vty,
|
|
|
|
"%% VRF subcommand only applicable for netns-based vrfs.\n");
|
|
|
|
return CMD_WARNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vrf_all) {
|
|
|
|
struct vrf *vrf;
|
|
|
|
|
|
|
|
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
|
|
|
|
struct zebra_vrf *zvrf;
|
|
|
|
|
|
|
|
zvrf = vrf->info;
|
|
|
|
if (!zvrf)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
show_zvrf_rtadv_helper(vty, zvrf);
|
|
|
|
}
|
|
|
|
|
|
|
|
return CMD_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vrf_name)
|
|
|
|
zvrf = zebra_vrf_lookup_by_name(vrf_name);
|
|
|
|
else
|
|
|
|
zvrf = zebra_vrf_lookup_by_name(VRF_DEFAULT_NAME);
|
|
|
|
|
|
|
|
if (!zvrf) {
|
|
|
|
vty_out(vty, "%% VRF '%s' specified does not exist\n",
|
|
|
|
vrf_name);
|
|
|
|
return CMD_WARNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
show_zvrf_rtadv_helper(vty, zvrf);
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct rtadv_rdnss *rtadv_rdnss_set(struct zebra_if *zif,
|
|
|
|
struct rtadv_rdnss *rdnss)
|
|
|
|
{
|
|
|
|
struct rtadv_rdnss *p;
|
|
|
|
|
|
|
|
p = rtadv_rdnss_new();
|
|
|
|
memcpy(p, rdnss, sizeof(struct rtadv_rdnss));
|
|
|
|
listnode_add(zif->rtadv.AdvRDNSSList, p);
|
|
|
|
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
void rtadv_rdnss_reset(struct zebra_if *zif, struct rtadv_rdnss *p)
|
|
|
|
{
|
|
|
|
listnode_delete(zif->rtadv.AdvRDNSSList, p);
|
|
|
|
rtadv_rdnss_free(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct rtadv_dnssl *rtadv_dnssl_set(struct zebra_if *zif,
|
|
|
|
struct rtadv_dnssl *dnssl)
|
|
|
|
{
|
|
|
|
struct rtadv_dnssl *p;
|
|
|
|
|
|
|
|
p = rtadv_dnssl_new();
|
|
|
|
memcpy(p, dnssl, sizeof(struct rtadv_dnssl));
|
|
|
|
listnode_add(zif->rtadv.AdvDNSSLList, p);
|
|
|
|
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
void rtadv_dnssl_reset(struct zebra_if *zif, struct rtadv_dnssl *p)
|
|
|
|
{
|
|
|
|
listnode_delete(zif->rtadv.AdvDNSSLList, p);
|
|
|
|
rtadv_dnssl_free(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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 %u milliseconds\n",
|
|
|
|
rtadv->AdvRetransTimer);
|
|
|
|
vty_out(vty, " ND advertised hop-count limit is %d hops\n",
|
|
|
|
rtadv->AdvCurHopLimit);
|
|
|
|
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->UseFastRexmit)
|
|
|
|
vty_out(vty,
|
|
|
|
" ND router advertisements do not use fast retransmit\n");
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void rtadv_event(struct zebra_vrf *zvrf, enum rtadv_event event, int val)
|
|
|
|
{
|
|
|
|
struct rtadv *rtadv;
|
|
|
|
|
|
|
|
if (IS_ZEBRA_DEBUG_EVENT) {
|
|
|
|
struct vrf *vrf = zvrf->vrf;
|
|
|
|
|
|
|
|
zlog_debug("%s(%s) with event: %d and val: %d", __func__,
|
|
|
|
VRF_LOGNAME(vrf), event, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
rtadv = &zvrf->rtadv;
|
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
case RTADV_START:
|
|
|
|
event_add_read(zrouter.master, rtadv_read, zvrf, rtadv->sock,
|
|
|
|
&rtadv->ra_read);
|
|
|
|
event_add_event(zrouter.master, rtadv_timer, zvrf, 0,
|
|
|
|
&rtadv->ra_timer);
|
|
|
|
break;
|
|
|
|
case RTADV_STOP:
|
|
|
|
EVENT_OFF(rtadv->ra_timer);
|
|
|
|
EVENT_OFF(rtadv->ra_read);
|
|
|
|
break;
|
|
|
|
case RTADV_TIMER:
|
|
|
|
event_add_timer(zrouter.master, rtadv_timer, zvrf, val,
|
|
|
|
&rtadv->ra_timer);
|
|
|
|
break;
|
|
|
|
case RTADV_TIMER_MSEC:
|
|
|
|
event_add_timer_msec(zrouter.master, rtadv_timer, zvrf, val,
|
|
|
|
&rtadv->ra_timer);
|
|
|
|
break;
|
|
|
|
case RTADV_READ:
|
|
|
|
event_add_read(zrouter.master, rtadv_read, zvrf, rtadv->sock,
|
|
|
|
&rtadv->ra_read);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void rtadv_if_up(struct zebra_if *zif)
|
|
|
|
{
|
|
|
|
struct zebra_vrf *zvrf = rtadv_interface_get_zvrf(zif->ifp);
|
|
|
|
|
|
|
|
/* Enable fast tx of RA if enabled && RA interval is not in msecs */
|
|
|
|
if (zif->rtadv.AdvSendAdvertisements &&
|
|
|
|
(zif->rtadv.MaxRtrAdvInterval >= 1000) &&
|
|
|
|
zif->rtadv.UseFastRexmit) {
|
|
|
|
zif->rtadv.inFastRexmit = 1;
|
|
|
|
zif->rtadv.NumFastReXmitsRemain = RTADV_NUM_FAST_REXMITS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* startup the state machine, if it hasn't been already
|
|
|
|
* due to a delayed ifindex on startup ordering
|
|
|
|
*/
|
|
|
|
if (zif->rtadv.AdvSendAdvertisements)
|
|
|
|
rtadv_start_interface_events(zvrf, zif);
|
|
|
|
}
|
|
|
|
|
|
|
|
void rtadv_if_init(struct zebra_if *zif)
|
|
|
|
{
|
|
|
|
/* Set default router advertise values. */
|
|
|
|
struct rtadvconf *rtadv;
|
|
|
|
|
|
|
|
rtadv = &zif->rtadv;
|
|
|
|
|
|
|
|
rtadv->AdvSendAdvertisements = 0;
|
|
|
|
rtadv->MaxRtrAdvInterval = RTADV_MAX_RTR_ADV_INTERVAL;
|
|
|
|
rtadv->MinRtrAdvInterval = RTADV_MIN_RTR_ADV_INTERVAL;
|
|
|
|
rtadv->AdvIntervalTimer = 0;
|
|
|
|
rtadv->AdvManagedFlag = 0;
|
|
|
|
rtadv->AdvOtherConfigFlag = 0;
|
|
|
|
rtadv->AdvHomeAgentFlag = 0;
|
|
|
|
rtadv->AdvLinkMTU = 0;
|
|
|
|
rtadv->AdvReachableTime = 0;
|
|
|
|
rtadv->AdvRetransTimer = 0;
|
|
|
|
rtadv->AdvCurHopLimit = RTADV_DEFAULT_HOPLIMIT;
|
|
|
|
memset(&rtadv->lastadvcurhoplimit, 0,
|
|
|
|
sizeof(rtadv->lastadvcurhoplimit));
|
|
|
|
memset(&rtadv->lastadvmanagedflag, 0,
|
|
|
|
sizeof(rtadv->lastadvmanagedflag));
|
|
|
|
memset(&rtadv->lastadvotherconfigflag, 0,
|
|
|
|
sizeof(rtadv->lastadvotherconfigflag));
|
|
|
|
memset(&rtadv->lastadvreachabletime, 0,
|
|
|
|
sizeof(rtadv->lastadvreachabletime));
|
|
|
|
memset(&rtadv->lastadvretranstimer, 0,
|
|
|
|
sizeof(rtadv->lastadvretranstimer));
|
|
|
|
rtadv->AdvDefaultLifetime = -1; /* derive from MaxRtrAdvInterval */
|
|
|
|
rtadv->HomeAgentPreference = 0;
|
|
|
|
rtadv->HomeAgentLifetime = -1; /* derive from AdvDefaultLifetime */
|
|
|
|
rtadv->AdvIntervalOption = 0;
|
|
|
|
rtadv->UseFastRexmit = true;
|
|
|
|
rtadv->DefaultPreference = RTADV_PREF_MEDIUM;
|
|
|
|
|
|
|
|
rtadv_prefixes_init(rtadv->prefixes);
|
|
|
|
|
|
|
|
rtadv->AdvRDNSSList = list_new();
|
|
|
|
rtadv->AdvDNSSLList = list_new();
|
|
|
|
}
|
|
|
|
|
|
|
|
void rtadv_if_fini(struct zebra_if *zif)
|
|
|
|
{
|
|
|
|
struct rtadvconf *rtadv;
|
|
|
|
struct rtadv_prefix *rp;
|
|
|
|
|
|
|
|
rtadv = &zif->rtadv;
|
|
|
|
|
|
|
|
while ((rp = rtadv_prefixes_pop(rtadv->prefixes)))
|
|
|
|
rtadv_prefix_free(rp);
|
|
|
|
|
|
|
|
list_delete(&rtadv->AdvRDNSSList);
|
|
|
|
list_delete(&rtadv->AdvDNSSLList);
|
|
|
|
}
|
|
|
|
|
|
|
|
void rtadv_vrf_init(struct zebra_vrf *zvrf)
|
|
|
|
{
|
|
|
|
if (!vrf_is_backend_netns() && (zvrf_id(zvrf) != VRF_DEFAULT))
|
|
|
|
return;
|
|
|
|
|
|
|
|
zvrf->rtadv.sock = rtadv_make_socket(zvrf->zns->ns_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
void rtadv_vrf_terminate(struct zebra_vrf *zvrf)
|
|
|
|
{
|
|
|
|
if (!vrf_is_backend_netns() && (zvrf_id(zvrf) != VRF_DEFAULT))
|
|
|
|
return;
|
|
|
|
|
|
|
|
rtadv_event(zvrf, RTADV_STOP, 0);
|
|
|
|
if (zvrf->rtadv.sock >= 0) {
|
|
|
|
close(zvrf->rtadv.sock);
|
|
|
|
zvrf->rtadv.sock = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
adv_if_clean(zvrf);
|
|
|
|
adv_msec_if_clean(zvrf);
|
|
|
|
}
|
|
|
|
|
|
|
|
void rtadv_cmd_init(void)
|
|
|
|
{
|
|
|
|
interfaces_configured_for_ra_from_bgp = 0;
|
|
|
|
|
|
|
|
hook_register(zebra_if_extra_info, nd_dump_vty);
|
|
|
|
|
|
|
|
install_element(VIEW_NODE, &show_ipv6_nd_ra_if_cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int if_join_all_router(int sock, struct interface *ifp)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
struct ipv6_mreq mreq;
|
|
|
|
|
|
|
|
memset(&mreq, 0, sizeof(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(%s:%u): Join All-Routers multicast group, socket %u",
|
|
|
|
ifp->name, ifp->vrf->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(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(%s:%u): Failed to leave group, socket %u error %s",
|
|
|
|
ifp->name, ifp->vrf->name, ifp->ifindex, sock,
|
|
|
|
safe_strerror(errno));
|
|
|
|
|
|
|
|
if (IS_ZEBRA_DEBUG_EVENT)
|
|
|
|
zlog_debug(
|
|
|
|
"%s(%s:%u): Leave All-Routers multicast group, socket %u",
|
|
|
|
ifp->name, ifp->vrf->name, ifp->ifindex, sock);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool rtadv_compiled_in(void)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else /* !HAVE_RTADV */
|
|
|
|
/*
|
|
|
|
* If the end user does not have RADV enabled we should
|
|
|
|
* handle this better
|
|
|
|
*/
|
|
|
|
void zebra_interface_radv_disable(ZAPI_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
if (IS_ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug(
|
|
|
|
"Received %s command, but ZEBRA is not compiled with Router Advertisements on",
|
|
|
|
zserv_command_string(hdr->command));
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void zebra_interface_radv_enable(ZAPI_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
if (IS_ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug(
|
|
|
|
"Received %s command, but ZEBRA is not compiled with Router Advertisements on",
|
|
|
|
zserv_command_string(hdr->command));
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool rtadv_compiled_in(void)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* HAVE_RTADV */
|
|
|
|
|
|
|
|
uint32_t rtadv_get_interfaces_configured_from_bgp(void)
|
|
|
|
{
|
|
|
|
return interfaces_configured_for_ra_from_bgp;
|
|
|
|
}
|
|
|
|
|
|
|
|
void rtadv_init(void)
|
|
|
|
{
|
|
|
|
if (CMSG_SPACE(sizeof(struct in6_pktinfo)) > RTADV_ADATA_SIZE) {
|
2025-02-05 10:17:20 +01:00
|
|
|
zlog_debug("%s: RTADV_ADATA_SIZE chosen will not work on this platform, please use a larger size",
|
2025-02-05 10:03:58 +01:00
|
|
|
__func__);
|
|
|
|
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
}
|