diff options
author | Jun-ichiro itojun Hagino <itojun@cvs.openbsd.org> | 2001-02-16 16:01:01 +0000 |
---|---|---|
committer | Jun-ichiro itojun Hagino <itojun@cvs.openbsd.org> | 2001-02-16 16:01:01 +0000 |
commit | ecd82efc92808a3b9161e0fb0fb90d746ce8a310 (patch) | |
tree | 265dd7dea85ff82beafdb73a72622554ddc95020 | |
parent | adcce09577c48aeb228910c0f7e4f83726f03ff2 (diff) |
pull in new pcb notification code from kame. better handling of scope address.
-rw-r--r-- | sys/netinet/in_pcb.h | 4 | ||||
-rw-r--r-- | sys/netinet/tcp_subr.c | 114 | ||||
-rw-r--r-- | sys/netinet/udp_usrreq.c | 148 | ||||
-rw-r--r-- | sys/netinet6/icmp6.c | 194 | ||||
-rw-r--r-- | sys/netinet6/in6.h | 6 | ||||
-rw-r--r-- | sys/netinet6/in6_pcb.c | 121 | ||||
-rw-r--r-- | sys/netinet6/in6_prefix.h | 4 | ||||
-rw-r--r-- | sys/netinet6/ip6_input.c | 4 | ||||
-rw-r--r-- | sys/netinet6/ip6protosw.h | 12 | ||||
-rw-r--r-- | sys/netinet6/mld6.c | 35 | ||||
-rw-r--r-- | sys/netinet6/raw_ip6.c | 102 |
11 files changed, 490 insertions, 254 deletions
diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h index b6f8853704c..de65f08b901 100644 --- a/sys/netinet/in_pcb.h +++ b/sys/netinet/in_pcb.h @@ -1,4 +1,4 @@ -/* $OpenBSD: in_pcb.h,v 1.31 2001/02/16 14:45:11 itojun Exp $ */ +/* $OpenBSD: in_pcb.h,v 1.32 2001/02/16 16:00:53 itojun Exp $ */ /* $NetBSD: in_pcb.h,v 1.14 1996/02/13 23:42:00 christos Exp $ */ /* @@ -263,7 +263,7 @@ struct rtentry * /* INET6 stuff */ int in6_pcbnotify __P((struct inpcbtable *, struct sockaddr *, - u_int, struct in6_addr *, u_int, int, + u_int, struct sockaddr *, u_int, int, void *, void (*)(struct inpcb *, int))); struct in6_addr *in6_selectsrc __P((struct sockaddr_in6 *, struct ip6_pktopts *, diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c index 3309968706e..c3c8c678c01 100644 --- a/sys/netinet/tcp_subr.c +++ b/sys/netinet/tcp_subr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tcp_subr.c,v 1.38 2000/12/21 00:54:10 itojun Exp $ */ +/* $OpenBSD: tcp_subr.c,v 1.39 2001/02/16 16:00:54 itojun Exp $ */ /* $NetBSD: tcp_subr.c,v 1.22 1996/02/13 23:44:00 christos Exp $ */ /* @@ -724,16 +724,17 @@ tcp6_ctlinput(cmd, sa, d) struct sockaddr *sa; void *d; { - struct tcphdr *thp; struct tcphdr th; void (*notify) __P((struct inpcb *, int)) = tcp_notify; - int nmatch; - struct sockaddr_in6 sa6; struct ip6_hdr *ip6; + const struct sockaddr_in6 *sa6_src = NULL; + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa; struct mbuf *m; int off; - struct in6_addr finaldst; - struct in6_addr s; + struct { + u_int16_t th_sport; + u_int16_t th_dport; + } *thp; if (sa->sa_family != AF_INET6 || sa->sa_len != sizeof(struct sockaddr_in6)) @@ -758,26 +759,13 @@ tcp6_ctlinput(cmd, sa, d) m = ip6cp->ip6c_m; ip6 = ip6cp->ip6c_ip6; off = ip6cp->ip6c_off; - - /* translate addresses into internal form */ - bcopy(ip6cp->ip6c_finaldst, &finaldst, sizeof(finaldst)); - if (IN6_IS_ADDR_LINKLOCAL(&finaldst)) { - finaldst.s6_addr16[1] = - htons(m->m_pkthdr.rcvif->if_index); - } - bcopy(&ip6->ip6_src, &s, sizeof(s)); - if (IN6_IS_ADDR_LINKLOCAL(&s)) - s.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); + sa6_src = ip6cp->ip6c_src; } else { m = NULL; ip6 = NULL; + sa6_src = &sa6_any; } - /* translate addresses into internal form */ - sa6 = *(struct sockaddr_in6 *)sa; - if (IN6_IS_ADDR_LINKLOCAL(&sa6.sin6_addr) && m && m->m_pkthdr.rcvif) - sa6.sin6_addr.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); - if (ip6) { /* * XXX: We assume that when ip6 is non NULL, @@ -785,18 +773,15 @@ tcp6_ctlinput(cmd, sa, d) */ /* check if we can safely examine src and dst ports */ - if (m->m_pkthdr.len < off + sizeof(th)) + if (m->m_pkthdr.len < off + sizeof(*thp)) return; - if (m->m_len < off + sizeof(th)) { - /* - * this should be rare case, - * so we compromise on this copy... - */ - m_copydata(m, off, sizeof(th), (caddr_t)&th); - thp = &th; - } else - thp = (struct tcphdr *)(mtod(m, caddr_t) + off); + bzero(&th, sizeof(th)); +#ifdef DIAGNOSTIC + if (sizeof(*thp) > sizeof(th)) + panic("assumption failed in tcp6_ctlinput"); +#endif + m_copydata(m, off, sizeof(*thp), (caddr_t)&th); if (cmd == PRC_MSGSIZE) { int valid = 0; @@ -806,27 +791,32 @@ tcp6_ctlinput(cmd, sa, d) * corresponding to the address in the ICMPv6 message * payload. */ - if (in_pcblookup(&tcbtable, &finaldst, - thp->th_dport, &s, thp->th_sport, - INPLOOKUP_WILDCARD)) + if (in6_pcbhashlookup(&tcbtable, &sa6->sin6_addr, + th.th_dport, (struct in6_addr *)&sa6_src->sin6_addr, + th.th_sport)) + valid++; + else if (in_pcblookup(&tcbtable, &sa6->sin6_addr, + th.th_dport, (struct in6_addr *)&sa6_src->sin6_addr, + th.th_sport, INPLOOKUP_IPV6)) valid++; /* - * Now that we've validated that we are actually - * communicating with the host indicated in the ICMPv6 - * message, recalculate the new MTU, and create the - * corresponding routing entry. + * Depending on the value of "valid" and routing table + * size (mtudisc_{hi,lo}wat), we will: + * - recalcurate the new MTU and create the + * corresponding routing entry, or + * - ignore the MTU change notification. */ icmp6_mtudisc_update((struct ip6ctlparam *)d, valid); return; } - nmatch = in6_pcbnotify(&tcbtable, (struct sockaddr *)&sa6, - thp->th_dport, &s, thp->th_sport, cmd, notify); + (void) in6_pcbnotify(&tcbtable, sa, th.th_dport, + (struct sockaddr *)sa6_src, th.th_sport, cmd, NULL, notify); } else { - (void) in6_pcbnotify(&tcbtable, (struct sockaddr *)&sa6, 0, - &zeroin6_addr, 0, cmd, notify); + (void) in6_pcbnotify(&tcbtable, sa, 0, + (struct sockaddr *)sa6_src, 0, cmd, NULL, notify); } } #endif @@ -881,7 +871,7 @@ tcp_ctlinput(cmd, sa, v) if (ip) { th = (struct tcphdr *)((caddr_t)ip + (ip->ip_hl << 2)); in_pcbnotify(&tcbtable, sa, th->th_dport, ip->ip_src, - th->th_sport, errno, notify); + th->th_sport, errno, notify); } else in_pcbnotifyall(&tcbtable, sa, errno, notify); @@ -903,6 +893,25 @@ tcp_quench(inp, errno) tp->snd_cwnd = tp->t_maxseg; } +#ifdef INET6 +/* + * Path MTU Discovery handlers. + */ +void +tcp6_mtudisc_callback(faddr) + struct in6_addr *faddr; +{ + struct sockaddr_in6 sin6; + + bzero(&sin6, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_addr = *faddr; + (void) in6_pcbnotify(&tcbtable, (struct sockaddr *)&sin6, 0, + (struct sockaddr *)&sa6_any, 0, PRC_MSGSIZE, NULL, tcp_mtudisc); +} +#endif /* INET6 */ + /* * On receipt of path MTU corrections, flush old route and replace it * with the new one. Retransmit all unacknowledged packets, to ensure @@ -961,25 +970,6 @@ tcp_mtudisc_increase(inp, errno) } } -#ifdef INET6 -/* - * Path MTU Discovery handlers. - */ -void -tcp6_mtudisc_callback(faddr) - struct in6_addr *faddr; -{ - struct sockaddr_in6 sin6; - - bzero(&sin6, sizeof(sin6)); - sin6.sin6_family = AF_INET6; - sin6.sin6_len = sizeof(struct sockaddr_in6); - sin6.sin6_addr = *faddr; - (void) in6_pcbnotify(&tcbtable, (struct sockaddr *)&sin6, 0, - &zeroin6_addr, 0, PRC_MSGSIZE, tcp_mtudisc); -} -#endif /* INET6 */ - #ifdef TCP_SIGNATURE int tcp_signature_tdb_attach() diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c index 6d8bc00de36..ba13ec596d8 100644 --- a/sys/netinet/udp_usrreq.c +++ b/sys/netinet/udp_usrreq.c @@ -1,4 +1,4 @@ -/* $OpenBSD: udp_usrreq.c,v 1.52 2000/12/11 08:04:56 itojun Exp $ */ +/* $OpenBSD: udp_usrreq.c,v 1.53 2001/02/16 16:00:54 itojun Exp $ */ /* $NetBSD: udp_usrreq.c,v 1.28 1996/03/16 23:54:03 christos Exp $ */ /* @@ -657,17 +657,21 @@ udp6_ctlinput(cmd, sa, d) struct sockaddr *sa; void *d; { - struct udphdr *uhp; struct udphdr uh; struct sockaddr_in6 sa6; - struct ip6_hdr *ip6; + register struct ip6_hdr *ip6; struct mbuf *m; int off; - struct in6_addr s; + void *cmdarg; + struct ip6ctlparam *ip6cp = NULL; struct in6_addr finaldst; + struct udp_portonly { + u_int16_t uh_sport; + u_int16_t uh_dport; + } *uhp; void (*notify) __P((struct inpcb *, int)) = udp_notify; - if (!sa) + if (sa == NULL) return; if (sa->sa_family != AF_INET6 || sa->sa_len != sizeof(struct sockaddr_in6)) @@ -686,60 +690,96 @@ udp6_ctlinput(cmd, sa, d) /* if the parameter is from icmp6, decode it. */ if (d != NULL) { - struct ip6ctlparam *ip6cp = (struct ip6ctlparam *)d; + ip6cp = (struct ip6ctlparam *)d; m = ip6cp->ip6c_m; ip6 = ip6cp->ip6c_ip6; off = ip6cp->ip6c_off; - - /* translate addresses into internal form */ - bcopy(ip6cp->ip6c_finaldst, &finaldst, sizeof(finaldst)); - if (IN6_IS_ADDR_LINKLOCAL(&finaldst)) { - finaldst.s6_addr16[1] = - htons(m->m_pkthdr.rcvif->if_index); - } - bcopy(&ip6->ip6_src, &s, sizeof(s)); - if (IN6_IS_ADDR_LINKLOCAL(&s)) - s.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); + cmdarg = ip6cp->ip6c_cmdarg; } else { m = NULL; ip6 = NULL; + cmdarg = NULL; + /* XXX: translate addresses into internal form */ + sa6 = *(struct sockaddr_in6 *)sa; +#ifndef SCOPEDROUTING + if (in6_embedscope(&sa6.sin6_addr, &sa6, NULL, NULL)) { + /* should be impossbile */ + printf("udp6_ctlinput: in6_embedscope failed\n"); + return; + } +#endif } - /* translate addresses into internal form */ - sa6 = *(struct sockaddr_in6 *)sa; - if (IN6_IS_ADDR_LINKLOCAL(&sa6.sin6_addr) && m && m->m_pkthdr.rcvif) - sa6.sin6_addr.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); + if (ip6cp && ip6cp->ip6c_finaldst) { + bzero(&sa6, sizeof(sa6)); + sa6.sin6_family = AF_INET6; + sa6.sin6_len = sizeof(sa6); + sa6.sin6_addr = *ip6cp->ip6c_finaldst; + /* XXX: assuming M is valid in this case */ + sa6.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.rcvif, + ip6cp->ip6c_finaldst); +#ifndef SCOPEDROUTING + if (in6_embedscope(ip6cp->ip6c_finaldst, &sa6, NULL, NULL)) { + /* should be impossbile */ + printf("udp6_ctlinput: in6_embedscope failed\n"); + return; + } +#endif + } else { + /* XXX: translate addresses into internal form */ + sa6 = *(struct sockaddr_in6 *)sa; +#ifndef SCOPEDROUTING + if (in6_embedscope(&sa6.sin6_addr, &sa6, NULL, NULL)) { + /* should be impossbile */ + printf("udp6_ctlinput: in6_embedscope failed\n"); + return; + } +#endif + } if (ip6) { /* * XXX: We assume that when IPV6 is non NULL, * M and OFF are valid. */ + struct sockaddr_in6 sa6_src; /* check if we can safely examine src and dst ports */ - if (m->m_pkthdr.len < off + sizeof(uh)) + if (m->m_pkthdr.len < off + sizeof(*uhp)) return; - if (m->m_len < off + sizeof(uh)) { - /* - * this should be rare case, - * so we compromise on this copy... - */ - m_copydata(m, off, sizeof(uh), (caddr_t)&uh); - uhp = &uh; - } else - uhp = (struct udphdr *)(mtod(m, caddr_t) + off); + bzero(&uh, sizeof(uh)); + m_copydata(m, off, sizeof(*uhp), (caddr_t)&uh); + + bzero(&sa6_src, sizeof(sa6_src)); + sa6_src.sin6_family = AF_INET6; + sa6_src.sin6_len = sizeof(sa6_src); + sa6_src.sin6_addr = ip6->ip6_src; + sa6_src.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.rcvif, + &ip6->ip6_src); +#ifndef SCOPEDROUTING + if (in6_embedscope(&sa6_src.sin6_addr, &sa6_src, NULL, NULL)) { + /* should be impossbile */ + printf("udp6_ctlinput: in6_embedscope failed\n"); + return; + } +#endif if (cmd == PRC_MSGSIZE) { int valid = 0; + /* * Check to see if we have a valid UDP socket * corresponding to the address in the ICMPv6 message * payload. */ - if (in_pcblookup(&udbtable, &finaldst, uhp->uh_dport, - &s, uhp->uh_sport, INPLOOKUP_IPV6)) - valid++; + if (in6_pcbhashlookup(&udbtable, &finaldst, + uh.uh_dport, &sa6_src.sin6_addr, uh.uh_sport)) + valid = 1; + else if (in_pcblookup(&udbtable, &sa6.sin6_addr, + uh.uh_dport, &sa6_src.sin6_addr, uh.uh_sport, + INPLOOKUP_IPV6)) + valid = 1; #if 0 /* * As the use of sendto(2) is fairly popular, @@ -748,28 +788,36 @@ udp6_ctlinput(cmd, sa, d) * We should at least check if the local address (= s) * is really ours. */ - else if (in_pcblookup(&udbtable, &finaldst, - uhp->uh_dport, &s, uhp->uh_sport, + else if (in_pcblookup(&udbtable, &sa6.sin6_addr, + uh.uh_dport, &sa6_src.sin6_addr, uh.uh_sport, INPLOOKUP_WILDCARD | INPLOOKUP_IPV6)) - valid++; + valid = 1; #endif /* - * Now that we've validated that we are actually - * communicating with the host indicated in the ICMPv6 - * message, recalculate the new MTU, and create the - * corresponding routing entry. + * Depending on the value of "valid" and routing table + * size (mtudisc_{hi,lo}wat), we will: + * - recalcurate the new MTU and create the + * corresponding routing entry, or + * - ignore the MTU change notification. */ icmp6_mtudisc_update((struct ip6ctlparam *)d, valid); - return; + /* + * regardless of if we called icmp6_mtudisc_update(), + * we need to call in6_pcbnotify(), to notify path + * MTU change to the userland (2292bis-02), because + * some unconnected sockets may share the same + * destination and want to know the path MTU. + */ } (void) in6_pcbnotify(&udbtable, (struct sockaddr *)&sa6, - uhp->uh_dport, &s, uhp->uh_sport, cmd, notify); + uh.uh_dport, (struct sockaddr *)&sa6_src, + uh.uh_sport, cmd, cmdarg, notify); } else { (void) in6_pcbnotify(&udbtable, (struct sockaddr *)&sa6, 0, - &zeroin6_addr, 0, cmd, notify); + (struct sockaddr *)&sa6_any, 0, cmd, cmdarg, notify); } } #endif @@ -786,7 +834,7 @@ udp_ctlinput(cmd, sa, v) void (*notify) __P((struct inpcb *, int)) = udp_notify; int errno; - if (!sa) + if (sa == NULL) return NULL; if (sa->sa_family != AF_INET || sa->sa_len != sizeof(struct sockaddr_in)) @@ -945,11 +993,12 @@ udp_output(m, va_alist) int payload = sizeof(struct ip6_hdr); struct in6_addr *laddr; struct ifnet *oifp = NULL; + int flags; struct sockaddr_in6 tmp; ipv6->ip6_flow = htonl(0x60000000) | (inp->inp_ipv6.ip6_flow & htonl(0x0fffffff)); - + ipv6->ip6_nxt = IPPROTO_UDP; if (sin6) tmp = *sin6; @@ -988,6 +1037,12 @@ udp_output(m, va_alist) uh->uh_ulen = htons(ipv6->ip6_plen); uh->uh_sum = 0; + flags = 0; +#ifdef IN6P_MINMTU + if (inp->inp_flags & IN6P_MINMTU) + flags |= IPV6_MINMTU; +#endif + /* * Always calculate udp checksum for IPv6 datagrams */ @@ -996,8 +1051,7 @@ udp_output(m, va_alist) uh->uh_sum = 0xffff; error = ip6_output(m, inp->inp_outputopts6, &inp->inp_route6, - inp->inp_socket->so_options & SO_DONTROUTE, - inp->inp_moptions6, NULL); + flags, inp->inp_moptions6, NULL); } else #endif /* INET6 */ { diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c index f94b6df8dba..25b4d787cb5 100644 --- a/sys/netinet6/icmp6.c +++ b/sys/netinet6/icmp6.c @@ -1,5 +1,5 @@ -/* $OpenBSD: icmp6.c,v 1.32 2001/02/08 18:46:22 itojun Exp $ */ -/* $KAME: icmp6.c,v 1.195 2001/02/08 15:35:31 itojun Exp $ */ +/* $OpenBSD: icmp6.c,v 1.33 2001/02/16 16:00:55 itojun Exp $ */ +/* $KAME: icmp6.c,v 1.201 2001/02/16 12:23:40 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -179,6 +179,7 @@ static int ni6_addrs __P((struct icmp6_nodeinfo *, struct mbuf *, struct ifnet **, char *)); static int ni6_store_addrs __P((struct icmp6_nodeinfo *, struct icmp6_nodeinfo *, struct ifnet *, int)); +static int icmp6_notify_error __P((struct mbuf *, int, int, int)); static struct rtentry *icmp6_mtudisc_clone __P((struct sockaddr *)); static void icmp6_mtudisc_timeout __P((struct rtentry *, struct rttimer *)); static void icmp6_redirect_timeout __P((struct rtentry *, struct rttimer *)); @@ -200,7 +201,7 @@ icmp6_errcount(stat, type, code) struct icmp6errstat *stat; int type, code; { - switch(type) { + switch (type) { case ICMP6_DST_UNREACH: switch (code) { case ICMP6_DST_UNREACH_NOROUTE: @@ -224,7 +225,7 @@ icmp6_errcount(stat, type, code) stat->icp6errs_packet_too_big++; return; case ICMP6_TIME_EXCEEDED: - switch(code) { + switch (code) { case ICMP6_TIME_EXCEED_TRANSIT: stat->icp6errs_time_exceed_transit++; return; @@ -234,7 +235,7 @@ icmp6_errcount(stat, type, code) } break; case ICMP6_PARAM_PROB: - switch(code) { + switch (code) { case ICMP6_PARAMPROB_HEADER: stat->icp6errs_paramprob_header++; return; @@ -423,7 +424,6 @@ icmp6_input(mp, offp, proto) int off = *offp; int icmp6len = m->m_pkthdr.len - *offp; int code, sum, noff; - struct sockaddr_in6 icmp6src; #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr), IPPROTO_DONE); @@ -860,37 +860,69 @@ icmp6_input(mp, offp, proto) break; } deliver: - if (icmp6len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr)) { - icmp6stat.icp6s_tooshort++; - goto freeit; + if (icmp6_notify_error(m, off, icmp6len, code)) { + /* In this case, m should've been freed. */ + return(IPPROTO_DONE); } + break; + + badcode: + icmp6stat.icp6s_badcode++; + break; + + badlen: + icmp6stat.icp6s_badlen++; + break; + } + + /* deliver the packet to appropriate sockets */ + icmp6_rip6_input(&m, *offp); + + return IPPROTO_DONE; + + freeit: + m_freem(m); + return IPPROTO_DONE; +} + +static int +icmp6_notify_error(m, off, icmp6len, code) + struct mbuf *m; + int off, icmp6len; +{ + struct icmp6_hdr *icmp6; + struct ip6_hdr *eip6; + u_int32_t notifymtu; + struct sockaddr_in6 icmp6src, icmp6dst; + + if (icmp6len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr)) { + icmp6stat.icp6s_tooshort++; + goto freeit; + } #ifndef PULLDOWN_TEST - IP6_EXTHDR_CHECK(m, off, - sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr), - IPPROTO_DONE); - icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off); + IP6_EXTHDR_CHECK(m, off, + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr), + -1); + icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off); #else - IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, - sizeof(*icmp6) + sizeof(struct ip6_hdr)); - if (icmp6 == NULL) { - icmp6stat.icp6s_tooshort++; - return IPPROTO_DONE; - } + IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, + sizeof(*icmp6) + sizeof(struct ip6_hdr)); + if (icmp6 == NULL) { + icmp6stat.icp6s_tooshort++; + return(-1); + } #endif - bzero(&icmp6src, sizeof(icmp6src)); - icmp6src.sin6_len = sizeof(struct sockaddr_in6); - icmp6src.sin6_family = AF_INET6; - icmp6src.sin6_addr = ((struct ip6_hdr *)(icmp6 + 1))->ip6_dst; + eip6 = (struct ip6_hdr *)(icmp6 + 1); - /* Detect the upper level protocol */ - { + /* Detect the upper level protocol */ + { void (*ctlfunc) __P((int, struct sockaddr *, void *)); - struct ip6_hdr *eip6 = (struct ip6_hdr *)(icmp6 + 1); u_int8_t nxt = eip6->ip6_nxt; int eoff = off + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr); struct ip6ctlparam ip6cp; struct in6_addr *finaldst = NULL; + int icmp6type = icmp6->icmp6_type; struct ip6_frag *fh; struct ip6_rthdr *rth; struct ip6_rthdr0 *rth0; @@ -899,22 +931,22 @@ icmp6_input(mp, offp, proto) while (1) { /* XXX: should avoid inf. loop explicitly? */ struct ip6_ext *eh; - switch(nxt) { + switch (nxt) { case IPPROTO_HOPOPTS: case IPPROTO_DSTOPTS: case IPPROTO_AH: #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, 0, eoff + sizeof(struct ip6_ext), - IPPROTO_DONE); + -1); eh = (struct ip6_ext *)(mtod(m, caddr_t) + eoff); #else IP6_EXTHDR_GET(eh, struct ip6_ext *, m, - eoff, sizeof(*eh)); + eoff, sizeof(*eh)); if (eh == NULL) { icmp6stat.icp6s_tooshort++; - return IPPROTO_DONE; + return(-1); } #endif @@ -935,15 +967,15 @@ icmp6_input(mp, offp, proto) */ #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, 0, eoff + sizeof(*rth), - IPPROTO_DONE); + -1); rth = (struct ip6_rthdr *)(mtod(m, caddr_t) + eoff); #else IP6_EXTHDR_GET(rth, struct ip6_rthdr *, m, - eoff, sizeof(*rth)); + eoff, sizeof(*rth)); if (rth == NULL) { icmp6stat.icp6s_tooshort++; - return IPPROTO_DONE; + return(-1); } #endif rthlen = (rth->ip6r_len + 1) << 3; @@ -961,7 +993,7 @@ icmp6_input(mp, offp, proto) #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, 0, eoff + rthlen, - IPPROTO_DONE); + -1); rth0 = (struct ip6_rthdr0 *)(mtod(m, caddr_t) + eoff); #else IP6_EXTHDR_GET(rth0, @@ -969,7 +1001,7 @@ icmp6_input(mp, offp, proto) eoff, rthlen); if (rth0 == NULL) { icmp6stat.icp6s_tooshort++; - return IPPROTO_DONE; + return(-1); } #endif /* just ignore a bogus header */ @@ -984,15 +1016,15 @@ icmp6_input(mp, offp, proto) #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, 0, eoff + sizeof(struct ip6_frag), - IPPROTO_DONE); + -1); fh = (struct ip6_frag *)(mtod(m, caddr_t) + eoff); #else IP6_EXTHDR_GET(fh, struct ip6_frag *, m, - eoff, sizeof(*fh)); + eoff, sizeof(*fh)); if (fh == NULL) { icmp6stat.icp6s_tooshort++; - return IPPROTO_DONE; + return(-1); } #endif /* @@ -1024,44 +1056,82 @@ icmp6_input(mp, offp, proto) icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off); #else IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, - sizeof(*icmp6) + sizeof(struct ip6_hdr)); + sizeof(*icmp6) + sizeof(struct ip6_hdr)); if (icmp6 == NULL) { icmp6stat.icp6s_tooshort++; - return IPPROTO_DONE; + return(-1); } #endif + + eip6 = (struct ip6_hdr *)(icmp6 + 1); + bzero(&icmp6dst, sizeof(icmp6dst)); + icmp6dst.sin6_len = sizeof(struct sockaddr_in6); + icmp6dst.sin6_family = AF_INET6; if (finaldst == NULL) - finaldst = &((struct ip6_hdr *)(icmp6 + 1))->ip6_dst; + icmp6dst.sin6_addr = eip6->ip6_dst; + else + icmp6dst.sin6_addr = *finaldst; + icmp6dst.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.rcvif, + &icmp6dst.sin6_addr); +#ifndef SCOPEDROUTING + if (in6_embedscope(&icmp6dst.sin6_addr, &icmp6dst, + NULL, NULL)) { + /* should be impossbile */ + nd6log((LOG_DEBUG, + "icmp6_notify_error: in6_embedscope failed\n")); + goto freeit; + } +#endif + + /* + * retrieve parameters from the inner IPv6 header, and convert + * them into sockaddr structures. + */ + bzero(&icmp6src, sizeof(icmp6src)); + icmp6src.sin6_len = sizeof(struct sockaddr_in6); + icmp6src.sin6_family = AF_INET6; + icmp6src.sin6_addr = eip6->ip6_src; + icmp6src.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.rcvif, + &icmp6src.sin6_addr); +#ifndef SCOPEDROUTING + if (in6_embedscope(&icmp6src.sin6_addr, &icmp6src, + NULL, NULL)) { + /* should be impossbile */ + nd6log((LOG_DEBUG, + "icmp6_notify_error: in6_embedscope failed\n")); + goto freeit; + } +#endif + icmp6src.sin6_flowinfo = + (eip6->ip6_flow & IPV6_FLOWLABEL_MASK); + + if (finaldst == NULL) + finaldst = &eip6->ip6_dst; ip6cp.ip6c_m = m; ip6cp.ip6c_icmp6 = icmp6; ip6cp.ip6c_ip6 = (struct ip6_hdr *)(icmp6 + 1); ip6cp.ip6c_off = eoff; ip6cp.ip6c_finaldst = finaldst; + ip6cp.ip6c_src = &icmp6src; + ip6cp.ip6c_nxt = nxt; + + if (icmp6type == ICMP6_PACKET_TOO_BIG) { + notifymtu = ntohl(icmp6->icmp6_mtu); + ip6cp.ip6c_cmdarg = (void *)¬ifymtu; + } ctlfunc = (void (*) __P((int, struct sockaddr *, void *))) (inet6sw[ip6_protox[nxt]].pr_ctlinput); if (ctlfunc) { - (void) (*ctlfunc)(code, (struct sockaddr *)&icmp6src, - &ip6cp); + (void) (*ctlfunc)(code, (struct sockaddr *)&icmp6dst, + &ip6cp); } - } - break; - - badcode: - icmp6stat.icp6s_badcode++; - break; - - badlen: - icmp6stat.icp6s_badlen++; - break; } + return(0); - icmp6_rip6_input(&m, *offp); - return IPPROTO_DONE; - - freeit: + freeit: m_freem(m); - return IPPROTO_DONE; + return(-1); } void @@ -1615,7 +1685,7 @@ ni6_addrs(ni6, m, ifpp, subj) int niflags = ni6->ni_flags; if ((niflags & NI_NODEADDR_FLAG_ALL) == 0) { - switch(ni6->ni_code) { + switch (ni6->ni_code) { case ICMP6_NI_SUBJ_IPV6: if (subj == NULL) /* must be impossible... */ return(0); @@ -1656,7 +1726,7 @@ ni6_addrs(ni6, m, ifpp, subj) */ /* What do we have to do about ::1? */ - switch(in6_addrscope(&ifa6->ia_addr.sin6_addr)) { + switch (in6_addrscope(&ifa6->ia_addr.sin6_addr)) { case IPV6_ADDR_SCOPE_LINKLOCAL: if ((niflags & NI_NODEADDR_FLAG_LINKLOCAL) == 0) continue; @@ -1742,7 +1812,7 @@ ni6_store_addrs(ni6, nni6, ifp0, resid) continue; /* we now collect deprecated addrs */ /* What do we have to do about ::1? */ - switch(in6_addrscope(&ifa6->ia_addr.sin6_addr)) { + switch (in6_addrscope(&ifa6->ia_addr.sin6_addr)) { case IPV6_ADDR_SCOPE_LINKLOCAL: if ((niflags & NI_NODEADDR_FLAG_LINKLOCAL) == 0) continue; @@ -2686,7 +2756,7 @@ icmp6_ctloutput(op, so, level, optname, mp) return EINVAL; } - switch(op) { + switch (op) { case PRCO_SETOPT: switch (optname) { case ICMP6_FILTER: diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h index 26b5095186a..71e65293c56 100644 --- a/sys/netinet6/in6.h +++ b/sys/netinet6/in6.h @@ -1,5 +1,5 @@ -/* $OpenBSD: in6.h,v 1.18 2000/10/17 21:46:19 itojun Exp $ */ -/* $KAME: in6.h,v 1.52 2000/07/15 15:28:02 itojun Exp $ */ +/* $OpenBSD: in6.h,v 1.19 2001/02/16 16:00:56 itojun Exp $ */ +/* $KAME: in6.h,v 1.77 2001/02/09 06:17:40 jinmei Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -163,6 +163,8 @@ struct sockaddr_in6 { #endif #ifdef _KERNEL +extern const struct sockaddr_in6 sa6_any; + extern const struct in6_addr in6mask0; extern const struct in6_addr in6mask32; extern const struct in6_addr in6mask64; diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c index a596849812f..d99f1d87e6e 100644 --- a/sys/netinet6/in6_pcb.c +++ b/sys/netinet6/in6_pcb.c @@ -1,4 +1,4 @@ -/* $OpenBSD: in6_pcb.c,v 1.24 2001/02/16 08:22:05 itojun Exp $ */ +/* $OpenBSD: in6_pcb.c,v 1.25 2001/02/16 16:00:56 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -554,28 +554,36 @@ in6_pcbconnect(inp, nam) * Must be called at splnet. */ int -in6_pcbnotify(head, dst, fport_arg, la, lport_arg, cmd, notify) +in6_pcbnotify(head, dst, fport_arg, src, lport_arg, cmd, cmdarg, notify) struct inpcbtable *head; - struct sockaddr *dst; + struct sockaddr *dst, *src; uint fport_arg; - struct in6_addr *la; uint lport_arg; int cmd; + void *cmdarg; void (*notify) __P((struct inpcb *, int)); { struct inpcb *inp, *ninp; - struct in6_addr *faddr,laddr = *la; u_short fport = fport_arg, lport = lport_arg; + struct sockaddr_in6 sa6_src, *sa6_dst; int errno, nmatch = 0; - int do_rtchange = (notify == in_rtchange); + u_int32_t flowinfo; if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET6) return 1; - faddr = &(((struct sockaddr_in6 *)dst)->sin6_addr); - if (IN6_IS_ADDR_UNSPECIFIED(faddr)) + + sa6_dst = (struct sockaddr_in6 *)dst; + if (IN6_IS_ADDR_UNSPECIFIED(&sa6_dst->sin6_addr)) return 1; - if (IN6_IS_ADDR_V4MAPPED(faddr)) - printf("Huh? Thought in6_pcbnotify() never got called with mapped!\n"); + if (IN6_IS_ADDR_V4MAPPED(&sa6_dst->sin6_addr)) + printf("Huh? Thought in6_pcbnotify() never got " + "called with mapped!\n"); + + /* + * note that src can be NULL when we get notify by local fragmentation. + */ + sa6_src = (src == NULL) ? sa6_any : *(struct sockaddr_in6 *)src; + flowinfo = sa6_src.sin6_flowinfo; /* * Redirects go to all references to the destination, @@ -588,9 +596,10 @@ in6_pcbnotify(head, dst, fport_arg, la, lport_arg, cmd, notify) if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) { fport = 0; lport = 0; - laddr = in6addr_any; + sa6_src.sin6_addr = in6addr_any; - notify = in_rtchange; + if (cmd != PRC_HOSTDEAD) + notify = in_rtchange; } errno = inet6ctlerrmap[cmd]; @@ -598,39 +607,77 @@ in6_pcbnotify(head, dst, fport_arg, la, lport_arg, cmd, notify) inp != (struct inpcb *)&head->inpt_queue; inp = ninp) { ninp = inp->inp_queue.cqe_next; -#ifdef INET6 if ((inp->inp_flags & INP_IPV6) == 0) continue; -#endif - - if (do_rtchange) { - /* - * Since a non-connected PCB might have a cached route, - * we always call in_rtchange without matching - * the PCB to the src/dst pair. - * - * XXX: we assume in_rtchange does not free the PCB. - */ - if (IN6_ARE_ADDR_EQUAL(&inp->inp_route6.ro_dst.sin6_addr, - faddr)) { - in_rtchange(inp, errno); - if (notify == in_rtchange) { - /* there's nothing to do any more */ - continue; - } - } + /* + * Under the following condition, notify of redirects + * to the pcb, without making address matches against inpcb. + * - redirect notification is arrived. + * - the inpcb is unconnected. + * - the inpcb is caching !RTF_HOST routing entry. + * - the ICMPv6 notification is from the gateway cached in the + * inpcb. i.e. ICMPv6 notification is from nexthop gateway + * the inpcb used very recently. + * + * This is to improve interaction between netbsd/openbsd + * redirect handling code, and inpcb route cache code. + * without the clause, !RTF_HOST routing entry (which carries + * gateway used by inpcb right before the ICMPv6 redirect) + * will be cached forever in unconnected inpcb. + * + * There still is a question regarding to what is TRT: + * - On bsdi/freebsd, RTF_HOST (cloned) routing entry will be + * generated on packet output. inpcb will always cache + * RTF_HOST routing entry so there's no need for the clause + * (ICMPv6 redirect will update RTF_HOST routing entry, + * and inpcb is caching it already). + * However, bsdi/freebsd are vulnerable to local DoS attacks + * due to the cloned routing entries. + * - Specwise, "destination cache" is mentioned in RFC2461. + * Jinmei says that it implies bsdi/freebsd behavior, itojun + * is not really convinced. + * - Having hiwat/lowat on # of cloned host route (redirect/ + * pmtud) may be a good idea. netbsd/openbsd has it. see + * icmp6_mtudisc_update(). + */ + if ((PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) && + IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6) && + inp->inp_route.ro_rt && + !(inp->inp_route.ro_rt->rt_flags & RTF_HOST)) { + struct sockaddr_in6 *dst6; + + dst6 = (struct sockaddr_in6 *)&inp->inp_route.ro_dst; + if (IN6_ARE_ADDR_EQUAL(&dst6->sin6_addr, + &sa6_dst->sin6_addr)) + goto do_notify; } - if (!IN6_ARE_ADDR_EQUAL(&inp->inp_faddr6, faddr) || - !inp->inp_socket || - (lport && inp->inp_lport != lport) || - (!IN6_IS_ADDR_UNSPECIFIED(&laddr) && !IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, &laddr)) || - (fport && inp->inp_fport != fport)) { + /* + * Detect if we should notify the error. If no source and + * destination ports are specifed, but non-zero flowinfo and + * local address match, notify the error. This is the case + * when the error is delivered with an encrypted buffer + * by ESP. Otherwise, just compare addresses and ports + * as usual. + */ + if (lport == 0 && fport == 0 && flowinfo && + inp->inp_socket != NULL && + flowinfo == (inp->inp_flowinfo & IPV6_FLOWLABEL_MASK) && + IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, &sa6_src.sin6_addr)) + goto do_notify; + else if (!IN6_ARE_ADDR_EQUAL(&inp->inp_faddr6, + &sa6_dst->sin6_addr) || + inp->inp_socket == 0 || + (lport && inp->inp_lport != lport) || + (!IN6_IS_ADDR_UNSPECIFIED(&sa6_src.sin6_addr) && + !IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, + &sa6_src.sin6_addr)) || + (fport && inp->inp_fport != fport)) { continue; } + do_notify: nmatch++; - if (notify) (*notify)(inp, errno); } diff --git a/sys/netinet6/in6_prefix.h b/sys/netinet6/in6_prefix.h index c8ba52446ff..0647e255eb7 100644 --- a/sys/netinet6/in6_prefix.h +++ b/sys/netinet6/in6_prefix.h @@ -1,5 +1,5 @@ -/* $OpenBSD: in6_prefix.h,v 1.3 2001/02/08 14:51:22 itojun Exp $ */ -/* $KAME: in6_prefix.h,v 1.9 2001/02/08 10:57:00 itojun Exp $ */ +/* $OpenBSD: in6_prefix.h,v 1.4 2001/02/16 16:00:57 itojun Exp $ */ +/* $KAME: in6_prefix.h,v 1.10 2001/02/08 16:30:30 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, 1998 and 1999 WIDE Project. diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c index 02f37e21cf0..87425ebfec3 100644 --- a/sys/netinet6/ip6_input.c +++ b/sys/netinet6/ip6_input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip6_input.c,v 1.25 2001/02/16 14:58:12 itojun Exp $ */ +/* $OpenBSD: ip6_input.c,v 1.26 2001/02/16 16:00:57 itojun Exp $ */ /* $KAME: ip6_input.c,v 1.176 2001/02/14 07:13:39 itojun Exp $ */ /* @@ -970,7 +970,7 @@ ip6_savecontrol(in6p, mp, ip6, m) /* RFC 2292 sec. 5 */ if ((in6p->in6p_flags & IN6P_PKTINFO) != 0) { - struct in6_pktinfo pi6, *prevpi = NULL; + struct in6_pktinfo pi6; bcopy(&ip6->ip6_dst, &pi6.ipi6_addr, sizeof(struct in6_addr)); if (IN6_IS_SCOPE_LINKLOCAL(&pi6.ipi6_addr)) pi6.ipi6_addr.s6_addr16[1] = 0; diff --git a/sys/netinet6/ip6protosw.h b/sys/netinet6/ip6protosw.h index dcbae2d90ae..1dbe69fb175 100644 --- a/sys/netinet6/ip6protosw.h +++ b/sys/netinet6/ip6protosw.h @@ -1,5 +1,5 @@ -/* $OpenBSD: ip6protosw.h,v 1.3 2000/12/11 08:04:56 itojun Exp $ */ -/* $KAME: ip6protosw.h,v 1.14 2000/10/18 18:15:53 itojun Exp $ */ +/* $OpenBSD: ip6protosw.h,v 1.4 2001/02/16 16:00:58 itojun Exp $ */ +/* $KAME: ip6protosw.h,v 1.22 2001/02/08 18:02:08 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -98,13 +98,21 @@ struct in6_addr; * ip6c_finaldst usually points to ip6c_ip6->ip6_dst. if the original * (internal) packet carries a routing header, it may point the final * dstination address in the routing header. + * + * ip6c_src: ip6c_ip6->ip6_src + scope info + flowlabel in ip6c_ip6 + * (beware of flowlabel, if you try to compare it against others) + * ip6c_dst: ip6c_finaldst + scope info */ struct ip6ctlparam { struct mbuf *ip6c_m; /* start of mbuf chain */ struct icmp6_hdr *ip6c_icmp6; /* icmp6 header of target packet */ struct ip6_hdr *ip6c_ip6; /* ip6 header of target packet */ int ip6c_off; /* offset of the target proto header */ + struct sockaddr_in6 *ip6c_src; /* srcaddr w/ additional info */ + struct sockaddr_in6 *ip6c_dst; /* (final) dstaddr w/ additional info */ struct in6_addr *ip6c_finaldst; /* final destination address */ + void *ip6c_cmdarg; /* control command dependent data */ + u_int8_t ip6c_nxt; /* final next header field */ }; struct ip6protosw { diff --git a/sys/netinet6/mld6.c b/sys/netinet6/mld6.c index 5a8dba7b6ac..badc566b512 100644 --- a/sys/netinet6/mld6.c +++ b/sys/netinet6/mld6.c @@ -1,5 +1,5 @@ -/* $OpenBSD: mld6.c,v 1.8 2001/02/16 08:48:06 itojun Exp $ */ -/* $KAME: mld6.c,v 1.25 2001/01/16 14:14:18 itojun Exp $ */ +/* $OpenBSD: mld6.c,v 1.9 2001/02/16 16:00:58 itojun Exp $ */ +/* $KAME: mld6.c,v 1.26 2001/02/16 14:50:35 itojun Exp $ */ /* * Copyright (C) 1998 WIDE Project. @@ -189,31 +189,34 @@ mld6_input(m, off) struct in6_ifaddr *ia; int timer; /* timer value in the MLD query header */ +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, off, sizeof(*mldh),); + mldh = (struct mld6_hdr *)(mtod(m, caddr_t) + off); +#else + IP6_EXTHDR_GET(mldh, struct mld6_hdr *, m, off, sizeof(*mldh)); + if (mldh == NULL) { + icmp6stat.icp6s_tooshort++; + return; + } +#endif + /* source address validation */ + ip6 = mtod(m, struct ip6_hdr *);/* in case mpullup */ if (!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) { log(LOG_ERR, - "mld6_input: src %s is not link-local\n", - ip6_sprintf(&ip6->ip6_src)); + "mld6_input: src %s is not link-local (grp=%s)\n", + ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&mldh->mld6_addr)); /* * spec (RFC2710) does not explicitly * specify to discard the packet from a non link-local * source address. But we believe it's expected to do so. + * XXX: do we have to allow :: as source? */ m_freem(m); return; } -#ifndef PULLDOWN_TEST - IP6_EXTHDR_CHECK(m, off, sizeof(*mldh),); - mldh = (struct mld6_hdr *)(mtod(m, caddr_t) + off); -#else - IP6_EXTHDR_GET(mldh, struct mld6_hdr *, m, off, sizeof(*mldh)); - if (mldh == NULL) { - icmp6stat.icp6s_tooshort++; - return; - } -#endif - /* * In the MLD6 specification, there are 3 states and a flag. * @@ -231,7 +234,7 @@ mld6_input(m, off) break; if (!IN6_IS_ADDR_UNSPECIFIED(&mldh->mld6_addr) && - !IN6_IS_ADDR_MULTICAST(&mldh->mld6_addr)) + !IN6_IS_ADDR_MULTICAST(&mldh->mld6_addr)) break; /* print error or log stat? */ if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr)) mldh->mld6_addr.s6_addr16[1] = diff --git a/sys/netinet6/raw_ip6.c b/sys/netinet6/raw_ip6.c index 783be6eb8b8..486b931ffa1 100644 --- a/sys/netinet6/raw_ip6.c +++ b/sys/netinet6/raw_ip6.c @@ -1,4 +1,4 @@ -/* $OpenBSD: raw_ip6.c,v 1.1 2001/02/08 18:46:23 itojun Exp $ */ +/* $OpenBSD: raw_ip6.c,v 1.2 2001/02/16 16:01:00 itojun Exp $ */ /* $KAME: raw_ip6.c,v 1.65 2001/02/08 18:36:17 itojun Exp $ */ /* @@ -261,11 +261,14 @@ rip6_ctlinput(cmd, sa, d) struct sockaddr *sa; void *d; { - struct sockaddr_in6 sa6; struct ip6_hdr *ip6; struct mbuf *m; int off; + struct ip6ctlparam *ip6cp = NULL; + const struct sockaddr_in6 *sa6_src = NULL; + void *cmdarg; void (*notify) __P((struct in6pcb *, int)) = in6_rtchange; + int nxt; if (sa->sa_family != AF_INET6 || sa->sa_len != sizeof(struct sockaddr_in6)) @@ -277,43 +280,102 @@ rip6_ctlinput(cmd, sa, d) notify = in6_rtchange, d = NULL; else if (cmd == PRC_HOSTDEAD) d = NULL; +#if defined(__NetBSD__) || defined(__OpenBSD__) + else if (cmd == PRC_MSGSIZE) + ; /* special code is present, see below */ +#endif else if (inet6ctlerrmap[cmd] == 0) return; /* if the parameter is from icmp6, decode it. */ if (d != NULL) { - struct ip6ctlparam *ip6cp = (struct ip6ctlparam *)d; + ip6cp = (struct ip6ctlparam *)d; m = ip6cp->ip6c_m; ip6 = ip6cp->ip6c_ip6; off = ip6cp->ip6c_off; + cmdarg = ip6cp->ip6c_cmdarg; + sa6_src = ip6cp->ip6c_src; + nxt = ip6cp->ip6c_nxt; } else { m = NULL; ip6 = NULL; + cmdarg = NULL; + sa6_src = &sa6_any; + nxt = -1; } - /* translate addresses into internal form */ - sa6 = *(struct sockaddr_in6 *)sa; - if (IN6_IS_ADDR_LINKLOCAL(&sa6.sin6_addr) && m && m->m_pkthdr.rcvif) - sa6.sin6_addr.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); +#if defined(__NetBSD__) || defined(__OpenBSD__) + if (ip6 && cmd == PRC_MSGSIZE) { + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa; + int valid = 0; + struct in6pcb *in6p; - if (ip6) { /* - * XXX: We assume that when IPV6 is non NULL, - * M and OFF are valid. + * Check to see if we have a valid raw IPv6 socket + * corresponding to the address in the ICMPv6 message + * payload, and the protocol (ip6_nxt) meets the socket. + * XXX chase extension headers, or pass final nxt value + * from icmp6_notify_error() */ - struct in6_addr s; + in6p = NULL; +#ifdef __NetBSD__ + in6p = in6_pcblookup_connect(&rawin6pcb, + &sa6->sin6_addr, 0, + (struct in6_addr *)&sa6_src->sin6_addr, 0, 0); +#elif defined(__OpenBSD__) + in6p = in6_pcbhashlookup(&rawin6pcbtable, &sa6->sin6_addr, 0, + (struct in6_addr *)&sa6_src->sin6_addr, 0); +#endif +#if 0 + if (!in6p) { + /* + * As the use of sendto(2) is fairly popular, + * we may want to allow non-connected pcb too. + * But it could be too weak against attacks... + * We should at least check if the local + * address (= s) is really ours. + */ +#ifdef __NetBSD__ + in6p = in6_pcblookup_bind(&rawin6pcb, + &sa6->sin6_addr, 0, 0)) +#elif defined(__OpenBSD__) + in6p = in_pcblookup(&rawin6pcbtable, &sa6->sin6_addr, 0, + (struct in6_addr *)&sa6_src->sin6_addr, 0, + INPLOOKUP_WILDCARD | INPLOOKUP_IPV6); +#endif + } +#endif - /* translate addresses into internal form */ - bcopy(&ip6->ip6_src, &s, sizeof(s)); - if (IN6_IS_ADDR_LINKLOCAL(&s)) - s.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); + if (in6p && in6p->in6p_ip6.ip6_nxt && + in6p->in6p_ip6.ip6_nxt == nxt) + valid++; - (void) in6_pcbnotify(&rawin6pcbtable, (struct sockaddr *)&sa6, - 0, &s, 0, cmd, notify); - } else { - (void) in6_pcbnotify(&rawin6pcbtable, (struct sockaddr *)&sa6, 0, - &zeroin6_addr, 0, cmd, notify); + /* + * Depending on the value of "valid" and routing table + * size (mtudisc_{hi,lo}wat), we will: + * - recalcurate the new MTU and create the + * corresponding routing entry, or + * - ignore the MTU change notification. + */ + icmp6_mtudisc_update((struct ip6ctlparam *)d, valid); + + /* + * regardless of if we called icmp6_mtudisc_update(), + * we need to call in6_pcbnotify(), to notify path + * MTU change to the userland (2292bis-02), because + * some unconnected sockets may share the same + * destination and want to know the path MTU. + */ } +#endif + +#ifdef __OpenBSD__ + (void) in6_pcbnotify(&rawin6pcbtable, sa, 0, + (struct sockaddr *)sa6_src, 0, cmd, cmdarg, notify); +#else + (void) in6_pcbnotify(&rawin6pcb, sa, 0, + (struct sockaddr *)sa6_src, 0, cmd, cmdarg, notify); +#endif } /* |