diff options
author | Jun-ichiro itojun Hagino <itojun@cvs.openbsd.org> | 2000-06-03 13:04:40 +0000 |
---|---|---|
committer | Jun-ichiro itojun Hagino <itojun@cvs.openbsd.org> | 2000-06-03 13:04:40 +0000 |
commit | 03f6b065c4622c0aa987fd9652c2a7a6c809085c (patch) | |
tree | 9a36f19dc4504f436655e50d70dccb846c7959ac | |
parent | 92f28b9442b1f8219e83e2fef99b2a22772b0084 (diff) |
correctly handle ctlinput messages for IPv6.
-rw-r--r-- | sys/netinet/tcp_subr.c | 107 | ||||
-rw-r--r-- | sys/netinet/tcp_usrreq.c | 12 | ||||
-rw-r--r-- | sys/netinet6/in6_pcb.c | 61 |
3 files changed, 132 insertions, 48 deletions
diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c index c27361f00d3..7c1764ab993 100644 --- a/sys/netinet/tcp_subr.c +++ b/sys/netinet/tcp_subr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tcp_subr.c,v 1.25 2000/03/21 04:53:13 angelos Exp $ */ +/* $OpenBSD: tcp_subr.c,v 1.26 2000/06/03 13:04:39 itojun Exp $ */ /* $NetBSD: tcp_subr.c,v 1.22 1996/02/13 23:44:00 christos Exp $ */ /* @@ -79,6 +79,8 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>. #include <netinet6/ip6_var.h> #include <netinet6/tcpipv6.h> #include <sys/domain.h> +#include <netinet6/in6_var.h> +#include <netinet6/ip6protosw.h> #endif /* INET6 */ #ifdef TCP_SIGNATURE @@ -703,7 +705,74 @@ tcp6_ctlinput(cmd, sa, d) struct sockaddr *sa; void *d; { - (void)tcp_ctlinput(cmd, sa, NULL); /*XXX*/ + register struct tcphdr *thp; + struct tcphdr th; + void (*notify) __P((struct inpcb *, int)) = tcp_notify; + struct sockaddr_in6 sa6; + struct mbuf *m; + struct ip6_hdr *ip6; + int off; + + if (sa->sa_family != AF_INET6 || + sa->sa_len != sizeof(struct sockaddr_in6)) + return; + if (cmd == PRC_QUENCH) + notify = tcp_quench; +#if 0 + else if (cmd == PRC_MSGSIZE) + notify = tcp_mtudisc; +#endif + else if (!PRC_IS_REDIRECT(cmd) && + ((unsigned)cmd > PRC_NCMDS || inet6ctlerrmap[cmd] == 0)) + return; + + /* if the parameter is from icmp6, decode it. */ + if (d != NULL) { + struct ip6ctlparam *ip6cp = (struct ip6ctlparam *)d; + m = ip6cp->ip6c_m; + ip6 = ip6cp->ip6c_ip6; + off = ip6cp->ip6c_off; + } else { + m = NULL; + ip6 = NULL; + } + + /* 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 IPV6 is non NULL, + * M and OFF are valid. + */ + struct ip6_hdr ip6_tmp; + + /* translate addresses into internal form */ + ip6_tmp = *ip6; + if (IN6_IS_ADDR_LINKLOCAL(&ip6_tmp.ip6_src)) + ip6_tmp.ip6_src.s6_addr16[1] = + htons(m->m_pkthdr.rcvif->if_index); + if (IN6_IS_ADDR_LINKLOCAL(&ip6_tmp.ip6_dst)) + ip6_tmp.ip6_dst.s6_addr16[1] = + htons(m->m_pkthdr.rcvif->if_index); + + 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); + (void)in6_pcbnotify(&tcbtable, (struct sockaddr *)&sa6, + thp->th_dport, &ip6_tmp.ip6_src, + thp->th_sport, cmd, notify); + } else { + (void)in6_pcbnotify(&tcbtable, (struct sockaddr *)&sa6, 0, + &zeroin6_addr, 0, cmd, notify); + } } #endif @@ -719,6 +788,9 @@ tcp_ctlinput(cmd, sa, v) void (*notify) __P((struct inpcb *, int)) = tcp_notify; int errno; + if (sa->sa_family != AF_INET) + return NULL; + if ((unsigned)cmd >= PRC_NCMDS) return NULL; errno = inetctlerrmap[cmd]; @@ -731,32 +803,13 @@ tcp_ctlinput(cmd, sa, v) else if (errno == 0) return NULL; -#ifdef INET6 - if (sa->sa_family == AF_INET6) { - if (ip) { - struct ip6_hdr *ipv6 = (struct ip6_hdr *)ip; - - th = (struct tcphdr *)(ipv6 + 1); -#if 0 /*XXX*/ - in6_pcbnotify(&tcbtable, sa, th->th_dport, - &ipv6->ip6_src, th->th_sport, cmd, notify); -#endif - } else { -#if 0 /*XXX*/ - in6_pcbnotify(&tcbtable, sa, 0, - (struct in6_addr *)&in6addr_any, 0, cmd, notify); -#endif - } + 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); } else -#endif /* INET6 */ - { - 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); - } else - in_pcbnotifyall(&tcbtable, sa, errno, notify); - } + in_pcbnotifyall(&tcbtable, sa, errno, notify); + return NULL; } diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c index 093739ad071..bbb22297121 100644 --- a/sys/netinet/tcp_usrreq.c +++ b/sys/netinet/tcp_usrreq.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tcp_usrreq.c,v 1.39 1999/12/21 17:49:28 provos Exp $ */ +/* $OpenBSD: tcp_usrreq.c,v 1.40 2000/06/03 13:04:39 itojun Exp $ */ /* $NetBSD: tcp_usrreq.c,v 1.20 1996/02/13 23:44:16 christos Exp $ */ /* @@ -415,7 +415,15 @@ tcp_usrreq(so, req, m, nam, control) * After a receive, possibly send window update to peer. */ case PRU_RCVD: - (void) tcp_output(tp); + /* + * soreceive() calls this function when a user receives + * ancillary data on a listening socket. We don't call + * tcp_output in such a case, since there is no header + * template for a listening socket and hence the kernel + * will panic. + */ + if ((so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING)) != 0) + (void) tcp_output(tp); break; /* diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c index d824bbf0658..5db6a39a412 100644 --- a/sys/netinet6/in6_pcb.c +++ b/sys/netinet6/in6_pcb.c @@ -1,4 +1,4 @@ -/* $OpenBSD: in6_pcb.c,v 1.13 2000/05/28 23:58:51 itojun Exp $ */ +/* $OpenBSD: in6_pcb.c,v 1.14 2000/06/03 13:04:39 itojun Exp $ */ /* %%% copyright-nrl-95 @@ -171,21 +171,23 @@ in6_pcbbind(inp, nam) { struct sockaddr_in sin; + bzero(&sin, sizeof(sin)); sin.sin_port = 0; sin.sin_len = sizeof(sin); sin.sin_family = AF_INET; sin.sin_addr.s_addr = sin6->sin6_addr.s6_addr32[3]; - bzero(&sin.sin_zero,8); sin6->sin6_port = 0; /* Yechhhh, because of upcoming call to ifa_ifwithaddr(), which does bcmp's over the PORTS as well. (What about flow?) */ sin6->sin6_flowinfo = 0; - if (ifa_ifwithaddr((struct sockaddr *)sin6) == 0) + if (ifa_ifwithaddr((struct sockaddr *)sin6) == 0) { if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr) || - ifa_ifwithaddr((struct sockaddr *)&sin) == 0) + ifa_ifwithaddr((struct sockaddr *)&sin) == 0) { return EADDRNOTAVAIL; + } + } } if (lport) { @@ -215,6 +217,7 @@ in6_pcbbind(inp, nam) t = in_pcblookup(head, (struct in_addr *)&fa, 0, (struct in_addr *)&la, lport, wild); + } else #endif @@ -534,10 +537,11 @@ in6_pcbnotify(head, dst, fport_arg, la, lport_arg, cmd, notify) int cmd; void (*notify) __P((struct inpcb *, int)); { - register struct inpcb *inp, *oinp; + register struct inpcb *inp, *ninp; struct in6_addr *faddr,laddr = *la; u_short fport = fport_arg, lport = lport_arg; - int errno; + int errno, nmatch = 0; + int do_rtchange = (notify == in_rtchange); if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET6) return 1; @@ -551,45 +555,64 @@ in6_pcbnotify(head, dst, fport_arg, la, lport_arg, cmd, notify) /* * Redirects go to all references to the destination, - * and use in_rtchange to invalidate the route cache. - * Dead host indications: notify all references to the destination. + * and use in6_rtchange to invalidate the route cache. + * Dead host indications: also use in6_rtchange to invalidate + * the cache, and deliver the error to all the sockets. * Otherwise, if we have knowledge of the local port and address, * deliver only to that socket. */ - if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) { fport = 0; lport = 0; laddr = in6addr_any; - if (cmd != PRC_HOSTDEAD) - notify = in_rtchange; + + notify = in_rtchange; } errno = inet6ctlerrmap[cmd]; for (inp = head->inpt_queue.cqh_first; - inp != (struct inpcb *)&head->inpt_queue;) + inp != (struct inpcb *)&head->inpt_queue; inp = ninp) { + ninp = inp->inp_queue.cqe_next; #ifdef INET6 - if (!(inp->inp_flags & INP_IPV6)) { - inp = inp->inp_queue.cqe_next; + if (!(inp->inp_flags & INP_IPV6)) 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) + { + continue; /* there's nothing to do any more */ + } + } 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)) { - inp = inp->inp_queue.cqe_next; continue; } - oinp = inp; - inp = inp->inp_queue.cqe_next; + nmatch++; + if (notify) - (*notify)(oinp, errno); + { + (*notify)(inp, errno); + } } return 0; } |