📄 rtadv.c
字号:
/* Router advertisement * 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 GNU Zebra; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */#include <zebra.h>#include "memory.h"#include "sockopt.h"#include "thread.h"#include "if.h"#include "log.h"#include "prefix.h"#include "linklist.h"#include "command.h"#include "zebra/interface.h"#include "zebra/rtadv.h"#include "zebra/debug.h"#if defined (HAVE_IPV6) && defined (RTADV)/* 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"enum rtadv_event {RTADV_START, RTADV_STOP, RTADV_TIMER, RTADV_READ};void rtadv_event (enum rtadv_event, int);int if_join_all_router (int, struct interface *);int if_leave_all_router (int, struct interface *);/* Structure which hold status of router advertisement. */struct rtadv{ int sock; int adv_if_count; struct thread *ra_read; struct thread *ra_timer;};struct rtadv *rtadv = NULL;struct rtadv *rtadv_new (){ struct rtadv *new; new = XMALLOC (MTYPE_TMP, sizeof (struct rtadv)); memset (new, 0, sizeof (struct rtadv)); return new;}voidrtadv_free (struct rtadv *rtadv){ XFREE (MTYPE_TMP, rtadv);}intrtadv_recv_packet (int sock, u_char *buf, int buflen, struct sockaddr_in6 *from, unsigned int *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. */ 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) *hoplimit = *((int *) CMSG_DATA (cmsgptr)); } return ret;}#define RTADV_MSG_SIZE 4096/* Send router advertisement packet. */voidrtadv_send_packet (int sock, struct interface *ifp){ struct msghdr msg; struct iovec iov; struct cmsghdr *cmsgptr; struct in6_pktinfo *pkt; struct sockaddr_in6 addr;#ifdef HAVE_SOCKADDR_DL struct sockaddr_dl *sdl;#endif /* HAVE_SOCKADDR_DL */ char adata [sizeof (struct cmsghdr) + sizeof (struct in6_pktinfo)]; unsigned char buf[RTADV_MSG_SIZE]; struct nd_router_advert *rtadv; int ret; int len = 0; struct zebra_if *zif; u_char all_nodes_addr[] = {0xff,0x02,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; listnode node; /* Logging of packet. */ if (IS_ZEBRA_DEBUG_PACKET) zlog_info ("Router advertisement send to %s", ifp->name); /* 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); memcpy (&addr.sin6_addr, all_nodes_addr, sizeof (struct in6_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; rtadv->nd_ra_flags_reserved = 0; 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; rtadv->nd_ra_router_lifetime = htons (zif->rtadv.AdvDefaultLifetime); rtadv->nd_ra_reachable = htonl (zif->rtadv.AdvReachableTime); rtadv->nd_ra_retransmit = htonl (0); len = sizeof (struct nd_router_advert); /* Fill in prefix. */ for (node = listhead (zif->rtadv.AdvPrefixList); node; node = nextnode (node)) { struct nd_opt_prefix_info *pinfo; struct rtadv_prefix *rprefix; rprefix = getdata (node); 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; pinfo->nd_opt_pi_valid_time = htonl (rprefix->AdvValidLifetime); pinfo->nd_opt_pi_preferred_time = htonl (rprefix->AdvPreferredLifetime); pinfo->nd_opt_pi_reserved2 = 0; memcpy (&pinfo->nd_opt_pi_prefix, &rprefix->prefix.u.prefix6, sizeof (struct in6_addr));#ifdef DEBUG { u_char buf[INET6_ADDRSTRLEN]; zlog_info ("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. */#ifdef HAVE_SOCKADDR_DL sdl = &ifp->sdl; if (sdl != NULL && sdl->sdl_alen != 0) { buf[len++] = ND_OPT_SOURCE_LINKADDR; buf[len++] = (sdl->sdl_alen + 2) >> 3; memcpy (buf + len, LLADDR (sdl), sdl->sdl_alen); len += sdl->sdl_alen; }#else if (ifp->hw_addr_len != 0) { buf[len++] = ND_OPT_SOURCE_LINKADDR; buf[len++] = (ifp->hw_addr_len + 2) >> 3; memcpy (buf + len, ifp->hw_addr, ifp->hw_addr_len); len += ifp->hw_addr_len; }#endif /* HAVE_SOCKADDR_DL */ 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 = sizeof adata; iov.iov_base = buf; iov.iov_len = len; cmsgptr = (struct cmsghdr *)adata; cmsgptr->cmsg_len = sizeof adata; 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) perror ("sendmsg");}intrtadv_timer (struct thread *thread){ listnode node; struct interface *ifp; struct zebra_if *zif; rtadv->ra_timer = NULL; rtadv_event (RTADV_TIMER, 1); for (node = listhead (iflist); node; nextnode (node)) { ifp = getdata (node); if (if_is_loopback (ifp)) continue; zif = ifp->info; if (zif->rtadv.AdvSendAdvertisements) if (--zif->rtadv.AdvIntervalTimer <= 0) { zif->rtadv.AdvIntervalTimer = zif->rtadv.MaxRtrAdvInterval; rtadv_send_packet (rtadv->sock, ifp); } } return 0;}voidrtadv_process_solicit (struct interface *ifp){ zlog_info ("Router solicitation received on %s", ifp->name); rtadv_send_packet (rtadv->sock, ifp);}voidrtadv_process_advert (){ zlog_info ("Router advertisement received");}voidrtadv_process_packet (u_char *buf, int len, unsigned int ifindex, int hoplimit){ struct icmp6_hdr *icmph; struct interface *ifp; struct zebra_if *zif; /* Interface search. */ ifp = if_lookup_by_index (ifindex); if (ifp == NULL) { zlog_warn ("Unknown interface index: %d", ifindex); return; } 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_warn ("Invalid ICMPV6 packet length: %d", 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_warn ("Unwanted ICMPV6 message type: %d", icmph->icmp6_type); return; } /* Hoplimit check. */ if (hoplimit >= 0 && hoplimit != 255) { zlog_warn ("Invalid hoplimit %d for router advertisement ICMP packet", 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 (); return;}intrtadv_read (struct thread *thread){ int sock; int len; u_char buf[RTADV_MSG_SIZE]; struct sockaddr_in6 from; unsigned int ifindex; int hoplimit = -1; sock = THREAD_FD (thread); rtadv->ra_read = NULL; /* Register myself. */ rtadv_event (RTADV_READ, sock); len = rtadv_recv_packet (sock, buf, BUFSIZ, &from, &ifindex, &hoplimit); if (len < 0) { zlog_warn ("router solicitation recv failed: %s.", strerror (errno)); return len; } rtadv_process_packet (buf, len, ifindex, hoplimit); return 0;}intrtadv_make_socket (void){ int sock; int ret; struct icmp6_filter filter; sock = socket (AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); /* When we can't make ICMPV6 socket simply back. Router advertisement feature will not be supported. */ if (sock < 0) return -1; ret = setsockopt_ipv6_pktinfo (sock, 1); if (ret < 0) return ret; ret = setsockopt_ipv6_checksum (sock, 2); if (ret < 0) return ret; ret = setsockopt_ipv6_multicast_loop (sock, 0); if (ret < 0) return ret; ret = setsockopt_ipv6_unicast_hops (sock, 255); if (ret < 0) return ret; ret = setsockopt_ipv6_multicast_hops (sock, 255); if (ret < 0) return ret; ret = setsockopt_ipv6_hoplimit (sock, 1); if (ret < 0) 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", strerror (errno)); return ret; } return sock;}struct rtadv_prefix *rtadv_prefix_new (){ struct rtadv_prefix *new; new = XMALLOC (MTYPE_RTADV_PREFIX, sizeof (struct rtadv_prefix)); memset (new, 0, sizeof (struct rtadv_prefix)); return new;}voidrtadv_prefix_free (struct rtadv_prefix *rtadv_prefix){ XFREE (MTYPE_RTADV_PREFIX, rtadv_prefix);}struct rtadv_prefix *rtadv_prefix_lookup (list rplist, struct prefix *p){ listnode node; struct rtadv_prefix *rprefix; for (node = listhead (rplist); node; node = nextnode (node)) { rprefix = getdata (node); if (prefix_same (&rprefix->prefix, p)) return rprefix; } return NULL;}struct rtadv_prefix *rtadv_prefix_get (list rplist, struct prefix *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)); listnode_add (rplist, rprefix); return rprefix;}voidrtadv_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;}intrtadv_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;}DEFUN (ipv6_nd_suppress_ra, ipv6_nd_suppress_ra_cmd, "ipv6 nd suppress-ra", IP_STR "Neighbor discovery\n" "Suppress Router Advertisement\n"){ struct interface *ifp; struct zebra_if *zif; ifp = vty->index; zif = ifp->info; if (if_is_loopback (ifp)) { vty_out (vty, "Invalid interface%s", VTY_NEWLINE); return CMD_WARNING; } if (zif->rtadv.AdvSendAdvertisements) { zif->rtadv.AdvSendAdvertisements = 0; zif->rtadv.AdvIntervalTimer = 0; rtadv->adv_if_count--; if_leave_all_router (rtadv->sock, ifp);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -