rtsol.c revision 118661
195043Ssuz/* $KAME: rtsol.c,v 1.12 2001/11/12 11:47:11 jinmei Exp $ */ 266776Skris 355163Sshin/* 455163Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 555163Sshin * All rights reserved. 662632Skris * 755163Sshin * Redistribution and use in source and binary forms, with or without 855163Sshin * modification, are permitted provided that the following conditions 955163Sshin * are met: 1055163Sshin * 1. Redistributions of source code must retain the above copyright 1155163Sshin * notice, this list of conditions and the following disclaimer. 1255163Sshin * 2. Redistributions in binary form must reproduce the above copyright 1355163Sshin * notice, this list of conditions and the following disclaimer in the 1455163Sshin * documentation and/or other materials provided with the distribution. 1555163Sshin * 3. Neither the name of the project nor the names of its contributors 1655163Sshin * may be used to endorse or promote products derived from this software 1755163Sshin * without specific prior written permission. 1862632Skris * 1955163Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 2055163Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2155163Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2255163Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2355163Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2455163Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2555163Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2655163Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2755163Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2855163Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2955163Sshin * SUCH DAMAGE. 3055163Sshin * 3155163Sshin * $FreeBSD: head/usr.sbin/rtsold/rtsol.c 118661 2003-08-08 16:42:37Z ume $ 3255163Sshin */ 3355163Sshin 3455163Sshin#include <sys/param.h> 3555163Sshin#include <sys/socket.h> 3655163Sshin#include <sys/uio.h> 3755163Sshin#include <sys/time.h> 38118661Sume#include <fcntl.h> 3966776Skris#include <sys/queue.h> 40118661Sume#include <sys/wait.h> 41118661Sume#include <sys/stat.h> 4255163Sshin 4355163Sshin#include <net/if.h> 4455163Sshin#include <net/route.h> 4555163Sshin#include <net/if_dl.h> 4655163Sshin 4755163Sshin#include <netinet/in.h> 4855163Sshin#include <netinet/ip6.h> 4955163Sshin#include <netinet6/ip6_var.h> 5055163Sshin#include <netinet/icmp6.h> 5155163Sshin 5255163Sshin#include <arpa/inet.h> 5355163Sshin 5455163Sshin#include <time.h> 5555163Sshin#include <unistd.h> 5655163Sshin#include <stdio.h> 5755163Sshin#include <err.h> 5855163Sshin#include <errno.h> 5955163Sshin#include <string.h> 6055163Sshin#include <stdlib.h> 6155163Sshin#include <syslog.h> 6255163Sshin#include "rtsold.h" 6355163Sshin 6462632Skris#define ALLROUTER "ff02::2" 6555163Sshin 6655163Sshinstatic struct msghdr rcvmhdr; 6755163Sshinstatic struct msghdr sndmhdr; 6855163Sshinstatic struct iovec rcviov[2]; 6955163Sshinstatic struct iovec sndiov[2]; 7055163Sshinstatic struct sockaddr_in6 from; 7155163Sshin 7262632Skrisint rssock; 7355163Sshin 7455163Sshinstatic struct sockaddr_in6 sin6_allrouters = {sizeof(sin6_allrouters), AF_INET6}; 7555163Sshin 76118661Sumestatic void call_script __P((char *, char *)); 77118661Sumestatic int safefile __P((const char *)); 78118661Sume 7955163Sshinint 8055163Sshinsockopen() 8155163Sshin{ 8255163Sshin int on; 8355163Sshin struct icmp6_filter filt; 8455163Sshin static u_char answer[1500]; 8562632Skris int rcvcmsglen, sndcmsglen; 8662632Skris static u_char *rcvcmsgbuf = NULL, *sndcmsgbuf = NULL; 8755163Sshin 8862632Skris sndcmsglen = rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + 8962632Skris CMSG_SPACE(sizeof(int)); 9062632Skris if (rcvcmsgbuf == NULL && (rcvcmsgbuf = malloc(rcvcmsglen)) == NULL) { 91118660Sume warnmsg(LOG_ERR, __func__, 9262632Skris "malloc for receive msghdr failed"); 9362632Skris return(-1); 9462632Skris } 9562632Skris if (sndcmsgbuf == NULL && (sndcmsgbuf = malloc(sndcmsglen)) == NULL) { 96118660Sume warnmsg(LOG_ERR, __func__, 9762632Skris "malloc for send msghdr failed"); 9862632Skris return(-1); 9962632Skris } 10055163Sshin memset(&sin6_allrouters, 0, sizeof(struct sockaddr_in6)); 10195043Ssuz sin6_allrouters.sin6_family = AF_INET6; 10295043Ssuz sin6_allrouters.sin6_len = sizeof(sin6_allrouters); 10355163Sshin if (inet_pton(AF_INET6, ALLROUTER, 10455163Sshin &sin6_allrouters.sin6_addr.s6_addr) != 1) { 105118660Sume warnmsg(LOG_ERR, __func__, "inet_pton failed for %s", 10655163Sshin ALLROUTER); 10755163Sshin return(-1); 10855163Sshin } 10955163Sshin 11055163Sshin if ((rssock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { 111118660Sume warnmsg(LOG_ERR, __func__, "socket: %s", strerror(errno)); 11255163Sshin return(-1); 11355163Sshin } 11455163Sshin 11555163Sshin /* specify to tell receiving interface */ 11655163Sshin on = 1; 11762632Skris#ifdef IPV6_RECVPKTINFO 11862632Skris if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, 11962632Skris sizeof(on)) < 0) { 120118660Sume warnmsg(LOG_ERR, __func__, "IPV6_RECVPKTINFO: %s", 12162632Skris strerror(errno)); 12262632Skris exit(1); 12362632Skris } 12462632Skris#else /* old adv. API */ 12555163Sshin if (setsockopt(rssock, IPPROTO_IPV6, IPV6_PKTINFO, &on, 12655163Sshin sizeof(on)) < 0) { 127118660Sume warnmsg(LOG_ERR, __func__, "IPV6_PKTINFO: %s", 12855163Sshin strerror(errno)); 12955163Sshin exit(1); 13055163Sshin } 13162632Skris#endif 13255163Sshin 13355163Sshin on = 1; 13455163Sshin /* specify to tell value of hoplimit field of received IP6 hdr */ 13562632Skris#ifdef IPV6_RECVHOPLIMIT 13662632Skris if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, 13762632Skris sizeof(on)) < 0) { 138118660Sume warnmsg(LOG_ERR, __func__, "IPV6_RECVHOPLIMIT: %s", 13962632Skris strerror(errno)); 14062632Skris exit(1); 14162632Skris } 14262632Skris#else /* old adv. API */ 14355163Sshin if (setsockopt(rssock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, 14455163Sshin sizeof(on)) < 0) { 145118660Sume warnmsg(LOG_ERR, __func__, "IPV6_HOPLIMIT: %s", 14655163Sshin strerror(errno)); 14755163Sshin exit(1); 14855163Sshin } 14962632Skris#endif 15055163Sshin 15155163Sshin /* specfiy to accept only router advertisements on the socket */ 15255163Sshin ICMP6_FILTER_SETBLOCKALL(&filt); 15355163Sshin ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt); 15455163Sshin if (setsockopt(rssock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, 15555163Sshin sizeof(filt)) == -1) { 156118660Sume warnmsg(LOG_ERR, __func__, "setsockopt(ICMP6_FILTER): %s", 15755163Sshin strerror(errno)); 15855163Sshin return(-1); 15955163Sshin } 16055163Sshin 16155163Sshin /* initialize msghdr for receiving packets */ 16255163Sshin rcviov[0].iov_base = (caddr_t)answer; 16355163Sshin rcviov[0].iov_len = sizeof(answer); 16455163Sshin rcvmhdr.msg_name = (caddr_t)&from; 16555163Sshin rcvmhdr.msg_namelen = sizeof(from); 16655163Sshin rcvmhdr.msg_iov = rcviov; 16755163Sshin rcvmhdr.msg_iovlen = 1; 16855163Sshin rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf; 16962632Skris rcvmhdr.msg_controllen = rcvcmsglen; 17055163Sshin 17155163Sshin /* initialize msghdr for sending packets */ 17255163Sshin sndmhdr.msg_namelen = sizeof(struct sockaddr_in6); 17355163Sshin sndmhdr.msg_iov = sndiov; 17455163Sshin sndmhdr.msg_iovlen = 1; 17555163Sshin sndmhdr.msg_control = (caddr_t)sndcmsgbuf; 17662632Skris sndmhdr.msg_controllen = sndcmsglen; 17755163Sshin 17855163Sshin return(rssock); 17955163Sshin} 18055163Sshin 18155163Sshinvoid 18255163Sshinsendpacket(struct ifinfo *ifinfo) 18355163Sshin{ 18455163Sshin int i; 18555163Sshin struct cmsghdr *cm; 18655163Sshin struct in6_pktinfo *pi; 18755163Sshin 18855163Sshin sndmhdr.msg_name = (caddr_t)&sin6_allrouters; 18955163Sshin sndmhdr.msg_iov[0].iov_base = (caddr_t)ifinfo->rs_data; 19055163Sshin sndmhdr.msg_iov[0].iov_len = ifinfo->rs_datalen; 19155163Sshin 19255163Sshin cm = CMSG_FIRSTHDR(&sndmhdr); 19355163Sshin /* specify the outgoing interface */ 19455163Sshin cm->cmsg_level = IPPROTO_IPV6; 19555163Sshin cm->cmsg_type = IPV6_PKTINFO; 19655163Sshin cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); 19755163Sshin pi = (struct in6_pktinfo *)CMSG_DATA(cm); 19855163Sshin memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/ 19955163Sshin pi->ipi6_ifindex = ifinfo->sdl->sdl_index; 20055163Sshin 20155163Sshin /* specify the hop limit of the packet */ 20255163Sshin { 20355163Sshin int hoplimit = 255; 20455163Sshin 20555163Sshin cm = CMSG_NXTHDR(&sndmhdr, cm); 20655163Sshin cm->cmsg_level = IPPROTO_IPV6; 20755163Sshin cm->cmsg_type = IPV6_HOPLIMIT; 20855163Sshin cm->cmsg_len = CMSG_LEN(sizeof(int)); 20955163Sshin memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int)); 21055163Sshin } 21155163Sshin 21255163Sshin warnmsg(LOG_DEBUG, 213118660Sume __func__, "send RS on %s, whose state is %d", 21455163Sshin ifinfo->ifname, ifinfo->state); 21555163Sshin 21655163Sshin i = sendmsg(rssock, &sndmhdr, 0); 21755163Sshin 21855163Sshin if (i < 0 || i != ifinfo->rs_datalen) { 21955163Sshin /* 22055163Sshin * ENETDOWN is not so serious, especially when using several 22155163Sshin * network cards on a mobile node. We ignore it. 22255163Sshin */ 22355163Sshin if (errno != ENETDOWN || dflag > 0) 224118660Sume warnmsg(LOG_ERR, __func__, "sendmsg on %s: %s", 22555163Sshin ifinfo->ifname, strerror(errno)); 22655163Sshin } 22755163Sshin 22855163Sshin /* update counter */ 22955163Sshin ifinfo->probes++; 23055163Sshin} 23155163Sshin 23255163Sshinvoid 23355163Sshinrtsol_input(int s) 23455163Sshin{ 23555163Sshin int i; 23655163Sshin int *hlimp = NULL; 23755163Sshin struct icmp6_hdr *icp; 23855163Sshin int ifindex = 0; 23955163Sshin struct cmsghdr *cm; 24055163Sshin struct in6_pktinfo *pi = NULL; 24155163Sshin struct ifinfo *ifi = NULL; 242118661Sume struct nd_router_advert *nd_ra; 24355163Sshin u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; 24455163Sshin 24555163Sshin /* get message */ 24655163Sshin if ((i = recvmsg(s, &rcvmhdr, 0)) < 0) { 247118660Sume warnmsg(LOG_ERR, __func__, "recvmsg: %s", strerror(errno)); 24855163Sshin return; 24955163Sshin } 25055163Sshin 25155163Sshin /* extract optional information via Advanced API */ 25255163Sshin for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr); 25355163Sshin cm; 25455163Sshin cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) { 25555163Sshin if (cm->cmsg_level == IPPROTO_IPV6 && 25655163Sshin cm->cmsg_type == IPV6_PKTINFO && 25755163Sshin cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { 25855163Sshin pi = (struct in6_pktinfo *)(CMSG_DATA(cm)); 25955163Sshin ifindex = pi->ipi6_ifindex; 26055163Sshin } 26155163Sshin if (cm->cmsg_level == IPPROTO_IPV6 && 26255163Sshin cm->cmsg_type == IPV6_HOPLIMIT && 26355163Sshin cm->cmsg_len == CMSG_LEN(sizeof(int))) 26455163Sshin hlimp = (int *)CMSG_DATA(cm); 26555163Sshin } 26655163Sshin 26755163Sshin if (ifindex == 0) { 26855163Sshin warnmsg(LOG_ERR, 269118660Sume __func__, "failed to get receiving interface"); 27055163Sshin return; 27155163Sshin } 27255163Sshin if (hlimp == NULL) { 27355163Sshin warnmsg(LOG_ERR, 274118660Sume __func__, "failed to get receiving hop limit"); 27555163Sshin return; 27655163Sshin } 27755163Sshin 27855163Sshin if (i < sizeof(struct nd_router_advert)) { 27955163Sshin warnmsg(LOG_ERR, 280118660Sume __func__, "packet size(%d) is too short", i); 28155163Sshin return; 28255163Sshin } 28355163Sshin 28455163Sshin icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base; 28555163Sshin 28655163Sshin if (icp->icmp6_type != ND_ROUTER_ADVERT) { 287118660Sume warnmsg(LOG_ERR, __func__, 28855163Sshin "invalid icmp type(%d) from %s on %s", icp->icmp6_type, 28955163Sshin inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, 29055163Sshin INET6_ADDRSTRLEN), 29155163Sshin if_indextoname(pi->ipi6_ifindex, ifnamebuf)); 29255163Sshin return; 29355163Sshin } 29455163Sshin 29555163Sshin if (icp->icmp6_code != 0) { 296118660Sume warnmsg(LOG_ERR, __func__, 29755163Sshin "invalid icmp code(%d) from %s on %s", icp->icmp6_code, 29855163Sshin inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, 29955163Sshin INET6_ADDRSTRLEN), 30055163Sshin if_indextoname(pi->ipi6_ifindex, ifnamebuf)); 30155163Sshin return; 30255163Sshin } 30355163Sshin 30455163Sshin if (*hlimp != 255) { 305118660Sume warnmsg(LOG_NOTICE, __func__, 30655163Sshin "invalid RA with hop limit(%d) from %s on %s", 30755163Sshin *hlimp, 30855163Sshin inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, 30955163Sshin INET6_ADDRSTRLEN), 31055163Sshin if_indextoname(pi->ipi6_ifindex, ifnamebuf)); 31155163Sshin return; 31255163Sshin } 31355163Sshin 31455163Sshin if (pi && !IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) { 315118660Sume warnmsg(LOG_NOTICE, __func__, 31655163Sshin "invalid RA with non link-local source from %s on %s", 31755163Sshin inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, 31855163Sshin INET6_ADDRSTRLEN), 31955163Sshin if_indextoname(pi->ipi6_ifindex, ifnamebuf)); 32055163Sshin return; 32155163Sshin } 32255163Sshin 32355163Sshin /* xxx: more validation? */ 32455163Sshin 32555163Sshin if ((ifi = find_ifinfo(pi->ipi6_ifindex)) == NULL) { 326118660Sume warnmsg(LOG_NOTICE, __func__, 32755163Sshin "received RA from %s on an unexpeced IF(%s)", 32855163Sshin inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, 32955163Sshin INET6_ADDRSTRLEN), 33055163Sshin if_indextoname(pi->ipi6_ifindex, ifnamebuf)); 33155163Sshin return; 33255163Sshin } 33355163Sshin 334118660Sume warnmsg(LOG_DEBUG, __func__, 33555163Sshin "received RA from %s on %s, state is %d", 33655163Sshin inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, 33755163Sshin INET6_ADDRSTRLEN), 33855163Sshin ifi->ifname, ifi->state); 33955163Sshin 340118661Sume nd_ra = (struct nd_router_advert *)icp; 341118661Sume 342118661Sume /* 343118661Sume * Process the "O bit." 344118661Sume * If the value of OtherConfigFlag changes from FALSE to TRUE, the 345118661Sume * host should invoke the stateful autoconfiguration protocol, 346118661Sume * requesting information. 347118661Sume * [RFC 2462 Section 5.5.3] 348118661Sume */ 349118661Sume if (((nd_ra->nd_ra_flags_reserved) & ND_RA_FLAG_OTHER) && 350118661Sume !ifi->otherconfig) { 351118661Sume warnmsg(LOG_DEBUG, __func__, 352118661Sume "OtherConfigFlag on %s is turned on", ifi->ifname); 353118661Sume ifi->otherconfig = 1; 354118661Sume call_script(otherconf_script, ifi->ifname); 355118661Sume } 356118661Sume 35755163Sshin ifi->racnt++; 35855163Sshin 35955163Sshin switch(ifi->state) { 36055163Sshin case IFS_IDLE: /* should be ignored */ 36155163Sshin case IFS_DELAY: /* right? */ 36255163Sshin break; 36355163Sshin case IFS_PROBE: 36455163Sshin ifi->state = IFS_IDLE; 36555163Sshin ifi->probes = 0; 36655163Sshin rtsol_timer_update(ifi); 36755163Sshin break; 36855163Sshin } 36955163Sshin} 370118661Sume 371118661Sumestatic void 372118661Sumecall_script(scriptpath, ifname) 373118661Sume char *scriptpath, *ifname; 374118661Sume{ 375118661Sume pid_t pid, wpid; 376118661Sume 377118661Sume if (scriptpath == NULL) 378118661Sume return; 379118661Sume 380118661Sume /* launch the script */ 381118661Sume pid = fork(); 382118661Sume if (pid < 0) { 383118661Sume warnmsg(LOG_ERR, __func__, 384118661Sume "failed to fork: %s", strerror(errno)); 385118661Sume return; 386118661Sume } else if (pid) { 387118661Sume int wstatus; 388118661Sume 389118661Sume do { 390118661Sume wpid = wait(&wstatus); 391118661Sume } while (wpid != pid && wpid > 0); 392118661Sume 393118661Sume if (wpid < 0) 394118661Sume warnmsg(LOG_ERR, __func__, 395118661Sume "wait: %s", strerror(errno)); 396118661Sume else { 397118661Sume warnmsg(LOG_DEBUG, __func__, 398118661Sume "script \"%s\" terminated", scriptpath); 399118661Sume } 400118661Sume } else { 401118661Sume char *argv[3]; 402118661Sume int fd; 403118661Sume 404118661Sume argv[0] = scriptpath; 405118661Sume argv[1] = ifname; 406118661Sume argv[2] = NULL; 407118661Sume 408118661Sume if (safefile(scriptpath)) { 409118661Sume warnmsg(LOG_ERR, __func__, 410118661Sume "script \"%s\" cannot be executed safely", 411118661Sume scriptpath); 412118661Sume exit(1); 413118661Sume } 414118661Sume 415118661Sume if ((fd = open("/dev/null", O_RDWR)) != -1) { 416118661Sume dup2(fd, STDIN_FILENO); 417118661Sume dup2(fd, STDOUT_FILENO); 418118661Sume dup2(fd, STDERR_FILENO); 419118661Sume if (fd > STDERR_FILENO) 420118661Sume close(fd); 421118661Sume } 422118661Sume 423118661Sume execv(scriptpath, argv); 424118661Sume 425118661Sume warnmsg(LOG_ERR, __func__, "child: exec failed: %s", 426118661Sume strerror(errno)); 427118661Sume exit(0); 428118661Sume } 429118661Sume 430118661Sume return; 431118661Sume} 432118661Sume 433118661Sumestatic int 434118661Sumesafefile(path) 435118661Sume const char *path; 436118661Sume{ 437118661Sume struct stat s; 438118661Sume uid_t myuid; 439118661Sume 440118661Sume /* no setuid */ 441118661Sume if (getuid() != geteuid()) { 442118661Sume warnmsg(LOG_NOTICE, __func__, 443118661Sume "setuid'ed execution not allowed\n"); 444118661Sume return (-1); 445118661Sume } 446118661Sume 447118661Sume if (lstat(path, &s) != 0) { 448118661Sume warnmsg(LOG_NOTICE, __func__, "lstat failed: %s", 449118661Sume strerror(errno)); 450118661Sume return (-1); 451118661Sume } 452118661Sume 453118661Sume /* the file must be owned by the running uid */ 454118661Sume myuid = getuid(); 455118661Sume if (s.st_uid != myuid) { 456118661Sume warnmsg(LOG_NOTICE, __func__, 457118661Sume "%s has invalid owner uid\n", path); 458118661Sume return (-1); 459118661Sume } 460118661Sume 461118661Sume switch (s.st_mode & S_IFMT) { 462118661Sume case S_IFREG: 463118661Sume break; 464118661Sume default: 465118661Sume warnmsg(LOG_NOTICE, __func__, 466118661Sume "%s is an invalid file type 0x%o\n", 467118661Sume path, (s.st_mode & S_IFMT)); 468118661Sume return (-1); 469118661Sume } 470118661Sume 471118661Sume return (0); 472118661Sume} 473