rtsol.c revision 55163
155163Sshin/* 255163Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 355163Sshin * All rights reserved. 455163Sshin * 555163Sshin * Redistribution and use in source and binary forms, with or without 655163Sshin * modification, are permitted provided that the following conditions 755163Sshin * are met: 855163Sshin * 1. Redistributions of source code must retain the above copyright 955163Sshin * notice, this list of conditions and the following disclaimer. 1055163Sshin * 2. Redistributions in binary form must reproduce the above copyright 1155163Sshin * notice, this list of conditions and the following disclaimer in the 1255163Sshin * documentation and/or other materials provided with the distribution. 1355163Sshin * 3. Neither the name of the project nor the names of its contributors 1455163Sshin * may be used to endorse or promote products derived from this software 1555163Sshin * without specific prior written permission. 1655163Sshin * 1755163Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 1855163Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1955163Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2055163Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2155163Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2255163Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2355163Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2455163Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2555163Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2655163Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2755163Sshin * SUCH DAMAGE. 2855163Sshin * 2955163Sshin * $FreeBSD: head/usr.sbin/rtsold/rtsol.c 55163 1999-12-28 02:37:14Z shin $ 3055163Sshin */ 3155163Sshin 3255163Sshin#include <sys/param.h> 3355163Sshin#include <sys/socket.h> 3455163Sshin#include <sys/uio.h> 3555163Sshin#include <sys/time.h> 3655163Sshin 3755163Sshin#include <net/if.h> 3855163Sshin#include <net/route.h> 3955163Sshin#include <net/if_dl.h> 4055163Sshin 4155163Sshin#include <netinet/in.h> 4255163Sshin#include <netinet/ip6.h> 4355163Sshin#include <netinet6/ip6_var.h> 4455163Sshin#include <netinet/icmp6.h> 4555163Sshin 4655163Sshin#include <arpa/inet.h> 4755163Sshin 4855163Sshin#include <time.h> 4955163Sshin#include <unistd.h> 5055163Sshin#include <stdio.h> 5155163Sshin#include <err.h> 5255163Sshin#include <errno.h> 5355163Sshin#include <string.h> 5455163Sshin#include <stdlib.h> 5555163Sshin#include <syslog.h> 5655163Sshin#include "rtsold.h" 5755163Sshin 5855163Sshin#define ALLROUTER "ff02::2" 5955163Sshin 6055163Sshinstatic struct msghdr rcvmhdr; 6155163Sshinstatic struct msghdr sndmhdr; 6255163Sshinstatic struct iovec rcviov[2]; 6355163Sshinstatic struct iovec sndiov[2]; 6455163Sshinstatic struct sockaddr_in6 from; 6555163Sshin 6655163Sshinstatic int rssock; 6755163Sshin 6855163Sshinstatic struct sockaddr_in6 sin6_allrouters = {sizeof(sin6_allrouters), AF_INET6}; 6955163Sshin 7055163Sshinint 7155163Sshinsockopen() 7255163Sshin{ 7355163Sshin int on; 7455163Sshin struct icmp6_filter filt; 7555163Sshin static u_char answer[1500]; 7655163Sshin static u_char rcvcmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo)) + 7755163Sshin CMSG_SPACE(sizeof(int))]; 7855163Sshin static u_char sndcmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo)) + 7955163Sshin CMSG_SPACE(sizeof(int))]; 8055163Sshin 8155163Sshin memset(&sin6_allrouters, 0, sizeof(struct sockaddr_in6)); 8255163Sshin if (inet_pton(AF_INET6, ALLROUTER, 8355163Sshin &sin6_allrouters.sin6_addr.s6_addr) != 1) { 8455163Sshin warnmsg(LOG_ERR, __FUNCTION__, "inet_pton failed for %s", 8555163Sshin ALLROUTER); 8655163Sshin return(-1); 8755163Sshin } 8855163Sshin 8955163Sshin if ((rssock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { 9055163Sshin warnmsg(LOG_ERR, __FUNCTION__, "socket: %s", strerror(errno)); 9155163Sshin return(-1); 9255163Sshin } 9355163Sshin 9455163Sshin /* specify to tell receiving interface */ 9555163Sshin on = 1; 9655163Sshin if (setsockopt(rssock, IPPROTO_IPV6, IPV6_PKTINFO, &on, 9755163Sshin sizeof(on)) < 0) { 9855163Sshin warnmsg(LOG_ERR, __FUNCTION__, "IPV6_PKTINFO: %s", 9955163Sshin strerror(errno)); 10055163Sshin exit(1); 10155163Sshin } 10255163Sshin 10355163Sshin on = 1; 10455163Sshin /* specify to tell value of hoplimit field of received IP6 hdr */ 10555163Sshin if (setsockopt(rssock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, 10655163Sshin sizeof(on)) < 0) { 10755163Sshin warnmsg(LOG_ERR, __FUNCTION__, "IPV6_HOPLIMIT: %s", 10855163Sshin strerror(errno)); 10955163Sshin exit(1); 11055163Sshin } 11155163Sshin 11255163Sshin /* specfiy to accept only router advertisements on the socket */ 11355163Sshin ICMP6_FILTER_SETBLOCKALL(&filt); 11455163Sshin ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt); 11555163Sshin if (setsockopt(rssock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, 11655163Sshin sizeof(filt)) == -1) { 11755163Sshin warnmsg(LOG_ERR, __FUNCTION__, "setsockopt(ICMP6_FILTER): %s", 11855163Sshin strerror(errno)); 11955163Sshin return(-1); 12055163Sshin } 12155163Sshin 12255163Sshin /* initialize msghdr for receiving packets */ 12355163Sshin rcviov[0].iov_base = (caddr_t)answer; 12455163Sshin rcviov[0].iov_len = sizeof(answer); 12555163Sshin rcvmhdr.msg_name = (caddr_t)&from; 12655163Sshin rcvmhdr.msg_namelen = sizeof(from); 12755163Sshin rcvmhdr.msg_iov = rcviov; 12855163Sshin rcvmhdr.msg_iovlen = 1; 12955163Sshin rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf; 13055163Sshin rcvmhdr.msg_controllen = sizeof(rcvcmsgbuf); 13155163Sshin 13255163Sshin /* initialize msghdr for sending packets */ 13355163Sshin sndmhdr.msg_namelen = sizeof(struct sockaddr_in6); 13455163Sshin sndmhdr.msg_iov = sndiov; 13555163Sshin sndmhdr.msg_iovlen = 1; 13655163Sshin sndmhdr.msg_control = (caddr_t)sndcmsgbuf; 13755163Sshin sndmhdr.msg_controllen = sizeof(sndcmsgbuf); 13855163Sshin 13955163Sshin return(rssock); 14055163Sshin} 14155163Sshin 14255163Sshinvoid 14355163Sshinsendpacket(struct ifinfo *ifinfo) 14455163Sshin{ 14555163Sshin int i; 14655163Sshin struct cmsghdr *cm; 14755163Sshin struct in6_pktinfo *pi; 14855163Sshin 14955163Sshin sndmhdr.msg_name = (caddr_t)&sin6_allrouters; 15055163Sshin sndmhdr.msg_iov[0].iov_base = (caddr_t)ifinfo->rs_data; 15155163Sshin sndmhdr.msg_iov[0].iov_len = ifinfo->rs_datalen; 15255163Sshin 15355163Sshin cm = CMSG_FIRSTHDR(&sndmhdr); 15455163Sshin /* specify the outgoing interface */ 15555163Sshin cm->cmsg_level = IPPROTO_IPV6; 15655163Sshin cm->cmsg_type = IPV6_PKTINFO; 15755163Sshin cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); 15855163Sshin pi = (struct in6_pktinfo *)CMSG_DATA(cm); 15955163Sshin memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/ 16055163Sshin pi->ipi6_ifindex = ifinfo->sdl->sdl_index; 16155163Sshin 16255163Sshin /* specify the hop limit of the packet */ 16355163Sshin { 16455163Sshin int hoplimit = 255; 16555163Sshin 16655163Sshin cm = CMSG_NXTHDR(&sndmhdr, cm); 16755163Sshin cm->cmsg_level = IPPROTO_IPV6; 16855163Sshin cm->cmsg_type = IPV6_HOPLIMIT; 16955163Sshin cm->cmsg_len = CMSG_LEN(sizeof(int)); 17055163Sshin memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int)); 17155163Sshin } 17255163Sshin 17355163Sshin warnmsg(LOG_DEBUG, 17455163Sshin __FUNCTION__, "send RS on %s, whose state is %d", 17555163Sshin ifinfo->ifname, ifinfo->state); 17655163Sshin 17755163Sshin i = sendmsg(rssock, &sndmhdr, 0); 17855163Sshin 17955163Sshin if (i < 0 || i != ifinfo->rs_datalen) { 18055163Sshin /* 18155163Sshin * ENETDOWN is not so serious, especially when using several 18255163Sshin * network cards on a mobile node. We ignore it. 18355163Sshin */ 18455163Sshin if (errno != ENETDOWN || dflag > 0) 18555163Sshin warnmsg(LOG_ERR, __FUNCTION__, "sendmsg on %s: %s", 18655163Sshin ifinfo->ifname, strerror(errno)); 18755163Sshin } 18855163Sshin 18955163Sshin /* update counter */ 19055163Sshin ifinfo->probes++; 19155163Sshin} 19255163Sshin 19355163Sshinvoid 19455163Sshinrtsol_input(int s) 19555163Sshin{ 19655163Sshin int i; 19755163Sshin int *hlimp = NULL; 19855163Sshin struct icmp6_hdr *icp; 19955163Sshin int ifindex = 0; 20055163Sshin struct cmsghdr *cm; 20155163Sshin struct in6_pktinfo *pi = NULL; 20255163Sshin struct ifinfo *ifi = NULL; 20355163Sshin u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; 20455163Sshin 20555163Sshin /* get message */ 20655163Sshin if ((i = recvmsg(s, &rcvmhdr, 0)) < 0) { 20755163Sshin warnmsg(LOG_ERR, __FUNCTION__, "recvmsg: %s", strerror(errno)); 20855163Sshin return; 20955163Sshin } 21055163Sshin 21155163Sshin /* extract optional information via Advanced API */ 21255163Sshin for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr); 21355163Sshin cm; 21455163Sshin cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) { 21555163Sshin if (cm->cmsg_level == IPPROTO_IPV6 && 21655163Sshin cm->cmsg_type == IPV6_PKTINFO && 21755163Sshin cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { 21855163Sshin pi = (struct in6_pktinfo *)(CMSG_DATA(cm)); 21955163Sshin ifindex = pi->ipi6_ifindex; 22055163Sshin } 22155163Sshin if (cm->cmsg_level == IPPROTO_IPV6 && 22255163Sshin cm->cmsg_type == IPV6_HOPLIMIT && 22355163Sshin cm->cmsg_len == CMSG_LEN(sizeof(int))) 22455163Sshin hlimp = (int *)CMSG_DATA(cm); 22555163Sshin } 22655163Sshin 22755163Sshin if (ifindex == 0) { 22855163Sshin warnmsg(LOG_ERR, 22955163Sshin __FUNCTION__, "failed to get receiving interface"); 23055163Sshin return; 23155163Sshin } 23255163Sshin if (hlimp == NULL) { 23355163Sshin warnmsg(LOG_ERR, 23455163Sshin __FUNCTION__, "failed to get receiving hop limit"); 23555163Sshin return; 23655163Sshin } 23755163Sshin 23855163Sshin if (i < sizeof(struct nd_router_advert)) { 23955163Sshin warnmsg(LOG_ERR, 24055163Sshin __FUNCTION__, "packet size(%d) is too short", i); 24155163Sshin return; 24255163Sshin } 24355163Sshin 24455163Sshin icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base; 24555163Sshin 24655163Sshin if (icp->icmp6_type != ND_ROUTER_ADVERT) { 24755163Sshin warnmsg(LOG_ERR, __FUNCTION__, 24855163Sshin "invalid icmp type(%d) from %s on %s", icp->icmp6_type, 24955163Sshin inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, 25055163Sshin INET6_ADDRSTRLEN), 25155163Sshin if_indextoname(pi->ipi6_ifindex, ifnamebuf)); 25255163Sshin return; 25355163Sshin } 25455163Sshin 25555163Sshin if (icp->icmp6_code != 0) { 25655163Sshin warnmsg(LOG_ERR, __FUNCTION__, 25755163Sshin "invalid icmp code(%d) from %s on %s", icp->icmp6_code, 25855163Sshin inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, 25955163Sshin INET6_ADDRSTRLEN), 26055163Sshin if_indextoname(pi->ipi6_ifindex, ifnamebuf)); 26155163Sshin return; 26255163Sshin } 26355163Sshin 26455163Sshin if (*hlimp != 255) { 26555163Sshin warnmsg(LOG_NOTICE, __FUNCTION__, 26655163Sshin "invalid RA with hop limit(%d) from %s on %s", 26755163Sshin *hlimp, 26855163Sshin inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, 26955163Sshin INET6_ADDRSTRLEN), 27055163Sshin if_indextoname(pi->ipi6_ifindex, ifnamebuf)); 27155163Sshin return; 27255163Sshin } 27355163Sshin 27455163Sshin if (pi && !IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) { 27555163Sshin warnmsg(LOG_NOTICE, __FUNCTION__, 27655163Sshin "invalid RA with non link-local source from %s on %s", 27755163Sshin inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, 27855163Sshin INET6_ADDRSTRLEN), 27955163Sshin if_indextoname(pi->ipi6_ifindex, ifnamebuf)); 28055163Sshin return; 28155163Sshin } 28255163Sshin 28355163Sshin /* xxx: more validation? */ 28455163Sshin 28555163Sshin if ((ifi = find_ifinfo(pi->ipi6_ifindex)) == NULL) { 28655163Sshin warnmsg(LOG_NOTICE, __FUNCTION__, 28755163Sshin "received RA from %s on an unexpeced IF(%s)", 28855163Sshin inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, 28955163Sshin INET6_ADDRSTRLEN), 29055163Sshin if_indextoname(pi->ipi6_ifindex, ifnamebuf)); 29155163Sshin return; 29255163Sshin } 29355163Sshin 29455163Sshin warnmsg(LOG_DEBUG, __FUNCTION__, 29555163Sshin "received RA from %s on %s, state is %d", 29655163Sshin inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, 29755163Sshin INET6_ADDRSTRLEN), 29855163Sshin ifi->ifname, ifi->state); 29955163Sshin 30055163Sshin ifi->racnt++; 30155163Sshin 30255163Sshin switch(ifi->state) { 30355163Sshin case IFS_IDLE: /* should be ignored */ 30455163Sshin case IFS_DELAY: /* right? */ 30555163Sshin break; 30655163Sshin case IFS_PROBE: 30755163Sshin ifi->state = IFS_IDLE; 30855163Sshin ifi->probes = 0; 30955163Sshin rtsol_timer_update(ifi); 31055163Sshin break; 31155163Sshin } 31255163Sshin} 313