summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJun-ichiro itojun Hagino <itojun@cvs.openbsd.org>2000-06-03 13:04:40 +0000
committerJun-ichiro itojun Hagino <itojun@cvs.openbsd.org>2000-06-03 13:04:40 +0000
commit03f6b065c4622c0aa987fd9652c2a7a6c809085c (patch)
tree9a36f19dc4504f436655e50d70dccb846c7959ac
parent92f28b9442b1f8219e83e2fef99b2a22772b0084 (diff)
correctly handle ctlinput messages for IPv6.
-rw-r--r--sys/netinet/tcp_subr.c107
-rw-r--r--sys/netinet/tcp_usrreq.c12
-rw-r--r--sys/netinet6/in6_pcb.c61
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;
}