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