rtsol.c revision 222861
1124524Sume/* $KAME: rtsol.c,v 1.27 2003/10/05 00:09:36 itojun Exp $ */ 266776Skris 355163Sshin/* 455163Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 5222732Shrs * Copyright (C) 2011 Hiroki Sato 655163Sshin * All rights reserved. 762632Skris * 855163Sshin * Redistribution and use in source and binary forms, with or without 955163Sshin * modification, are permitted provided that the following conditions 1055163Sshin * are met: 1155163Sshin * 1. Redistributions of source code must retain the above copyright 1255163Sshin * notice, this list of conditions and the following disclaimer. 1355163Sshin * 2. Redistributions in binary form must reproduce the above copyright 1455163Sshin * notice, this list of conditions and the following disclaimer in the 1555163Sshin * documentation and/or other materials provided with the distribution. 1655163Sshin * 3. Neither the name of the project nor the names of its contributors 1755163Sshin * may be used to endorse or promote products derived from this software 1855163Sshin * without specific prior written permission. 1962632Skris * 2055163Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 2155163Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2255163Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2355163Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2455163Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2555163Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2655163Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2755163Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2855163Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2955163Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3055163Sshin * SUCH DAMAGE. 3155163Sshin * 3255163Sshin * $FreeBSD: head/usr.sbin/rtsold/rtsol.c 222861 2011-06-08 16:03:29Z hrs $ 3355163Sshin */ 3455163Sshin 3555163Sshin#include <sys/param.h> 3655163Sshin#include <sys/socket.h> 3755163Sshin#include <sys/uio.h> 3855163Sshin#include <sys/time.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 47222732Shrs#define __BSD_VISIBLE 1 /* IN6ADDR_LINKLOCAL_ALLROUTERS_INIT */ 4855163Sshin#include <netinet/in.h> 49222732Shrs#undef __BSD_VISIBLE 5055163Sshin#include <netinet/ip6.h> 5155163Sshin#include <netinet6/ip6_var.h> 5255163Sshin#include <netinet/icmp6.h> 5355163Sshin 5455163Sshin#include <arpa/inet.h> 5555163Sshin 56222732Shrs#include <netdb.h> 5755163Sshin#include <time.h> 58119026Sume#include <fcntl.h> 5955163Sshin#include <unistd.h> 6055163Sshin#include <stdio.h> 6155163Sshin#include <err.h> 6255163Sshin#include <errno.h> 6355163Sshin#include <string.h> 6455163Sshin#include <stdlib.h> 6555163Sshin#include <syslog.h> 6655163Sshin#include "rtsold.h" 6755163Sshin 6855163Sshinstatic struct msghdr rcvmhdr; 6955163Sshinstatic struct msghdr sndmhdr; 7055163Sshinstatic struct iovec rcviov[2]; 7155163Sshinstatic struct iovec sndiov[2]; 7255163Sshinstatic struct sockaddr_in6 from; 73119026Sumestatic int rcvcmsglen; 7455163Sshin 7562632Skrisint rssock; 76222732Shrsstruct ifinfo_head_t ifinfo_head = 77222732Shrs TAILQ_HEAD_INITIALIZER(ifinfo_head); 7855163Sshin 79222732Shrsstatic const struct sockaddr_in6 sin6_allrouters = { 80204407Suqs .sin6_len = sizeof(sin6_allrouters), 81204407Suqs .sin6_family = AF_INET6, 82222732Shrs .sin6_addr = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT, 83204407Suqs}; 8455163Sshin 85222732Shrsstatic void call_script(const int, const char *const *, void *); 86222732Shrsstatic size_t dname_labeldec(char *, size_t, const char *); 87173412Skevlostatic int safefile(const char *); 88222861Shrsstatic struct ra_opt *find_raopt(struct rainfo *, int, void *, size_t); 89118661Sume 90222732Shrs#define _ARGS_OTHER otherconf_script, ifi->ifname 91222732Shrs#define _ARGS_RESADD resolvconf_script, "-a", ifi->ifname 92222732Shrs#define _ARGS_RESDEL resolvconf_script, "-d", ifi->ifname 93222732Shrs 94222732Shrs#define CALL_SCRIPT(name, sm_head) \ 95222732Shrs do { \ 96222732Shrs const char *const sarg[] = { _ARGS_##name, NULL }; \ 97222732Shrs call_script(sizeof(sarg), sarg, sm_head); \ 98222732Shrs } while(0) 99222732Shrs 100222732Shrs#define ELM_MALLOC(p,error_action) \ 101222732Shrs do { \ 102222732Shrs p = malloc(sizeof(*p)); \ 103222732Shrs if (p == NULL) { \ 104222732Shrs warnmsg(LOG_ERR, __func__, "malloc failed: %s", \ 105222732Shrs strerror(errno)); \ 106222732Shrs error_action; \ 107222732Shrs } \ 108222732Shrs memset(p, 0, sizeof(*p)); \ 109222732Shrs } while(0) 110222732Shrs 11155163Sshinint 112124524Sumesockopen(void) 11355163Sshin{ 114119026Sume static u_char *rcvcmsgbuf = NULL, *sndcmsgbuf = NULL; 115119026Sume int sndcmsglen, on; 116119026Sume static u_char answer[1500]; 11755163Sshin struct icmp6_filter filt; 11855163Sshin 11962632Skris sndcmsglen = rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + 120118664Sume CMSG_SPACE(sizeof(int)); 12162632Skris if (rcvcmsgbuf == NULL && (rcvcmsgbuf = malloc(rcvcmsglen)) == NULL) { 122118660Sume warnmsg(LOG_ERR, __func__, 123118664Sume "malloc for receive msghdr failed"); 124222732Shrs return (-1); 12562632Skris } 126118664Sume if (sndcmsgbuf == NULL && (sndcmsgbuf = malloc(sndcmsglen)) == NULL) { 127118660Sume warnmsg(LOG_ERR, __func__, 128118664Sume "malloc for send msghdr failed"); 129222732Shrs return (-1); 13062632Skris } 13155163Sshin if ((rssock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { 132118660Sume warnmsg(LOG_ERR, __func__, "socket: %s", strerror(errno)); 133222732Shrs return (-1); 13455163Sshin } 13555163Sshin 13655163Sshin /* specify to tell receiving interface */ 13755163Sshin on = 1; 13862632Skris if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, 139118664Sume sizeof(on)) < 0) { 140118660Sume warnmsg(LOG_ERR, __func__, "IPV6_RECVPKTINFO: %s", 141118664Sume strerror(errno)); 14262632Skris exit(1); 14362632Skris } 14455163Sshin 145222732Shrs /* specify to tell value of hoplimit field of received IP6 hdr */ 14655163Sshin on = 1; 14762632Skris if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, 148118664Sume sizeof(on)) < 0) { 149118660Sume warnmsg(LOG_ERR, __func__, "IPV6_RECVHOPLIMIT: %s", 150118664Sume strerror(errno)); 15162632Skris exit(1); 15262632Skris } 15355163Sshin 15455163Sshin /* specfiy to accept only router advertisements on the socket */ 15555163Sshin ICMP6_FILTER_SETBLOCKALL(&filt); 15655163Sshin ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt); 15755163Sshin if (setsockopt(rssock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, 158118664Sume sizeof(filt)) == -1) { 159118660Sume warnmsg(LOG_ERR, __func__, "setsockopt(ICMP6_FILTER): %s", 160118664Sume strerror(errno)); 16155163Sshin return(-1); 16255163Sshin } 16355163Sshin 16455163Sshin /* initialize msghdr for receiving packets */ 16555163Sshin rcviov[0].iov_base = (caddr_t)answer; 16655163Sshin rcviov[0].iov_len = sizeof(answer); 16755163Sshin rcvmhdr.msg_name = (caddr_t)&from; 16855163Sshin rcvmhdr.msg_iov = rcviov; 16955163Sshin rcvmhdr.msg_iovlen = 1; 17055163Sshin rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf; 17155163Sshin 17255163Sshin /* initialize msghdr for sending packets */ 17355163Sshin sndmhdr.msg_namelen = sizeof(struct sockaddr_in6); 17455163Sshin sndmhdr.msg_iov = sndiov; 17555163Sshin sndmhdr.msg_iovlen = 1; 17655163Sshin sndmhdr.msg_control = (caddr_t)sndcmsgbuf; 17762632Skris sndmhdr.msg_controllen = sndcmsglen; 17855163Sshin 179222732Shrs return (rssock); 18055163Sshin} 18155163Sshin 18255163Sshinvoid 183222732Shrssendpacket(struct ifinfo *ifi) 18455163Sshin{ 185118664Sume struct in6_pktinfo *pi; 186118664Sume struct cmsghdr *cm; 187118664Sume int hoplimit = 255; 188204407Suqs ssize_t i; 189119026Sume struct sockaddr_in6 dst; 19055163Sshin 191119026Sume dst = sin6_allrouters; 192222732Shrs dst.sin6_scope_id = ifi->linkid; 193119026Sume 194119026Sume sndmhdr.msg_name = (caddr_t)&dst; 195222732Shrs sndmhdr.msg_iov[0].iov_base = (caddr_t)ifi->rs_data; 196222732Shrs sndmhdr.msg_iov[0].iov_len = ifi->rs_datalen; 19755163Sshin 19855163Sshin cm = CMSG_FIRSTHDR(&sndmhdr); 19955163Sshin /* specify the outgoing interface */ 20055163Sshin cm->cmsg_level = IPPROTO_IPV6; 20155163Sshin cm->cmsg_type = IPV6_PKTINFO; 20255163Sshin cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); 20355163Sshin pi = (struct in6_pktinfo *)CMSG_DATA(cm); 20455163Sshin memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/ 205222732Shrs pi->ipi6_ifindex = ifi->sdl->sdl_index; 20655163Sshin 20755163Sshin /* specify the hop limit of the packet */ 208118664Sume cm = CMSG_NXTHDR(&sndmhdr, cm); 209118664Sume cm->cmsg_level = IPPROTO_IPV6; 210118664Sume cm->cmsg_type = IPV6_HOPLIMIT; 211118664Sume cm->cmsg_len = CMSG_LEN(sizeof(int)); 212118664Sume memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int)); 21355163Sshin 214118664Sume warnmsg(LOG_DEBUG, __func__, 215118664Sume "send RS on %s, whose state is %d", 216222732Shrs ifi->ifname, ifi->state); 21755163Sshin i = sendmsg(rssock, &sndmhdr, 0); 218222732Shrs if (i < 0 || (size_t)i != ifi->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", 225222732Shrs ifi->ifname, strerror(errno)); 22655163Sshin } 22755163Sshin 22855163Sshin /* update counter */ 229222732Shrs ifi->probes++; 23055163Sshin} 23155163Sshin 23255163Sshinvoid 23355163Sshinrtsol_input(int s) 23455163Sshin{ 235119026Sume u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; 236222732Shrs int l, ifindex = 0, *hlimp = NULL; 237222732Shrs ssize_t msglen; 23855163Sshin struct in6_pktinfo *pi = NULL; 23955163Sshin struct ifinfo *ifi = NULL; 240222732Shrs struct ra_opt *rao = NULL; 241119026Sume struct icmp6_hdr *icp; 242118661Sume struct nd_router_advert *nd_ra; 243119026Sume struct cmsghdr *cm; 244222861Shrs struct rainfo *rai; 245222732Shrs char *raoptp; 246222732Shrs char *p; 247222732Shrs struct in6_addr *addr; 248222732Shrs struct nd_opt_hdr *ndo; 249222732Shrs struct nd_opt_rdnss *rdnss; 250222732Shrs struct nd_opt_dnssl *dnssl; 251222732Shrs size_t len; 252222732Shrs char nsbuf[INET6_ADDRSTRLEN + 1 + IFNAMSIZ + 1]; 253222732Shrs char dname[NI_MAXHOST]; 254222732Shrs struct timeval now; 255222732Shrs struct timeval lifetime; 256222861Shrs int newent_rai; 257222861Shrs int newent_rao; 25855163Sshin 259119026Sume /* get message. namelen and controllen must always be initialized. */ 260119026Sume rcvmhdr.msg_namelen = sizeof(from); 261119026Sume rcvmhdr.msg_controllen = rcvcmsglen; 262222732Shrs if ((msglen = recvmsg(s, &rcvmhdr, 0)) < 0) { 263118660Sume warnmsg(LOG_ERR, __func__, "recvmsg: %s", strerror(errno)); 26455163Sshin return; 26555163Sshin } 26655163Sshin 26755163Sshin /* extract optional information via Advanced API */ 268118664Sume for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr); cm; 269118664Sume cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) { 27055163Sshin if (cm->cmsg_level == IPPROTO_IPV6 && 27155163Sshin cm->cmsg_type == IPV6_PKTINFO && 27255163Sshin cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { 27355163Sshin pi = (struct in6_pktinfo *)(CMSG_DATA(cm)); 27455163Sshin ifindex = pi->ipi6_ifindex; 27555163Sshin } 27655163Sshin if (cm->cmsg_level == IPPROTO_IPV6 && 27755163Sshin cm->cmsg_type == IPV6_HOPLIMIT && 27855163Sshin cm->cmsg_len == CMSG_LEN(sizeof(int))) 27955163Sshin hlimp = (int *)CMSG_DATA(cm); 28055163Sshin } 28155163Sshin 28255163Sshin if (ifindex == 0) { 283118664Sume warnmsg(LOG_ERR, __func__, 284118664Sume "failed to get receiving interface"); 28555163Sshin return; 28655163Sshin } 28755163Sshin if (hlimp == NULL) { 288118664Sume warnmsg(LOG_ERR, __func__, 289118664Sume "failed to get receiving hop limit"); 29055163Sshin return; 29155163Sshin } 29255163Sshin 293222732Shrs if ((size_t)msglen < sizeof(struct nd_router_advert)) { 294118906Sume warnmsg(LOG_INFO, __func__, 295222732Shrs "packet size(%zd) is too short", msglen); 29655163Sshin return; 29755163Sshin } 29855163Sshin 29955163Sshin icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base; 30055163Sshin 30155163Sshin if (icp->icmp6_type != ND_ROUTER_ADVERT) { 302118906Sume /* 303118906Sume * this should not happen because we configured a filter 304118906Sume * that only passes RAs on the receiving socket. 305118906Sume */ 306118660Sume warnmsg(LOG_ERR, __func__, 307118664Sume "invalid icmp type(%d) from %s on %s", icp->icmp6_type, 308118664Sume inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, 309118664Sume INET6_ADDRSTRLEN), 310118664Sume if_indextoname(pi->ipi6_ifindex, ifnamebuf)); 31155163Sshin return; 31255163Sshin } 31355163Sshin 31455163Sshin if (icp->icmp6_code != 0) { 315118906Sume warnmsg(LOG_INFO, __func__, 316118664Sume "invalid icmp code(%d) from %s on %s", icp->icmp6_code, 317118664Sume inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, 318118664Sume INET6_ADDRSTRLEN), 319118664Sume if_indextoname(pi->ipi6_ifindex, ifnamebuf)); 32055163Sshin return; 32155163Sshin } 32255163Sshin 32355163Sshin if (*hlimp != 255) { 324118906Sume warnmsg(LOG_INFO, __func__, 325118664Sume "invalid RA with hop limit(%d) from %s on %s", 326118664Sume *hlimp, 327118664Sume inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, 328118664Sume INET6_ADDRSTRLEN), 329118664Sume if_indextoname(pi->ipi6_ifindex, ifnamebuf)); 33055163Sshin return; 33155163Sshin } 33255163Sshin 33355163Sshin if (pi && !IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) { 334118906Sume warnmsg(LOG_INFO, __func__, 335118664Sume "invalid RA with non link-local source from %s on %s", 336118664Sume inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, 337118664Sume INET6_ADDRSTRLEN), 338118664Sume if_indextoname(pi->ipi6_ifindex, ifnamebuf)); 33955163Sshin return; 34055163Sshin } 34155163Sshin 34255163Sshin /* xxx: more validation? */ 34355163Sshin 34455163Sshin if ((ifi = find_ifinfo(pi->ipi6_ifindex)) == NULL) { 345118906Sume warnmsg(LOG_INFO, __func__, 346118664Sume "received RA from %s on an unexpected IF(%s)", 347118664Sume inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, 348118664Sume INET6_ADDRSTRLEN), 349118664Sume if_indextoname(pi->ipi6_ifindex, ifnamebuf)); 35055163Sshin return; 35155163Sshin } 35255163Sshin 353118660Sume warnmsg(LOG_DEBUG, __func__, 354118664Sume "received RA from %s on %s, state is %d", 355118664Sume inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, INET6_ADDRSTRLEN), 356118664Sume ifi->ifname, ifi->state); 35755163Sshin 358118661Sume nd_ra = (struct nd_router_advert *)icp; 359118661Sume 360118661Sume /* 361118661Sume * Process the "O bit." 362118661Sume * If the value of OtherConfigFlag changes from FALSE to TRUE, the 363118661Sume * host should invoke the stateful autoconfiguration protocol, 364118661Sume * requesting information. 365118661Sume * [RFC 2462 Section 5.5.3] 366118661Sume */ 367118661Sume if (((nd_ra->nd_ra_flags_reserved) & ND_RA_FLAG_OTHER) && 368118661Sume !ifi->otherconfig) { 369118661Sume warnmsg(LOG_DEBUG, __func__, 370118661Sume "OtherConfigFlag on %s is turned on", ifi->ifname); 371118661Sume ifi->otherconfig = 1; 372222732Shrs CALL_SCRIPT(OTHER, NULL); 373118661Sume } 374222732Shrs gettimeofday(&now, NULL); 375222861Shrs newent_rai = 0; 376222861Shrs rai = find_rainfo(ifi, &from); 377222861Shrs if (rai == NULL) { 378222861Shrs ELM_MALLOC(rai, exit(1)); 379222861Shrs rai->rai_ifinfo = ifi; 380222861Shrs TAILQ_INIT(&rai->rai_ra_opt); 381222861Shrs memcpy(&rai->rai_saddr.sin6_addr, &from.sin6_addr, 382222861Shrs sizeof(rai->rai_saddr.sin6_addr)); 383222861Shrs newent_rai = 1; 384222861Shrs } 385222732Shrs 386222732Shrs#define RA_OPT_NEXT_HDR(x) (struct nd_opt_hdr *)((char *)x + \ 387222732Shrs (((struct nd_opt_hdr *)x)->nd_opt_len * 8)) 388222732Shrs /* Process RA options. */ 389222732Shrs warnmsg(LOG_DEBUG, __func__, "Processing RA"); 390222732Shrs raoptp = (char *)icp + sizeof(struct nd_router_advert); 391222732Shrs while (raoptp < (char *)icp + msglen) { 392222732Shrs ndo = (struct nd_opt_hdr *)raoptp; 393222732Shrs warnmsg(LOG_DEBUG, __func__, "ndo = %p", raoptp); 394222732Shrs warnmsg(LOG_DEBUG, __func__, "ndo->nd_opt_type = %d", 395222732Shrs ndo->nd_opt_type); 396222732Shrs warnmsg(LOG_DEBUG, __func__, "ndo->nd_opt_len = %d", 397222732Shrs ndo->nd_opt_len); 398222732Shrs 399222732Shrs switch (ndo->nd_opt_type) { 400222732Shrs case ND_OPT_RDNSS: 401222732Shrs rdnss = (struct nd_opt_rdnss *)raoptp; 402222732Shrs 403222732Shrs /* Optlen sanity check (Section 5.3.1 in RFC 6106) */ 404222732Shrs if (rdnss->nd_opt_rdnss_len < 3) { 405222732Shrs warnmsg(LOG_INFO, __func__, 406222732Shrs "too short RDNSS option" 407222732Shrs "in RA from %s was ignored.", 408222732Shrs inet_ntop(AF_INET6, &from.sin6_addr, 409222732Shrs ntopbuf, INET6_ADDRSTRLEN)); 410222732Shrs break; 411222732Shrs } 412222732Shrs 413222732Shrs addr = (struct in6_addr *)(raoptp + sizeof(*rdnss)); 414222732Shrs while ((char *)addr < (char *)RA_OPT_NEXT_HDR(raoptp)) { 415222732Shrs if (inet_ntop(AF_INET6, addr, ntopbuf, 416222732Shrs INET6_ADDRSTRLEN) == NULL) { 417222732Shrs warnmsg(LOG_INFO, __func__, 418222732Shrs "an invalid address in RDNSS option" 419222732Shrs " in RA from %s was ignored.", 420222732Shrs inet_ntop(AF_INET6, &from.sin6_addr, 421222732Shrs ntopbuf, INET6_ADDRSTRLEN)); 422222732Shrs addr++; 423222732Shrs continue; 424222732Shrs } 425222732Shrs if (IN6_IS_ADDR_LINKLOCAL(addr)) 426222732Shrs /* XXX: % has to be escaped here */ 427222732Shrs l = snprintf(nsbuf, sizeof(nsbuf), 428222732Shrs "%s%c%s", ntopbuf, 429222732Shrs SCOPE_DELIMITER, 430222732Shrs ifi->ifname); 431222732Shrs else 432222732Shrs l = snprintf(nsbuf, sizeof(nsbuf), 433222732Shrs "%s", ntopbuf); 434222732Shrs if (l < 0 || (size_t)l >= sizeof(nsbuf)) { 435222732Shrs warnmsg(LOG_ERR, __func__, 436222732Shrs "address copying error in " 437222732Shrs "RDNSS option: %d.", l); 438222732Shrs addr++; 439222732Shrs continue; 440222732Shrs } 441222732Shrs warnmsg(LOG_DEBUG, __func__, "nsbuf = %s", 442222732Shrs nsbuf); 443222732Shrs 444222861Shrs newent_rao = 0; 445222861Shrs rao = find_raopt(rai, ndo->nd_opt_type, nsbuf, 446222861Shrs strlen(nsbuf)); 447222861Shrs if (rao == NULL) { 448222861Shrs ELM_MALLOC(rao, break); 449222861Shrs rao->rao_type = ndo->nd_opt_type; 450222861Shrs rao->rao_len = strlen(nsbuf); 451222861Shrs rao->rao_msg = strdup(nsbuf); 452222861Shrs if (rao->rao_msg == NULL) { 453222861Shrs warnmsg(LOG_ERR, __func__, 454222861Shrs "strdup failed: %s", 455222861Shrs strerror(errno)); 456222861Shrs free(rao); 457222861Shrs addr++; 458222861Shrs continue; 459222861Shrs } 460222861Shrs newent_rao = 1; 461222732Shrs } 462222732Shrs /* Set expiration timer */ 463222861Shrs memset(&rao->rao_expire, 0, 464222861Shrs sizeof(rao->rao_expire)); 465222732Shrs memset(&lifetime, 0, sizeof(lifetime)); 466222861Shrs lifetime.tv_sec = 467222861Shrs ntohl(rdnss->nd_opt_rdnss_lifetime); 468222732Shrs timeradd(&now, &lifetime, &rao->rao_expire); 469222732Shrs 470222861Shrs if (newent_rao) 471222861Shrs TAILQ_INSERT_TAIL(&rai->rai_ra_opt, 472222861Shrs rao, rao_next); 473222732Shrs addr++; 474222732Shrs } 475222732Shrs break; 476222732Shrs case ND_OPT_DNSSL: 477222732Shrs dnssl = (struct nd_opt_dnssl *)raoptp; 478222732Shrs 479222732Shrs /* Optlen sanity check (Section 5.3.1 in RFC 6106) */ 480222732Shrs if (dnssl->nd_opt_dnssl_len < 2) { 481222732Shrs warnmsg(LOG_INFO, __func__, 482222732Shrs "too short DNSSL option" 483222732Shrs "in RA from %s was ignored.", 484222732Shrs inet_ntop(AF_INET6, &from.sin6_addr, 485222732Shrs ntopbuf, INET6_ADDRSTRLEN)); 486222732Shrs break; 487222732Shrs } 488222732Shrs 489222732Shrs /* 490222732Shrs * Ensure NUL-termination in DNSSL in case of 491222732Shrs * malformed field. 492222732Shrs */ 493222732Shrs p = (char *)RA_OPT_NEXT_HDR(raoptp); 494222732Shrs *(p - 1) = '\0'; 495222732Shrs 496222732Shrs p = raoptp + sizeof(*dnssl); 497222732Shrs while (1 < (len = dname_labeldec(dname, sizeof(dname), 498222732Shrs p))) { 499222732Shrs /* length == 1 means empty string */ 500222732Shrs warnmsg(LOG_DEBUG, __func__, "dname = %s", 501222732Shrs dname); 502222732Shrs 503222861Shrs newent_rao = 0; 504222861Shrs rao = find_raopt(rai, ndo->nd_opt_type, dname, 505222861Shrs strlen(dname)); 506222861Shrs if (rao == NULL) { 507222861Shrs ELM_MALLOC(rao, break); 508222861Shrs rao->rao_type = ndo->nd_opt_type; 509222861Shrs rao->rao_len = strlen(dname); 510222861Shrs rao->rao_msg = strdup(dname); 511222861Shrs if (rao->rao_msg == NULL) { 512222861Shrs warnmsg(LOG_ERR, __func__, 513222861Shrs "strdup failed: %s", 514222861Shrs strerror(errno)); 515222861Shrs free(rao); 516222861Shrs addr++; 517222861Shrs continue; 518222861Shrs } 519222861Shrs newent_rao = 1; 520222732Shrs } 521222732Shrs /* Set expiration timer */ 522222861Shrs memset(&rao->rao_expire, 0, 523222861Shrs sizeof(rao->rao_expire)); 524222732Shrs memset(&lifetime, 0, sizeof(lifetime)); 525222861Shrs lifetime.tv_sec = 526222861Shrs ntohl(dnssl->nd_opt_dnssl_lifetime); 527222732Shrs timeradd(&now, &lifetime, &rao->rao_expire); 528222732Shrs 529222861Shrs if (newent_rao) 530222861Shrs TAILQ_INSERT_TAIL(&rai->rai_ra_opt, 531222861Shrs rao, rao_next); 532222732Shrs p += len; 533222732Shrs } 534222732Shrs break; 535222732Shrs default: 536222732Shrs /* nothing to do for other options */ 537222732Shrs break; 538222732Shrs } 539222732Shrs raoptp = (char *)RA_OPT_NEXT_HDR(raoptp); 540222732Shrs } 541222861Shrs if (newent_rai) 542222861Shrs TAILQ_INSERT_TAIL(&ifi->ifi_rainfo, rai, rai_next); 543222861Shrs 544222732Shrs ra_opt_handler(ifi); 54555163Sshin ifi->racnt++; 54655163Sshin 547118664Sume switch (ifi->state) { 548118664Sume case IFS_IDLE: /* should be ignored */ 549118664Sume case IFS_DELAY: /* right? */ 550118664Sume break; 551118664Sume case IFS_PROBE: 552118664Sume ifi->state = IFS_IDLE; 553118664Sume ifi->probes = 0; 554118664Sume rtsol_timer_update(ifi); 555118664Sume break; 55655163Sshin } 55755163Sshin} 558118661Sume 559222732Shrsstatic char resstr_ns_prefix[] = "nameserver "; 560222732Shrsstatic char resstr_sh_prefix[] = "search "; 561222732Shrsstatic char resstr_nl[] = "\n"; 562222732Shrsstatic char resstr_sp[] = " "; 563222732Shrs 564222732Shrsint 565222732Shrsra_opt_handler(struct ifinfo *ifi) 566222732Shrs{ 567222732Shrs struct ra_opt *rao; 568222861Shrs struct rainfo *rai; 569222732Shrs struct script_msg *smp1, *smp2, *smp3; 570222732Shrs struct timeval now; 571222732Shrs TAILQ_HEAD(, script_msg) sm_rdnss_head = 572222732Shrs TAILQ_HEAD_INITIALIZER(sm_rdnss_head); 573222732Shrs TAILQ_HEAD(, script_msg) sm_dnssl_head = 574222732Shrs TAILQ_HEAD_INITIALIZER(sm_dnssl_head); 575222732Shrs int dcount, dlen; 576222732Shrs 577222732Shrs dcount = 0; 578222732Shrs dlen = strlen(resstr_sh_prefix) + strlen(resstr_nl); 579222732Shrs gettimeofday(&now, NULL); 580222732Shrs 581222861Shrs /* 582222861Shrs * All options from multiple RAs with the same or different 583222861Shrs * source addresses on a single interface will be gathered and 584222861Shrs * handled, not overridden. [RFC 4861 6.3.4] 585222861Shrs */ 586222861Shrs TAILQ_FOREACH(rai, &ifi->ifi_rainfo, rai_next) { 587222861Shrs TAILQ_FOREACH(rao, &rai->rai_ra_opt, rao_next) { 588222861Shrs switch (rao->rao_type) { 589222861Shrs case ND_OPT_RDNSS: 590222861Shrs if (timercmp(&now, &rao->rao_expire, >)) { 591222861Shrs warnmsg(LOG_INFO, __func__, 592222861Shrs "expired rdnss entry: %s", 593222861Shrs (char *)rao->rao_msg); 594222861Shrs break; 595222861Shrs } 596222861Shrs ELM_MALLOC(smp1, continue); 597222861Shrs ELM_MALLOC(smp2, goto free1); 598222861Shrs ELM_MALLOC(smp3, goto free2); 599222861Shrs smp1->sm_msg = resstr_ns_prefix; 600222861Shrs TAILQ_INSERT_TAIL(&sm_rdnss_head, smp1, 601222861Shrs sm_next); 602222861Shrs smp2->sm_msg = rao->rao_msg; 603222861Shrs TAILQ_INSERT_TAIL(&sm_rdnss_head, smp2, 604222861Shrs sm_next); 605222861Shrs smp3->sm_msg = resstr_nl; 606222861Shrs TAILQ_INSERT_TAIL(&sm_rdnss_head, smp3, 607222861Shrs sm_next); 608222861Shrs ifi->ifi_rdnss = IFI_DNSOPT_STATE_RECEIVED; 609222861Shrs 610222732Shrs break; 611222861Shrs case ND_OPT_DNSSL: 612222861Shrs if (timercmp(&now, &rao->rao_expire, >)) { 613222861Shrs warnmsg(LOG_INFO, __func__, 614222861Shrs "expired dnssl entry: %s", 615222861Shrs (char *)rao->rao_msg); 616222861Shrs break; 617222861Shrs } 618222861Shrs dcount++; 619222861Shrs /* Check resolv.conf(5) restrictions. */ 620222861Shrs if (dcount > 6) { 621222861Shrs warnmsg(LOG_INFO, __func__, 622222861Shrs "dnssl entry exceeding maximum count (%d>6)" 623222861Shrs ": %s", dcount, (char *)rao->rao_msg); 624222861Shrs break; 625222861Shrs } 626222861Shrs if (256 < dlen + strlen(rao->rao_msg) + 627222861Shrs strlen(resstr_sp)) { 628222861Shrs warnmsg(LOG_INFO, __func__, 629222861Shrs "dnssl entry exceeding maximum length " 630222861Shrs "(>256): %s", (char *)rao->rao_msg); 631222861Shrs break; 632222861Shrs } 633222861Shrs ELM_MALLOC(smp1, continue); 634222861Shrs ELM_MALLOC(smp2, goto free1); 635222861Shrs if (TAILQ_EMPTY(&sm_dnssl_head)) { 636222861Shrs ELM_MALLOC(smp3, goto free2); 637222861Shrs smp3->sm_msg = resstr_sh_prefix; 638222861Shrs TAILQ_INSERT_TAIL(&sm_dnssl_head, smp3, 639222861Shrs sm_next); 640222861Shrs } 641222861Shrs smp1->sm_msg = rao->rao_msg; 642222861Shrs TAILQ_INSERT_TAIL(&sm_dnssl_head, smp1, 643222861Shrs sm_next); 644222861Shrs smp2->sm_msg = resstr_sp; 645222861Shrs TAILQ_INSERT_TAIL(&sm_dnssl_head, smp2, 646222861Shrs sm_next); 647222861Shrs dlen += strlen(rao->rao_msg) + 648222861Shrs strlen(resstr_sp); 649222732Shrs break; 650222861Shrs 651222861Shrs ifi->ifi_dnssl = IFI_DNSOPT_STATE_RECEIVED; 652222861Shrs default: 653222732Shrs break; 654222732Shrs } 655222861Shrs continue; 656222732Shrsfree2: 657222861Shrs free(smp2); 658222732Shrsfree1: 659222861Shrs free(smp1); 660222861Shrs } 661222732Shrs } 662222732Shrs /* Add \n for DNSSL list. */ 663222732Shrs if (!TAILQ_EMPTY(&sm_dnssl_head)) { 664222732Shrs ELM_MALLOC(smp1, goto ra_opt_handler_freeit); 665222732Shrs smp1->sm_msg = resstr_nl; 666222732Shrs TAILQ_INSERT_TAIL(&sm_dnssl_head, smp1, sm_next); 667222732Shrs } 668222732Shrs TAILQ_CONCAT(&sm_rdnss_head, &sm_dnssl_head, sm_next); 669222732Shrs 670222732Shrs if (!TAILQ_EMPTY(&sm_rdnss_head)) 671222732Shrs CALL_SCRIPT(RESADD, &sm_rdnss_head); 672222861Shrs else if (ifi->ifi_rdnss == IFI_DNSOPT_STATE_RECEIVED || 673222861Shrs ifi->ifi_dnssl == IFI_DNSOPT_STATE_RECEIVED) { 674222732Shrs CALL_SCRIPT(RESDEL, NULL); 675222861Shrs ifi->ifi_rdnss = IFI_DNSOPT_STATE_NOINFO; 676222861Shrs ifi->ifi_dnssl = IFI_DNSOPT_STATE_NOINFO; 677222861Shrs } 678222732Shrs 679222732Shrsra_opt_handler_freeit: 680222732Shrs /* Clear script message queue. */ 681222732Shrs if (!TAILQ_EMPTY(&sm_rdnss_head)) { 682222732Shrs while ((smp1 = TAILQ_FIRST(&sm_rdnss_head)) != NULL) { 683222732Shrs TAILQ_REMOVE(&sm_rdnss_head, smp1, sm_next); 684222732Shrs free(smp1); 685222732Shrs } 686222732Shrs } 687222861Shrs if (!TAILQ_EMPTY(&sm_dnssl_head)) { 688222861Shrs while ((smp1 = TAILQ_FIRST(&sm_dnssl_head)) != NULL) { 689222861Shrs TAILQ_REMOVE(&sm_dnssl_head, smp1, sm_next); 690222861Shrs free(smp1); 691222861Shrs } 692222861Shrs } 693222732Shrs return (0); 694222732Shrs} 695222732Shrs 696222861Shrsstatic struct ra_opt * 697222861Shrsfind_raopt(struct rainfo *rai, int type, void *msg, size_t len) 698222861Shrs{ 699222861Shrs struct ra_opt *rao; 700222861Shrs 701222861Shrs TAILQ_FOREACH(rao, &rai->rai_ra_opt, rao_next) { 702222861Shrs if (rao->rao_type == type && 703222861Shrs rao->rao_len == strlen(msg) && 704222861Shrs memcmp(rao->rao_msg, msg, len) == 0) 705222861Shrs break; 706222861Shrs } 707222861Shrs 708222861Shrs return (rao); 709222861Shrs} 710222861Shrs 711118661Sumestatic void 712222732Shrscall_script(const int argc, const char *const argv[], void *head) 713118661Sume{ 714222732Shrs const char *scriptpath; 715222732Shrs int fd[2]; 716222732Shrs int error; 717118661Sume pid_t pid, wpid; 718222732Shrs TAILQ_HEAD(, script_msg) *sm_head; 719118661Sume 720222732Shrs if ((scriptpath = argv[0]) == NULL) 721118661Sume return; 722118661Sume 723222732Shrs fd[0] = fd[1] = -1; 724222732Shrs sm_head = head; 725222732Shrs if (sm_head != NULL && !TAILQ_EMPTY(sm_head)) { 726222732Shrs error = pipe(fd); 727222732Shrs if (error) { 728222732Shrs warnmsg(LOG_ERR, __func__, 729222732Shrs "failed to create a pipe: %s", strerror(errno)); 730222732Shrs return; 731222732Shrs } 732222732Shrs } 733222732Shrs 734118661Sume /* launch the script */ 735118661Sume pid = fork(); 736118661Sume if (pid < 0) { 737118661Sume warnmsg(LOG_ERR, __func__, 738118661Sume "failed to fork: %s", strerror(errno)); 739118661Sume return; 740222732Shrs } else if (pid) { /* parent */ 741118661Sume int wstatus; 742118661Sume 743222732Shrs if (fd[0] != -1) { /* Send message to the child if any. */ 744222732Shrs ssize_t len; 745222732Shrs struct script_msg *smp; 746222732Shrs 747222732Shrs close(fd[0]); 748222732Shrs TAILQ_FOREACH(smp, sm_head, sm_next) { 749222732Shrs len = strlen(smp->sm_msg); 750222732Shrs warnmsg(LOG_DEBUG, __func__, 751222732Shrs "write to child = %s(%zd)", 752222732Shrs smp->sm_msg, len); 753222732Shrs if (write(fd[1], smp->sm_msg, len) != len) { 754222732Shrs warnmsg(LOG_ERR, __func__, 755222732Shrs "write to child failed: %s", 756222732Shrs strerror(errno)); 757222732Shrs break; 758222732Shrs } 759222732Shrs } 760222732Shrs close(fd[1]); 761222732Shrs } 762118661Sume do { 763118661Sume wpid = wait(&wstatus); 764118661Sume } while (wpid != pid && wpid > 0); 765118661Sume 766118661Sume if (wpid < 0) 767118661Sume warnmsg(LOG_ERR, __func__, 768118661Sume "wait: %s", strerror(errno)); 769222732Shrs else 770118661Sume warnmsg(LOG_DEBUG, __func__, 771118661Sume "script \"%s\" terminated", scriptpath); 772222732Shrs } else { /* child */ 773222732Shrs int nullfd; 774222732Shrs char **_argv; 775118661Sume 776118661Sume if (safefile(scriptpath)) { 777118661Sume warnmsg(LOG_ERR, __func__, 778118661Sume "script \"%s\" cannot be executed safely", 779118661Sume scriptpath); 780118661Sume exit(1); 781118661Sume } 782222732Shrs nullfd = open("/dev/null", O_RDWR); 783222732Shrs if (nullfd < 0) { 784222732Shrs warnmsg(LOG_ERR, __func__, 785222732Shrs "open /dev/null: %s", strerror(errno)); 786222732Shrs exit(1); 787222732Shrs } 788222732Shrs if (fd[0] != -1) { /* Receive message from STDIN if any. */ 789222732Shrs close(fd[1]); 790222732Shrs if (fd[0] != STDIN_FILENO) { 791222732Shrs /* Connect a pipe read-end to child's STDIN. */ 792222732Shrs if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) { 793222732Shrs warnmsg(LOG_ERR, __func__, 794222732Shrs "dup2 STDIN: %s", strerror(errno)); 795222732Shrs exit(1); 796222732Shrs } 797222732Shrs close(fd[0]); 798222732Shrs } 799222732Shrs } else 800222732Shrs dup2(nullfd, STDIN_FILENO); 801222732Shrs 802222732Shrs dup2(nullfd, STDOUT_FILENO); 803222732Shrs dup2(nullfd, STDERR_FILENO); 804222732Shrs if (nullfd > STDERR_FILENO) 805222732Shrs close(nullfd); 806118661Sume 807222732Shrs _argv = malloc(sizeof(*_argv) * argc); 808222732Shrs if (_argv == NULL) { 809222732Shrs warnmsg(LOG_ERR, __func__, 810222732Shrs "malloc: %s", strerror(errno)); 811222732Shrs exit(1); 812118661Sume } 813222732Shrs memcpy(_argv, argv, (size_t)argc); 814222732Shrs execv(scriptpath, (char *const *)_argv); 815118661Sume warnmsg(LOG_ERR, __func__, "child: exec failed: %s", 816118661Sume strerror(errno)); 817222732Shrs exit(1); 818118661Sume } 819118661Sume 820118661Sume return; 821118661Sume} 822118661Sume 823118661Sumestatic int 824124524Sumesafefile(const char *path) 825118661Sume{ 826118661Sume struct stat s; 827118661Sume uid_t myuid; 828118661Sume 829118661Sume /* no setuid */ 830118661Sume if (getuid() != geteuid()) { 831118661Sume warnmsg(LOG_NOTICE, __func__, 832118661Sume "setuid'ed execution not allowed\n"); 833118661Sume return (-1); 834118661Sume } 835118661Sume 836118661Sume if (lstat(path, &s) != 0) { 837118661Sume warnmsg(LOG_NOTICE, __func__, "lstat failed: %s", 838118661Sume strerror(errno)); 839118661Sume return (-1); 840118661Sume } 841118661Sume 842118661Sume /* the file must be owned by the running uid */ 843118661Sume myuid = getuid(); 844118661Sume if (s.st_uid != myuid) { 845118661Sume warnmsg(LOG_NOTICE, __func__, 846118661Sume "%s has invalid owner uid\n", path); 847118661Sume return (-1); 848118661Sume } 849118661Sume 850118661Sume switch (s.st_mode & S_IFMT) { 851118661Sume case S_IFREG: 852118661Sume break; 853118661Sume default: 854118661Sume warnmsg(LOG_NOTICE, __func__, 855118661Sume "%s is an invalid file type 0x%o\n", 856118661Sume path, (s.st_mode & S_IFMT)); 857118661Sume return (-1); 858118661Sume } 859118661Sume 860118661Sume return (0); 861118661Sume} 862222732Shrs 863222732Shrs/* Decode domain name label encoding in RFC 1035 Section 3.1 */ 864222732Shrsstatic size_t 865222732Shrsdname_labeldec(char *dst, size_t dlen, const char *src) 866222732Shrs{ 867222732Shrs size_t len; 868222732Shrs const char *src_origin; 869222732Shrs const char *src_last; 870222732Shrs const char *dst_origin; 871222732Shrs 872222732Shrs src_origin = src; 873222732Shrs src_last = strchr(src, '\0'); 874222732Shrs dst_origin = dst; 875222732Shrs memset(dst, '\0', dlen); 876222732Shrs while (src && (len = (uint8_t)(*src++) & 0x3f) && 877222732Shrs (src + len) <= src_last) { 878222732Shrs if (dst != dst_origin) 879222732Shrs *dst++ = '.'; 880222732Shrs warnmsg(LOG_DEBUG, __func__, "labellen = %zd", len); 881222732Shrs memcpy(dst, src, len); 882222732Shrs src += len; 883222732Shrs dst += len; 884222732Shrs } 885222732Shrs *dst = '\0'; 886222732Shrs 887222732Shrs /* 888222732Shrs * XXX validate that domain name only contains valid characters 889222732Shrs * for two reasons: 1) correctness, 2) we do not want to pass 890222732Shrs * possible malicious, unescaped characters like `` to a script 891222732Shrs * or program that could be exploited that way. 892222732Shrs */ 893222732Shrs 894222732Shrs return (src - src_origin); 895222732Shrs} 896