rtsol.c revision 62632
1/*
2 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the project nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $FreeBSD: head/usr.sbin/rtsold/rtsol.c 62632 2000-07-05 10:14:11Z kris $
30 */
31
32#include <sys/param.h>
33#include <sys/socket.h>
34#include <sys/uio.h>
35#include <sys/time.h>
36
37#include <net/if.h>
38#include <net/route.h>
39#include <net/if_dl.h>
40
41#include <netinet/in.h>
42#include <netinet/ip6.h>
43#include <netinet6/ip6_var.h>
44#include <netinet/icmp6.h>
45
46#include <arpa/inet.h>
47
48#include <time.h>
49#include <unistd.h>
50#include <stdio.h>
51#include <err.h>
52#include <errno.h>
53#include <string.h>
54#include <stdlib.h>
55#include <syslog.h>
56#include "rtsold.h"
57
58#define ALLROUTER "ff02::2"
59
60static struct msghdr rcvmhdr;
61static struct msghdr sndmhdr;
62static struct iovec rcviov[2];
63static struct iovec sndiov[2];
64static struct sockaddr_in6 from;
65
66int rssock;
67
68static struct sockaddr_in6 sin6_allrouters = {sizeof(sin6_allrouters), AF_INET6};
69
70int
71sockopen()
72{
73	int on;
74	struct icmp6_filter filt;
75	static u_char answer[1500];
76	int rcvcmsglen, sndcmsglen;
77	static u_char *rcvcmsgbuf = NULL, *sndcmsgbuf = NULL;
78
79	sndcmsglen = rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
80		CMSG_SPACE(sizeof(int));
81	if (rcvcmsgbuf == NULL && (rcvcmsgbuf = malloc(rcvcmsglen)) == NULL) {
82		warnmsg(LOG_ERR, __FUNCTION__,
83			"malloc for receive msghdr failed");
84		return(-1);
85	}
86	if (sndcmsgbuf == NULL && (sndcmsgbuf = malloc(sndcmsglen)) == NULL) {
87		warnmsg(LOG_ERR, __FUNCTION__,
88			"malloc for send msghdr failed");
89		return(-1);
90	}
91	memset(&sin6_allrouters, 0, sizeof(struct sockaddr_in6));
92	if (inet_pton(AF_INET6, ALLROUTER,
93		      &sin6_allrouters.sin6_addr.s6_addr) != 1) {
94		warnmsg(LOG_ERR, __FUNCTION__, "inet_pton failed for %s",
95		       ALLROUTER);
96		return(-1);
97	}
98
99	if ((rssock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
100		warnmsg(LOG_ERR, __FUNCTION__, "socket: %s", strerror(errno));
101		return(-1);
102	}
103
104	/* specify to tell receiving interface */
105	on = 1;
106#ifdef IPV6_RECVPKTINFO
107	if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
108		       sizeof(on)) < 0) {
109		warnmsg(LOG_ERR, __FUNCTION__, "IPV6_RECVPKTINFO: %s",
110		       strerror(errno));
111		exit(1);
112	}
113#else  /* old adv. API */
114	if (setsockopt(rssock, IPPROTO_IPV6, IPV6_PKTINFO, &on,
115		       sizeof(on)) < 0) {
116		warnmsg(LOG_ERR, __FUNCTION__, "IPV6_PKTINFO: %s",
117		       strerror(errno));
118		exit(1);
119	}
120#endif
121
122	on = 1;
123	/* specify to tell value of hoplimit field of received IP6 hdr */
124#ifdef IPV6_RECVHOPLIMIT
125	if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
126		       sizeof(on)) < 0) {
127		warnmsg(LOG_ERR, __FUNCTION__, "IPV6_RECVHOPLIMIT: %s",
128		       strerror(errno));
129		exit(1);
130	}
131#else  /* old adv. API */
132	if (setsockopt(rssock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on,
133		       sizeof(on)) < 0) {
134		warnmsg(LOG_ERR, __FUNCTION__, "IPV6_HOPLIMIT: %s",
135		       strerror(errno));
136		exit(1);
137	}
138#endif
139
140	/* specfiy to accept only router advertisements on the socket */
141	ICMP6_FILTER_SETBLOCKALL(&filt);
142	ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
143	if (setsockopt(rssock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
144		       sizeof(filt)) == -1) {
145		warnmsg(LOG_ERR, __FUNCTION__, "setsockopt(ICMP6_FILTER): %s",
146		       strerror(errno));
147		return(-1);
148	}
149
150	/* initialize msghdr for receiving packets */
151	rcviov[0].iov_base = (caddr_t)answer;
152	rcviov[0].iov_len = sizeof(answer);
153	rcvmhdr.msg_name = (caddr_t)&from;
154	rcvmhdr.msg_namelen = sizeof(from);
155	rcvmhdr.msg_iov = rcviov;
156	rcvmhdr.msg_iovlen = 1;
157	rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf;
158	rcvmhdr.msg_controllen = rcvcmsglen;
159
160	/* initialize msghdr for sending packets */
161	sndmhdr.msg_namelen = sizeof(struct sockaddr_in6);
162	sndmhdr.msg_iov = sndiov;
163	sndmhdr.msg_iovlen = 1;
164	sndmhdr.msg_control = (caddr_t)sndcmsgbuf;
165	sndmhdr.msg_controllen = sndcmsglen;
166
167	return(rssock);
168}
169
170void
171sendpacket(struct ifinfo *ifinfo)
172{
173	int i;
174	struct cmsghdr *cm;
175	struct in6_pktinfo *pi;
176
177	sndmhdr.msg_name = (caddr_t)&sin6_allrouters;
178	sndmhdr.msg_iov[0].iov_base = (caddr_t)ifinfo->rs_data;
179	sndmhdr.msg_iov[0].iov_len = ifinfo->rs_datalen;
180
181	cm = CMSG_FIRSTHDR(&sndmhdr);
182	/* specify the outgoing interface */
183	cm->cmsg_level = IPPROTO_IPV6;
184	cm->cmsg_type = IPV6_PKTINFO;
185	cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
186	pi = (struct in6_pktinfo *)CMSG_DATA(cm);
187	memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr));	/*XXX*/
188	pi->ipi6_ifindex = ifinfo->sdl->sdl_index;
189
190	/* specify the hop limit of the packet */
191	{
192		int hoplimit = 255;
193
194		cm = CMSG_NXTHDR(&sndmhdr, cm);
195		cm->cmsg_level = IPPROTO_IPV6;
196		cm->cmsg_type = IPV6_HOPLIMIT;
197		cm->cmsg_len = CMSG_LEN(sizeof(int));
198		memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int));
199	}
200
201	warnmsg(LOG_DEBUG,
202	       __FUNCTION__, "send RS on %s, whose state is %d",
203	       ifinfo->ifname, ifinfo->state);
204
205	i = sendmsg(rssock, &sndmhdr, 0);
206
207	if (i < 0 || i != ifinfo->rs_datalen) {
208		/*
209		 * ENETDOWN is not so serious, especially when using several
210		 * network cards on a mobile node. We ignore it.
211		 */
212		if (errno != ENETDOWN || dflag > 0)
213			warnmsg(LOG_ERR, __FUNCTION__, "sendmsg on %s: %s",
214				ifinfo->ifname, strerror(errno));
215	}
216
217	/* update counter */
218	ifinfo->probes++;
219}
220
221void
222rtsol_input(int s)
223{
224	int i;
225	int *hlimp = NULL;
226	struct icmp6_hdr *icp;
227	int ifindex = 0;
228	struct cmsghdr *cm;
229	struct in6_pktinfo *pi = NULL;
230	struct ifinfo *ifi = NULL;
231	u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
232
233	/* get message */
234	if ((i = recvmsg(s, &rcvmhdr, 0)) < 0) {
235		warnmsg(LOG_ERR, __FUNCTION__, "recvmsg: %s", strerror(errno));
236		return;
237	}
238
239	/* extract optional information via Advanced API */
240	for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr);
241	     cm;
242	     cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) {
243		if (cm->cmsg_level == IPPROTO_IPV6 &&
244		    cm->cmsg_type == IPV6_PKTINFO &&
245		    cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
246			pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
247			ifindex = pi->ipi6_ifindex;
248		}
249		if (cm->cmsg_level == IPPROTO_IPV6 &&
250		    cm->cmsg_type == IPV6_HOPLIMIT &&
251		    cm->cmsg_len == CMSG_LEN(sizeof(int)))
252			hlimp = (int *)CMSG_DATA(cm);
253	}
254
255	if (ifindex == 0) {
256		warnmsg(LOG_ERR,
257		       __FUNCTION__, "failed to get receiving interface");
258		return;
259	}
260	if (hlimp == NULL) {
261		warnmsg(LOG_ERR,
262		       __FUNCTION__, "failed to get receiving hop limit");
263		return;
264	}
265
266	if (i < sizeof(struct nd_router_advert)) {
267		warnmsg(LOG_ERR,
268		       __FUNCTION__, "packet size(%d) is too short", i);
269		return;
270	}
271
272	icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base;
273
274	if (icp->icmp6_type != ND_ROUTER_ADVERT) {
275		warnmsg(LOG_ERR, __FUNCTION__,
276			"invalid icmp type(%d) from %s on %s", icp->icmp6_type,
277		       inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
278				 INET6_ADDRSTRLEN),
279		       if_indextoname(pi->ipi6_ifindex, ifnamebuf));
280		return;
281	}
282
283	if (icp->icmp6_code != 0) {
284		warnmsg(LOG_ERR, __FUNCTION__,
285			"invalid icmp code(%d) from %s on %s", icp->icmp6_code,
286		       inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
287				 INET6_ADDRSTRLEN),
288		       if_indextoname(pi->ipi6_ifindex, ifnamebuf));
289		return;
290	}
291
292	if (*hlimp != 255) {
293		warnmsg(LOG_NOTICE, __FUNCTION__,
294			"invalid RA with hop limit(%d) from %s on %s",
295		       *hlimp,
296		       inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
297				 INET6_ADDRSTRLEN),
298		       if_indextoname(pi->ipi6_ifindex, ifnamebuf));
299		return;
300	}
301
302	if (pi && !IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) {
303		warnmsg(LOG_NOTICE, __FUNCTION__,
304			"invalid RA with non link-local source from %s on %s",
305		       inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
306				 INET6_ADDRSTRLEN),
307		       if_indextoname(pi->ipi6_ifindex, ifnamebuf));
308		return;
309	}
310
311	/* xxx: more validation? */
312
313	if ((ifi = find_ifinfo(pi->ipi6_ifindex)) == NULL) {
314		warnmsg(LOG_NOTICE, __FUNCTION__,
315			"received RA from %s on an unexpeced IF(%s)",
316		       inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
317				 INET6_ADDRSTRLEN),
318		       if_indextoname(pi->ipi6_ifindex, ifnamebuf));
319		return;
320	}
321
322	warnmsg(LOG_DEBUG, __FUNCTION__,
323		"received RA from %s on %s, state is %d",
324	       inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
325			 INET6_ADDRSTRLEN),
326	       ifi->ifname, ifi->state);
327
328	ifi->racnt++;
329
330	switch(ifi->state) {
331	 case IFS_IDLE:		/* should be ignored */
332	 case IFS_DELAY:		/* right? */
333		 break;
334	 case IFS_PROBE:
335		 ifi->state = IFS_IDLE;
336		 ifi->probes = 0;
337		 rtsol_timer_update(ifi);
338		 break;
339	}
340}
341