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