rtsol.c revision 118664
1164190Sjkoshy/* $KAME: rtsol.c,v 1.12 2001/11/12 11:47:11 jinmei Exp $ */ 2164190Sjkoshy 3164190Sjkoshy/* 4164190Sjkoshy * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 5164190Sjkoshy * All rights reserved. 6164190Sjkoshy * 7164190Sjkoshy * Redistribution and use in source and binary forms, with or without 8164190Sjkoshy * modification, are permitted provided that the following conditions 9164190Sjkoshy * are met: 10164190Sjkoshy * 1. Redistributions of source code must retain the above copyright 11164190Sjkoshy * notice, this list of conditions and the following disclaimer. 12164190Sjkoshy * 2. Redistributions in binary form must reproduce the above copyright 13164190Sjkoshy * notice, this list of conditions and the following disclaimer in the 14164190Sjkoshy * documentation and/or other materials provided with the distribution. 15164190Sjkoshy * 3. Neither the name of the project nor the names of its contributors 16164190Sjkoshy * may be used to endorse or promote products derived from this software 17164190Sjkoshy * without specific prior written permission. 18164190Sjkoshy * 19164190Sjkoshy * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 20164190Sjkoshy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21164190Sjkoshy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22164190Sjkoshy * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 23164190Sjkoshy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24164190Sjkoshy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25164190Sjkoshy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26164190Sjkoshy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27164190Sjkoshy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28164190Sjkoshy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29164190Sjkoshy * SUCH DAMAGE. 30164190Sjkoshy * 31164190Sjkoshy * $FreeBSD: head/usr.sbin/rtsold/rtsol.c 118664 2003-08-08 16:56:01Z ume $ 32164190Sjkoshy */ 33164190Sjkoshy 34164190Sjkoshy#include <sys/param.h> 35164190Sjkoshy#include <sys/socket.h> 36164190Sjkoshy#include <sys/uio.h> 37164190Sjkoshy#include <sys/time.h> 38164190Sjkoshy#include <fcntl.h> 39164190Sjkoshy#include <sys/queue.h> 40164190Sjkoshy#include <sys/wait.h> 41164190Sjkoshy#include <sys/stat.h> 42164190Sjkoshy 43164190Sjkoshy#include <net/if.h> 44164190Sjkoshy#include <net/route.h> 45164190Sjkoshy#include <net/if_dl.h> 46164190Sjkoshy 47164190Sjkoshy#include <netinet/in.h> 48164190Sjkoshy#include <netinet/ip6.h> 49164190Sjkoshy#include <netinet6/ip6_var.h> 50164190Sjkoshy#include <netinet/icmp6.h> 51164190Sjkoshy 52164190Sjkoshy#include <arpa/inet.h> 53164190Sjkoshy 54164190Sjkoshy#include <time.h> 55164190Sjkoshy#include <unistd.h> 56164190Sjkoshy#include <stdio.h> 57164190Sjkoshy#include <err.h> 58164190Sjkoshy#include <errno.h> 59164190Sjkoshy#include <string.h> 60164190Sjkoshy#include <stdlib.h> 61164190Sjkoshy#include <syslog.h> 62164190Sjkoshy#include "rtsold.h" 63164190Sjkoshy 64164190Sjkoshy#define ALLROUTER "ff02::2" 65164190Sjkoshy 66164190Sjkoshystatic struct msghdr rcvmhdr; 67164190Sjkoshystatic struct msghdr sndmhdr; 68164190Sjkoshystatic struct iovec rcviov[2]; 69164190Sjkoshystatic struct iovec sndiov[2]; 70164190Sjkoshystatic struct sockaddr_in6 from; 71164190Sjkoshy 72164190Sjkoshyint rssock; 73164190Sjkoshy 74165535Sjkoshystatic struct sockaddr_in6 sin6_allrouters = 75164190Sjkoshy{sizeof(sin6_allrouters), AF_INET6}; 76164190Sjkoshy 77164190Sjkoshystatic void call_script __P((char *, char *)); 78164190Sjkoshystatic int safefile __P((const char *)); 79164190Sjkoshy 80164190Sjkoshyint 81164190Sjkoshysockopen() 82164190Sjkoshy{ 83164190Sjkoshy int on; 84164190Sjkoshy struct icmp6_filter filt; 85164190Sjkoshy static u_char answer[1500]; 86164190Sjkoshy int rcvcmsglen, sndcmsglen; 87164190Sjkoshy static u_char *rcvcmsgbuf = NULL, *sndcmsgbuf = NULL; 88164190Sjkoshy 89164190Sjkoshy sndcmsglen = rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + 90164190Sjkoshy CMSG_SPACE(sizeof(int)); 91164190Sjkoshy if (rcvcmsgbuf == NULL && (rcvcmsgbuf = malloc(rcvcmsglen)) == NULL) { 92164190Sjkoshy warnmsg(LOG_ERR, __func__, 93164190Sjkoshy "malloc for receive msghdr failed"); 94164190Sjkoshy return(-1); 95164190Sjkoshy } 96164190Sjkoshy if (sndcmsgbuf == NULL && (sndcmsgbuf = malloc(sndcmsglen)) == NULL) { 97164190Sjkoshy warnmsg(LOG_ERR, __func__, 98164190Sjkoshy "malloc for send msghdr failed"); 99164190Sjkoshy return(-1); 100164190Sjkoshy } 101164190Sjkoshy memset(&sin6_allrouters, 0, sizeof(struct sockaddr_in6)); 102164190Sjkoshy sin6_allrouters.sin6_family = AF_INET6; 103164190Sjkoshy sin6_allrouters.sin6_len = sizeof(sin6_allrouters); 104164190Sjkoshy if (inet_pton(AF_INET6, ALLROUTER, 105164190Sjkoshy &sin6_allrouters.sin6_addr.s6_addr) != 1) { 106164190Sjkoshy warnmsg(LOG_ERR, __func__, "inet_pton failed for %s", 107164190Sjkoshy ALLROUTER); 108164190Sjkoshy return(-1); 109164190Sjkoshy } 110164190Sjkoshy 111165535Sjkoshy if ((rssock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { 112165535Sjkoshy warnmsg(LOG_ERR, __func__, "socket: %s", strerror(errno)); 113165535Sjkoshy return(-1); 114164190Sjkoshy } 115164190Sjkoshy 116164190Sjkoshy /* specify to tell receiving interface */ 117164190Sjkoshy on = 1; 118164190Sjkoshy#ifdef IPV6_RECVPKTINFO 119164190Sjkoshy if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, 120164190Sjkoshy sizeof(on)) < 0) { 121164190Sjkoshy warnmsg(LOG_ERR, __func__, "IPV6_RECVPKTINFO: %s", 122164190Sjkoshy strerror(errno)); 123164190Sjkoshy exit(1); 124164190Sjkoshy } 125164190Sjkoshy#else /* old adv. API */ 126164190Sjkoshy if (setsockopt(rssock, IPPROTO_IPV6, IPV6_PKTINFO, &on, 127164190Sjkoshy sizeof(on)) < 0) { 128164190Sjkoshy warnmsg(LOG_ERR, __func__, "IPV6_PKTINFO: %s", 129164190Sjkoshy strerror(errno)); 130164190Sjkoshy exit(1); 131164190Sjkoshy } 132164190Sjkoshy#endif 133164190Sjkoshy 134164190Sjkoshy on = 1; 135164190Sjkoshy /* specify to tell value of hoplimit field of received IP6 hdr */ 136164190Sjkoshy#ifdef IPV6_RECVHOPLIMIT 137164190Sjkoshy if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, 138164190Sjkoshy sizeof(on)) < 0) { 139164190Sjkoshy warnmsg(LOG_ERR, __func__, "IPV6_RECVHOPLIMIT: %s", 140164190Sjkoshy strerror(errno)); 141164190Sjkoshy exit(1); 142164190Sjkoshy } 143164190Sjkoshy#else /* old adv. API */ 144164190Sjkoshy if (setsockopt(rssock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, 145164190Sjkoshy sizeof(on)) < 0) { 146164190Sjkoshy warnmsg(LOG_ERR, __func__, "IPV6_HOPLIMIT: %s", 147164190Sjkoshy strerror(errno)); 148164190Sjkoshy exit(1); 149164190Sjkoshy } 150164190Sjkoshy#endif 151164190Sjkoshy 152164190Sjkoshy /* specfiy to accept only router advertisements on the socket */ 153164190Sjkoshy ICMP6_FILTER_SETBLOCKALL(&filt); 154164190Sjkoshy ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt); 155164190Sjkoshy if (setsockopt(rssock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, 156164190Sjkoshy sizeof(filt)) == -1) { 157164190Sjkoshy warnmsg(LOG_ERR, __func__, "setsockopt(ICMP6_FILTER): %s", 158164190Sjkoshy strerror(errno)); 159164190Sjkoshy return(-1); 160164190Sjkoshy } 161164190Sjkoshy 162164190Sjkoshy /* initialize msghdr for receiving packets */ 163164190Sjkoshy rcviov[0].iov_base = (caddr_t)answer; 164164190Sjkoshy rcviov[0].iov_len = sizeof(answer); 165164190Sjkoshy rcvmhdr.msg_name = (caddr_t)&from; 166164190Sjkoshy rcvmhdr.msg_namelen = sizeof(from); 167164190Sjkoshy rcvmhdr.msg_iov = rcviov; 168210348Skaiw rcvmhdr.msg_iovlen = 1; 169210348Skaiw rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf; 170210348Skaiw rcvmhdr.msg_controllen = rcvcmsglen; 171210348Skaiw 172164190Sjkoshy /* initialize msghdr for sending packets */ 173164190Sjkoshy sndmhdr.msg_namelen = sizeof(struct sockaddr_in6); 174164190Sjkoshy sndmhdr.msg_iov = sndiov; 175164190Sjkoshy sndmhdr.msg_iovlen = 1; 176164190Sjkoshy sndmhdr.msg_control = (caddr_t)sndcmsgbuf; 177164190Sjkoshy sndmhdr.msg_controllen = sndcmsglen; 178210338Skaiw 179210338Skaiw return(rssock); 180164190Sjkoshy} 181164190Sjkoshy 182164190Sjkoshyvoid 183210330Skaiwsendpacket(struct ifinfo *ifinfo) 184164190Sjkoshy{ 185164190Sjkoshy struct in6_pktinfo *pi; 186164190Sjkoshy struct cmsghdr *cm; 187164190Sjkoshy int hoplimit = 255; 188164190Sjkoshy int i; 189164190Sjkoshy 190165535Sjkoshy sndmhdr.msg_name = (caddr_t)&sin6_allrouters; 191164190Sjkoshy sndmhdr.msg_iov[0].iov_base = (caddr_t)ifinfo->rs_data; 192164190Sjkoshy sndmhdr.msg_iov[0].iov_len = ifinfo->rs_datalen; 193164190Sjkoshy 194164190Sjkoshy cm = CMSG_FIRSTHDR(&sndmhdr); 195164190Sjkoshy /* specify the outgoing interface */ 196164190Sjkoshy cm->cmsg_level = IPPROTO_IPV6; 197164190Sjkoshy cm->cmsg_type = IPV6_PKTINFO; 198164190Sjkoshy cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); 199 pi = (struct in6_pktinfo *)CMSG_DATA(cm); 200 memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/ 201 pi->ipi6_ifindex = ifinfo->sdl->sdl_index; 202 203 /* specify the hop limit of the packet */ 204 cm = CMSG_NXTHDR(&sndmhdr, cm); 205 cm->cmsg_level = IPPROTO_IPV6; 206 cm->cmsg_type = IPV6_HOPLIMIT; 207 cm->cmsg_len = CMSG_LEN(sizeof(int)); 208 memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int)); 209 210 warnmsg(LOG_DEBUG, __func__, 211 "send RS on %s, whose state is %d", 212 ifinfo->ifname, ifinfo->state); 213 i = sendmsg(rssock, &sndmhdr, 0); 214 if (i < 0 || i != ifinfo->rs_datalen) { 215 /* 216 * ENETDOWN is not so serious, especially when using several 217 * network cards on a mobile node. We ignore it. 218 */ 219 if (errno != ENETDOWN || dflag > 0) 220 warnmsg(LOG_ERR, __func__, "sendmsg on %s: %s", 221 ifinfo->ifname, strerror(errno)); 222 } 223 224 /* update counter */ 225 ifinfo->probes++; 226} 227 228void 229rtsol_input(int s) 230{ 231 int i; 232 int *hlimp = NULL; 233 struct icmp6_hdr *icp; 234 int ifindex = 0; 235 struct cmsghdr *cm; 236 struct in6_pktinfo *pi = NULL; 237 struct ifinfo *ifi = NULL; 238 struct nd_router_advert *nd_ra; 239 u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; 240 241 /* get message */ 242 if ((i = recvmsg(s, &rcvmhdr, 0)) < 0) { 243 warnmsg(LOG_ERR, __func__, "recvmsg: %s", strerror(errno)); 244 return; 245 } 246 247 /* extract optional information via Advanced API */ 248 for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr); cm; 249 cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) { 250 if (cm->cmsg_level == IPPROTO_IPV6 && 251 cm->cmsg_type == IPV6_PKTINFO && 252 cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { 253 pi = (struct in6_pktinfo *)(CMSG_DATA(cm)); 254 ifindex = pi->ipi6_ifindex; 255 } 256 if (cm->cmsg_level == IPPROTO_IPV6 && 257 cm->cmsg_type == IPV6_HOPLIMIT && 258 cm->cmsg_len == CMSG_LEN(sizeof(int))) 259 hlimp = (int *)CMSG_DATA(cm); 260 } 261 262 if (ifindex == 0) { 263 warnmsg(LOG_ERR, __func__, 264 "failed to get receiving interface"); 265 return; 266 } 267 if (hlimp == NULL) { 268 warnmsg(LOG_ERR, __func__, 269 "failed to get receiving hop limit"); 270 return; 271 } 272 273 if (i < sizeof(struct nd_router_advert)) { 274 warnmsg(LOG_ERR, __func__, 275 "packet size(%d) is too short", i); 276 return; 277 } 278 279 icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base; 280 281 if (icp->icmp6_type != ND_ROUTER_ADVERT) { 282 warnmsg(LOG_ERR, __func__, 283 "invalid icmp type(%d) from %s on %s", icp->icmp6_type, 284 inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, 285 INET6_ADDRSTRLEN), 286 if_indextoname(pi->ipi6_ifindex, ifnamebuf)); 287 return; 288 } 289 290 if (icp->icmp6_code != 0) { 291 warnmsg(LOG_ERR, __func__, 292 "invalid icmp code(%d) from %s on %s", icp->icmp6_code, 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 (*hlimp != 255) { 300 warnmsg(LOG_NOTICE, __func__, 301 "invalid RA with hop limit(%d) from %s on %s", 302 *hlimp, 303 inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, 304 INET6_ADDRSTRLEN), 305 if_indextoname(pi->ipi6_ifindex, ifnamebuf)); 306 return; 307 } 308 309 if (pi && !IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) { 310 warnmsg(LOG_NOTICE, __func__, 311 "invalid RA with non link-local source from %s on %s", 312 inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, 313 INET6_ADDRSTRLEN), 314 if_indextoname(pi->ipi6_ifindex, ifnamebuf)); 315 return; 316 } 317 318 /* xxx: more validation? */ 319 320 if ((ifi = find_ifinfo(pi->ipi6_ifindex)) == NULL) { 321 warnmsg(LOG_NOTICE, __func__, 322 "received RA from %s on an unexpected IF(%s)", 323 inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, 324 INET6_ADDRSTRLEN), 325 if_indextoname(pi->ipi6_ifindex, ifnamebuf)); 326 return; 327 } 328 329 warnmsg(LOG_DEBUG, __func__, 330 "received RA from %s on %s, state is %d", 331 inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, INET6_ADDRSTRLEN), 332 ifi->ifname, ifi->state); 333 334 nd_ra = (struct nd_router_advert *)icp; 335 336 /* 337 * Process the "O bit." 338 * If the value of OtherConfigFlag changes from FALSE to TRUE, the 339 * host should invoke the stateful autoconfiguration protocol, 340 * requesting information. 341 * [RFC 2462 Section 5.5.3] 342 */ 343 if (((nd_ra->nd_ra_flags_reserved) & ND_RA_FLAG_OTHER) && 344 !ifi->otherconfig) { 345 warnmsg(LOG_DEBUG, __func__, 346 "OtherConfigFlag on %s is turned on", ifi->ifname); 347 ifi->otherconfig = 1; 348 call_script(otherconf_script, ifi->ifname); 349 } 350 351 ifi->racnt++; 352 353 switch (ifi->state) { 354 case IFS_IDLE: /* should be ignored */ 355 case IFS_DELAY: /* right? */ 356 break; 357 case IFS_PROBE: 358 ifi->state = IFS_IDLE; 359 ifi->probes = 0; 360 rtsol_timer_update(ifi); 361 break; 362 } 363} 364 365static void 366call_script(scriptpath, ifname) 367 char *scriptpath, *ifname; 368{ 369 pid_t pid, wpid; 370 371 if (scriptpath == NULL) 372 return; 373 374 /* launch the script */ 375 pid = fork(); 376 if (pid < 0) { 377 warnmsg(LOG_ERR, __func__, 378 "failed to fork: %s", strerror(errno)); 379 return; 380 } else if (pid) { 381 int wstatus; 382 383 do { 384 wpid = wait(&wstatus); 385 } while (wpid != pid && wpid > 0); 386 387 if (wpid < 0) 388 warnmsg(LOG_ERR, __func__, 389 "wait: %s", strerror(errno)); 390 else { 391 warnmsg(LOG_DEBUG, __func__, 392 "script \"%s\" terminated", scriptpath); 393 } 394 } else { 395 char *argv[3]; 396 int fd; 397 398 argv[0] = scriptpath; 399 argv[1] = ifname; 400 argv[2] = NULL; 401 402 if (safefile(scriptpath)) { 403 warnmsg(LOG_ERR, __func__, 404 "script \"%s\" cannot be executed safely", 405 scriptpath); 406 exit(1); 407 } 408 409 if ((fd = open("/dev/null", O_RDWR)) != -1) { 410 dup2(fd, STDIN_FILENO); 411 dup2(fd, STDOUT_FILENO); 412 dup2(fd, STDERR_FILENO); 413 if (fd > STDERR_FILENO) 414 close(fd); 415 } 416 417 execv(scriptpath, argv); 418 419 warnmsg(LOG_ERR, __func__, "child: exec failed: %s", 420 strerror(errno)); 421 exit(0); 422 } 423 424 return; 425} 426 427static int 428safefile(path) 429 const char *path; 430{ 431 struct stat s; 432 uid_t myuid; 433 434 /* no setuid */ 435 if (getuid() != geteuid()) { 436 warnmsg(LOG_NOTICE, __func__, 437 "setuid'ed execution not allowed\n"); 438 return (-1); 439 } 440 441 if (lstat(path, &s) != 0) { 442 warnmsg(LOG_NOTICE, __func__, "lstat failed: %s", 443 strerror(errno)); 444 return (-1); 445 } 446 447 /* the file must be owned by the running uid */ 448 myuid = getuid(); 449 if (s.st_uid != myuid) { 450 warnmsg(LOG_NOTICE, __func__, 451 "%s has invalid owner uid\n", path); 452 return (-1); 453 } 454 455 switch (s.st_mode & S_IFMT) { 456 case S_IFREG: 457 break; 458 default: 459 warnmsg(LOG_NOTICE, __func__, 460 "%s is an invalid file type 0x%o\n", 461 path, (s.st_mode & S_IFMT)); 462 return (-1); 463 } 464 465 return (0); 466} 467