rtsol.c revision 204407
1145519Sdarrenr/*	$KAME: rtsol.c,v 1.27 2003/10/05 00:09:36 itojun Exp $	*/
2145510Sdarrenr
322514Sdarrenr/*
4255332Scy * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
522514Sdarrenr * All rights reserved.
680486Sdarrenr *
7255332Scy * Redistribution and use in source and binary forms, with or without
822514Sdarrenr * modification, are permitted provided that the following conditions
922514Sdarrenr * are met:
1026119Sdarrenr * 1. Redistributions of source code must retain the above copyright
1126119Sdarrenr *    notice, this list of conditions and the following disclaimer.
1226119Sdarrenr * 2. Redistributions in binary form must reproduce the above copyright
1324583Sdarrenr *    notice, this list of conditions and the following disclaimer in the
1424583Sdarrenr *    documentation and/or other materials provided with the distribution.
1524583Sdarrenr * 3. Neither the name of the project nor the names of its contributors
1624583Sdarrenr *    may be used to endorse or promote products derived from this software
1724583Sdarrenr *    without specific prior written permission.
1824583Sdarrenr *
1924583Sdarrenr * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2092686Sdarrenr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2124583Sdarrenr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2253024Sguido * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2322514Sdarrenr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2453024Sguido * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2553024Sguido * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2653024Sguido * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2724583Sdarrenr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2853024Sguido * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2953024Sguido * SUCH DAMAGE.
3053024Sguido *
3153024Sguido * $FreeBSD: head/usr.sbin/rtsold/rtsol.c 204407 2010-02-27 10:19:39Z uqs $
3253024Sguido */
3353024Sguido
3426119Sdarrenr#include <sys/param.h>
35#include <sys/socket.h>
36#include <sys/uio.h>
37#include <sys/time.h>
38#include <sys/queue.h>
39#include <sys/wait.h>
40#include <sys/stat.h>
41
42#include <net/if.h>
43#include <net/route.h>
44#include <net/if_dl.h>
45
46#include <netinet/in.h>
47#include <netinet/ip6.h>
48#include <netinet6/ip6_var.h>
49#include <netinet/icmp6.h>
50
51#include <arpa/inet.h>
52
53#include <time.h>
54#include <fcntl.h>
55#include <unistd.h>
56#include <stdio.h>
57#include <err.h>
58#include <errno.h>
59#include <string.h>
60#include <stdlib.h>
61#include <syslog.h>
62#include "rtsold.h"
63
64#define ALLROUTER "ff02::2"
65
66static struct msghdr rcvmhdr;
67static struct msghdr sndmhdr;
68static struct iovec rcviov[2];
69static struct iovec sndiov[2];
70static struct sockaddr_in6 from;
71static int rcvcmsglen;
72
73int rssock;
74
75static struct sockaddr_in6 sin6_allrouters = {
76	.sin6_len =	sizeof(sin6_allrouters),
77	.sin6_family =	AF_INET6,
78};
79
80static void call_script(char *, char *);
81static int safefile(const char *);
82
83int
84sockopen(void)
85{
86	static u_char *rcvcmsgbuf = NULL, *sndcmsgbuf = NULL;
87	int sndcmsglen, on;
88	static u_char answer[1500];
89	struct icmp6_filter filt;
90
91	sndcmsglen = rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
92	    CMSG_SPACE(sizeof(int));
93	if (rcvcmsgbuf == NULL && (rcvcmsgbuf = malloc(rcvcmsglen)) == NULL) {
94		warnmsg(LOG_ERR, __func__,
95		    "malloc for receive msghdr failed");
96		return(-1);
97	}
98	if (sndcmsgbuf == NULL && (sndcmsgbuf = malloc(sndcmsglen)) == NULL) {
99		warnmsg(LOG_ERR, __func__,
100		    "malloc for send msghdr failed");
101		return(-1);
102	}
103	memset(&sin6_allrouters, 0, sizeof(struct sockaddr_in6));
104	sin6_allrouters.sin6_family = AF_INET6;
105	sin6_allrouters.sin6_len = sizeof(sin6_allrouters);
106	if (inet_pton(AF_INET6, ALLROUTER,
107	    &sin6_allrouters.sin6_addr.s6_addr) != 1) {
108		warnmsg(LOG_ERR, __func__, "inet_pton failed for %s",
109		    ALLROUTER);
110		return(-1);
111	}
112
113	if ((rssock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
114		warnmsg(LOG_ERR, __func__, "socket: %s", strerror(errno));
115		return(-1);
116	}
117
118	/* specify to tell receiving interface */
119	on = 1;
120#ifdef IPV6_RECVPKTINFO
121	if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
122	    sizeof(on)) < 0) {
123		warnmsg(LOG_ERR, __func__, "IPV6_RECVPKTINFO: %s",
124		    strerror(errno));
125		exit(1);
126	}
127#else  /* old adv. API */
128	if (setsockopt(rssock, IPPROTO_IPV6, IPV6_PKTINFO, &on,
129	    sizeof(on)) < 0) {
130		warnmsg(LOG_ERR, __func__, "IPV6_PKTINFO: %s",
131		    strerror(errno));
132		exit(1);
133	}
134#endif
135
136	on = 1;
137	/* specify to tell value of hoplimit field of received IP6 hdr */
138#ifdef IPV6_RECVHOPLIMIT
139	if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
140	    sizeof(on)) < 0) {
141		warnmsg(LOG_ERR, __func__, "IPV6_RECVHOPLIMIT: %s",
142		    strerror(errno));
143		exit(1);
144	}
145#else  /* old adv. API */
146	if (setsockopt(rssock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on,
147	    sizeof(on)) < 0) {
148		warnmsg(LOG_ERR, __func__, "IPV6_HOPLIMIT: %s",
149		    strerror(errno));
150		exit(1);
151	}
152#endif
153
154	/* specfiy to accept only router advertisements on the socket */
155	ICMP6_FILTER_SETBLOCKALL(&filt);
156	ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
157	if (setsockopt(rssock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
158	    sizeof(filt)) == -1) {
159		warnmsg(LOG_ERR, __func__, "setsockopt(ICMP6_FILTER): %s",
160		    strerror(errno));
161		return(-1);
162	}
163
164	/* initialize msghdr for receiving packets */
165	rcviov[0].iov_base = (caddr_t)answer;
166	rcviov[0].iov_len = sizeof(answer);
167	rcvmhdr.msg_name = (caddr_t)&from;
168	rcvmhdr.msg_iov = rcviov;
169	rcvmhdr.msg_iovlen = 1;
170	rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf;
171
172	/* initialize msghdr for sending packets */
173	sndmhdr.msg_namelen = sizeof(struct sockaddr_in6);
174	sndmhdr.msg_iov = sndiov;
175	sndmhdr.msg_iovlen = 1;
176	sndmhdr.msg_control = (caddr_t)sndcmsgbuf;
177	sndmhdr.msg_controllen = sndcmsglen;
178
179	return(rssock);
180}
181
182void
183sendpacket(struct ifinfo *ifinfo)
184{
185	struct in6_pktinfo *pi;
186	struct cmsghdr *cm;
187	int hoplimit = 255;
188	ssize_t i;
189	struct sockaddr_in6 dst;
190
191	dst = sin6_allrouters;
192	dst.sin6_scope_id = ifinfo->linkid;
193
194	sndmhdr.msg_name = (caddr_t)&dst;
195	sndmhdr.msg_iov[0].iov_base = (caddr_t)ifinfo->rs_data;
196	sndmhdr.msg_iov[0].iov_len = ifinfo->rs_datalen;
197
198	cm = CMSG_FIRSTHDR(&sndmhdr);
199	/* specify the outgoing interface */
200	cm->cmsg_level = IPPROTO_IPV6;
201	cm->cmsg_type = IPV6_PKTINFO;
202	cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
203	pi = (struct in6_pktinfo *)CMSG_DATA(cm);
204	memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr));	/*XXX*/
205	pi->ipi6_ifindex = ifinfo->sdl->sdl_index;
206
207	/* specify the hop limit of the packet */
208	cm = CMSG_NXTHDR(&sndmhdr, cm);
209	cm->cmsg_level = IPPROTO_IPV6;
210	cm->cmsg_type = IPV6_HOPLIMIT;
211	cm->cmsg_len = CMSG_LEN(sizeof(int));
212	memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int));
213
214	warnmsg(LOG_DEBUG, __func__,
215	    "send RS on %s, whose state is %d",
216	    ifinfo->ifname, ifinfo->state);
217	i = sendmsg(rssock, &sndmhdr, 0);
218	if (i < 0 || (size_t)i != ifinfo->rs_datalen) {
219		/*
220		 * ENETDOWN is not so serious, especially when using several
221		 * network cards on a mobile node. We ignore it.
222		 */
223		if (errno != ENETDOWN || dflag > 0)
224			warnmsg(LOG_ERR, __func__, "sendmsg on %s: %s",
225			    ifinfo->ifname, strerror(errno));
226	}
227
228	/* update counter */
229	ifinfo->probes++;
230}
231
232void
233rtsol_input(int s)
234{
235	u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
236	int ifindex = 0, *hlimp = NULL;
237	ssize_t i;
238	struct in6_pktinfo *pi = NULL;
239	struct ifinfo *ifi = NULL;
240	struct icmp6_hdr *icp;
241	struct nd_router_advert *nd_ra;
242	struct cmsghdr *cm;
243
244	/* get message.  namelen and controllen must always be initialized. */
245	rcvmhdr.msg_namelen = sizeof(from);
246	rcvmhdr.msg_controllen = rcvcmsglen;
247	if ((i = recvmsg(s, &rcvmhdr, 0)) < 0) {
248		warnmsg(LOG_ERR, __func__, "recvmsg: %s", strerror(errno));
249		return;
250	}
251
252	/* extract optional information via Advanced API */
253	for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr); cm;
254	    cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) {
255		if (cm->cmsg_level == IPPROTO_IPV6 &&
256		    cm->cmsg_type == IPV6_PKTINFO &&
257		    cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
258			pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
259			ifindex = pi->ipi6_ifindex;
260		}
261		if (cm->cmsg_level == IPPROTO_IPV6 &&
262		    cm->cmsg_type == IPV6_HOPLIMIT &&
263		    cm->cmsg_len == CMSG_LEN(sizeof(int)))
264			hlimp = (int *)CMSG_DATA(cm);
265	}
266
267	if (ifindex == 0) {
268		warnmsg(LOG_ERR, __func__,
269		    "failed to get receiving interface");
270		return;
271	}
272	if (hlimp == NULL) {
273		warnmsg(LOG_ERR, __func__,
274		    "failed to get receiving hop limit");
275		return;
276	}
277
278	if ((size_t)i < sizeof(struct nd_router_advert)) {
279		warnmsg(LOG_INFO, __func__,
280		    "packet size(%zd) is too short", i);
281		return;
282	}
283
284	icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base;
285
286	if (icp->icmp6_type != ND_ROUTER_ADVERT) {
287		/*
288		 * this should not happen because we configured a filter
289		 * that only passes RAs on the receiving socket.
290		 */
291		warnmsg(LOG_ERR, __func__,
292		    "invalid icmp type(%d) from %s on %s", icp->icmp6_type,
293		    inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
294		    INET6_ADDRSTRLEN),
295		    if_indextoname(pi->ipi6_ifindex, ifnamebuf));
296		return;
297	}
298
299	if (icp->icmp6_code != 0) {
300		warnmsg(LOG_INFO, __func__,
301		    "invalid icmp code(%d) from %s on %s", icp->icmp6_code,
302		    inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
303		    INET6_ADDRSTRLEN),
304		    if_indextoname(pi->ipi6_ifindex, ifnamebuf));
305		return;
306	}
307
308	if (*hlimp != 255) {
309		warnmsg(LOG_INFO, __func__,
310		    "invalid RA with hop limit(%d) from %s on %s",
311		    *hlimp,
312		    inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
313		    INET6_ADDRSTRLEN),
314		    if_indextoname(pi->ipi6_ifindex, ifnamebuf));
315		return;
316	}
317
318	if (pi && !IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) {
319		warnmsg(LOG_INFO, __func__,
320		    "invalid RA with non link-local source from %s on %s",
321		    inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
322		    INET6_ADDRSTRLEN),
323		    if_indextoname(pi->ipi6_ifindex, ifnamebuf));
324		return;
325	}
326
327	/* xxx: more validation? */
328
329	if ((ifi = find_ifinfo(pi->ipi6_ifindex)) == NULL) {
330		warnmsg(LOG_INFO, __func__,
331		    "received RA from %s on an unexpected IF(%s)",
332		    inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
333		    INET6_ADDRSTRLEN),
334		    if_indextoname(pi->ipi6_ifindex, ifnamebuf));
335		return;
336	}
337
338	warnmsg(LOG_DEBUG, __func__,
339	    "received RA from %s on %s, state is %d",
340	    inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, INET6_ADDRSTRLEN),
341	    ifi->ifname, ifi->state);
342
343	nd_ra = (struct nd_router_advert *)icp;
344
345	/*
346	 * Process the "O bit."
347	 * If the value of OtherConfigFlag changes from FALSE to TRUE, the
348	 * host should invoke the stateful autoconfiguration protocol,
349	 * requesting information.
350	 * [RFC 2462 Section 5.5.3]
351	 */
352	if (((nd_ra->nd_ra_flags_reserved) & ND_RA_FLAG_OTHER) &&
353	    !ifi->otherconfig) {
354		warnmsg(LOG_DEBUG, __func__,
355		    "OtherConfigFlag on %s is turned on", ifi->ifname);
356		ifi->otherconfig = 1;
357		call_script(otherconf_script, ifi->ifname);
358	}
359
360	ifi->racnt++;
361
362	switch (ifi->state) {
363	case IFS_IDLE:		/* should be ignored */
364	case IFS_DELAY:		/* right? */
365		break;
366	case IFS_PROBE:
367		ifi->state = IFS_IDLE;
368		ifi->probes = 0;
369		rtsol_timer_update(ifi);
370		break;
371	}
372}
373
374static void
375call_script(char *scriptpath, char *ifname)
376{
377	pid_t pid, wpid;
378
379	if (scriptpath == NULL)
380		return;
381
382	/* launch the script */
383	pid = fork();
384	if (pid < 0) {
385		warnmsg(LOG_ERR, __func__,
386		    "failed to fork: %s", strerror(errno));
387		return;
388	} else if (pid) {
389		int wstatus;
390
391		do {
392			wpid = wait(&wstatus);
393		} while (wpid != pid && wpid > 0);
394
395		if (wpid < 0)
396			warnmsg(LOG_ERR, __func__,
397			    "wait: %s", strerror(errno));
398		else {
399			warnmsg(LOG_DEBUG, __func__,
400			    "script \"%s\" terminated", scriptpath);
401		}
402	} else {
403		char *argv[3];
404		int fd;
405
406		argv[0] = scriptpath;
407		argv[1] = ifname;
408		argv[2] = NULL;
409
410		if (safefile(scriptpath)) {
411			warnmsg(LOG_ERR, __func__,
412			    "script \"%s\" cannot be executed safely",
413			    scriptpath);
414			exit(1);
415		}
416
417		if ((fd = open("/dev/null", O_RDWR)) != -1) {
418			dup2(fd, STDIN_FILENO);
419			dup2(fd, STDOUT_FILENO);
420			dup2(fd, STDERR_FILENO);
421			if (fd > STDERR_FILENO)
422				close(fd);
423		}
424
425		execv(scriptpath, argv);
426
427		warnmsg(LOG_ERR, __func__, "child: exec failed: %s",
428		    strerror(errno));
429		exit(0);
430	}
431
432	return;
433}
434
435static int
436safefile(const char *path)
437{
438	struct stat s;
439	uid_t myuid;
440
441	/* no setuid */
442	if (getuid() != geteuid()) {
443		warnmsg(LOG_NOTICE, __func__,
444		    "setuid'ed execution not allowed\n");
445		return (-1);
446	}
447
448	if (lstat(path, &s) != 0) {
449		warnmsg(LOG_NOTICE, __func__, "lstat failed: %s",
450		    strerror(errno));
451		return (-1);
452	}
453
454	/* the file must be owned by the running uid */
455	myuid = getuid();
456	if (s.st_uid != myuid) {
457		warnmsg(LOG_NOTICE, __func__,
458		    "%s has invalid owner uid\n", path);
459		return (-1);
460	}
461
462	switch (s.st_mode & S_IFMT) {
463	case S_IFREG:
464		break;
465	default:
466		warnmsg(LOG_NOTICE, __func__,
467		    "%s is an invalid file type 0x%o\n",
468		    path, (s.st_mode & S_IFMT));
469		return (-1);
470	}
471
472	return (0);
473}
474