| 1 | /* $KAME: rtsol.c,v 1.11 2000/08/13 06:14:59 itojun Exp $ */ |
| 2 | |
| 3 | /* |
| 4 | * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. |
| 5 | * All rights reserved. |
| 6 | * |
| 7 | * Redistribution and use in source and binary forms, with or without |
| 8 | * modification, are permitted provided that the following conditions |
| 9 | * are met: |
| 10 | * 1. Redistributions of source code must retain the above copyright |
| 11 | * notice, this list of conditions and the following disclaimer. |
| 12 | * 2. Redistributions in binary form must reproduce the above copyright |
| 13 | * notice, this list of conditions and the following disclaimer in the |
| 14 | * documentation and/or other materials provided with the distribution. |
| 15 | * 3. Neither the name of the project nor the names of its contributors |
| 16 | * may be used to endorse or promote products derived from this software |
| 17 | * without specific prior written permission. |
| 18 | * |
| 19 | * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND |
| 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE |
| 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 29 | * SUCH DAMAGE. |
| 30 | * |
| 31 | * $FreeBSD: src/usr.sbin/rtsold/rtsol.c,v 1.1.2.3 2001/07/03 11:02:16 ume Exp $ |
| 32 | */ |
| 33 | |
| 34 | #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 | |
| 40 | #include <net/if.h> |
| 41 | #include <net/route.h> |
| 42 | #include <net/if_dl.h> |
| 43 | |
| 44 | #include <netinet/in.h> |
| 45 | #include <netinet/ip6.h> |
| 46 | #include <netinet6/ip6_var.h> |
| 47 | #include <netinet/icmp6.h> |
| 48 | |
| 49 | #include <arpa/inet.h> |
| 50 | |
| 51 | #include <time.h> |
| 52 | #include <unistd.h> |
| 53 | #include <stdio.h> |
| 54 | #include <err.h> |
| 55 | #include <errno.h> |
| 56 | #include <string.h> |
| 57 | #include <stdlib.h> |
| 58 | #include <syslog.h> |
| 59 | #include "rtsold.h" |
| 60 | |
| 61 | #define ALLROUTER "ff02::2" |
| 62 | |
| 63 | static struct msghdr rcvmhdr; |
| 64 | static struct msghdr sndmhdr; |
| 65 | static struct iovec rcviov[2]; |
| 66 | static struct iovec sndiov[2]; |
| 67 | static struct sockaddr_in6 from; |
| 68 | |
| 69 | int rssock; |
| 70 | |
| 71 | static struct sockaddr_in6 sin6_allrouters = {sizeof(sin6_allrouters), AF_INET6}; |
| 72 | |
| 73 | int |
| 74 | sockopen() |
| 75 | { |
| 76 | int on; |
| 77 | struct icmp6_filter filt; |
| 78 | static u_char answer[1500]; |
| 79 | int rcvcmsglen, sndcmsglen; |
| 80 | static u_char *rcvcmsgbuf = NULL, *sndcmsgbuf = NULL; |
| 81 | |
| 82 | sndcmsglen = rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + |
| 83 | CMSG_SPACE(sizeof(int)); |
| 84 | if (rcvcmsgbuf == NULL && (rcvcmsgbuf = malloc(rcvcmsglen)) == NULL) { |
| 85 | warnmsg(LOG_ERR, __FUNCTION__, |
| 86 | "malloc for receive msghdr failed"); |
| 87 | return(-1); |
| 88 | } |
| 89 | if (sndcmsgbuf == NULL && (sndcmsgbuf = malloc(sndcmsglen)) == NULL) { |
| 90 | warnmsg(LOG_ERR, __FUNCTION__, |
| 91 | "malloc for send msghdr failed"); |
| 92 | return(-1); |
| 93 | } |
| 94 | memset(&sin6_allrouters, 0, sizeof(struct sockaddr_in6)); |
| 95 | if (inet_pton(AF_INET6, ALLROUTER, |
| 96 | &sin6_allrouters.sin6_addr.s6_addr) != 1) { |
| 97 | warnmsg(LOG_ERR, __FUNCTION__, "inet_pton failed for %s", |
| 98 | ALLROUTER); |
| 99 | return(-1); |
| 100 | } |
| 101 | |
| 102 | if ((rssock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { |
| 103 | warnmsg(LOG_ERR, __FUNCTION__, "socket: %s", strerror(errno)); |
| 104 | return(-1); |
| 105 | } |
| 106 | |
| 107 | /* specify to tell receiving interface */ |
| 108 | on = 1; |
| 109 | #ifdef IPV6_RECVPKTINFO |
| 110 | if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, |
| 111 | sizeof(on)) < 0) { |
| 112 | warnmsg(LOG_ERR, __FUNCTION__, "IPV6_RECVPKTINFO: %s", |
| 113 | strerror(errno)); |
| 114 | exit(1); |
| 115 | } |
| 116 | #else /* old adv. API */ |
| 117 | if (setsockopt(rssock, IPPROTO_IPV6, IPV6_PKTINFO, &on, |
| 118 | sizeof(on)) < 0) { |
| 119 | warnmsg(LOG_ERR, __FUNCTION__, "IPV6_PKTINFO: %s", |
| 120 | strerror(errno)); |
| 121 | exit(1); |
| 122 | } |
| 123 | #endif |
| 124 | |
| 125 | on = 1; |
| 126 | /* specify to tell value of hoplimit field of received IP6 hdr */ |
| 127 | #ifdef IPV6_RECVHOPLIMIT |
| 128 | if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, |
| 129 | sizeof(on)) < 0) { |
| 130 | warnmsg(LOG_ERR, __FUNCTION__, "IPV6_RECVHOPLIMIT: %s", |
| 131 | strerror(errno)); |
| 132 | exit(1); |
| 133 | } |
| 134 | #else /* old adv. API */ |
| 135 | if (setsockopt(rssock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, |
| 136 | sizeof(on)) < 0) { |
| 137 | warnmsg(LOG_ERR, __FUNCTION__, "IPV6_HOPLIMIT: %s", |
| 138 | strerror(errno)); |
| 139 | exit(1); |
| 140 | } |
| 141 | #endif |
| 142 | |
| 143 | /* specfiy to accept only router advertisements on the socket */ |
| 144 | ICMP6_FILTER_SETBLOCKALL(&filt); |
| 145 | ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt); |
| 146 | if (setsockopt(rssock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, |
| 147 | sizeof(filt)) == -1) { |
| 148 | warnmsg(LOG_ERR, __FUNCTION__, "setsockopt(ICMP6_FILTER): %s", |
| 149 | strerror(errno)); |
| 150 | return(-1); |
| 151 | } |
| 152 | |
| 153 | /* initialize msghdr for receiving packets */ |
| 154 | rcviov[0].iov_base = (caddr_t)answer; |
| 155 | rcviov[0].iov_len = sizeof(answer); |
| 156 | rcvmhdr.msg_name = (caddr_t)&from; |
| 157 | rcvmhdr.msg_namelen = sizeof(from); |
| 158 | rcvmhdr.msg_iov = rcviov; |
| 159 | rcvmhdr.msg_iovlen = 1; |
| 160 | rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf; |
| 161 | rcvmhdr.msg_controllen = rcvcmsglen; |
| 162 | |
| 163 | /* initialize msghdr for sending packets */ |
| 164 | sndmhdr.msg_namelen = sizeof(struct sockaddr_in6); |
| 165 | sndmhdr.msg_iov = sndiov; |
| 166 | sndmhdr.msg_iovlen = 1; |
| 167 | sndmhdr.msg_control = (caddr_t)sndcmsgbuf; |
| 168 | sndmhdr.msg_controllen = sndcmsglen; |
| 169 | |
| 170 | return(rssock); |
| 171 | } |
| 172 | |
| 173 | void |
| 174 | sendpacket(struct ifinfo *ifinfo) |
| 175 | { |
| 176 | int i; |
| 177 | struct cmsghdr *cm; |
| 178 | struct in6_pktinfo *pi; |
| 179 | |
| 180 | sndmhdr.msg_name = (caddr_t)&sin6_allrouters; |
| 181 | sndmhdr.msg_iov[0].iov_base = (caddr_t)ifinfo->rs_data; |
| 182 | sndmhdr.msg_iov[0].iov_len = ifinfo->rs_datalen; |
| 183 | |
| 184 | cm = CMSG_FIRSTHDR(&sndmhdr); |
| 185 | /* specify the outgoing interface */ |
| 186 | cm->cmsg_level = IPPROTO_IPV6; |
| 187 | cm->cmsg_type = IPV6_PKTINFO; |
| 188 | cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); |
| 189 | pi = (struct in6_pktinfo *)CMSG_DATA(cm); |
| 190 | memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/ |
| 191 | pi->ipi6_ifindex = ifinfo->sdl->sdl_index; |
| 192 | |
| 193 | /* specify the hop limit of the packet */ |
| 194 | { |
| 195 | int hoplimit = 255; |
| 196 | |
| 197 | cm = CMSG_NXTHDR(&sndmhdr, cm); |
| 198 | cm->cmsg_level = IPPROTO_IPV6; |
| 199 | cm->cmsg_type = IPV6_HOPLIMIT; |
| 200 | cm->cmsg_len = CMSG_LEN(sizeof(int)); |
| 201 | memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int)); |
| 202 | } |
| 203 | |
| 204 | warnmsg(LOG_DEBUG, |
| 205 | __FUNCTION__, "send RS on %s, whose state is %d", |
| 206 | ifinfo->ifname, ifinfo->state); |
| 207 | |
| 208 | i = sendmsg(rssock, &sndmhdr, 0); |
| 209 | |
| 210 | if (i < 0 || i != ifinfo->rs_datalen) { |
| 211 | /* |
| 212 | * ENETDOWN is not so serious, especially when using several |
| 213 | * network cards on a mobile node. We ignore it. |
| 214 | */ |
| 215 | if (errno != ENETDOWN || dflag > 0) |
| 216 | warnmsg(LOG_ERR, __FUNCTION__, "sendmsg on %s: %s", |
| 217 | ifinfo->ifname, strerror(errno)); |
| 218 | } |
| 219 | |
| 220 | /* update counter */ |
| 221 | ifinfo->probes++; |
| 222 | } |
| 223 | |
| 224 | void |
| 225 | rtsol_input(int s) |
| 226 | { |
| 227 | int i; |
| 228 | int *hlimp = NULL; |
| 229 | struct icmp6_hdr *icp; |
| 230 | int ifindex = 0; |
| 231 | struct cmsghdr *cm; |
| 232 | struct in6_pktinfo *pi = NULL; |
| 233 | struct ifinfo *ifi = NULL; |
| 234 | u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; |
| 235 | |
| 236 | /* get message */ |
| 237 | if ((i = recvmsg(s, &rcvmhdr, 0)) < 0) { |
| 238 | warnmsg(LOG_ERR, __FUNCTION__, "recvmsg: %s", strerror(errno)); |
| 239 | return; |
| 240 | } |
| 241 | |
| 242 | /* extract optional information via Advanced API */ |
| 243 | for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr); |
| 244 | cm; |
| 245 | cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) { |
| 246 | if (cm->cmsg_level == IPPROTO_IPV6 && |
| 247 | cm->cmsg_type == IPV6_PKTINFO && |
| 248 | cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { |
| 249 | pi = (struct in6_pktinfo *)(CMSG_DATA(cm)); |
| 250 | ifindex = pi->ipi6_ifindex; |
| 251 | } |
| 252 | if (cm->cmsg_level == IPPROTO_IPV6 && |
| 253 | cm->cmsg_type == IPV6_HOPLIMIT && |
| 254 | cm->cmsg_len == CMSG_LEN(sizeof(int))) |
| 255 | hlimp = (int *)CMSG_DATA(cm); |
| 256 | } |
| 257 | |
| 258 | if (ifindex == 0) { |
| 259 | warnmsg(LOG_ERR, |
| 260 | __FUNCTION__, "failed to get receiving interface"); |
| 261 | return; |
| 262 | } |
| 263 | if (hlimp == NULL) { |
| 264 | warnmsg(LOG_ERR, |
| 265 | __FUNCTION__, "failed to get receiving hop limit"); |
| 266 | return; |
| 267 | } |
| 268 | |
| 269 | if (i < sizeof(struct nd_router_advert)) { |
| 270 | warnmsg(LOG_ERR, |
| 271 | __FUNCTION__, "packet size(%d) is too short", i); |
| 272 | return; |
| 273 | } |
| 274 | |
| 275 | icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base; |
| 276 | |
| 277 | if (icp->icmp6_type != ND_ROUTER_ADVERT) { |
| 278 | warnmsg(LOG_ERR, __FUNCTION__, |
| 279 | "invalid icmp type(%d) from %s on %s", icp->icmp6_type, |
| 280 | inet_ntop(AF_INET6, &from.sin6_addr, (char *)ntopbuf, |
| 281 | INET6_ADDRSTRLEN), |
| 282 | if_indextoname(pi->ipi6_ifindex, (char *)ifnamebuf)); |
| 283 | return; |
| 284 | } |
| 285 | |
| 286 | if (icp->icmp6_code != 0) { |
| 287 | warnmsg(LOG_ERR, __FUNCTION__, |
| 288 | "invalid icmp code(%d) from %s on %s", icp->icmp6_code, |
| 289 | inet_ntop(AF_INET6, &from.sin6_addr, (char *)ntopbuf, |
| 290 | INET6_ADDRSTRLEN), |
| 291 | if_indextoname(pi->ipi6_ifindex, (char *)ifnamebuf)); |
| 292 | return; |
| 293 | } |
| 294 | |
| 295 | if (*hlimp != 255) { |
| 296 | warnmsg(LOG_NOTICE, __FUNCTION__, |
| 297 | "invalid RA with hop limit(%d) from %s on %s", |
| 298 | *hlimp, |
| 299 | inet_ntop(AF_INET6, &from.sin6_addr, (char *)ntopbuf, |
| 300 | INET6_ADDRSTRLEN), |
| 301 | if_indextoname(pi->ipi6_ifindex, (char *)ifnamebuf)); |
| 302 | return; |
| 303 | } |
| 304 | |
| 305 | if (pi && !IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) { |
| 306 | warnmsg(LOG_NOTICE, __FUNCTION__, |
| 307 | "invalid RA with non link-local source from %s on %s", |
| 308 | inet_ntop(AF_INET6, &from.sin6_addr, (char *)ntopbuf, |
| 309 | INET6_ADDRSTRLEN), |
| 310 | if_indextoname(pi->ipi6_ifindex, (char *)ifnamebuf)); |
| 311 | return; |
| 312 | } |
| 313 | |
| 314 | /* xxx: more validation? */ |
| 315 | |
| 316 | if ((ifi = find_ifinfo(pi->ipi6_ifindex)) == NULL) { |
| 317 | warnmsg(LOG_NOTICE, __FUNCTION__, |
| 318 | "received RA from %s on an unexpeced IF(%s)", |
| 319 | inet_ntop(AF_INET6, &from.sin6_addr, (char *)ntopbuf, |
| 320 | INET6_ADDRSTRLEN), |
| 321 | if_indextoname(pi->ipi6_ifindex, (char *)ifnamebuf)); |
| 322 | return; |
| 323 | } |
| 324 | |
| 325 | warnmsg(LOG_DEBUG, __FUNCTION__, |
| 326 | "received RA from %s on %s, state is %d", |
| 327 | inet_ntop(AF_INET6, &from.sin6_addr, (char *)ntopbuf, |
| 328 | INET6_ADDRSTRLEN), |
| 329 | ifi->ifname, ifi->state); |
| 330 | |
| 331 | ifi->racnt++; |
| 332 | |
| 333 | switch(ifi->state) { |
| 334 | case IFS_IDLE: /* should be ignored */ |
| 335 | case IFS_DELAY: /* right? */ |
| 336 | break; |
| 337 | case IFS_PROBE: |
| 338 | ifi->state = IFS_IDLE; |
| 339 | ifi->probes = 0; |
| 340 | rtsol_timer_update(ifi); |
| 341 | break; |
| 342 | } |
| 343 | } |