Annotation of sys/netinet6/icmp6.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: icmp6.c,v 1.94 2007/06/01 00:52:38 henning Exp $ */
! 2: /* $KAME: icmp6.c,v 1.217 2001/06/20 15:03:29 jinmei Exp $ */
! 3:
! 4: /*
! 5: * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
! 6: * All rights reserved.
! 7: *
! 8: * Redistribution and use in source and binary forms, with or without
! 9: * modification, are permitted provided that the following conditions
! 10: * are met:
! 11: * 1. Redistributions of source code must retain the above copyright
! 12: * notice, this list of conditions and the following disclaimer.
! 13: * 2. Redistributions in binary form must reproduce the above copyright
! 14: * notice, this list of conditions and the following disclaimer in the
! 15: * documentation and/or other materials provided with the distribution.
! 16: * 3. Neither the name of the project nor the names of its contributors
! 17: * may be used to endorse or promote products derived from this software
! 18: * without specific prior written permission.
! 19: *
! 20: * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
! 21: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 22: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 23: * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
! 24: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 25: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 26: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 27: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 28: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 29: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 30: * SUCH DAMAGE.
! 31: */
! 32:
! 33: /*
! 34: * Copyright (c) 1982, 1986, 1988, 1993
! 35: * The Regents of the University of California. All rights reserved.
! 36: *
! 37: * Redistribution and use in source and binary forms, with or without
! 38: * modification, are permitted provided that the following conditions
! 39: * are met:
! 40: * 1. Redistributions of source code must retain the above copyright
! 41: * notice, this list of conditions and the following disclaimer.
! 42: * 2. Redistributions in binary form must reproduce the above copyright
! 43: * notice, this list of conditions and the following disclaimer in the
! 44: * documentation and/or other materials provided with the distribution.
! 45: * 3. Neither the name of the University nor the names of its contributors
! 46: * may be used to endorse or promote products derived from this software
! 47: * without specific prior written permission.
! 48: *
! 49: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
! 50: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 51: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 52: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 53: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 54: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 55: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 56: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 57: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 58: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 59: * SUCH DAMAGE.
! 60: *
! 61: * @(#)ip_icmp.c 8.2 (Berkeley) 1/4/94
! 62: */
! 63:
! 64: #include "faith.h"
! 65: #include "carp.h"
! 66:
! 67: #include <sys/param.h>
! 68: #include <sys/systm.h>
! 69: #include <sys/malloc.h>
! 70: #include <sys/mbuf.h>
! 71: #include <sys/protosw.h>
! 72: #include <sys/socket.h>
! 73: #include <sys/socketvar.h>
! 74: #include <sys/time.h>
! 75: #include <sys/kernel.h>
! 76: #include <sys/syslog.h>
! 77: #include <sys/domain.h>
! 78:
! 79: #include <net/if.h>
! 80: #include <net/route.h>
! 81: #include <net/if_dl.h>
! 82: #include <net/if_types.h>
! 83:
! 84: #include <netinet/in.h>
! 85: #include <netinet/in_var.h>
! 86: #include <netinet/in_systm.h>
! 87: #include <netinet/ip.h>
! 88: #include <netinet/ip6.h>
! 89: #include <netinet6/ip6_var.h>
! 90: #include <netinet/icmp6.h>
! 91: #include <netinet6/mld6_var.h>
! 92: #include <netinet/in_pcb.h>
! 93: #include <netinet6/nd6.h>
! 94: #include <netinet6/in6_ifattach.h>
! 95: #include <netinet6/ip6protosw.h>
! 96:
! 97: #if NCARP > 0
! 98: #include <netinet/ip_carp.h>
! 99: #endif
! 100:
! 101: /* inpcb members */
! 102: #define in6pcb inpcb
! 103: #define in6p_laddr inp_laddr6
! 104: #define in6p_faddr inp_faddr6
! 105: #define in6p_icmp6filt inp_icmp6filt
! 106: #define in6p_route inp_route
! 107: #define in6p_socket inp_socket
! 108: #define in6p_flags inp_flags
! 109: #define in6p_moptions inp_moptions6
! 110: #define in6p_outputopts inp_outputopts6
! 111: #define in6p_ip6 inp_ipv6
! 112: #define in6p_flowinfo inp_flowinfo
! 113: #define in6p_sp inp_sp
! 114: #define in6p_next inp_next
! 115: #define in6p_prev inp_prev
! 116: /* macro names */
! 117: #define sotoin6pcb sotoinpcb
! 118: /* function names */
! 119: #define in6_pcbdetach in_pcbdetach
! 120: #define in6_rtchange in_rtchange
! 121:
! 122: /*
! 123: * for KAME src sync over BSD*'s. XXX: FreeBSD (>=3) are VERY different from
! 124: * others...
! 125: */
! 126: #define in6p_ip6_nxt inp_ipv6.ip6_nxt
! 127:
! 128: extern struct domain inet6domain;
! 129: extern struct ip6protosw inet6sw[];
! 130: extern u_char ip6_protox[];
! 131:
! 132: struct icmp6stat icmp6stat;
! 133:
! 134: extern struct inpcbtable rawin6pcbtable;
! 135: extern int icmp6errppslim;
! 136: static int icmp6errpps_count = 0;
! 137: static struct timeval icmp6errppslim_last;
! 138: extern int icmp6_nodeinfo;
! 139:
! 140: /*
! 141: * List of callbacks to notify when Path MTU changes are made.
! 142: */
! 143: struct icmp6_mtudisc_callback {
! 144: LIST_ENTRY(icmp6_mtudisc_callback) mc_list;
! 145: void (*mc_func)(struct in6_addr *);
! 146: };
! 147:
! 148: LIST_HEAD(, icmp6_mtudisc_callback) icmp6_mtudisc_callbacks =
! 149: LIST_HEAD_INITIALIZER(&icmp6_mtudisc_callbacks);
! 150:
! 151: static struct rttimer_queue *icmp6_mtudisc_timeout_q = NULL;
! 152: extern int pmtu_expire;
! 153:
! 154: /* XXX do these values make any sense? */
! 155: static int icmp6_mtudisc_hiwat = 1280;
! 156: static int icmp6_mtudisc_lowat = 256;
! 157:
! 158: /*
! 159: * keep track of # of redirect routes.
! 160: */
! 161: static struct rttimer_queue *icmp6_redirect_timeout_q = NULL;
! 162:
! 163: /* XXX experimental, turned off */
! 164: static int icmp6_redirect_hiwat = -1;
! 165: static int icmp6_redirect_lowat = -1;
! 166:
! 167: static void icmp6_errcount(struct icmp6errstat *, int, int);
! 168: static int icmp6_rip6_input(struct mbuf **, int);
! 169: static int icmp6_ratelimit(const struct in6_addr *, const int, const int);
! 170: static const char *icmp6_redirect_diag(struct in6_addr *,
! 171: struct in6_addr *, struct in6_addr *);
! 172: static struct mbuf *ni6_input(struct mbuf *, int);
! 173: static struct mbuf *ni6_nametodns(const char *, int, int);
! 174: static int ni6_dnsmatch(const char *, int, const char *, int);
! 175: static int ni6_addrs(struct icmp6_nodeinfo *, struct mbuf *,
! 176: struct ifnet **, char *);
! 177: static int ni6_store_addrs(struct icmp6_nodeinfo *, struct icmp6_nodeinfo *,
! 178: struct ifnet *, int);
! 179: static int icmp6_notify_error(struct mbuf *, int, int, int);
! 180: static struct rtentry *icmp6_mtudisc_clone(struct sockaddr *);
! 181: static void icmp6_mtudisc_timeout(struct rtentry *, struct rttimer *);
! 182: static void icmp6_redirect_timeout(struct rtentry *, struct rttimer *);
! 183:
! 184: void
! 185: icmp6_init()
! 186: {
! 187: mld6_init();
! 188: icmp6_mtudisc_timeout_q = rt_timer_queue_create(pmtu_expire);
! 189: icmp6_redirect_timeout_q = rt_timer_queue_create(icmp6_redirtimeout);
! 190: }
! 191:
! 192: static void
! 193: icmp6_errcount(stat, type, code)
! 194: struct icmp6errstat *stat;
! 195: int type, code;
! 196: {
! 197: switch (type) {
! 198: case ICMP6_DST_UNREACH:
! 199: switch (code) {
! 200: case ICMP6_DST_UNREACH_NOROUTE:
! 201: stat->icp6errs_dst_unreach_noroute++;
! 202: return;
! 203: case ICMP6_DST_UNREACH_ADMIN:
! 204: stat->icp6errs_dst_unreach_admin++;
! 205: return;
! 206: case ICMP6_DST_UNREACH_BEYONDSCOPE:
! 207: stat->icp6errs_dst_unreach_beyondscope++;
! 208: return;
! 209: case ICMP6_DST_UNREACH_ADDR:
! 210: stat->icp6errs_dst_unreach_addr++;
! 211: return;
! 212: case ICMP6_DST_UNREACH_NOPORT:
! 213: stat->icp6errs_dst_unreach_noport++;
! 214: return;
! 215: }
! 216: break;
! 217: case ICMP6_PACKET_TOO_BIG:
! 218: stat->icp6errs_packet_too_big++;
! 219: return;
! 220: case ICMP6_TIME_EXCEEDED:
! 221: switch (code) {
! 222: case ICMP6_TIME_EXCEED_TRANSIT:
! 223: stat->icp6errs_time_exceed_transit++;
! 224: return;
! 225: case ICMP6_TIME_EXCEED_REASSEMBLY:
! 226: stat->icp6errs_time_exceed_reassembly++;
! 227: return;
! 228: }
! 229: break;
! 230: case ICMP6_PARAM_PROB:
! 231: switch (code) {
! 232: case ICMP6_PARAMPROB_HEADER:
! 233: stat->icp6errs_paramprob_header++;
! 234: return;
! 235: case ICMP6_PARAMPROB_NEXTHEADER:
! 236: stat->icp6errs_paramprob_nextheader++;
! 237: return;
! 238: case ICMP6_PARAMPROB_OPTION:
! 239: stat->icp6errs_paramprob_option++;
! 240: return;
! 241: }
! 242: break;
! 243: case ND_REDIRECT:
! 244: stat->icp6errs_redirect++;
! 245: return;
! 246: }
! 247: stat->icp6errs_unknown++;
! 248: }
! 249:
! 250: /*
! 251: * Register a Path MTU Discovery callback.
! 252: */
! 253: void
! 254: icmp6_mtudisc_callback_register(func)
! 255: void (*func)(struct in6_addr *);
! 256: {
! 257: struct icmp6_mtudisc_callback *mc;
! 258:
! 259: for (mc = LIST_FIRST(&icmp6_mtudisc_callbacks); mc != NULL;
! 260: mc = LIST_NEXT(mc, mc_list)) {
! 261: if (mc->mc_func == func)
! 262: return;
! 263: }
! 264:
! 265: mc = malloc(sizeof(*mc), M_PCB, M_NOWAIT);
! 266: if (mc == NULL)
! 267: panic("icmp6_mtudisc_callback_register");
! 268:
! 269: mc->mc_func = func;
! 270: LIST_INSERT_HEAD(&icmp6_mtudisc_callbacks, mc, mc_list);
! 271: }
! 272:
! 273: /*
! 274: * Generate an error packet of type error in response to bad IP6 packet.
! 275: */
! 276: void
! 277: icmp6_error(m, type, code, param)
! 278: struct mbuf *m;
! 279: int type, code, param;
! 280: {
! 281: struct ip6_hdr *oip6, *nip6;
! 282: struct icmp6_hdr *icmp6;
! 283: u_int preplen;
! 284: int off;
! 285: int nxt;
! 286:
! 287: icmp6stat.icp6s_error++;
! 288:
! 289: /* count per-type-code statistics */
! 290: icmp6_errcount(&icmp6stat.icp6s_outerrhist, type, code);
! 291:
! 292: if (m->m_len < sizeof(struct ip6_hdr)) {
! 293: m = m_pullup(m, sizeof(struct ip6_hdr));
! 294: if (m == NULL)
! 295: return;
! 296: }
! 297: oip6 = mtod(m, struct ip6_hdr *);
! 298:
! 299: /*
! 300: * If the destination address of the erroneous packet is a multicast
! 301: * address, or the packet was sent using link-layer multicast,
! 302: * we should basically suppress sending an error (RFC 2463, Section
! 303: * 2.4).
! 304: * We have two exceptions (the item e.2 in that section):
! 305: * - the Pakcet Too Big message can be sent for path MTU discovery.
! 306: * - the Parameter Problem Message that can be allowed an icmp6 error
! 307: * in the option type field. This check has been done in
! 308: * ip6_unknown_opt(), so we can just check the type and code.
! 309: */
! 310: if ((m->m_flags & (M_BCAST|M_MCAST) ||
! 311: IN6_IS_ADDR_MULTICAST(&oip6->ip6_dst)) &&
! 312: (type != ICMP6_PACKET_TOO_BIG &&
! 313: (type != ICMP6_PARAM_PROB ||
! 314: code != ICMP6_PARAMPROB_OPTION)))
! 315: goto freeit;
! 316:
! 317: /*
! 318: * RFC 2463, 2.4 (e.5): source address check.
! 319: * XXX: the case of anycast source?
! 320: */
! 321: if (IN6_IS_ADDR_UNSPECIFIED(&oip6->ip6_src) ||
! 322: IN6_IS_ADDR_MULTICAST(&oip6->ip6_src))
! 323: goto freeit;
! 324:
! 325: /*
! 326: * If we are about to send ICMPv6 against ICMPv6 error/redirect,
! 327: * don't do it.
! 328: */
! 329: nxt = -1;
! 330: off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt);
! 331: if (off >= 0 && nxt == IPPROTO_ICMPV6) {
! 332: struct icmp6_hdr *icp;
! 333:
! 334: IP6_EXTHDR_GET(icp, struct icmp6_hdr *, m, off,
! 335: sizeof(*icp));
! 336: if (icp == NULL) {
! 337: icmp6stat.icp6s_tooshort++;
! 338: return;
! 339: }
! 340: if (icp->icmp6_type < ICMP6_ECHO_REQUEST ||
! 341: icp->icmp6_type == ND_REDIRECT) {
! 342: /*
! 343: * ICMPv6 error
! 344: * Special case: for redirect (which is
! 345: * informational) we must not send icmp6 error.
! 346: */
! 347: icmp6stat.icp6s_canterror++;
! 348: goto freeit;
! 349: } else {
! 350: /* ICMPv6 informational - send the error */
! 351: }
! 352: }
! 353: else {
! 354: /* non-ICMPv6 - send the error */
! 355: }
! 356:
! 357: oip6 = mtod(m, struct ip6_hdr *); /* adjust pointer */
! 358:
! 359: /* Finally, do rate limitation check. */
! 360: if (icmp6_ratelimit(&oip6->ip6_src, type, code)) {
! 361: icmp6stat.icp6s_toofreq++;
! 362: goto freeit;
! 363: }
! 364:
! 365: /*
! 366: * OK, ICMP6 can be generated.
! 367: */
! 368:
! 369: if (m->m_pkthdr.len >= ICMPV6_PLD_MAXLEN)
! 370: m_adj(m, ICMPV6_PLD_MAXLEN - m->m_pkthdr.len);
! 371:
! 372: preplen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr);
! 373: M_PREPEND(m, preplen, M_DONTWAIT);
! 374: if (m && m->m_len < preplen)
! 375: m = m_pullup(m, preplen);
! 376: if (m == NULL) {
! 377: nd6log((LOG_DEBUG, "ENOBUFS in icmp6_error %d\n", __LINE__));
! 378: return;
! 379: }
! 380:
! 381: nip6 = mtod(m, struct ip6_hdr *);
! 382: nip6->ip6_src = oip6->ip6_src;
! 383: nip6->ip6_dst = oip6->ip6_dst;
! 384:
! 385: if (IN6_IS_SCOPE_EMBED(&oip6->ip6_src))
! 386: oip6->ip6_src.s6_addr16[1] = 0;
! 387: if (IN6_IS_SCOPE_EMBED(&oip6->ip6_dst))
! 388: oip6->ip6_dst.s6_addr16[1] = 0;
! 389:
! 390: icmp6 = (struct icmp6_hdr *)(nip6 + 1);
! 391: icmp6->icmp6_type = type;
! 392: icmp6->icmp6_code = code;
! 393: icmp6->icmp6_pptr = htonl((u_int32_t)param);
! 394:
! 395: /*
! 396: * icmp6_reflect() is designed to be in the input path.
! 397: * icmp6_error() can be called from both input and outut path,
! 398: * and if we are in output path rcvif could contain bogus value.
! 399: * clear m->m_pkthdr.rcvif for safety, we should have enough scope
! 400: * information in ip header (nip6).
! 401: */
! 402: m->m_pkthdr.rcvif = NULL;
! 403:
! 404: icmp6stat.icp6s_outhist[type]++;
! 405: icmp6_reflect(m, sizeof(struct ip6_hdr)); /* header order: IPv6 - ICMPv6 */
! 406:
! 407: return;
! 408:
! 409: freeit:
! 410: /*
! 411: * If we can't tell wheter or not we can generate ICMP6, free it.
! 412: */
! 413: m_freem(m);
! 414: }
! 415:
! 416: /*
! 417: * Process a received ICMP6 message.
! 418: */
! 419: int
! 420: icmp6_input(mp, offp, proto)
! 421: struct mbuf **mp;
! 422: int *offp, proto;
! 423: {
! 424: struct mbuf *m = *mp, *n;
! 425: struct ip6_hdr *ip6, *nip6;
! 426: struct icmp6_hdr *icmp6, *nicmp6;
! 427: int off = *offp;
! 428: int icmp6len = m->m_pkthdr.len - *offp;
! 429: int code, sum, noff;
! 430:
! 431: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_msg);
! 432:
! 433: /*
! 434: * Locate icmp6 structure in mbuf, and check
! 435: * that not corrupted and of at least minimum length
! 436: */
! 437:
! 438: ip6 = mtod(m, struct ip6_hdr *);
! 439: if (icmp6len < sizeof(struct icmp6_hdr)) {
! 440: icmp6stat.icp6s_tooshort++;
! 441: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_error);
! 442: goto freeit;
! 443: }
! 444:
! 445: /*
! 446: * calculate the checksum
! 447: */
! 448: IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, sizeof(*icmp6));
! 449: if (icmp6 == NULL) {
! 450: icmp6stat.icp6s_tooshort++;
! 451: return IPPROTO_DONE;
! 452: }
! 453: code = icmp6->icmp6_code;
! 454:
! 455: if ((sum = in6_cksum(m, IPPROTO_ICMPV6, off, icmp6len)) != 0) {
! 456: nd6log((LOG_ERR,
! 457: "ICMP6 checksum error(%d|%x) %s\n",
! 458: icmp6->icmp6_type, sum, ip6_sprintf(&ip6->ip6_src)));
! 459: icmp6stat.icp6s_checksum++;
! 460: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_error);
! 461: goto freeit;
! 462: }
! 463:
! 464: #if defined(NFAITH) && 0 < NFAITH
! 465: if (m->m_pkthdr.rcvif && m->m_pkthdr.rcvif->if_type == IFT_FAITH) {
! 466: /*
! 467: * Deliver very specific ICMP6 type only.
! 468: * This is important to deliver TOOBIG. Otherwise PMTUD
! 469: * will not work.
! 470: */
! 471: switch (icmp6->icmp6_type) {
! 472: case ICMP6_DST_UNREACH:
! 473: case ICMP6_PACKET_TOO_BIG:
! 474: case ICMP6_TIME_EXCEEDED:
! 475: break;
! 476: default:
! 477: goto freeit;
! 478: }
! 479: }
! 480: #endif
! 481:
! 482: #if NCARP > 0
! 483: if (m->m_pkthdr.rcvif->if_type == IFT_CARP &&
! 484: m->m_pkthdr.rcvif->if_flags & IFF_LINK0 &&
! 485: icmp6->icmp6_type == ICMP6_ECHO_REQUEST &&
! 486: carp_lsdrop(m, AF_INET6, ip6->ip6_src.s6_addr32,
! 487: ip6->ip6_dst.s6_addr32))
! 488: goto freeit;
! 489: #endif
! 490: icmp6stat.icp6s_inhist[icmp6->icmp6_type]++;
! 491:
! 492: switch (icmp6->icmp6_type) {
! 493: case ICMP6_DST_UNREACH:
! 494: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_dstunreach);
! 495: switch (code) {
! 496: case ICMP6_DST_UNREACH_NOROUTE:
! 497: code = PRC_UNREACH_NET;
! 498: break;
! 499: case ICMP6_DST_UNREACH_ADMIN:
! 500: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_adminprohib);
! 501: code = PRC_UNREACH_PROTOCOL; /* is this a good code? */
! 502: break;
! 503: case ICMP6_DST_UNREACH_ADDR:
! 504: code = PRC_HOSTDEAD;
! 505: break;
! 506: #ifdef COMPAT_RFC1885
! 507: case ICMP6_DST_UNREACH_NOTNEIGHBOR:
! 508: code = PRC_UNREACH_SRCFAIL;
! 509: break;
! 510: #else
! 511: case ICMP6_DST_UNREACH_BEYONDSCOPE:
! 512: /* I mean "source address was incorrect." */
! 513: code = PRC_PARAMPROB;
! 514: break;
! 515: #endif
! 516: case ICMP6_DST_UNREACH_NOPORT:
! 517: code = PRC_UNREACH_PORT;
! 518: break;
! 519: default:
! 520: goto badcode;
! 521: }
! 522: goto deliver;
! 523:
! 524: case ICMP6_PACKET_TOO_BIG:
! 525: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_pkttoobig);
! 526:
! 527: code = PRC_MSGSIZE;
! 528:
! 529: /*
! 530: * Updating the path MTU will be done after examining
! 531: * intermediate extension headers.
! 532: */
! 533: goto deliver;
! 534:
! 535: case ICMP6_TIME_EXCEEDED:
! 536: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_timeexceed);
! 537: switch (code) {
! 538: case ICMP6_TIME_EXCEED_TRANSIT:
! 539: code = PRC_TIMXCEED_INTRANS;
! 540: break;
! 541: case ICMP6_TIME_EXCEED_REASSEMBLY:
! 542: code = PRC_TIMXCEED_REASS;
! 543: break;
! 544: default:
! 545: goto badcode;
! 546: }
! 547: goto deliver;
! 548:
! 549: case ICMP6_PARAM_PROB:
! 550: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_paramprob);
! 551: switch (code) {
! 552: case ICMP6_PARAMPROB_NEXTHEADER:
! 553: code = PRC_UNREACH_PROTOCOL;
! 554: break;
! 555: case ICMP6_PARAMPROB_HEADER:
! 556: case ICMP6_PARAMPROB_OPTION:
! 557: code = PRC_PARAMPROB;
! 558: break;
! 559: default:
! 560: goto badcode;
! 561: }
! 562: goto deliver;
! 563:
! 564: case ICMP6_ECHO_REQUEST:
! 565: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_echo);
! 566: if (code != 0)
! 567: goto badcode;
! 568: /*
! 569: * Copy mbuf to send to two data paths: userland socket(s),
! 570: * and to the querier (echo reply).
! 571: * m: a copy for socket, n: a copy for querier
! 572: */
! 573: if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
! 574: /* Give up local */
! 575: n = m;
! 576: m = NULL;
! 577: goto deliverecho;
! 578: }
! 579: /*
! 580: * If the first mbuf is shared, or the first mbuf is too short,
! 581: * copy the first part of the data into a fresh mbuf.
! 582: * Otherwise, we will wrongly overwrite both copies.
! 583: */
! 584: if ((n->m_flags & M_EXT) != 0 ||
! 585: n->m_len < off + sizeof(struct icmp6_hdr)) {
! 586: struct mbuf *n0 = n;
! 587: const int maxlen = sizeof(*nip6) + sizeof(*nicmp6);
! 588:
! 589: /*
! 590: * Prepare an internal mbuf. m_pullup() doesn't
! 591: * always copy the length we specified.
! 592: */
! 593: if (maxlen >= MCLBYTES) {
! 594: /* Give up remote */
! 595: m_freem(n0);
! 596: break;
! 597: }
! 598: MGETHDR(n, M_DONTWAIT, n0->m_type);
! 599: if (n && maxlen >= MHLEN) {
! 600: MCLGET(n, M_DONTWAIT);
! 601: if ((n->m_flags & M_EXT) == 0) {
! 602: m_free(n);
! 603: n = NULL;
! 604: }
! 605: }
! 606: if (n == NULL) {
! 607: /* Give up local */
! 608: m_freem(n0);
! 609: n = m;
! 610: m = NULL;
! 611: goto deliverecho;
! 612: }
! 613: M_MOVE_PKTHDR(n, n0);
! 614: /*
! 615: * Copy IPv6 and ICMPv6 only.
! 616: */
! 617: nip6 = mtod(n, struct ip6_hdr *);
! 618: bcopy(ip6, nip6, sizeof(struct ip6_hdr));
! 619: nicmp6 = (struct icmp6_hdr *)(nip6 + 1);
! 620: bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr));
! 621: noff = sizeof(struct ip6_hdr);
! 622: n->m_len = noff + sizeof(struct icmp6_hdr);
! 623: /*
! 624: * Adjust mbuf. ip6_plen will be adjusted in
! 625: * ip6_output().
! 626: * n->m_pkthdr.len == n0->m_pkthdr.len at this point.
! 627: */
! 628: n->m_pkthdr.len += noff + sizeof(struct icmp6_hdr);
! 629: n->m_pkthdr.len -= (off + sizeof(struct icmp6_hdr));
! 630: m_adj(n0, off + sizeof(struct icmp6_hdr));
! 631: n->m_next = n0;
! 632: } else {
! 633: deliverecho:
! 634: nip6 = mtod(n, struct ip6_hdr *);
! 635: IP6_EXTHDR_GET(nicmp6, struct icmp6_hdr *, n, off,
! 636: sizeof(*nicmp6));
! 637: noff = off;
! 638: }
! 639: nicmp6->icmp6_type = ICMP6_ECHO_REPLY;
! 640: nicmp6->icmp6_code = 0;
! 641: if (n) {
! 642: icmp6stat.icp6s_reflect++;
! 643: icmp6stat.icp6s_outhist[ICMP6_ECHO_REPLY]++;
! 644: icmp6_reflect(n, noff);
! 645: }
! 646: if (!m)
! 647: goto freeit;
! 648: break;
! 649:
! 650: case ICMP6_ECHO_REPLY:
! 651: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_echoreply);
! 652: if (code != 0)
! 653: goto badcode;
! 654: break;
! 655:
! 656: case MLD_LISTENER_QUERY:
! 657: case MLD_LISTENER_REPORT:
! 658: if (icmp6len < sizeof(struct mld_hdr))
! 659: goto badlen;
! 660: if (icmp6->icmp6_type == MLD_LISTENER_QUERY) /* XXX: ugly... */
! 661: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldquery);
! 662: else
! 663: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldreport);
! 664: if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
! 665: /* give up local */
! 666: mld6_input(m, off);
! 667: m = NULL;
! 668: goto freeit;
! 669: }
! 670: mld6_input(n, off);
! 671: /* m stays. */
! 672: break;
! 673:
! 674: case MLD_LISTENER_DONE:
! 675: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mlddone);
! 676: if (icmp6len < sizeof(struct mld_hdr)) /* necessary? */
! 677: goto badlen;
! 678: break; /* nothing to be done in kernel */
! 679:
! 680: case MLD_MTRACE_RESP:
! 681: case MLD_MTRACE:
! 682: /* XXX: these two are experimental. not officially defined. */
! 683: /* XXX: per-interface statistics? */
! 684: break; /* just pass it to applications */
! 685:
! 686: case ICMP6_WRUREQUEST: /* ICMP6_FQDN_QUERY */
! 687: {
! 688: enum { WRU, FQDN } mode;
! 689:
! 690: if (!icmp6_nodeinfo)
! 691: break;
! 692:
! 693: if (icmp6len == sizeof(struct icmp6_hdr) + 4)
! 694: mode = WRU;
! 695: else if (icmp6len >= sizeof(struct icmp6_nodeinfo))
! 696: mode = FQDN;
! 697: else
! 698: goto badlen;
! 699:
! 700: if (mode == FQDN) {
! 701: n = m_copym(m, 0, M_COPYALL, M_DONTWAIT);
! 702: if (n)
! 703: n = ni6_input(n, off);
! 704: /* XXX meaningless if n == NULL */
! 705: noff = sizeof(struct ip6_hdr);
! 706: } else {
! 707: u_char *p;
! 708: int maxlen, maxhlen;
! 709:
! 710: if ((icmp6_nodeinfo & 1) == 0)
! 711: break;
! 712:
! 713: if (code != 0)
! 714: goto badcode;
! 715: maxlen = sizeof(*nip6) + sizeof(*nicmp6) + 4;
! 716: if (maxlen >= MCLBYTES) {
! 717: /* Give up remote */
! 718: break;
! 719: }
! 720: MGETHDR(n, M_DONTWAIT, m->m_type);
! 721: if (n && maxlen > MHLEN) {
! 722: MCLGET(n, M_DONTWAIT);
! 723: if ((n->m_flags & M_EXT) == 0) {
! 724: m_free(n);
! 725: n = NULL;
! 726: }
! 727: }
! 728: if (n == NULL) {
! 729: /* Give up remote */
! 730: break;
! 731: }
! 732: n->m_pkthdr.rcvif = NULL;
! 733: n->m_len = 0;
! 734: maxhlen = M_TRAILINGSPACE(n) - maxlen;
! 735: if (maxhlen > hostnamelen)
! 736: maxhlen = hostnamelen;
! 737: /*
! 738: * Copy IPv6 and ICMPv6 only.
! 739: */
! 740: nip6 = mtod(n, struct ip6_hdr *);
! 741: bcopy(ip6, nip6, sizeof(struct ip6_hdr));
! 742: nicmp6 = (struct icmp6_hdr *)(nip6 + 1);
! 743: bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr));
! 744: p = (u_char *)(nicmp6 + 1);
! 745: bzero(p, 4);
! 746: bcopy(hostname, p + 4, maxhlen); /* meaningless TTL */
! 747: noff = sizeof(struct ip6_hdr);
! 748: M_DUP_PKTHDR(n, m); /* just for rcvif */
! 749: n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) +
! 750: sizeof(struct icmp6_hdr) + 4 + maxhlen;
! 751: nicmp6->icmp6_type = ICMP6_WRUREPLY;
! 752: nicmp6->icmp6_code = 0;
! 753: }
! 754: #undef hostnamelen
! 755: if (n) {
! 756: icmp6stat.icp6s_reflect++;
! 757: icmp6stat.icp6s_outhist[ICMP6_WRUREPLY]++;
! 758: icmp6_reflect(n, noff);
! 759: }
! 760: break;
! 761: }
! 762:
! 763: case ICMP6_WRUREPLY:
! 764: if (code != 0)
! 765: goto badcode;
! 766: break;
! 767:
! 768: case ND_ROUTER_SOLICIT:
! 769: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_routersolicit);
! 770: if (code != 0)
! 771: goto badcode;
! 772: if (icmp6len < sizeof(struct nd_router_solicit))
! 773: goto badlen;
! 774: if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
! 775: /* give up local */
! 776: nd6_rs_input(m, off, icmp6len);
! 777: m = NULL;
! 778: goto freeit;
! 779: }
! 780: nd6_rs_input(n, off, icmp6len);
! 781: /* m stays. */
! 782: break;
! 783:
! 784: case ND_ROUTER_ADVERT:
! 785: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_routeradvert);
! 786: if (code != 0)
! 787: goto badcode;
! 788: if (icmp6len < sizeof(struct nd_router_advert))
! 789: goto badlen;
! 790: if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
! 791: /* give up local */
! 792: nd6_ra_input(m, off, icmp6len);
! 793: m = NULL;
! 794: goto freeit;
! 795: }
! 796: nd6_ra_input(n, off, icmp6len);
! 797: /* m stays. */
! 798: break;
! 799:
! 800: case ND_NEIGHBOR_SOLICIT:
! 801: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighborsolicit);
! 802: if (code != 0)
! 803: goto badcode;
! 804: if (icmp6len < sizeof(struct nd_neighbor_solicit))
! 805: goto badlen;
! 806: if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
! 807: /* give up local */
! 808: nd6_ns_input(m, off, icmp6len);
! 809: m = NULL;
! 810: goto freeit;
! 811: }
! 812: nd6_ns_input(n, off, icmp6len);
! 813: /* m stays. */
! 814: break;
! 815:
! 816: case ND_NEIGHBOR_ADVERT:
! 817: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighboradvert);
! 818: if (code != 0)
! 819: goto badcode;
! 820: if (icmp6len < sizeof(struct nd_neighbor_advert))
! 821: goto badlen;
! 822: if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
! 823: /* give up local */
! 824: nd6_na_input(m, off, icmp6len);
! 825: m = NULL;
! 826: goto freeit;
! 827: }
! 828: nd6_na_input(n, off, icmp6len);
! 829: /* m stays. */
! 830: break;
! 831:
! 832: case ND_REDIRECT:
! 833: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_redirect);
! 834: if (code != 0)
! 835: goto badcode;
! 836: if (icmp6len < sizeof(struct nd_redirect))
! 837: goto badlen;
! 838: if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
! 839: /* give up local */
! 840: icmp6_redirect_input(m, off);
! 841: m = NULL;
! 842: goto freeit;
! 843: }
! 844: icmp6_redirect_input(n, off);
! 845: /* m stays. */
! 846: break;
! 847:
! 848: case ICMP6_ROUTER_RENUMBERING:
! 849: if (code != ICMP6_ROUTER_RENUMBERING_COMMAND &&
! 850: code != ICMP6_ROUTER_RENUMBERING_RESULT)
! 851: goto badcode;
! 852: if (icmp6len < sizeof(struct icmp6_router_renum))
! 853: goto badlen;
! 854: break;
! 855:
! 856: default:
! 857: nd6log((LOG_DEBUG,
! 858: "icmp6_input: unknown type %d(src=%s, dst=%s, ifid=%d)\n",
! 859: icmp6->icmp6_type, ip6_sprintf(&ip6->ip6_src),
! 860: ip6_sprintf(&ip6->ip6_dst),
! 861: m->m_pkthdr.rcvif ? m->m_pkthdr.rcvif->if_index : 0));
! 862: if (icmp6->icmp6_type < ICMP6_ECHO_REQUEST) {
! 863: /* ICMPv6 error: MUST deliver it by spec... */
! 864: code = PRC_NCMDS;
! 865: /* deliver */
! 866: } else {
! 867: /* ICMPv6 informational: MUST not deliver */
! 868: break;
! 869: }
! 870: deliver:
! 871: if (icmp6_notify_error(m, off, icmp6len, code)) {
! 872: /* In this case, m should've been freed. */
! 873: return (IPPROTO_DONE);
! 874: }
! 875: break;
! 876:
! 877: badcode:
! 878: icmp6stat.icp6s_badcode++;
! 879: break;
! 880:
! 881: badlen:
! 882: icmp6stat.icp6s_badlen++;
! 883: break;
! 884: }
! 885:
! 886: /* deliver the packet to appropriate sockets */
! 887: icmp6_rip6_input(&m, *offp);
! 888:
! 889: return IPPROTO_DONE;
! 890:
! 891: freeit:
! 892: m_freem(m);
! 893: return IPPROTO_DONE;
! 894: }
! 895:
! 896: static int
! 897: icmp6_notify_error(m, off, icmp6len, code)
! 898: struct mbuf *m;
! 899: int off, icmp6len, code;
! 900: {
! 901: struct icmp6_hdr *icmp6;
! 902: struct ip6_hdr *eip6;
! 903: u_int32_t notifymtu;
! 904: struct sockaddr_in6 icmp6src, icmp6dst;
! 905:
! 906: if (icmp6len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr)) {
! 907: icmp6stat.icp6s_tooshort++;
! 908: goto freeit;
! 909: }
! 910: IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off,
! 911: sizeof(*icmp6) + sizeof(struct ip6_hdr));
! 912: if (icmp6 == NULL) {
! 913: icmp6stat.icp6s_tooshort++;
! 914: return (-1);
! 915: }
! 916: eip6 = (struct ip6_hdr *)(icmp6 + 1);
! 917:
! 918: /* Detect the upper level protocol */
! 919: {
! 920: void (*ctlfunc)(int, struct sockaddr *, void *);
! 921: u_int8_t nxt = eip6->ip6_nxt;
! 922: int eoff = off + sizeof(struct icmp6_hdr) +
! 923: sizeof(struct ip6_hdr);
! 924: struct ip6ctlparam ip6cp;
! 925: struct in6_addr *finaldst = NULL;
! 926: int icmp6type = icmp6->icmp6_type;
! 927: struct ip6_frag *fh;
! 928: struct ip6_rthdr *rth;
! 929: struct ip6_rthdr0 *rth0;
! 930: int rthlen;
! 931:
! 932: while (1) { /* XXX: should avoid infinite loop explicitly? */
! 933: struct ip6_ext *eh;
! 934:
! 935: switch (nxt) {
! 936: case IPPROTO_HOPOPTS:
! 937: case IPPROTO_DSTOPTS:
! 938: case IPPROTO_AH:
! 939: IP6_EXTHDR_GET(eh, struct ip6_ext *, m,
! 940: eoff, sizeof(*eh));
! 941: if (eh == NULL) {
! 942: icmp6stat.icp6s_tooshort++;
! 943: return (-1);
! 944: }
! 945:
! 946: if (nxt == IPPROTO_AH)
! 947: eoff += (eh->ip6e_len + 2) << 2;
! 948: else
! 949: eoff += (eh->ip6e_len + 1) << 3;
! 950: nxt = eh->ip6e_nxt;
! 951: break;
! 952: case IPPROTO_ROUTING:
! 953: /*
! 954: * When the erroneous packet contains a
! 955: * routing header, we should examine the
! 956: * header to determine the final destination.
! 957: * Otherwise, we can't properly update
! 958: * information that depends on the final
! 959: * destination (e.g. path MTU).
! 960: */
! 961: IP6_EXTHDR_GET(rth, struct ip6_rthdr *, m,
! 962: eoff, sizeof(*rth));
! 963: if (rth == NULL) {
! 964: icmp6stat.icp6s_tooshort++;
! 965: return (-1);
! 966: }
! 967: rthlen = (rth->ip6r_len + 1) << 3;
! 968: /*
! 969: * XXX: currently there is no
! 970: * officially defined type other
! 971: * than type-0.
! 972: * Note that if the segment left field
! 973: * is 0, all intermediate hops must
! 974: * have been passed.
! 975: */
! 976: if (rth->ip6r_segleft &&
! 977: rth->ip6r_type == IPV6_RTHDR_TYPE_0) {
! 978: int hops;
! 979:
! 980: IP6_EXTHDR_GET(rth0,
! 981: struct ip6_rthdr0 *, m,
! 982: eoff, rthlen);
! 983: if (rth0 == NULL) {
! 984: icmp6stat.icp6s_tooshort++;
! 985: return (-1);
! 986: }
! 987: /* just ignore a bogus header */
! 988: if ((rth0->ip6r0_len % 2) == 0 &&
! 989: (hops = rth0->ip6r0_len/2))
! 990: finaldst = (struct in6_addr *)(rth0 + 1) + (hops - 1);
! 991: }
! 992: eoff += rthlen;
! 993: nxt = rth->ip6r_nxt;
! 994: break;
! 995: case IPPROTO_FRAGMENT:
! 996: IP6_EXTHDR_GET(fh, struct ip6_frag *, m,
! 997: eoff, sizeof(*fh));
! 998: if (fh == NULL) {
! 999: icmp6stat.icp6s_tooshort++;
! 1000: return (-1);
! 1001: }
! 1002: /*
! 1003: * Data after a fragment header is meaningless
! 1004: * unless it is the first fragment, but
! 1005: * we'll go to the notify label for path MTU
! 1006: * discovery.
! 1007: */
! 1008: if (fh->ip6f_offlg & IP6F_OFF_MASK)
! 1009: goto notify;
! 1010:
! 1011: eoff += sizeof(struct ip6_frag);
! 1012: nxt = fh->ip6f_nxt;
! 1013: break;
! 1014: default:
! 1015: /*
! 1016: * This case includes ESP and the No Next
! 1017: * Header. In such cases going to the notify
! 1018: * label does not have any meaning
! 1019: * (i.e. ctlfunc will be NULL), but we go
! 1020: * anyway since we might have to update
! 1021: * path MTU information.
! 1022: */
! 1023: goto notify;
! 1024: }
! 1025: }
! 1026: notify:
! 1027: IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off,
! 1028: sizeof(*icmp6) + sizeof(struct ip6_hdr));
! 1029: if (icmp6 == NULL) {
! 1030: icmp6stat.icp6s_tooshort++;
! 1031: return (-1);
! 1032: }
! 1033:
! 1034: eip6 = (struct ip6_hdr *)(icmp6 + 1);
! 1035: bzero(&icmp6dst, sizeof(icmp6dst));
! 1036: icmp6dst.sin6_len = sizeof(struct sockaddr_in6);
! 1037: icmp6dst.sin6_family = AF_INET6;
! 1038: if (finaldst == NULL)
! 1039: icmp6dst.sin6_addr = eip6->ip6_dst;
! 1040: else
! 1041: icmp6dst.sin6_addr = *finaldst;
! 1042: icmp6dst.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.rcvif,
! 1043: &icmp6dst.sin6_addr);
! 1044: if (in6_embedscope(&icmp6dst.sin6_addr, &icmp6dst,
! 1045: NULL, NULL)) {
! 1046: /* should be impossbile */
! 1047: nd6log((LOG_DEBUG,
! 1048: "icmp6_notify_error: in6_embedscope failed\n"));
! 1049: goto freeit;
! 1050: }
! 1051:
! 1052: /*
! 1053: * retrieve parameters from the inner IPv6 header, and convert
! 1054: * them into sockaddr structures.
! 1055: */
! 1056: bzero(&icmp6src, sizeof(icmp6src));
! 1057: icmp6src.sin6_len = sizeof(struct sockaddr_in6);
! 1058: icmp6src.sin6_family = AF_INET6;
! 1059: icmp6src.sin6_addr = eip6->ip6_src;
! 1060: icmp6src.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.rcvif,
! 1061: &icmp6src.sin6_addr);
! 1062: if (in6_embedscope(&icmp6src.sin6_addr, &icmp6src,
! 1063: NULL, NULL)) {
! 1064: /* should be impossbile */
! 1065: nd6log((LOG_DEBUG,
! 1066: "icmp6_notify_error: in6_embedscope failed\n"));
! 1067: goto freeit;
! 1068: }
! 1069: icmp6src.sin6_flowinfo =
! 1070: (eip6->ip6_flow & IPV6_FLOWLABEL_MASK);
! 1071:
! 1072: if (finaldst == NULL)
! 1073: finaldst = &eip6->ip6_dst;
! 1074: ip6cp.ip6c_m = m;
! 1075: ip6cp.ip6c_icmp6 = icmp6;
! 1076: ip6cp.ip6c_ip6 = (struct ip6_hdr *)(icmp6 + 1);
! 1077: ip6cp.ip6c_off = eoff;
! 1078: ip6cp.ip6c_finaldst = finaldst;
! 1079: ip6cp.ip6c_src = &icmp6src;
! 1080: ip6cp.ip6c_nxt = nxt;
! 1081:
! 1082: if (icmp6type == ICMP6_PACKET_TOO_BIG) {
! 1083: notifymtu = ntohl(icmp6->icmp6_mtu);
! 1084: ip6cp.ip6c_cmdarg = (void *)¬ifymtu;
! 1085: }
! 1086:
! 1087: ctlfunc = (void (*)(int, struct sockaddr *, void *))
! 1088: (inet6sw[ip6_protox[nxt]].pr_ctlinput);
! 1089: if (ctlfunc) {
! 1090: (void) (*ctlfunc)(code, (struct sockaddr *)&icmp6dst,
! 1091: &ip6cp);
! 1092: }
! 1093: }
! 1094: return (0);
! 1095:
! 1096: freeit:
! 1097: m_freem(m);
! 1098: return (-1);
! 1099: }
! 1100:
! 1101: void
! 1102: icmp6_mtudisc_update(ip6cp, validated)
! 1103: struct ip6ctlparam *ip6cp;
! 1104: int validated;
! 1105: {
! 1106: unsigned long rtcount;
! 1107: struct icmp6_mtudisc_callback *mc;
! 1108: struct in6_addr *dst = ip6cp->ip6c_finaldst;
! 1109: struct icmp6_hdr *icmp6 = ip6cp->ip6c_icmp6;
! 1110: struct mbuf *m = ip6cp->ip6c_m; /* will be necessary for scope issue */
! 1111: u_int mtu = ntohl(icmp6->icmp6_mtu);
! 1112: struct rtentry *rt = NULL;
! 1113: struct sockaddr_in6 sin6;
! 1114:
! 1115: /*
! 1116: * allow non-validated cases if memory is plenty, to make traffic
! 1117: * from non-connected pcb happy.
! 1118: */
! 1119: rtcount = rt_timer_count(icmp6_mtudisc_timeout_q);
! 1120: if (validated) {
! 1121: if (0 <= icmp6_mtudisc_hiwat && rtcount > icmp6_mtudisc_hiwat)
! 1122: return;
! 1123: else if (0 <= icmp6_mtudisc_lowat &&
! 1124: rtcount > icmp6_mtudisc_lowat) {
! 1125: /*
! 1126: * XXX nuke a victim, install the new one.
! 1127: */
! 1128: }
! 1129: } else {
! 1130: if (0 <= icmp6_mtudisc_lowat && rtcount > icmp6_mtudisc_lowat)
! 1131: return;
! 1132: }
! 1133:
! 1134: bzero(&sin6, sizeof(sin6));
! 1135: sin6.sin6_family = PF_INET6;
! 1136: sin6.sin6_len = sizeof(struct sockaddr_in6);
! 1137: sin6.sin6_addr = *dst;
! 1138: /* XXX normally, this won't happen */
! 1139: if (IN6_IS_ADDR_LINKLOCAL(dst)) {
! 1140: sin6.sin6_addr.s6_addr16[1] =
! 1141: htons(m->m_pkthdr.rcvif->if_index);
! 1142: }
! 1143: /* sin6.sin6_scope_id = XXX: should be set if DST is a scoped addr */
! 1144: rt = icmp6_mtudisc_clone((struct sockaddr *)&sin6);
! 1145:
! 1146: if (rt && (rt->rt_flags & RTF_HOST) &&
! 1147: !(rt->rt_rmx.rmx_locks & RTV_MTU) &&
! 1148: (rt->rt_rmx.rmx_mtu > mtu || rt->rt_rmx.rmx_mtu == 0)) {
! 1149: if (mtu < IN6_LINKMTU(rt->rt_ifp)) {
! 1150: icmp6stat.icp6s_pmtuchg++;
! 1151: rt->rt_rmx.rmx_mtu = mtu;
! 1152: }
! 1153: }
! 1154: if (rt) { /* XXX: need braces to avoid conflict with else in RTFREE. */
! 1155: RTFREE(rt);
! 1156: }
! 1157:
! 1158: /*
! 1159: * Notify protocols that the MTU for this destination
! 1160: * has changed.
! 1161: */
! 1162: for (mc = LIST_FIRST(&icmp6_mtudisc_callbacks); mc != NULL;
! 1163: mc = LIST_NEXT(mc, mc_list))
! 1164: (*mc->mc_func)(&sin6.sin6_addr);
! 1165: }
! 1166:
! 1167: /*
! 1168: * Process a Node Information Query packet, based on
! 1169: * draft-ietf-ipngwg-icmp-name-lookups-07.
! 1170: *
! 1171: * Spec incompatibilities:
! 1172: * - IPv6 Subject address handling
! 1173: * - IPv4 Subject address handling support missing
! 1174: * - Proxy reply (answer even if it's not for me)
! 1175: * - joins NI group address at in6_ifattach() time only, does not cope
! 1176: * with hostname changes by sethostname(3)
! 1177: */
! 1178: #ifndef offsetof /* XXX */
! 1179: #define offsetof(type, member) ((size_t)(&((type *)0)->member))
! 1180: #endif
! 1181: static struct mbuf *
! 1182: ni6_input(m, off)
! 1183: struct mbuf *m;
! 1184: int off;
! 1185: {
! 1186: struct icmp6_nodeinfo *ni6, *nni6;
! 1187: struct mbuf *n = NULL;
! 1188: u_int16_t qtype;
! 1189: int subjlen;
! 1190: int replylen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo);
! 1191: struct ni_reply_fqdn *fqdn;
! 1192: int addrs; /* for NI_QTYPE_NODEADDR */
! 1193: struct ifnet *ifp = NULL; /* for NI_QTYPE_NODEADDR */
! 1194: struct sockaddr_in6 sin6; /* double meaning; ip6_dst and subjectaddr */
! 1195: struct ip6_hdr *ip6;
! 1196: int oldfqdn = 0; /* if 1, return pascal string (03 draft) */
! 1197: char *subj = NULL;
! 1198:
! 1199: ip6 = mtod(m, struct ip6_hdr *);
! 1200: IP6_EXTHDR_GET(ni6, struct icmp6_nodeinfo *, m, off, sizeof(*ni6));
! 1201: if (ni6 == NULL) {
! 1202: /* m is already reclaimed */
! 1203: return NULL;
! 1204: }
! 1205:
! 1206: /*
! 1207: * Validate IPv6 destination address.
! 1208: *
! 1209: * The Responder must discard the Query without further processing
! 1210: * unless it is one of the Responder's unicast or anycast addresses, or
! 1211: * a link-local scope multicast address which the Responder has joined.
! 1212: * [icmp-name-lookups-07, Section 4.]
! 1213: */
! 1214: bzero(&sin6, sizeof(sin6));
! 1215: sin6.sin6_family = AF_INET6;
! 1216: sin6.sin6_len = sizeof(struct sockaddr_in6);
! 1217: bcopy(&ip6->ip6_dst, &sin6.sin6_addr, sizeof(sin6.sin6_addr));
! 1218: /* XXX scopeid */
! 1219: if (ifa_ifwithaddr((struct sockaddr *)&sin6))
! 1220: ; /* unicast/anycast, fine */
! 1221: else if (IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr))
! 1222: ; /* link-local multicast, fine */
! 1223: else
! 1224: goto bad;
! 1225:
! 1226: /* validate query Subject field. */
! 1227: qtype = ntohs(ni6->ni_qtype);
! 1228: subjlen = m->m_pkthdr.len - off - sizeof(struct icmp6_nodeinfo);
! 1229: switch (qtype) {
! 1230: case NI_QTYPE_NOOP:
! 1231: case NI_QTYPE_SUPTYPES:
! 1232: /* 07 draft */
! 1233: if (ni6->ni_code == ICMP6_NI_SUBJ_FQDN && subjlen == 0)
! 1234: break;
! 1235: /* FALLTHROUGH */
! 1236: case NI_QTYPE_FQDN:
! 1237: case NI_QTYPE_NODEADDR:
! 1238: switch (ni6->ni_code) {
! 1239: case ICMP6_NI_SUBJ_IPV6:
! 1240: #if ICMP6_NI_SUBJ_IPV6 != 0
! 1241: case 0:
! 1242: #endif
! 1243: /*
! 1244: * backward compatibility - try to accept 03 draft
! 1245: * format, where no Subject is present.
! 1246: */
! 1247: if (qtype == NI_QTYPE_FQDN && ni6->ni_code == 0 &&
! 1248: subjlen == 0) {
! 1249: oldfqdn++;
! 1250: break;
! 1251: }
! 1252: #if ICMP6_NI_SUBJ_IPV6 != 0
! 1253: if (ni6->ni_code != ICMP6_NI_SUBJ_IPV6)
! 1254: goto bad;
! 1255: #endif
! 1256:
! 1257: if (subjlen != sizeof(sin6.sin6_addr))
! 1258: goto bad;
! 1259:
! 1260: /*
! 1261: * Validate Subject address.
! 1262: *
! 1263: * Not sure what exactly "address belongs to the node"
! 1264: * means in the spec, is it just unicast, or what?
! 1265: *
! 1266: * At this moment we consider Subject address as
! 1267: * "belong to the node" if the Subject address equals
! 1268: * to the IPv6 destination address; validation for
! 1269: * IPv6 destination address should have done enough
! 1270: * check for us.
! 1271: *
! 1272: * We do not do proxy at this moment.
! 1273: */
! 1274: /* m_pulldown instead of copy? */
! 1275: m_copydata(m, off + sizeof(struct icmp6_nodeinfo),
! 1276: subjlen, (caddr_t)&sin6.sin6_addr);
! 1277: /* XXX kame scope hack */
! 1278: if (IN6_IS_SCOPE_EMBED(&sin6.sin6_addr)) {
! 1279: if ((m->m_flags & M_PKTHDR) != 0 &&
! 1280: m->m_pkthdr.rcvif) {
! 1281: sin6.sin6_addr.s6_addr16[1] =
! 1282: htons(m->m_pkthdr.rcvif->if_index);
! 1283: }
! 1284: }
! 1285: subj = (char *)&sin6;
! 1286: if (IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &sin6.sin6_addr))
! 1287: break;
! 1288:
! 1289: /*
! 1290: * XXX if we are to allow other cases, we should really
! 1291: * be careful about scope here.
! 1292: * basically, we should disallow queries toward IPv6
! 1293: * destination X with subject Y, if scope(X) > scope(Y).
! 1294: * if we allow scope(X) > scope(Y), it will result in
! 1295: * information leakage across scope boundary.
! 1296: */
! 1297: goto bad;
! 1298:
! 1299: case ICMP6_NI_SUBJ_FQDN:
! 1300: /*
! 1301: * Validate Subject name with gethostname(3).
! 1302: *
! 1303: * The behavior may need some debate, since:
! 1304: * - we are not sure if the node has FQDN as
! 1305: * hostname (returned by gethostname(3)).
! 1306: * - the code does wildcard match for truncated names.
! 1307: * however, we are not sure if we want to perform
! 1308: * wildcard match, if gethostname(3) side has
! 1309: * truncated hostname.
! 1310: */
! 1311: n = ni6_nametodns(hostname, hostnamelen, 0);
! 1312: if (!n || n->m_next || n->m_len == 0)
! 1313: goto bad;
! 1314: IP6_EXTHDR_GET(subj, char *, m,
! 1315: off + sizeof(struct icmp6_nodeinfo), subjlen);
! 1316: if (subj == NULL)
! 1317: goto bad;
! 1318: if (!ni6_dnsmatch(subj, subjlen, mtod(n, const char *),
! 1319: n->m_len)) {
! 1320: goto bad;
! 1321: }
! 1322: m_freem(n);
! 1323: n = NULL;
! 1324: break;
! 1325:
! 1326: case ICMP6_NI_SUBJ_IPV4: /* XXX: to be implemented? */
! 1327: default:
! 1328: goto bad;
! 1329: }
! 1330: break;
! 1331: }
! 1332:
! 1333: /* refuse based on configuration. XXX ICMP6_NI_REFUSED? */
! 1334: switch (qtype) {
! 1335: case NI_QTYPE_FQDN:
! 1336: if ((icmp6_nodeinfo & 1) == 0)
! 1337: goto bad;
! 1338: break;
! 1339: case NI_QTYPE_NODEADDR:
! 1340: if ((icmp6_nodeinfo & 2) == 0)
! 1341: goto bad;
! 1342: break;
! 1343: }
! 1344:
! 1345: /* guess reply length */
! 1346: switch (qtype) {
! 1347: case NI_QTYPE_NOOP:
! 1348: break; /* no reply data */
! 1349: case NI_QTYPE_SUPTYPES:
! 1350: replylen += sizeof(u_int32_t);
! 1351: break;
! 1352: case NI_QTYPE_FQDN:
! 1353: /* XXX will append an mbuf */
! 1354: replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_namelen);
! 1355: break;
! 1356: case NI_QTYPE_NODEADDR:
! 1357: addrs = ni6_addrs(ni6, m, &ifp, subj);
! 1358: if ((replylen += addrs * (sizeof(struct in6_addr) +
! 1359: sizeof(u_int32_t))) > MCLBYTES)
! 1360: replylen = MCLBYTES; /* XXX: will truncate pkt later */
! 1361: break;
! 1362: default:
! 1363: /*
! 1364: * XXX: We must return a reply with the ICMP6 code
! 1365: * `unknown Qtype' in this case. However we regard the case
! 1366: * as an FQDN query for backward compatibility.
! 1367: * Older versions set a random value to this field,
! 1368: * so it rarely varies in the defined qtypes.
! 1369: * But the mechanism is not reliable...
! 1370: * maybe we should obsolete older versions.
! 1371: */
! 1372: qtype = NI_QTYPE_FQDN;
! 1373: /* XXX will append an mbuf */
! 1374: replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_namelen);
! 1375: oldfqdn++;
! 1376: break;
! 1377: }
! 1378:
! 1379: /* allocate an mbuf to reply. */
! 1380: MGETHDR(n, M_DONTWAIT, m->m_type);
! 1381: if (n == NULL) {
! 1382: m_freem(m);
! 1383: return (NULL);
! 1384: }
! 1385: M_DUP_PKTHDR(n, m); /* just for rcvif */
! 1386: if (replylen > MHLEN) {
! 1387: if (replylen > MCLBYTES) {
! 1388: /*
! 1389: * XXX: should we try to allocate more? But MCLBYTES
! 1390: * is probably much larger than IPV6_MMTU...
! 1391: */
! 1392: goto bad;
! 1393: }
! 1394: MCLGET(n, M_DONTWAIT);
! 1395: if ((n->m_flags & M_EXT) == 0) {
! 1396: goto bad;
! 1397: }
! 1398: }
! 1399: n->m_pkthdr.len = n->m_len = replylen;
! 1400:
! 1401: /* copy mbuf header and IPv6 + Node Information base headers */
! 1402: bcopy(mtod(m, caddr_t), mtod(n, caddr_t), sizeof(struct ip6_hdr));
! 1403: nni6 = (struct icmp6_nodeinfo *)(mtod(n, struct ip6_hdr *) + 1);
! 1404: bcopy((caddr_t)ni6, (caddr_t)nni6, sizeof(struct icmp6_nodeinfo));
! 1405:
! 1406: /* qtype dependent procedure */
! 1407: switch (qtype) {
! 1408: case NI_QTYPE_NOOP:
! 1409: nni6->ni_code = ICMP6_NI_SUCCESS;
! 1410: nni6->ni_flags = 0;
! 1411: break;
! 1412: case NI_QTYPE_SUPTYPES:
! 1413: {
! 1414: u_int32_t v;
! 1415: nni6->ni_code = ICMP6_NI_SUCCESS;
! 1416: nni6->ni_flags = htons(0x0000); /* raw bitmap */
! 1417: /* supports NOOP, SUPTYPES, FQDN, and NODEADDR */
! 1418: v = (u_int32_t)htonl(0x0000000f);
! 1419: bcopy(&v, nni6 + 1, sizeof(u_int32_t));
! 1420: break;
! 1421: }
! 1422: case NI_QTYPE_FQDN:
! 1423: nni6->ni_code = ICMP6_NI_SUCCESS;
! 1424: fqdn = (struct ni_reply_fqdn *)(mtod(n, caddr_t) +
! 1425: sizeof(struct ip6_hdr) +
! 1426: sizeof(struct icmp6_nodeinfo));
! 1427: nni6->ni_flags = 0; /* XXX: meaningless TTL */
! 1428: fqdn->ni_fqdn_ttl = 0; /* ditto. */
! 1429: /*
! 1430: * XXX do we really have FQDN in variable "hostname"?
! 1431: */
! 1432: n->m_next = ni6_nametodns(hostname, hostnamelen, oldfqdn);
! 1433: if (n->m_next == NULL)
! 1434: goto bad;
! 1435: /* XXX we assume that n->m_next is not a chain */
! 1436: if (n->m_next->m_next != NULL)
! 1437: goto bad;
! 1438: n->m_pkthdr.len += n->m_next->m_len;
! 1439: break;
! 1440: case NI_QTYPE_NODEADDR:
! 1441: {
! 1442: int lenlim, copied;
! 1443:
! 1444: nni6->ni_code = ICMP6_NI_SUCCESS;
! 1445: n->m_pkthdr.len = n->m_len =
! 1446: sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo);
! 1447: lenlim = M_TRAILINGSPACE(n);
! 1448: copied = ni6_store_addrs(ni6, nni6, ifp, lenlim);
! 1449: /* XXX: reset mbuf length */
! 1450: n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) +
! 1451: sizeof(struct icmp6_nodeinfo) + copied;
! 1452: break;
! 1453: }
! 1454: default:
! 1455: break; /* XXX impossible! */
! 1456: }
! 1457:
! 1458: nni6->ni_type = ICMP6_NI_REPLY;
! 1459: m_freem(m);
! 1460: return (n);
! 1461:
! 1462: bad:
! 1463: m_freem(m);
! 1464: if (n)
! 1465: m_freem(n);
! 1466: return (NULL);
! 1467: }
! 1468: #undef hostnamelen
! 1469:
! 1470: #define isupper(x) ('A' <= (x) && (x) <= 'Z')
! 1471: #define isalpha(x) (('A' <= (x) && (x) <= 'Z') || ('a' <= (x) && (x) <= 'z'))
! 1472: #define isalnum(x) (isalpha(x) || ('0' <= (x) && (x) <= '9'))
! 1473: #define tolower(x) (isupper(x) ? (x) + 'a' - 'A' : (x))
! 1474:
! 1475: /*
! 1476: * make a mbuf with DNS-encoded string. no compression support.
! 1477: *
! 1478: * XXX names with less than 2 dots (like "foo" or "foo.section") will be
! 1479: * treated as truncated name (two \0 at the end). this is a wild guess.
! 1480: */
! 1481: static struct mbuf *
! 1482: ni6_nametodns(name, namelen, old)
! 1483: const char *name;
! 1484: int namelen;
! 1485: int old; /* return pascal string if non-zero */
! 1486: {
! 1487: struct mbuf *m;
! 1488: char *cp, *ep;
! 1489: const char *p, *q;
! 1490: int i, len, nterm;
! 1491:
! 1492: if (old)
! 1493: len = namelen + 1;
! 1494: else
! 1495: len = MCLBYTES;
! 1496:
! 1497: /* because MAXHOSTNAMELEN is usually 256, we use cluster mbuf */
! 1498: MGET(m, M_DONTWAIT, MT_DATA);
! 1499: if (m && len > MLEN) {
! 1500: MCLGET(m, M_DONTWAIT);
! 1501: if ((m->m_flags & M_EXT) == 0)
! 1502: goto fail;
! 1503: }
! 1504: if (!m)
! 1505: goto fail;
! 1506: m->m_next = NULL;
! 1507:
! 1508: if (old) {
! 1509: m->m_len = len;
! 1510: *mtod(m, char *) = namelen;
! 1511: bcopy(name, mtod(m, char *) + 1, namelen);
! 1512: return m;
! 1513: } else {
! 1514: m->m_len = 0;
! 1515: cp = mtod(m, char *);
! 1516: ep = mtod(m, char *) + M_TRAILINGSPACE(m);
! 1517:
! 1518: /* if not certain about my name, return empty buffer */
! 1519: if (namelen == 0)
! 1520: return m;
! 1521:
! 1522: /*
! 1523: * guess if it looks like shortened hostname, or FQDN.
! 1524: * shortened hostname needs two trailing "\0".
! 1525: */
! 1526: i = 0;
! 1527: for (p = name; p < name + namelen; p++) {
! 1528: if (*p && *p == '.')
! 1529: i++;
! 1530: }
! 1531: if (i < 2)
! 1532: nterm = 2;
! 1533: else
! 1534: nterm = 1;
! 1535:
! 1536: p = name;
! 1537: while (cp < ep && p < name + namelen) {
! 1538: i = 0;
! 1539: for (q = p; q < name + namelen && *q && *q != '.'; q++)
! 1540: i++;
! 1541: /* result does not fit into mbuf */
! 1542: if (cp + i + 1 >= ep)
! 1543: goto fail;
! 1544: /*
! 1545: * DNS label length restriction, RFC1035 page 8.
! 1546: * "i == 0" case is included here to avoid returning
! 1547: * 0-length label on "foo..bar".
! 1548: */
! 1549: if (i <= 0 || i >= 64)
! 1550: goto fail;
! 1551: *cp++ = i;
! 1552: if (!isalpha(p[0]) || !isalnum(p[i - 1]))
! 1553: goto fail;
! 1554: while (i > 0) {
! 1555: if (!isalnum(*p) && *p != '-')
! 1556: goto fail;
! 1557: if (isupper(*p)) {
! 1558: *cp++ = tolower(*p);
! 1559: p++;
! 1560: } else
! 1561: *cp++ = *p++;
! 1562: i--;
! 1563: }
! 1564: p = q;
! 1565: if (p < name + namelen && *p == '.')
! 1566: p++;
! 1567: }
! 1568: /* termination */
! 1569: if (cp + nterm >= ep)
! 1570: goto fail;
! 1571: while (nterm-- > 0)
! 1572: *cp++ = '\0';
! 1573: m->m_len = cp - mtod(m, char *);
! 1574: return m;
! 1575: }
! 1576:
! 1577: panic("should not reach here");
! 1578: /* NOTREACHED */
! 1579:
! 1580: fail:
! 1581: if (m)
! 1582: m_freem(m);
! 1583: return NULL;
! 1584: }
! 1585:
! 1586: /*
! 1587: * check if two DNS-encoded string matches. takes care of truncated
! 1588: * form (with \0\0 at the end). no compression support.
! 1589: * XXX upper/lowercase match (see RFC2065)
! 1590: */
! 1591: static int
! 1592: ni6_dnsmatch(a, alen, b, blen)
! 1593: const char *a;
! 1594: int alen;
! 1595: const char *b;
! 1596: int blen;
! 1597: {
! 1598: const char *a0, *b0;
! 1599: int l;
! 1600:
! 1601: /* simplest case - need validation? */
! 1602: if (alen == blen && bcmp(a, b, alen) == 0)
! 1603: return 1;
! 1604:
! 1605: a0 = a;
! 1606: b0 = b;
! 1607:
! 1608: /* termination is mandatory */
! 1609: if (alen < 2 || blen < 2)
! 1610: return 0;
! 1611: if (a0[alen - 1] != '\0' || b0[blen - 1] != '\0')
! 1612: return 0;
! 1613: alen--;
! 1614: blen--;
! 1615:
! 1616: while (a - a0 < alen && b - b0 < blen) {
! 1617: if (a - a0 + 1 > alen || b - b0 + 1 > blen)
! 1618: return 0;
! 1619:
! 1620: if ((signed char)a[0] < 0 || (signed char)b[0] < 0)
! 1621: return 0;
! 1622: /* we don't support compression yet */
! 1623: if (a[0] >= 64 || b[0] >= 64)
! 1624: return 0;
! 1625:
! 1626: /* truncated case */
! 1627: if (a[0] == 0 && a - a0 == alen - 1)
! 1628: return 1;
! 1629: if (b[0] == 0 && b - b0 == blen - 1)
! 1630: return 1;
! 1631: if (a[0] == 0 || b[0] == 0)
! 1632: return 0;
! 1633:
! 1634: if (a[0] != b[0])
! 1635: return 0;
! 1636: l = a[0];
! 1637: if (a - a0 + 1 + l > alen || b - b0 + 1 + l > blen)
! 1638: return 0;
! 1639: if (bcmp(a + 1, b + 1, l) != 0)
! 1640: return 0;
! 1641:
! 1642: a += 1 + l;
! 1643: b += 1 + l;
! 1644: }
! 1645:
! 1646: if (a - a0 == alen && b - b0 == blen)
! 1647: return 1;
! 1648: else
! 1649: return 0;
! 1650: }
! 1651:
! 1652: /*
! 1653: * calculate the number of addresses to be returned in the node info reply.
! 1654: */
! 1655: static int
! 1656: ni6_addrs(ni6, m, ifpp, subj)
! 1657: struct icmp6_nodeinfo *ni6;
! 1658: struct mbuf *m;
! 1659: struct ifnet **ifpp;
! 1660: char *subj;
! 1661: {
! 1662: struct ifnet *ifp;
! 1663: struct in6_ifaddr *ifa6;
! 1664: struct ifaddr *ifa;
! 1665: struct sockaddr_in6 *subj_ip6 = NULL; /* XXX pedant */
! 1666: int addrs = 0, addrsofif, iffound = 0;
! 1667: int niflags = ni6->ni_flags;
! 1668:
! 1669: if ((niflags & NI_NODEADDR_FLAG_ALL) == 0) {
! 1670: switch (ni6->ni_code) {
! 1671: case ICMP6_NI_SUBJ_IPV6:
! 1672: if (subj == NULL) /* must be impossible... */
! 1673: return (0);
! 1674: subj_ip6 = (struct sockaddr_in6 *)subj;
! 1675: break;
! 1676: default:
! 1677: /*
! 1678: * XXX: we only support IPv6 subject address for
! 1679: * this Qtype.
! 1680: */
! 1681: return (0);
! 1682: }
! 1683: }
! 1684:
! 1685: TAILQ_FOREACH(ifp, &ifnet, if_list) {
! 1686: addrsofif = 0;
! 1687: TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
! 1688: if (ifa->ifa_addr->sa_family != AF_INET6)
! 1689: continue;
! 1690: ifa6 = (struct in6_ifaddr *)ifa;
! 1691:
! 1692: if ((niflags & NI_NODEADDR_FLAG_ALL) == 0 &&
! 1693: IN6_ARE_ADDR_EQUAL(&subj_ip6->sin6_addr,
! 1694: &ifa6->ia_addr.sin6_addr))
! 1695: iffound = 1;
! 1696:
! 1697: /*
! 1698: * IPv4-mapped addresses can only be returned by a
! 1699: * Node Information proxy, since they represent
! 1700: * addresses of IPv4-only nodes, which perforce do
! 1701: * not implement this protocol.
! 1702: * [icmp-name-lookups-07, Section 5.4]
! 1703: * So we don't support NI_NODEADDR_FLAG_COMPAT in
! 1704: * this function at this moment.
! 1705: */
! 1706:
! 1707: /* What do we have to do about ::1? */
! 1708: switch (in6_addrscope(&ifa6->ia_addr.sin6_addr)) {
! 1709: case IPV6_ADDR_SCOPE_LINKLOCAL:
! 1710: if ((niflags & NI_NODEADDR_FLAG_LINKLOCAL) == 0)
! 1711: continue;
! 1712: break;
! 1713: case IPV6_ADDR_SCOPE_SITELOCAL:
! 1714: if ((niflags & NI_NODEADDR_FLAG_SITELOCAL) == 0)
! 1715: continue;
! 1716: break;
! 1717: case IPV6_ADDR_SCOPE_GLOBAL:
! 1718: if ((niflags & NI_NODEADDR_FLAG_GLOBAL) == 0)
! 1719: continue;
! 1720: break;
! 1721: default:
! 1722: continue;
! 1723: }
! 1724:
! 1725: /*
! 1726: * check if anycast is okay.
! 1727: * XXX: just experimental. not in the spec.
! 1728: */
! 1729: if ((ifa6->ia6_flags & IN6_IFF_ANYCAST) != 0 &&
! 1730: (niflags & NI_NODEADDR_FLAG_ANYCAST) == 0)
! 1731: continue; /* we need only unicast addresses */
! 1732:
! 1733: addrsofif++; /* count the address */
! 1734: }
! 1735: if (iffound) {
! 1736: *ifpp = ifp;
! 1737: return (addrsofif);
! 1738: }
! 1739:
! 1740: addrs += addrsofif;
! 1741: }
! 1742:
! 1743: return (addrs);
! 1744: }
! 1745:
! 1746: static int
! 1747: ni6_store_addrs(ni6, nni6, ifp0, resid)
! 1748: struct icmp6_nodeinfo *ni6, *nni6;
! 1749: struct ifnet *ifp0;
! 1750: int resid;
! 1751: {
! 1752: struct ifnet *ifp = ifp0 ? ifp0 : TAILQ_FIRST(&ifnet);
! 1753: struct in6_ifaddr *ifa6;
! 1754: struct ifaddr *ifa;
! 1755: struct ifnet *ifp_dep = NULL;
! 1756: int copied = 0, allow_deprecated = 0;
! 1757: u_char *cp = (u_char *)(nni6 + 1);
! 1758: int niflags = ni6->ni_flags;
! 1759: u_int32_t ltime;
! 1760:
! 1761: if (ifp0 == NULL && !(niflags & NI_NODEADDR_FLAG_ALL))
! 1762: return (0); /* needless to copy */
! 1763:
! 1764: again:
! 1765:
! 1766: for (; ifp != TAILQ_END(&ifnet); ifp = TAILQ_NEXT(ifp, if_list)) {
! 1767: TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
! 1768: if (ifa->ifa_addr->sa_family != AF_INET6)
! 1769: continue;
! 1770: ifa6 = (struct in6_ifaddr *)ifa;
! 1771:
! 1772: if ((ifa6->ia6_flags & IN6_IFF_DEPRECATED) != 0 &&
! 1773: allow_deprecated == 0) {
! 1774: /*
! 1775: * prefererred address should be put before
! 1776: * deprecated addresses.
! 1777: */
! 1778:
! 1779: /* record the interface for later search */
! 1780: if (ifp_dep == NULL)
! 1781: ifp_dep = ifp;
! 1782:
! 1783: continue;
! 1784: }
! 1785: else if ((ifa6->ia6_flags & IN6_IFF_DEPRECATED) == 0 &&
! 1786: allow_deprecated != 0)
! 1787: continue; /* we now collect deprecated addrs */
! 1788:
! 1789: /* What do we have to do about ::1? */
! 1790: switch (in6_addrscope(&ifa6->ia_addr.sin6_addr)) {
! 1791: case IPV6_ADDR_SCOPE_LINKLOCAL:
! 1792: if ((niflags & NI_NODEADDR_FLAG_LINKLOCAL) == 0)
! 1793: continue;
! 1794: break;
! 1795: case IPV6_ADDR_SCOPE_SITELOCAL:
! 1796: if ((niflags & NI_NODEADDR_FLAG_SITELOCAL) == 0)
! 1797: continue;
! 1798: break;
! 1799: case IPV6_ADDR_SCOPE_GLOBAL:
! 1800: if ((niflags & NI_NODEADDR_FLAG_GLOBAL) == 0)
! 1801: continue;
! 1802: break;
! 1803: default:
! 1804: continue;
! 1805: }
! 1806:
! 1807: /*
! 1808: * check if anycast is okay.
! 1809: * XXX: just experimental. not in the spec.
! 1810: */
! 1811: if ((ifa6->ia6_flags & IN6_IFF_ANYCAST) != 0 &&
! 1812: (niflags & NI_NODEADDR_FLAG_ANYCAST) == 0)
! 1813: continue;
! 1814:
! 1815: /* now we can copy the address */
! 1816: if (resid < sizeof(struct in6_addr) +
! 1817: sizeof(u_int32_t)) {
! 1818: /*
! 1819: * We give up much more copy.
! 1820: * Set the truncate flag and return.
! 1821: */
! 1822: nni6->ni_flags |=
! 1823: NI_NODEADDR_FLAG_TRUNCATE;
! 1824: return (copied);
! 1825: }
! 1826:
! 1827: /*
! 1828: * Set the TTL of the address.
! 1829: * The TTL value should be one of the following
! 1830: * according to the specification:
! 1831: *
! 1832: * 1. The remaining lifetime of a DHCP lease on the
! 1833: * address, or
! 1834: * 2. The remaining Valid Lifetime of a prefix from
! 1835: * which the address was derived through Stateless
! 1836: * Autoconfiguration.
! 1837: *
! 1838: * Note that we currently do not support stateful
! 1839: * address configuration by DHCPv6, so the former
! 1840: * case can't happen.
! 1841: *
! 1842: * TTL must be 2^31 > TTL >= 0.
! 1843: */
! 1844: if (ifa6->ia6_lifetime.ia6t_expire == 0)
! 1845: ltime = ND6_INFINITE_LIFETIME;
! 1846: else {
! 1847: if (ifa6->ia6_lifetime.ia6t_expire >
! 1848: time_second)
! 1849: ltime = htonl(ifa6->ia6_lifetime.ia6t_expire - time_second);
! 1850: else
! 1851: ltime = 0;
! 1852: }
! 1853:
! 1854: bcopy(<ime, cp, sizeof(u_int32_t));
! 1855: cp += sizeof(u_int32_t);
! 1856:
! 1857: /* copy the address itself */
! 1858: bcopy(&ifa6->ia_addr.sin6_addr, cp,
! 1859: sizeof(struct in6_addr));
! 1860: /* XXX: KAME link-local hack; remove ifindex */
! 1861: if (IN6_IS_ADDR_LINKLOCAL(&ifa6->ia_addr.sin6_addr))
! 1862: ((struct in6_addr *)cp)->s6_addr16[1] = 0;
! 1863: cp += sizeof(struct in6_addr);
! 1864:
! 1865: resid -= (sizeof(struct in6_addr) + sizeof(u_int32_t));
! 1866: copied += (sizeof(struct in6_addr) +
! 1867: sizeof(u_int32_t));
! 1868: }
! 1869: if (ifp0) /* we need search only on the specified IF */
! 1870: break;
! 1871: }
! 1872:
! 1873: if (allow_deprecated == 0 && ifp_dep != NULL) {
! 1874: ifp = ifp_dep;
! 1875: allow_deprecated = 1;
! 1876:
! 1877: goto again;
! 1878: }
! 1879:
! 1880: return (copied);
! 1881: }
! 1882:
! 1883: /*
! 1884: * XXX almost dup'ed code with rip6_input.
! 1885: */
! 1886: static int
! 1887: icmp6_rip6_input(mp, off)
! 1888: struct mbuf **mp;
! 1889: int off;
! 1890: {
! 1891: struct mbuf *m = *mp;
! 1892: struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
! 1893: struct in6pcb *in6p;
! 1894: struct in6pcb *last = NULL;
! 1895: struct sockaddr_in6 rip6src;
! 1896: struct icmp6_hdr *icmp6;
! 1897: struct mbuf *opts = NULL;
! 1898:
! 1899: IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, sizeof(*icmp6));
! 1900: if (icmp6 == NULL) {
! 1901: /* m is already reclaimed */
! 1902: return IPPROTO_DONE;
! 1903: }
! 1904:
! 1905: bzero(&rip6src, sizeof(rip6src));
! 1906: rip6src.sin6_len = sizeof(struct sockaddr_in6);
! 1907: rip6src.sin6_family = AF_INET6;
! 1908: /* KAME hack: recover scopeid */
! 1909: (void)in6_recoverscope(&rip6src, &ip6->ip6_src, m->m_pkthdr.rcvif);
! 1910:
! 1911: CIRCLEQ_FOREACH(in6p, &rawin6pcbtable.inpt_queue, inp_queue) {
! 1912: if (!(in6p->in6p_flags & INP_IPV6))
! 1913: continue;
! 1914: if (in6p->in6p_ip6_nxt != IPPROTO_ICMPV6)
! 1915: continue;
! 1916: if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) &&
! 1917: !IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst))
! 1918: continue;
! 1919: if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) &&
! 1920: !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src))
! 1921: continue;
! 1922: if (in6p->in6p_icmp6filt
! 1923: && ICMP6_FILTER_WILLBLOCK(icmp6->icmp6_type,
! 1924: in6p->in6p_icmp6filt))
! 1925: continue;
! 1926: if (last) {
! 1927: struct mbuf *n;
! 1928: if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) {
! 1929: if (last->in6p_flags & IN6P_CONTROLOPTS)
! 1930: ip6_savecontrol(last, n, &opts);
! 1931: /* strip intermediate headers */
! 1932: m_adj(n, off);
! 1933: if (sbappendaddr(&last->in6p_socket->so_rcv,
! 1934: (struct sockaddr *)&rip6src,
! 1935: n, opts) == 0) {
! 1936: /* should notify about lost packet */
! 1937: m_freem(n);
! 1938: if (opts)
! 1939: m_freem(opts);
! 1940: } else
! 1941: sorwakeup(last->in6p_socket);
! 1942: opts = NULL;
! 1943: }
! 1944: }
! 1945: last = in6p;
! 1946: }
! 1947: if (last) {
! 1948: if (last->in6p_flags & IN6P_CONTROLOPTS)
! 1949: ip6_savecontrol(last, m, &opts);
! 1950: /* strip intermediate headers */
! 1951: m_adj(m, off);
! 1952: if (sbappendaddr(&last->in6p_socket->so_rcv,
! 1953: (struct sockaddr *)&rip6src,
! 1954: m, opts) == 0) {
! 1955: m_freem(m);
! 1956: if (opts)
! 1957: m_freem(opts);
! 1958: } else
! 1959: sorwakeup(last->in6p_socket);
! 1960: } else {
! 1961: m_freem(m);
! 1962: ip6stat.ip6s_delivered--;
! 1963: }
! 1964: return IPPROTO_DONE;
! 1965: }
! 1966:
! 1967: /*
! 1968: * Reflect the ip6 packet back to the source.
! 1969: * OFF points to the icmp6 header, counted from the top of the mbuf.
! 1970: *
! 1971: * Note: RFC 1885 required that an echo reply should be truncated if it
! 1972: * did not fit in with (return) path MTU, and KAME code supported the
! 1973: * behavior. However, as a clarification after the RFC, this limitation
! 1974: * was removed in a revised version of the spec, RFC 2463. We had kept the
! 1975: * old behavior, with a (non-default) ifdef block, while the new version of
! 1976: * the spec was an internet-draft status, and even after the new RFC was
! 1977: * published. But it would rather make sense to clean the obsoleted part
! 1978: * up, and to make the code simpler at this stage.
! 1979: */
! 1980: void
! 1981: icmp6_reflect(m, off)
! 1982: struct mbuf *m;
! 1983: size_t off;
! 1984: {
! 1985: struct ip6_hdr *ip6;
! 1986: struct icmp6_hdr *icmp6;
! 1987: struct in6_ifaddr *ia;
! 1988: struct in6_addr t, *src = 0;
! 1989: int plen;
! 1990: int type, code;
! 1991: struct ifnet *outif = NULL;
! 1992: struct sockaddr_in6 sa6_src, sa6_dst;
! 1993:
! 1994: /* too short to reflect */
! 1995: if (off < sizeof(struct ip6_hdr)) {
! 1996: nd6log((LOG_DEBUG,
! 1997: "sanity fail: off=%lx, sizeof(ip6)=%lx in %s:%d\n",
! 1998: (u_long)off, (u_long)sizeof(struct ip6_hdr),
! 1999: __FILE__, __LINE__));
! 2000: goto bad;
! 2001: }
! 2002:
! 2003: /*
! 2004: * If there are extra headers between IPv6 and ICMPv6, strip
! 2005: * off that header first.
! 2006: */
! 2007: #ifdef DIAGNOSTIC
! 2008: if (sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) > MHLEN)
! 2009: panic("assumption failed in icmp6_reflect");
! 2010: #endif
! 2011: if (off > sizeof(struct ip6_hdr)) {
! 2012: size_t l;
! 2013: struct ip6_hdr nip6;
! 2014:
! 2015: l = off - sizeof(struct ip6_hdr);
! 2016: m_copydata(m, 0, sizeof(nip6), (caddr_t)&nip6);
! 2017: m_adj(m, l);
! 2018: l = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr);
! 2019: if (m->m_len < l) {
! 2020: if ((m = m_pullup(m, l)) == NULL)
! 2021: return;
! 2022: }
! 2023: bcopy((caddr_t)&nip6, mtod(m, caddr_t), sizeof(nip6));
! 2024: } else /* off == sizeof(struct ip6_hdr) */ {
! 2025: size_t l;
! 2026: l = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr);
! 2027: if (m->m_len < l) {
! 2028: if ((m = m_pullup(m, l)) == NULL)
! 2029: return;
! 2030: }
! 2031: }
! 2032: plen = m->m_pkthdr.len - sizeof(struct ip6_hdr);
! 2033: ip6 = mtod(m, struct ip6_hdr *);
! 2034: ip6->ip6_nxt = IPPROTO_ICMPV6;
! 2035: icmp6 = (struct icmp6_hdr *)(ip6 + 1);
! 2036: type = icmp6->icmp6_type; /* keep type for statistics */
! 2037: code = icmp6->icmp6_code; /* ditto. */
! 2038:
! 2039: t = ip6->ip6_dst;
! 2040: /*
! 2041: * ip6_input() drops a packet if its src is multicast.
! 2042: * So, the src is never multicast.
! 2043: */
! 2044: ip6->ip6_dst = ip6->ip6_src;
! 2045:
! 2046: /*
! 2047: * XXX: make sure to embed scope zone information, using
! 2048: * already embedded IDs or the received interface (if any).
! 2049: * Note that rcvif may be NULL.
! 2050: * TODO: scoped routing case (XXX).
! 2051: */
! 2052: bzero(&sa6_src, sizeof(sa6_src));
! 2053: sa6_src.sin6_family = AF_INET6;
! 2054: sa6_src.sin6_len = sizeof(sa6_src);
! 2055: sa6_src.sin6_addr = ip6->ip6_dst;
! 2056: in6_recoverscope(&sa6_src, &ip6->ip6_dst, m->m_pkthdr.rcvif);
! 2057: in6_embedscope(&ip6->ip6_dst, &sa6_src, NULL, NULL);
! 2058: bzero(&sa6_dst, sizeof(sa6_dst));
! 2059: sa6_dst.sin6_family = AF_INET6;
! 2060: sa6_dst.sin6_len = sizeof(sa6_dst);
! 2061: sa6_dst.sin6_addr = t;
! 2062: in6_recoverscope(&sa6_dst, &t, m->m_pkthdr.rcvif);
! 2063: in6_embedscope(&t, &sa6_dst, NULL, NULL);
! 2064:
! 2065: /*
! 2066: * If the incoming packet was addressed directly to us (i.e. unicast),
! 2067: * use dst as the src for the reply.
! 2068: * The IN6_IFF_NOTREADY case would be VERY rare, but is possible
! 2069: * (for example) when we encounter an error while forwarding procedure
! 2070: * destined to a duplicated address of ours.
! 2071: */
! 2072: for (ia = in6_ifaddr; ia; ia = ia->ia_next)
! 2073: if (IN6_ARE_ADDR_EQUAL(&t, &ia->ia_addr.sin6_addr) &&
! 2074: (ia->ia6_flags & (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY)) == 0) {
! 2075: src = &t;
! 2076: break;
! 2077: }
! 2078: if (ia == NULL && IN6_IS_ADDR_LINKLOCAL(&t) && (m->m_flags & M_LOOP)) {
! 2079: /*
! 2080: * This is the case if the dst is our link-local address
! 2081: * and the sender is also ourselves.
! 2082: */
! 2083: src = &t;
! 2084: }
! 2085:
! 2086: if (src == 0) {
! 2087: int e;
! 2088: struct route_in6 ro;
! 2089:
! 2090: /*
! 2091: * This case matches to multicasts, our anycast, or unicasts
! 2092: * that we do not own. Select a source address based on the
! 2093: * source address of the erroneous packet.
! 2094: */
! 2095: bzero(&ro, sizeof(ro));
! 2096: src = in6_selectsrc(&sa6_src, NULL, NULL, &ro, NULL, &e);
! 2097: if (ro.ro_rt) { /* XXX: see comments in icmp6_mtudisc_update */
! 2098: RTFREE(ro.ro_rt); /* XXX: we could use this */
! 2099: }
! 2100: if (src == NULL) {
! 2101: nd6log((LOG_DEBUG,
! 2102: "icmp6_reflect: source can't be determined: "
! 2103: "dst=%s, error=%d\n",
! 2104: ip6_sprintf(&sa6_src.sin6_addr), e));
! 2105: goto bad;
! 2106: }
! 2107: }
! 2108:
! 2109: ip6->ip6_src = *src;
! 2110:
! 2111: ip6->ip6_flow = 0;
! 2112: ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
! 2113: ip6->ip6_vfc |= IPV6_VERSION;
! 2114: ip6->ip6_nxt = IPPROTO_ICMPV6;
! 2115: if (m->m_pkthdr.rcvif) {
! 2116: /* XXX: This may not be the outgoing interface */
! 2117: ip6->ip6_hlim = ND_IFINFO(m->m_pkthdr.rcvif)->chlim;
! 2118: } else
! 2119: ip6->ip6_hlim = ip6_defhlim;
! 2120:
! 2121: icmp6->icmp6_cksum = 0;
! 2122: icmp6->icmp6_cksum = in6_cksum(m, IPPROTO_ICMPV6,
! 2123: sizeof(struct ip6_hdr), plen);
! 2124:
! 2125: /*
! 2126: * XXX option handling
! 2127: */
! 2128:
! 2129: m->m_flags &= ~(M_BCAST|M_MCAST);
! 2130:
! 2131: /*
! 2132: * To avoid a "too big" situation at an intermediate router
! 2133: * and the path MTU discovery process, specify the IPV6_MINMTU flag.
! 2134: * Note that only echo and node information replies are affected,
! 2135: * since the length of ICMP6 errors is limited to the minimum MTU.
! 2136: */
! 2137: if (ip6_output(m, NULL, NULL, IPV6_MINMTU, NULL, &outif, NULL) != 0 &&
! 2138: outif)
! 2139: icmp6_ifstat_inc(outif, ifs6_out_error);
! 2140:
! 2141: if (outif)
! 2142: icmp6_ifoutstat_inc(outif, type, code);
! 2143:
! 2144: return;
! 2145:
! 2146: bad:
! 2147: m_freem(m);
! 2148: return;
! 2149: }
! 2150:
! 2151: void
! 2152: icmp6_fasttimo()
! 2153: {
! 2154:
! 2155: mld6_fasttimeo();
! 2156: }
! 2157:
! 2158: static const char *
! 2159: icmp6_redirect_diag(src6, dst6, tgt6)
! 2160: struct in6_addr *src6;
! 2161: struct in6_addr *dst6;
! 2162: struct in6_addr *tgt6;
! 2163: {
! 2164: static char buf[1024];
! 2165: snprintf(buf, sizeof(buf), "(src=%s dst=%s tgt=%s)",
! 2166: ip6_sprintf(src6), ip6_sprintf(dst6), ip6_sprintf(tgt6));
! 2167: return buf;
! 2168: }
! 2169:
! 2170: void
! 2171: icmp6_redirect_input(m, off)
! 2172: struct mbuf *m;
! 2173: int off;
! 2174: {
! 2175: struct ifnet *ifp = m->m_pkthdr.rcvif;
! 2176: struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
! 2177: struct nd_redirect *nd_rd;
! 2178: int icmp6len = ntohs(ip6->ip6_plen);
! 2179: char *lladdr = NULL;
! 2180: int lladdrlen = 0;
! 2181: u_char *redirhdr = NULL;
! 2182: int redirhdrlen = 0;
! 2183: struct rtentry *rt = NULL;
! 2184: int is_router;
! 2185: int is_onlink;
! 2186: struct in6_addr src6 = ip6->ip6_src;
! 2187: struct in6_addr redtgt6;
! 2188: struct in6_addr reddst6;
! 2189: union nd_opts ndopts;
! 2190:
! 2191: if (!ifp)
! 2192: return;
! 2193:
! 2194: /* XXX if we are router, we don't update route by icmp6 redirect */
! 2195: if (ip6_forwarding)
! 2196: goto freeit;
! 2197: if (!icmp6_rediraccept)
! 2198: goto freeit;
! 2199:
! 2200: IP6_EXTHDR_GET(nd_rd, struct nd_redirect *, m, off, icmp6len);
! 2201: if (nd_rd == NULL) {
! 2202: icmp6stat.icp6s_tooshort++;
! 2203: return;
! 2204: }
! 2205: redtgt6 = nd_rd->nd_rd_target;
! 2206: reddst6 = nd_rd->nd_rd_dst;
! 2207:
! 2208: if (IN6_IS_ADDR_LINKLOCAL(&redtgt6))
! 2209: redtgt6.s6_addr16[1] = htons(ifp->if_index);
! 2210: if (IN6_IS_ADDR_LINKLOCAL(&reddst6))
! 2211: reddst6.s6_addr16[1] = htons(ifp->if_index);
! 2212:
! 2213: /* validation */
! 2214: if (!IN6_IS_ADDR_LINKLOCAL(&src6)) {
! 2215: nd6log((LOG_ERR,
! 2216: "ICMP6 redirect sent from %s rejected; "
! 2217: "must be from linklocal\n", ip6_sprintf(&src6)));
! 2218: goto bad;
! 2219: }
! 2220: if (ip6->ip6_hlim != 255) {
! 2221: nd6log((LOG_ERR,
! 2222: "ICMP6 redirect sent from %s rejected; "
! 2223: "hlim=%d (must be 255)\n",
! 2224: ip6_sprintf(&src6), ip6->ip6_hlim));
! 2225: goto bad;
! 2226: }
! 2227: {
! 2228: /* ip6->ip6_src must be equal to gw for icmp6->icmp6_reddst */
! 2229: struct sockaddr_in6 sin6;
! 2230: struct in6_addr *gw6;
! 2231:
! 2232: bzero(&sin6, sizeof(sin6));
! 2233: sin6.sin6_family = AF_INET6;
! 2234: sin6.sin6_len = sizeof(struct sockaddr_in6);
! 2235: bcopy(&reddst6, &sin6.sin6_addr, sizeof(reddst6));
! 2236: rt = rtalloc1((struct sockaddr *)&sin6, 0, 0);
! 2237: if (rt) {
! 2238: if (rt->rt_gateway == NULL ||
! 2239: rt->rt_gateway->sa_family != AF_INET6) {
! 2240: nd6log((LOG_ERR,
! 2241: "ICMP6 redirect rejected; no route "
! 2242: "with inet6 gateway found for redirect dst: %s\n",
! 2243: icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
! 2244: RTFREE(rt);
! 2245: goto bad;
! 2246: }
! 2247:
! 2248: gw6 = &(((struct sockaddr_in6 *)rt->rt_gateway)->sin6_addr);
! 2249: if (bcmp(&src6, gw6, sizeof(struct in6_addr)) != 0) {
! 2250: nd6log((LOG_ERR,
! 2251: "ICMP6 redirect rejected; "
! 2252: "not equal to gw-for-src=%s (must be same): "
! 2253: "%s\n",
! 2254: ip6_sprintf(gw6),
! 2255: icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
! 2256: RTFREE(rt);
! 2257: goto bad;
! 2258: }
! 2259: } else {
! 2260: nd6log((LOG_ERR,
! 2261: "ICMP6 redirect rejected; "
! 2262: "no route found for redirect dst: %s\n",
! 2263: icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
! 2264: goto bad;
! 2265: }
! 2266: RTFREE(rt);
! 2267: rt = NULL;
! 2268: }
! 2269: if (IN6_IS_ADDR_MULTICAST(&reddst6)) {
! 2270: nd6log((LOG_ERR,
! 2271: "ICMP6 redirect rejected; "
! 2272: "redirect dst must be unicast: %s\n",
! 2273: icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
! 2274: goto bad;
! 2275: }
! 2276:
! 2277: is_router = is_onlink = 0;
! 2278: if (IN6_IS_ADDR_LINKLOCAL(&redtgt6))
! 2279: is_router = 1; /* router case */
! 2280: if (bcmp(&redtgt6, &reddst6, sizeof(redtgt6)) == 0)
! 2281: is_onlink = 1; /* on-link destination case */
! 2282: if (!is_router && !is_onlink) {
! 2283: nd6log((LOG_ERR,
! 2284: "ICMP6 redirect rejected; "
! 2285: "neither router case nor onlink case: %s\n",
! 2286: icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
! 2287: goto bad;
! 2288: }
! 2289: /* validation passed */
! 2290:
! 2291: icmp6len -= sizeof(*nd_rd);
! 2292: nd6_option_init(nd_rd + 1, icmp6len, &ndopts);
! 2293: if (nd6_options(&ndopts) < 0) {
! 2294: nd6log((LOG_INFO, "icmp6_redirect_input: "
! 2295: "invalid ND option, rejected: %s\n",
! 2296: icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
! 2297: /* nd6_options have incremented stats */
! 2298: goto freeit;
! 2299: }
! 2300:
! 2301: if (ndopts.nd_opts_tgt_lladdr) {
! 2302: lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1);
! 2303: lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3;
! 2304: }
! 2305:
! 2306: if (ndopts.nd_opts_rh) {
! 2307: redirhdrlen = ndopts.nd_opts_rh->nd_opt_rh_len;
! 2308: redirhdr = (u_char *)(ndopts.nd_opts_rh + 1); /* xxx */
! 2309: }
! 2310:
! 2311: if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
! 2312: nd6log((LOG_INFO,
! 2313: "icmp6_redirect_input: lladdrlen mismatch for %s "
! 2314: "(if %d, icmp6 packet %d): %s\n",
! 2315: ip6_sprintf(&redtgt6), ifp->if_addrlen, lladdrlen - 2,
! 2316: icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
! 2317: goto bad;
! 2318: }
! 2319:
! 2320: /* RFC 2461 8.3 */
! 2321: nd6_cache_lladdr(ifp, &redtgt6, lladdr, lladdrlen, ND_REDIRECT,
! 2322: is_onlink ? ND_REDIRECT_ONLINK : ND_REDIRECT_ROUTER);
! 2323:
! 2324: if (!is_onlink) { /* better router case. perform rtredirect. */
! 2325: /* perform rtredirect */
! 2326: struct sockaddr_in6 sdst;
! 2327: struct sockaddr_in6 sgw;
! 2328: struct sockaddr_in6 ssrc;
! 2329: unsigned long rtcount;
! 2330: struct rtentry *newrt = NULL;
! 2331:
! 2332: /*
! 2333: * do not install redirect route, if the number of entries
! 2334: * is too much (> hiwat). note that, the node (= host) will
! 2335: * work just fine even if we do not install redirect route
! 2336: * (there will be additional hops, though).
! 2337: */
! 2338: rtcount = rt_timer_count(icmp6_redirect_timeout_q);
! 2339: if (0 <= icmp6_redirect_hiwat && rtcount > icmp6_redirect_hiwat)
! 2340: return;
! 2341: else if (0 <= icmp6_redirect_lowat &&
! 2342: rtcount > icmp6_redirect_lowat) {
! 2343: /*
! 2344: * XXX nuke a victim, install the new one.
! 2345: */
! 2346: }
! 2347:
! 2348: bzero(&sdst, sizeof(sdst));
! 2349: bzero(&sgw, sizeof(sgw));
! 2350: bzero(&ssrc, sizeof(ssrc));
! 2351: sdst.sin6_family = sgw.sin6_family = ssrc.sin6_family = AF_INET6;
! 2352: sdst.sin6_len = sgw.sin6_len = ssrc.sin6_len =
! 2353: sizeof(struct sockaddr_in6);
! 2354: bcopy(&redtgt6, &sgw.sin6_addr, sizeof(struct in6_addr));
! 2355: bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr));
! 2356: bcopy(&src6, &ssrc.sin6_addr, sizeof(struct in6_addr));
! 2357: rtredirect((struct sockaddr *)&sdst, (struct sockaddr *)&sgw,
! 2358: (struct sockaddr *)NULL, RTF_GATEWAY | RTF_HOST,
! 2359: (struct sockaddr *)&ssrc,
! 2360: &newrt);
! 2361:
! 2362: if (newrt) {
! 2363: (void)rt_timer_add(newrt, icmp6_redirect_timeout,
! 2364: icmp6_redirect_timeout_q);
! 2365: rtfree(newrt);
! 2366: }
! 2367: }
! 2368: /* finally update cached route in each socket via pfctlinput */
! 2369: {
! 2370: struct sockaddr_in6 sdst;
! 2371:
! 2372: bzero(&sdst, sizeof(sdst));
! 2373: sdst.sin6_family = AF_INET6;
! 2374: sdst.sin6_len = sizeof(struct sockaddr_in6);
! 2375: bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr));
! 2376: pfctlinput(PRC_REDIRECT_HOST, (struct sockaddr *)&sdst);
! 2377: }
! 2378:
! 2379: freeit:
! 2380: m_freem(m);
! 2381: return;
! 2382:
! 2383: bad:
! 2384: icmp6stat.icp6s_badredirect++;
! 2385: m_freem(m);
! 2386: }
! 2387:
! 2388: void
! 2389: icmp6_redirect_output(m0, rt)
! 2390: struct mbuf *m0;
! 2391: struct rtentry *rt;
! 2392: {
! 2393: struct ifnet *ifp; /* my outgoing interface */
! 2394: struct in6_addr *ifp_ll6;
! 2395: struct in6_addr *nexthop;
! 2396: struct ip6_hdr *sip6; /* m0 as struct ip6_hdr */
! 2397: struct mbuf *m = NULL; /* newly allocated one */
! 2398: struct ip6_hdr *ip6; /* m as struct ip6_hdr */
! 2399: struct nd_redirect *nd_rd;
! 2400: size_t maxlen;
! 2401: u_char *p;
! 2402: struct sockaddr_in6 src_sa;
! 2403:
! 2404: icmp6_errcount(&icmp6stat.icp6s_outerrhist, ND_REDIRECT, 0);
! 2405:
! 2406: /* if we are not router, we don't send icmp6 redirect */
! 2407: if (!ip6_forwarding)
! 2408: goto fail;
! 2409:
! 2410: /* sanity check */
! 2411: if (!m0 || !rt || !(rt->rt_flags & RTF_UP) || !(ifp = rt->rt_ifp))
! 2412: goto fail;
! 2413:
! 2414: /*
! 2415: * Address check:
! 2416: * the source address must identify a neighbor, and
! 2417: * the destination address must not be a multicast address
! 2418: * [RFC 2461, sec 8.2]
! 2419: */
! 2420: sip6 = mtod(m0, struct ip6_hdr *);
! 2421: bzero(&src_sa, sizeof(src_sa));
! 2422: src_sa.sin6_family = AF_INET6;
! 2423: src_sa.sin6_len = sizeof(src_sa);
! 2424: src_sa.sin6_addr = sip6->ip6_src;
! 2425: /* we don't currently use sin6_scope_id, but eventually use it */
! 2426: src_sa.sin6_scope_id = in6_addr2scopeid(ifp, &sip6->ip6_src);
! 2427: if (nd6_is_addr_neighbor(&src_sa, ifp) == 0)
! 2428: goto fail;
! 2429: if (IN6_IS_ADDR_MULTICAST(&sip6->ip6_dst))
! 2430: goto fail; /* what should we do here? */
! 2431:
! 2432: /* rate limit */
! 2433: if (icmp6_ratelimit(&sip6->ip6_src, ND_REDIRECT, 0))
! 2434: goto fail;
! 2435:
! 2436: /*
! 2437: * Since we are going to append up to 1280 bytes (= IPV6_MMTU),
! 2438: * we almost always ask for an mbuf cluster for simplicity.
! 2439: * (MHLEN < IPV6_MMTU is almost always true)
! 2440: */
! 2441: #if IPV6_MMTU >= MCLBYTES
! 2442: # error assumption failed about IPV6_MMTU and MCLBYTES
! 2443: #endif
! 2444: MGETHDR(m, M_DONTWAIT, MT_HEADER);
! 2445: if (m && IPV6_MMTU >= MHLEN)
! 2446: MCLGET(m, M_DONTWAIT);
! 2447: if (!m)
! 2448: goto fail;
! 2449: m->m_pkthdr.rcvif = NULL;
! 2450: m->m_len = 0;
! 2451: maxlen = M_TRAILINGSPACE(m);
! 2452: maxlen = min(IPV6_MMTU, maxlen);
! 2453: /* just for safety */
! 2454: if (maxlen < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) +
! 2455: ((sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7)) {
! 2456: goto fail;
! 2457: }
! 2458:
! 2459: {
! 2460: /* get ip6 linklocal address for ifp(my outgoing interface). */
! 2461: struct in6_ifaddr *ia;
! 2462: if ((ia = in6ifa_ifpforlinklocal(ifp,
! 2463: IN6_IFF_NOTREADY|
! 2464: IN6_IFF_ANYCAST)) == NULL)
! 2465: goto fail;
! 2466: ifp_ll6 = &ia->ia_addr.sin6_addr;
! 2467: }
! 2468:
! 2469: /* get ip6 linklocal address for the router. */
! 2470: if (rt->rt_gateway && (rt->rt_flags & RTF_GATEWAY)) {
! 2471: struct sockaddr_in6 *sin6;
! 2472: sin6 = (struct sockaddr_in6 *)rt->rt_gateway;
! 2473: nexthop = &sin6->sin6_addr;
! 2474: if (!IN6_IS_ADDR_LINKLOCAL(nexthop))
! 2475: nexthop = NULL;
! 2476: } else
! 2477: nexthop = NULL;
! 2478:
! 2479: /* ip6 */
! 2480: ip6 = mtod(m, struct ip6_hdr *);
! 2481: ip6->ip6_flow = 0;
! 2482: ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
! 2483: ip6->ip6_vfc |= IPV6_VERSION;
! 2484: /* ip6->ip6_plen will be set later */
! 2485: ip6->ip6_nxt = IPPROTO_ICMPV6;
! 2486: ip6->ip6_hlim = 255;
! 2487: /* ip6->ip6_src must be linklocal addr for my outgoing if. */
! 2488: bcopy(ifp_ll6, &ip6->ip6_src, sizeof(struct in6_addr));
! 2489: bcopy(&sip6->ip6_src, &ip6->ip6_dst, sizeof(struct in6_addr));
! 2490:
! 2491: /* ND Redirect */
! 2492: nd_rd = (struct nd_redirect *)(ip6 + 1);
! 2493: nd_rd->nd_rd_type = ND_REDIRECT;
! 2494: nd_rd->nd_rd_code = 0;
! 2495: nd_rd->nd_rd_reserved = 0;
! 2496: if (rt->rt_flags & RTF_GATEWAY) {
! 2497: /*
! 2498: * nd_rd->nd_rd_target must be a link-local address in
! 2499: * better router cases.
! 2500: */
! 2501: if (!nexthop)
! 2502: goto fail;
! 2503: bcopy(nexthop, &nd_rd->nd_rd_target,
! 2504: sizeof(nd_rd->nd_rd_target));
! 2505: bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst,
! 2506: sizeof(nd_rd->nd_rd_dst));
! 2507: } else {
! 2508: /* make sure redtgt == reddst */
! 2509: nexthop = &sip6->ip6_dst;
! 2510: bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_target,
! 2511: sizeof(nd_rd->nd_rd_target));
! 2512: bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst,
! 2513: sizeof(nd_rd->nd_rd_dst));
! 2514: }
! 2515:
! 2516: p = (u_char *)(nd_rd + 1);
! 2517:
! 2518: {
! 2519: /* target lladdr option */
! 2520: struct rtentry *rt_nexthop = NULL;
! 2521: int len;
! 2522: struct sockaddr_dl *sdl;
! 2523: struct nd_opt_hdr *nd_opt;
! 2524: char *lladdr;
! 2525:
! 2526: rt_nexthop = nd6_lookup(nexthop, 0, ifp);
! 2527: if (!rt_nexthop)
! 2528: goto nolladdropt;
! 2529: len = sizeof(*nd_opt) + ifp->if_addrlen;
! 2530: len = (len + 7) & ~7; /* round by 8 */
! 2531: /* safety check */
! 2532: if (len + (p - (u_char *)ip6) > maxlen)
! 2533: goto nolladdropt;
! 2534: if (!(rt_nexthop->rt_flags & RTF_GATEWAY) &&
! 2535: (rt_nexthop->rt_flags & RTF_LLINFO) &&
! 2536: (rt_nexthop->rt_gateway->sa_family == AF_LINK) &&
! 2537: (sdl = (struct sockaddr_dl *)rt_nexthop->rt_gateway) &&
! 2538: sdl->sdl_alen) {
! 2539: nd_opt = (struct nd_opt_hdr *)p;
! 2540: nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
! 2541: nd_opt->nd_opt_len = len >> 3;
! 2542: lladdr = (char *)(nd_opt + 1);
! 2543: bcopy(LLADDR(sdl), lladdr, ifp->if_addrlen);
! 2544: p += len;
! 2545: }
! 2546: }
! 2547: nolladdropt:;
! 2548:
! 2549: m->m_pkthdr.len = m->m_len = p - (u_char *)ip6;
! 2550:
! 2551: /* just to be safe */
! 2552: if (p - (u_char *)ip6 > maxlen)
! 2553: goto noredhdropt;
! 2554:
! 2555: {
! 2556: /* redirected header option */
! 2557: int len;
! 2558: struct nd_opt_rd_hdr *nd_opt_rh;
! 2559:
! 2560: /*
! 2561: * compute the maximum size for icmp6 redirect header option.
! 2562: * XXX room for auth header?
! 2563: */
! 2564: len = maxlen - (p - (u_char *)ip6);
! 2565: len &= ~7;
! 2566:
! 2567: /*
! 2568: * Redirected header option spec (RFC2461 4.6.3) talks nothing
! 2569: * about padding/truncate rule for the original IP packet.
! 2570: * From the discussion on IPv6imp in Feb 1999,
! 2571: * the consensus was:
! 2572: * - "attach as much as possible" is the goal
! 2573: * - pad if not aligned (original size can be guessed by
! 2574: * original ip6 header)
! 2575: * Following code adds the padding if it is simple enough,
! 2576: * and truncates if not.
! 2577: */
! 2578: if (len - sizeof(*nd_opt_rh) < m0->m_pkthdr.len) {
! 2579: /* not enough room, truncate */
! 2580: m_adj(m0, (len - sizeof(*nd_opt_rh)) -
! 2581: m0->m_pkthdr.len);
! 2582: } else {
! 2583: /*
! 2584: * enough room, truncate if not aligned.
! 2585: * we don't pad here for simplicity.
! 2586: */
! 2587: size_t extra;
! 2588:
! 2589: extra = m0->m_pkthdr.len % 8;
! 2590: if (extra) {
! 2591: /* truncate */
! 2592: m_adj(m0, -extra);
! 2593: }
! 2594: len = m0->m_pkthdr.len + sizeof(*nd_opt_rh);
! 2595: }
! 2596:
! 2597: nd_opt_rh = (struct nd_opt_rd_hdr *)p;
! 2598: bzero(nd_opt_rh, sizeof(*nd_opt_rh));
! 2599: nd_opt_rh->nd_opt_rh_type = ND_OPT_REDIRECTED_HEADER;
! 2600: nd_opt_rh->nd_opt_rh_len = len >> 3;
! 2601: p += sizeof(*nd_opt_rh);
! 2602: m->m_pkthdr.len = m->m_len = p - (u_char *)ip6;
! 2603:
! 2604: /* connect m0 to m */
! 2605: m->m_pkthdr.len += m0->m_pkthdr.len;
! 2606: m_cat(m, m0);
! 2607: m0 = NULL;
! 2608: }
! 2609: noredhdropt:
! 2610: if (m0) {
! 2611: m_freem(m0);
! 2612: m0 = NULL;
! 2613: }
! 2614:
! 2615: sip6 = mtod(m, struct ip6_hdr *);
! 2616: if (IN6_IS_ADDR_LINKLOCAL(&sip6->ip6_src))
! 2617: sip6->ip6_src.s6_addr16[1] = 0;
! 2618: if (IN6_IS_ADDR_LINKLOCAL(&sip6->ip6_dst))
! 2619: sip6->ip6_dst.s6_addr16[1] = 0;
! 2620: #if 0
! 2621: if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src))
! 2622: ip6->ip6_src.s6_addr16[1] = 0;
! 2623: if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst))
! 2624: ip6->ip6_dst.s6_addr16[1] = 0;
! 2625: #endif
! 2626: if (IN6_IS_ADDR_LINKLOCAL(&nd_rd->nd_rd_target))
! 2627: nd_rd->nd_rd_target.s6_addr16[1] = 0;
! 2628: if (IN6_IS_ADDR_LINKLOCAL(&nd_rd->nd_rd_dst))
! 2629: nd_rd->nd_rd_dst.s6_addr16[1] = 0;
! 2630:
! 2631: ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
! 2632:
! 2633: nd_rd->nd_rd_cksum = 0;
! 2634: nd_rd->nd_rd_cksum
! 2635: = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), ntohs(ip6->ip6_plen));
! 2636:
! 2637: /* send the packet to outside... */
! 2638: if (ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL) != 0)
! 2639: icmp6_ifstat_inc(ifp, ifs6_out_error);
! 2640:
! 2641: icmp6_ifstat_inc(ifp, ifs6_out_msg);
! 2642: icmp6_ifstat_inc(ifp, ifs6_out_redirect);
! 2643: icmp6stat.icp6s_outhist[ND_REDIRECT]++;
! 2644:
! 2645: return;
! 2646:
! 2647: fail:
! 2648: if (m)
! 2649: m_freem(m);
! 2650: if (m0)
! 2651: m_freem(m0);
! 2652: }
! 2653:
! 2654: /* NRL PCB */
! 2655: #define sotoin6pcb sotoinpcb
! 2656: #define in6pcb inpcb
! 2657: #define in6p_icmp6filt inp_icmp6filt
! 2658:
! 2659: /*
! 2660: * ICMPv6 socket option processing.
! 2661: */
! 2662: int
! 2663: icmp6_ctloutput(op, so, level, optname, mp)
! 2664: int op;
! 2665: struct socket *so;
! 2666: int level, optname;
! 2667: struct mbuf **mp;
! 2668: {
! 2669: int error = 0;
! 2670: int optlen;
! 2671: struct in6pcb *in6p = sotoin6pcb(so);
! 2672: struct mbuf *m = *mp;
! 2673:
! 2674: optlen = m ? m->m_len : 0;
! 2675:
! 2676: if (level != IPPROTO_ICMPV6) {
! 2677: if (op == PRCO_SETOPT && m)
! 2678: (void)m_free(m);
! 2679: return EINVAL;
! 2680: }
! 2681:
! 2682: switch (op) {
! 2683: case PRCO_SETOPT:
! 2684: switch (optname) {
! 2685: case ICMP6_FILTER:
! 2686: {
! 2687: struct icmp6_filter *p;
! 2688:
! 2689: if (optlen != sizeof(*p)) {
! 2690: error = EMSGSIZE;
! 2691: break;
! 2692: }
! 2693: p = mtod(m, struct icmp6_filter *);
! 2694: if (!p || !in6p->in6p_icmp6filt) {
! 2695: error = EINVAL;
! 2696: break;
! 2697: }
! 2698: bcopy(p, in6p->in6p_icmp6filt,
! 2699: sizeof(struct icmp6_filter));
! 2700: error = 0;
! 2701: break;
! 2702: }
! 2703:
! 2704: default:
! 2705: error = ENOPROTOOPT;
! 2706: break;
! 2707: }
! 2708: if (m)
! 2709: (void)m_freem(m);
! 2710: break;
! 2711:
! 2712: case PRCO_GETOPT:
! 2713: switch (optname) {
! 2714: case ICMP6_FILTER:
! 2715: {
! 2716: struct icmp6_filter *p;
! 2717:
! 2718: if (!in6p->in6p_icmp6filt) {
! 2719: error = EINVAL;
! 2720: break;
! 2721: }
! 2722: *mp = m = m_get(M_WAIT, MT_SOOPTS);
! 2723: m->m_len = sizeof(struct icmp6_filter);
! 2724: p = mtod(m, struct icmp6_filter *);
! 2725: bcopy(in6p->in6p_icmp6filt, p,
! 2726: sizeof(struct icmp6_filter));
! 2727: error = 0;
! 2728: break;
! 2729: }
! 2730:
! 2731: default:
! 2732: error = ENOPROTOOPT;
! 2733: break;
! 2734: }
! 2735: break;
! 2736: }
! 2737:
! 2738: return (error);
! 2739: }
! 2740:
! 2741: /* NRL PCB */
! 2742: #undef sotoin6pcb
! 2743: #undef in6pcb
! 2744: #undef in6p_icmp6filt
! 2745:
! 2746: /*
! 2747: * Perform rate limit check.
! 2748: * Returns 0 if it is okay to send the icmp6 packet.
! 2749: * Returns 1 if the router SHOULD NOT send this icmp6 packet due to rate
! 2750: * limitation.
! 2751: *
! 2752: * XXX per-destination/type check necessary?
! 2753: */
! 2754: static int
! 2755: icmp6_ratelimit(dst, type, code)
! 2756: const struct in6_addr *dst; /* not used at this moment */
! 2757: const int type; /* not used at this moment */
! 2758: const int code; /* not used at this moment */
! 2759: {
! 2760: int ret;
! 2761:
! 2762: ret = 0; /* okay to send */
! 2763:
! 2764: /* PPS limit */
! 2765: if (!ppsratecheck(&icmp6errppslim_last, &icmp6errpps_count,
! 2766: icmp6errppslim)) {
! 2767: /* The packet is subject to rate limit */
! 2768: ret++;
! 2769: }
! 2770:
! 2771: return ret;
! 2772: }
! 2773:
! 2774: static struct rtentry *
! 2775: icmp6_mtudisc_clone(dst)
! 2776: struct sockaddr *dst;
! 2777: {
! 2778: struct rtentry *rt;
! 2779: int error;
! 2780:
! 2781: rt = rtalloc1(dst, 1, 0);
! 2782: if (rt == 0)
! 2783: return NULL;
! 2784:
! 2785: /* If we didn't get a host route, allocate one */
! 2786: if ((rt->rt_flags & RTF_HOST) == 0) {
! 2787: struct rtentry *nrt;
! 2788:
! 2789: error = rtrequest((int) RTM_ADD, dst,
! 2790: (struct sockaddr *) rt->rt_gateway,
! 2791: (struct sockaddr *) 0,
! 2792: RTF_GATEWAY | RTF_HOST | RTF_DYNAMIC, &nrt, 0);
! 2793: if (error) {
! 2794: rtfree(rt);
! 2795: return NULL;
! 2796: }
! 2797: nrt->rt_rmx = rt->rt_rmx;
! 2798: rtfree(rt);
! 2799: rt = nrt;
! 2800: }
! 2801: error = rt_timer_add(rt, icmp6_mtudisc_timeout,
! 2802: icmp6_mtudisc_timeout_q);
! 2803: if (error) {
! 2804: rtfree(rt);
! 2805: return NULL;
! 2806: }
! 2807:
! 2808: return rt; /* caller need to call rtfree() */
! 2809: }
! 2810:
! 2811: static void
! 2812: icmp6_mtudisc_timeout(rt, r)
! 2813: struct rtentry *rt;
! 2814: struct rttimer *r;
! 2815: {
! 2816: if (rt == NULL)
! 2817: panic("icmp6_mtudisc_timeout: bad route to timeout");
! 2818: if ((rt->rt_flags & (RTF_DYNAMIC | RTF_HOST)) ==
! 2819: (RTF_DYNAMIC | RTF_HOST)) {
! 2820: rtrequest((int) RTM_DELETE, (struct sockaddr *)rt_key(rt),
! 2821: rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0, 0);
! 2822: } else {
! 2823: if (!(rt->rt_rmx.rmx_locks & RTV_MTU))
! 2824: rt->rt_rmx.rmx_mtu = 0;
! 2825: }
! 2826: }
! 2827:
! 2828: static void
! 2829: icmp6_redirect_timeout(rt, r)
! 2830: struct rtentry *rt;
! 2831: struct rttimer *r;
! 2832: {
! 2833: if (rt == NULL)
! 2834: panic("icmp6_redirect_timeout: bad route to timeout");
! 2835: if ((rt->rt_flags & (RTF_GATEWAY | RTF_DYNAMIC | RTF_HOST)) ==
! 2836: (RTF_GATEWAY | RTF_DYNAMIC | RTF_HOST)) {
! 2837: rtrequest((int) RTM_DELETE, (struct sockaddr *)rt_key(rt),
! 2838: rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0, 0);
! 2839: }
! 2840: }
! 2841:
! 2842: #include <uvm/uvm_extern.h>
! 2843: #include <sys/sysctl.h>
! 2844:
! 2845: int *icmpv6ctl_vars[ICMPV6CTL_MAXID] = ICMPV6CTL_VARS;
! 2846:
! 2847: int
! 2848: icmp6_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
! 2849: int *name;
! 2850: u_int namelen;
! 2851: void *oldp;
! 2852: size_t *oldlenp;
! 2853: void *newp;
! 2854: size_t newlen;
! 2855: {
! 2856:
! 2857: /* All sysctl names at this level are terminal. */
! 2858: if (namelen != 1)
! 2859: return ENOTDIR;
! 2860:
! 2861: switch (name[0]) {
! 2862:
! 2863: case ICMPV6CTL_STATS:
! 2864: return sysctl_rdstruct(oldp, oldlenp, newp,
! 2865: &icmp6stat, sizeof(icmp6stat));
! 2866: case ICMPV6CTL_ND6_DRLIST:
! 2867: case ICMPV6CTL_ND6_PRLIST:
! 2868: return nd6_sysctl(name[0], oldp, oldlenp, newp, newlen);
! 2869: default:
! 2870: if (name[0] < ICMPV6CTL_MAXID)
! 2871: return (sysctl_int_arr(icmpv6ctl_vars, name, namelen,
! 2872: oldp, oldlenp, newp, newlen));
! 2873: return ENOPROTOOPT;
! 2874: }
! 2875: /* NOTREACHED */
! 2876: }
CVSweb