diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1999-01-11 02:01:37 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1999-01-11 02:01:37 +0000 |
commit | 54ec4ac803b4008b00c68e4bd4a4fc1ec3f5e097 (patch) | |
tree | c20c66c2ad89268a8deb2019ddd5ee306d652421 /sys/netinet | |
parent | 2f10b00f0ab323af0befe8b6887b503520414090 (diff) |
netinet merge of NRL stuff. some indent and shrinkage needed; NRL/cmetz
Diffstat (limited to 'sys/netinet')
-rw-r--r-- | sys/netinet/in_pcb.c | 8 | ||||
-rw-r--r-- | sys/netinet/in_pcb.h | 6 | ||||
-rw-r--r-- | sys/netinet/ip_var.h | 5 | ||||
-rw-r--r-- | sys/netinet/raw_ip.c | 25 | ||||
-rw-r--r-- | sys/netinet/tcp.h | 10 | ||||
-rw-r--r-- | sys/netinet/tcp_debug.c | 61 | ||||
-rw-r--r-- | sys/netinet/tcp_debug.h | 5 | ||||
-rw-r--r-- | sys/netinet/tcp_input.c | 761 | ||||
-rw-r--r-- | sys/netinet/tcp_output.c | 129 | ||||
-rw-r--r-- | sys/netinet/tcp_subr.c | 329 | ||||
-rw-r--r-- | sys/netinet/tcp_usrreq.c | 116 | ||||
-rw-r--r-- | sys/netinet/tcp_var.h | 18 | ||||
-rw-r--r-- | sys/netinet/udp_usrreq.c | 407 |
13 files changed, 1519 insertions, 361 deletions
diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index 5428694fed0..b36fe6b55d3 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -1,4 +1,4 @@ -/* $OpenBSD: in_pcb.c,v 1.28 1999/01/08 07:47:22 deraadt Exp $ */ +/* $OpenBSD: in_pcb.c,v 1.29 1999/01/11 02:01:34 deraadt Exp $ */ /* $NetBSD: in_pcb.c,v 1.25 1996/02/13 23:41:53 christos Exp $ */ /* @@ -59,9 +59,7 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>. #include <sys/errno.h> #include <sys/time.h> #include <sys/proc.h> -#ifdef INET6 #include <sys/domain.h> -#endif /* INET6 */ #include <net/if.h> #include <net/route.h> @@ -74,6 +72,10 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>. #include <netinet/ip_var.h> #include <dev/rndvar.h> +#ifdef INET6 +#include <netinet6/ipv6_var.h> +#endif /* INET6 */ + #ifdef IPSEC #include <net/encap.h> #include <netinet/ip_ipsp.h> diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h index 9253b903021..bd13b189a3e 100644 --- a/sys/netinet/in_pcb.h +++ b/sys/netinet/in_pcb.h @@ -1,4 +1,4 @@ -/* $OpenBSD: in_pcb.h,v 1.14 1999/01/08 07:47:54 deraadt Exp $ */ +/* $OpenBSD: in_pcb.h,v 1.15 1999/01/11 02:01:34 deraadt Exp $ */ /* $NetBSD: in_pcb.h,v 1.14 1996/02/13 23:42:00 christos Exp $ */ /* @@ -181,6 +181,10 @@ struct inpcb * struct inpcb * in6_pcbhashlookup __P((struct inpcbtable *, struct in6_addr *, u_int, struct in6_addr *, u_int)); +int in6_pcbbind __P((struct inpcb *, struct mbuf *)); +int in6_pcbconnect __P((struct inpcb *, struct mbuf *)); +int in6_setsockaddr __P((struct inpcb *, struct mbuf *)); +int in6_setpeeraddr __P((struct inpcb *, struct mbuf *)); #endif /* INET6 */ void in_pcbinit __P((struct inpcbtable *, int)); struct inpcb * diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h index d249293406a..1583d199f98 100644 --- a/sys/netinet/ip_var.h +++ b/sys/netinet/ip_var.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_var.h,v 1.9 1998/12/26 12:35:12 provos Exp $ */ +/* $OpenBSD: ip_var.h,v 1.10 1999/01/11 02:01:34 deraadt Exp $ */ /* $NetBSD: ip_var.h,v 1.16 1996/02/13 23:43:20 christos Exp $ */ /* @@ -65,7 +65,7 @@ struct ipqent { LIST_ENTRY(ipqent) ipqe_q; union { struct ip *_ip; - struct tcpiphdr *_tcp; + struct tcphdr *_tcp; } _ipqe_u1; union { u_int8_t _mff; /* for IP fragmentation */ @@ -189,6 +189,7 @@ struct mbuf * void ip_stripoptions __P((struct mbuf *, struct mbuf *)); int ip_sysctl __P((int *, u_int, void *, size_t *, void *, size_t)); void ipintr __P((void)); +void ipv4_input __P((struct mbuf *, ...)); int rip_ctloutput __P((int, struct socket *, int, int, struct mbuf **)); void rip_init __P((void)); void rip_input __P((struct mbuf *, ...)); diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c index 344dc62fb23..75eb85dc4ed 100644 --- a/sys/netinet/raw_ip.c +++ b/sys/netinet/raw_ip.c @@ -1,4 +1,4 @@ -/* $OpenBSD: raw_ip.c,v 1.14 1999/01/08 21:51:23 provos Exp $ */ +/* $OpenBSD: raw_ip.c,v 1.15 1999/01/11 02:01:34 deraadt Exp $ */ /* $NetBSD: raw_ip.c,v 1.25 1996/02/18 18:58:33 christos Exp $ */ /* @@ -36,6 +36,18 @@ * @(#)raw_ip.c 8.2 (Berkeley) 1/4/94 */ +/* +%%% portions-copyright-nrl-95 +Portions of this software are Copyright 1995-1998 by Randall Atkinson, +Ronald Lee, Daniel McDonald, Bao Phan, and Chris Winters. All Rights +Reserved. All rights under this copyright have been assigned to the US +Naval Research Laboratory (NRL). The NRL Copyright Notice and License +Agreement Version 1.1 (January 17, 1995) applies to these portions of the +software. +You should have received a copy of the license with this software. If you +didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>. +*/ + #include <sys/param.h> #include <sys/malloc.h> #include <sys/mbuf.h> @@ -84,7 +96,8 @@ rip_init() in_pcbinit(&rawcbtable, 1); } -struct sockaddr_in ripsrc = { sizeof(ripsrc), AF_INET }; +struct sockaddr_in ripsrc = { sizeof(ripsrc), AF_INET }; + /* * Setup generic address and protocol structures * for raw_input routine, then pass them along with @@ -137,6 +150,7 @@ rip_input(m, va_alist) sorwakeup(last); } else { m_freem(m); + /* Perhaps should send an ICMP protocol unreachable here. */ ipstat.ips_noproto++; ipstat.ips_delivered--; } @@ -214,6 +228,13 @@ rip_output(m, va_alist) flags |= IP_RAWOUTPUT; ipstat.ips_rawout++; } +#ifdef INET6 + /* + * A thought: Even though raw IP shouldn't be able to set IPv6 + * multicast options, if it does, the last parameter to + * ip_output should be guarded against v6/v4 problems. + */ +#endif return (ip_output(m, inp->inp_options, &inp->inp_route, flags, inp->inp_moptions, inp)); } diff --git a/sys/netinet/tcp.h b/sys/netinet/tcp.h index d259fede69b..e2140aec80d 100644 --- a/sys/netinet/tcp.h +++ b/sys/netinet/tcp.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tcp.h,v 1.5 1999/01/07 09:14:54 deraadt Exp $ */ +/* $OpenBSD: tcp.h,v 1.6 1999/01/11 02:01:35 deraadt Exp $ */ /* $NetBSD: tcp.h,v 1.8 1995/04/17 05:32:58 cgd Exp $ */ /* @@ -36,7 +36,11 @@ * @(#)tcp.h 8.1 (Berkeley) 6/10/93 */ +#ifndef _NETINET_TCP_H_ +#define _NETINET_TCP_H_ + typedef u_int32_t tcp_seq; + /* * TCP header. * Per RFC 793, September, 1981. @@ -65,6 +69,8 @@ struct tcphdr { u_int16_t th_sum; /* checksum */ u_int16_t th_urp; /* urgent pointer */ }; +#define th_reseqlen th_urp /* TCP data length for + resequencing/reassembly */ #define TCPOPT_EOL 0 #define TCPOPT_NOP 1 @@ -111,3 +117,5 @@ struct tcphdr { #define TCP_NODELAY 0x01 /* don't delay send to coalesce packets */ #define TCP_MAXSEG 0x02 /* set maximum segment size */ #define TCP_SACK_DISABLE 0x300 /* disable SACKs(if enabled by deflt.)*/ + +#endif /* !_NETINET_TCP_H_ */ diff --git a/sys/netinet/tcp_debug.c b/sys/netinet/tcp_debug.c index 6adc013193e..a728744b64f 100644 --- a/sys/netinet/tcp_debug.c +++ b/sys/netinet/tcp_debug.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tcp_debug.c,v 1.3 1998/11/17 19:23:01 provos Exp $ */ +/* $OpenBSD: tcp_debug.c,v 1.4 1999/01/11 02:01:35 deraadt Exp $ */ /* $NetBSD: tcp_debug.c,v 1.10 1996/02/13 23:43:36 christos Exp $ */ /* @@ -36,6 +36,18 @@ * @(#)tcp_debug.c 8.1 (Berkeley) 6/10/93 */ +/* +%%% portions-copyright-nrl-95 +Portions of this software are Copyright 1995-1998 by Randall Atkinson, +Ronald Lee, Daniel McDonald, Bao Phan, and Chris Winters. All Rights +Reserved. All rights under this copyright have been assigned to the US +Naval Research Laboratory (NRL). The NRL Copyright Notice and License +Agreement Version 1.1 (January 17, 1995) applies to these portions of the +software. +You should have received a copy of the license with this software. If you +didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>. +*/ + #ifdef TCPDEBUG /* load symbolic names */ #define PRUREQUESTS @@ -68,6 +80,11 @@ #include <netinet/tcpip.h> #include <netinet/tcp_debug.h> +#ifdef INET6 +#include <netinet6/ipv6.h> +#include <netinet6/in6.h> +#endif /* INET6 */ + #ifdef TCPDEBUG int tcpconsdebug = 0; #endif @@ -75,17 +92,22 @@ int tcpconsdebug = 0; * Tcp debug routines */ void -tcp_trace(act, ostate, tp, ti, req) +tcp_trace(act, ostate, tp, ti, req, len) short act, ostate; struct tcpcb *tp; struct tcpiphdr *ti; int req; + int len; { #ifdef TCPDEBUG tcp_seq seq, ack; - int len, flags; + int flags; #endif struct tcp_debug *td = &tcp_debug[tcp_debx++]; +#ifdef INET6 + struct tcphdr *th; + struct tcpipv6hdr *ti6 = (struct tcpipv6hdr *)ti; +#endif if (tcp_debx == TCP_NDEBUG) tcp_debx = 0; @@ -97,10 +119,29 @@ tcp_trace(act, ostate, tp, ti, req) td->td_cb = *tp; else bzero((caddr_t)&td->td_cb, sizeof (*tp)); +#ifdef INET6 + if (tp->pf == PF_INET6) { + if (ti) { + th = &ti6->ti6_t; + td->td_ti6 = *ti6; + } else { + bzero(&td->td_ti6, sizeof(struct tcpipv6hdr)); + } + } else { + if (ti) { + th = &ti->ti_t; + td->td_ti = *ti; + } else { + bzero(&td->td_ti, sizeof(struct tcpiphdr)); + } + } +#else /* INET6 */ if (ti) td->td_ti = *ti; else bzero((caddr_t)&td->td_ti, sizeof (*ti)); +#endif /* INET6 */ + td->td_req = req; #ifdef TCPDEBUG if (tcpconsdebug == 0) @@ -117,26 +158,22 @@ tcp_trace(act, ostate, tp, ti, req) case TA_DROP: if (ti == 0) break; - seq = ti->ti_seq; - ack = ti->ti_ack; - len = ti->ti_len; + seq = th->th_seq; + ack = th->th_ack; if (act == TA_OUTPUT) { seq = ntohl(seq); ack = ntohl(ack); - len = ntohs((u_int16_t)len); } - if (act == TA_OUTPUT) - len -= sizeof (struct tcphdr); if (len) printf("[%x..%x)", seq, seq+len); else printf("%x", seq); - printf("@%x, urp=%x", ack, ti->ti_urp); - flags = ti->ti_flags; + printf("@%x, urp=%x", ack, th->th_urp); + flags = th->th_flags; if (flags) { #ifndef lint char *cp = "<"; -#define pf(f) { if (ti->ti_flags&TH_/**/f) { printf("%s%s", cp, "f"); cp = ","; } } +#define pf(f) { if (th->th_flags&TH_/**/f) { printf("%s%s", cp, "f"); cp = ","; } } pf(SYN); pf(ACK); pf(FIN); pf(RST); pf(PUSH); pf(URG); #endif printf(">"); diff --git a/sys/netinet/tcp_debug.h b/sys/netinet/tcp_debug.h index be53b602410..0504f95df92 100644 --- a/sys/netinet/tcp_debug.h +++ b/sys/netinet/tcp_debug.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tcp_debug.h,v 1.3 1997/08/26 20:02:31 deraadt Exp $ */ +/* $OpenBSD: tcp_debug.h,v 1.4 1999/01/11 02:01:35 deraadt Exp $ */ /* $NetBSD: tcp_debug.h,v 1.5 1994/06/29 06:38:38 cgd Exp $ */ /* @@ -36,12 +36,15 @@ * @(#)tcp_debug.h 8.1 (Berkeley) 6/10/93 */ +#include <netinet6/tcpipv6.h> + struct tcp_debug { n_time td_time; short td_act; short td_ostate; caddr_t td_tcb; struct tcpiphdr td_ti; + struct tcpipv6hdr td_ti6; short td_req; struct tcpcb td_cb; }; diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c index e08dd29ed12..2b368b72ebd 100644 --- a/sys/netinet/tcp_input.c +++ b/sys/netinet/tcp_input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tcp_input.c,v 1.23 1999/01/07 06:05:04 deraadt Exp $ */ +/* $OpenBSD: tcp_input.c,v 1.24 1999/01/11 02:01:35 deraadt Exp $ */ /* $NetBSD: tcp_input.c,v 1.23 1996/02/13 23:43:44 christos Exp $ */ /* @@ -36,6 +36,18 @@ * @(#)tcp_input.c 8.5 (Berkeley) 4/10/94 */ +/* +%%% portions-copyright-nrl-95 +Portions of this software are Copyright 1995-1998 by Randall Atkinson, +Ronald Lee, Daniel McDonald, Bao Phan, and Chris Winters. All Rights +Reserved. All rights under this copyright have been assigned to the US +Naval Research Laboratory (NRL). The NRL Copyright Notice and License +Agreement Version 1.1 (January 17, 1995) applies to these portions of the +software. +You should have received a copy of the license with this software. If you +didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>. +*/ + #ifndef TUBA_INCLUDE #include <sys/param.h> #include <sys/systm.h> @@ -65,6 +77,22 @@ #include <machine/stdarg.h> #include <sys/md5k.h> +#ifdef INET6 +#include <sys/domain.h> +#include <netinet6/in6_var.h> +#include <netinet6/ipv6.h> +#include <netinet6/ipv6_var.h> +#include <netinet6/tcpipv6.h> + +struct tcpiphdr tcp_saveti; +struct tcpipv6hdr tcp_saveti6; + +/* for the packet header length in the mbuf */ +#define M_PH_LEN(m) (((struct mbuf *)(m))->m_pkthdr.len) +#define M_V6_LEN(m) (M_PH_LEN(m) - sizeof(struct ipv6)) +#define M_V4_LEN(m) (M_PH_LEN(m) - sizeof(struct ip)) +#endif /* INET6 */ + int tcprexmtthresh = 3; struct tcpiphdr tcp_saveti; int tcptv_keep_init = TCPTV_KEEP_INIT; @@ -203,42 +231,25 @@ tck_chkcookie(ti) * Set DELACK for segments received in order, but ack immediately * when segments are out of order (so fast retransmit can work). */ -#define TCP_REASS(tp, ti, m, so, flags) { \ - if ((ti)->ti_seq == (tp)->rcv_nxt && \ - (tp)->segq.lh_first == NULL && \ - (tp)->t_state == TCPS_ESTABLISHED) { \ - if ((ti)->ti_flags & TH_PUSH) \ - tp->t_flags |= TF_ACKNOW; \ - else \ - tp->t_flags |= TF_DELACK; \ - (tp)->rcv_nxt += (ti)->ti_len; \ - flags = (ti)->ti_flags & TH_FIN; \ - tcpstat.tcps_rcvpack++;\ - tcpstat.tcps_rcvbyte += (ti)->ti_len;\ - sbappend(&(so)->so_rcv, (m)); \ - sorwakeup(so); \ - } else { \ - (flags) = tcp_reass((tp), (ti), (m)); \ - tp->t_flags |= TF_ACKNOW; \ - } \ -} + #ifndef TUBA_INCLUDE int -tcp_reass(tp, ti, m) +tcp_reass(tp, th, m, tlen) register struct tcpcb *tp; - register struct tcpiphdr *ti; + register struct tcphdr *th; struct mbuf *m; + int *tlen; { register struct ipqent *p, *q, *nq, *tiqe; struct socket *so = tp->t_inpcb->inp_socket; int flags; /* - * Call with ti==0 after become established to + * Call with th==0 after become established to * force pre-ESTABLISHED data up to user socket. */ - if (ti == 0) + if (th == 0) goto present; /* @@ -257,7 +268,7 @@ tcp_reass(tp, ti, m) */ for (p = NULL, q = tp->segq.lh_first; q != NULL; p = q, q = q->ipqe_q.le_next) - if (SEQ_GT(q->ipqe_tcp->ti_seq, ti->ti_seq)) + if (SEQ_GT(q->ipqe_tcp->th_seq, th->th_seq)) break; /* @@ -266,40 +277,40 @@ tcp_reass(tp, ti, m) * segment. If it provides all of our data, drop us. */ if (p != NULL) { - register struct tcpiphdr *phdr = p->ipqe_tcp; + register struct tcphdr *phdr = p->ipqe_tcp; register int i; /* conversion to int (in i) handles seq wraparound */ - i = phdr->ti_seq + phdr->ti_len - ti->ti_seq; + i = phdr->th_seq + phdr->th_reseqlen - th->th_seq; if (i > 0) { - if (i >= ti->ti_len) { + if (i >= *tlen) { tcpstat.tcps_rcvduppack++; - tcpstat.tcps_rcvdupbyte += ti->ti_len; + tcpstat.tcps_rcvdupbyte += *tlen; m_freem(m); FREE(tiqe, M_IPQ); return (0); } m_adj(m, i); - ti->ti_len -= i; - ti->ti_seq += i; + *tlen -= i; + th->th_seq += i; } } tcpstat.tcps_rcvoopack++; - tcpstat.tcps_rcvoobyte += ti->ti_len; + tcpstat.tcps_rcvoobyte += *tlen; /* * While we overlap succeeding segments trim them or, * if they are completely covered, dequeue them. */ for (; q != NULL; q = nq) { - register struct tcpiphdr *qhdr = q->ipqe_tcp; - register int i = (ti->ti_seq + ti->ti_len) - qhdr->ti_seq; + register struct tcphdr *qhdr = q->ipqe_tcp; + register int i = (th->th_seq + *tlen) - qhdr->th_seq; if (i <= 0) break; - if (i < qhdr->ti_len) { - qhdr->ti_seq += i; - qhdr->ti_len -= i; + if (i < qhdr->th_reseqlen) { + qhdr->th_seq += i; + qhdr->th_reseqlen -= i; m_adj(q->ipqe_m, i); break; } @@ -311,7 +322,8 @@ tcp_reass(tp, ti, m) /* Insert the new fragment queue entry into place. */ tiqe->ipqe_m = m; - tiqe->ipqe_tcp = ti; + th->th_reseqlen = *tlen; + tiqe->ipqe_tcp = th; if (p == NULL) { LIST_INSERT_HEAD(&tp->segq, tiqe, ipqe_q); } else { @@ -326,13 +338,13 @@ present: if (TCPS_HAVEESTABLISHED(tp->t_state) == 0) return (0); q = tp->segq.lh_first; - if (q == NULL || q->ipqe_tcp->ti_seq != tp->rcv_nxt) + if (q == NULL || q->ipqe_tcp->th_seq != tp->rcv_nxt) return (0); - if (tp->t_state == TCPS_SYN_RECEIVED && q->ipqe_tcp->ti_len) + if (tp->t_state == TCPS_SYN_RECEIVED && q->ipqe_tcp->th_reseqlen) return (0); do { - tp->rcv_nxt += q->ipqe_tcp->ti_len; - flags = q->ipqe_tcp->ti_flags & TH_FIN; + tp->rcv_nxt += q->ipqe_tcp->th_reseqlen; + flags = q->ipqe_tcp->th_flags & TH_FIN; nq = q->ipqe_q.le_next; LIST_REMOVE(q, ipqe_q); @@ -342,7 +354,7 @@ present: sbappend(&so->so_rcv, q->ipqe_m); FREE(q, M_IPQ); q = nq; - } while (q != NULL && q->ipqe_tcp->ti_seq == tp->rcv_nxt); + } while (q != NULL && q->ipqe_tcp->th_seq == tp->rcv_nxt); sorwakeup(so); return (flags); } @@ -424,31 +436,87 @@ tcp_input(m, va_alist) int ts_present = 0; int iphlen; va_list ap; + register struct tcphdr *th; +#ifdef INET6 + struct in6_addr laddr6; + unsigned short is_ipv6; /* Type of incoming datagram. */ + struct ipv6 *ipv6 = NULL; +#endif /* INET6 */ va_start(ap, m); iphlen = va_arg(ap, int); va_end(ap); tcpstat.tcps_rcvtotal++; + +#ifdef INET6 + /* + * Before we do ANYTHING, we have to figure out if it's TCP/IPv6 or + * TCP/IPv4. + */ + is_ipv6 = mtod(m, struct ip *)->ip_v == 6; +#endif /* INET6 */ + /* * Get IP and TCP header together in first mbuf. * Note: IP leaves IP header in first mbuf. */ +#ifndef INET6 ti = mtod(m, struct tcpiphdr *); +#else /* INET6 */ + if (!is_ipv6) +#endif /* INET6 */ if (iphlen > sizeof (struct ip)) ip_stripoptions(m, (struct mbuf *)0); - if (m->m_len < sizeof (struct tcpiphdr)) { - if ((m = m_pullup(m, sizeof (struct tcpiphdr))) == 0) { + if (m->m_len < iphlen + sizeof(struct tcphdr)) { + if ((m = m_pullup2(m, iphlen + sizeof(struct tcphdr))) == 0) { tcpstat.tcps_rcvshort++; return; } +#ifndef INET6 ti = mtod(m, struct tcpiphdr *); +#endif /* INET6 */ } + tlen = m->m_pkthdr.len - iphlen; + +#ifdef INET6 + /* + * After that, do initial segment processing which is still very + * dependent on what IP version you're using. + */ + + if (is_ipv6) { +#ifdef DIAGNOSTIC + if (iphlen < sizeof(struct ipv6)) { + m_freem(m); + return; + } +#endif /* DIAGNOSTIC */ + + /* strip off any options */ + if (iphlen > sizeof(struct ipv6)) { + ipv6_stripoptions(m, iphlen); + iphlen = sizeof(struct ipv6); + } + + ti = NULL; + ipv6 = mtod(m, struct ipv6 *); + + if (in6_cksum(m, IPPROTO_TCP, tlen, sizeof(struct ipv6))) { + tcpstat.tcps_rcvbadsum++; + goto drop; + } /* endif in6_cksum */ + } else { + ti = mtod(m, struct tcpiphdr *); +#endif /* INET6 */ + /* * Checksum extended TCP header and data. */ +#ifndef INET6 tlen = ((struct ip *)ti)->ip_len; +#endif /* INET6 */ len = sizeof (struct ip) + tlen; bzero(ti->ti_x1, sizeof ti->ti_x1); ti->ti_len = (u_int16_t)tlen; @@ -457,29 +525,39 @@ tcp_input(m, va_alist) tcpstat.tcps_rcvbadsum++; goto drop; } +#ifdef INET6 + } +#endif /* INET6 */ #endif /* TUBA_INCLUDE */ + th = (struct tcphdr *)(mtod(m, caddr_t) + iphlen); + /* * Check that TCP offset makes sense, * pull out TCP options and adjust length. XXX */ - off = ti->ti_off << 2; + off = th->th_off << 2; if (off < sizeof (struct tcphdr) || off > tlen) { tcpstat.tcps_rcvbadoff++; goto drop; } tlen -= off; - ti->ti_len = tlen; if (off > sizeof (struct tcphdr)) { - if (m->m_len < sizeof(struct ip) + off) { - if ((m = m_pullup(m, sizeof (struct ip) + off)) == 0) { + if (m->m_len < iphlen + off) { + if ((m = m_pullup2(m, iphlen + off)) == 0) { tcpstat.tcps_rcvshort++; return; } +#ifdef INET6 + if (is_ipv6) + ipv6 = mtod(m, struct ipv6 *); + else +#endif /* INET6 */ ti = mtod(m, struct tcpiphdr *); + th = (struct tcphdr *)(mtod(m, caddr_t) + iphlen); } optlen = off - sizeof (struct tcphdr); - optp = mtod(m, caddr_t) + sizeof (struct tcpiphdr); + optp = mtod(m, caddr_t) + iphlen + sizeof(struct tcphdr); /* * Do quick retrieval of timestamp options ("options * prediction?"). If timestamp is the only option and it's @@ -491,22 +569,22 @@ tcp_input(m, va_alist) (optlen > TCPOLEN_TSTAMP_APPA && optp[TCPOLEN_TSTAMP_APPA] == TCPOPT_EOL)) && *(u_int32_t *)optp == htonl(TCPOPT_TSTAMP_HDR) && - (ti->ti_flags & TH_SYN) == 0) { + (th->th_flags & TH_SYN) == 0) { ts_present = 1; ts_val = ntohl(*(u_int32_t *)(optp + 4)); ts_ecr = ntohl(*(u_int32_t *)(optp + 8)); optp = NULL; /* we've parsed the options */ } } - tiflags = ti->ti_flags; + tiflags = th->th_flags; /* * Convert TCP protocol specific fields to host format. */ - NTOHL(ti->ti_seq); - NTOHL(ti->ti_ack); - NTOHS(ti->ti_win); - NTOHS(ti->ti_urp); + NTOHL(th->th_seq); + NTOHL(th->th_ack); + NTOHS(th->th_win); + NTOHS(th->th_urp); #ifdef TCPCOOKIE /* @@ -515,7 +593,11 @@ tcp_input(m, va_alist) * of the packet to the friends list. */ +#ifdef INET6 + if (!is_ipv6 && (tiflags & TH_RST) && (ntohs(th->th_dport) == TCK_PORT)) +#else /* INET6 */ if ((tiflags & TH_RST) && (ntohs(ti->ti_dport) == TCK_PORT)) +#endif /* INET6 */ if (tck_chkcookie(ti)) goto drop; /* RST is no longer needed */ #endif /* TCPCOOKIE */ @@ -524,10 +606,23 @@ tcp_input(m, va_alist) * Locate pcb for segment. */ findpcb: +#ifdef INET6 + if (is_ipv6) { + inp = in6_pcbhashlookup(&tcbtable, &ipv6->ipv6_src, th->th_sport, + &ipv6->ipv6_dst, th->th_dport); + } else +#endif /* INET6 */ inp = in_pcbhashlookup(&tcbtable, ti->ti_src, ti->ti_sport, ti->ti_dst, ti->ti_dport); if (inp == 0) { ++tcpstat.tcps_pcbhashmiss; +#ifdef INET6 + if (is_ipv6) + inp = in_pcblookup(&tcbtable, &ipv6->ipv6_src, + th->th_sport, &ipv6->ipv6_dst, th->th_dport, + INPLOOKUP_WILDCARD | INPLOOKUP_IPV6); + else +#endif /* INET6 */ inp = in_pcblookup(&tcbtable, &ti->ti_src, ti->ti_sport, &ti->ti_dst, ti->ti_dport, INPLOOKUP_WILDCARD); /* @@ -550,14 +645,19 @@ findpcb: /* Unscale the window into a 32-bit value. */ if ((tiflags & TH_SYN) == 0) - tiwin = ti->ti_win << tp->snd_scale; + tiwin = th->th_win << tp->snd_scale; else - tiwin = ti->ti_win; + tiwin = th->th_win; so = inp->inp_socket; if (so->so_options & (SO_DEBUG|SO_ACCEPTCONN)) { if (so->so_options & SO_DEBUG) { ostate = tp->t_state; +#ifdef INET6 + if (is_ipv6) + tcp_saveti6 = *(mtod(m, struct tcpipv6hdr *)); + else +#endif /* INET6 */ tcp_saveti = *ti; } if (so->so_options & SO_ACCEPTCONN) { @@ -565,7 +665,7 @@ findpcb: so1 = sonewconn(so, 0); if (so1 == NULL) { - tcpdropoldhalfopen(tp, ti->ti_dport); + tcpdropoldhalfopen(tp, th->th_dport); so1 = sonewconn(so, 0); if (so1 == NULL) goto drop; @@ -583,13 +683,60 @@ findpcb: * we're committed to it below in TCPS_LISTEN. */ dropsocket++; +#ifdef INET6 + /* + * inp still has the OLD in_pcb stuff, set the + * v6-related flags on the new guy, too. This is + * done particularly for the case where an AF_INET6 + * socket is bound only to a port, and a v4 connection + * comes in on that port. + * we also copy the flowinfo from the original pcb + * to the new one. + */ + { + int flags = inp->inp_flags; + struct inpcb *oldinpcb = inp; + + inp = (struct inpcb *)so->so_pcb; + inp->inp_flags |= (flags & (INP_IPV6 | INP_IPV6_UNDEC + | INP_IPV6_MAPPED)); + if ((inp->inp_flags & INP_IPV6) && + !(inp->inp_flags & INP_IPV6_MAPPED)) { + inp->inp_ipv6.ipv6_hoplimit = + oldinpcb->inp_ipv6.ipv6_hoplimit; + inp->inp_ipv6.ipv6_versfl = + oldinpcb->inp_ipv6.ipv6_versfl; + } + } +#else /* INET6 */ inp = (struct inpcb *)so->so_pcb; - inp->inp_laddr = ti->ti_dst; - inp->inp_lport = ti->ti_dport; +#endif /* INET6 */ + inp->inp_lport = th->th_dport; +#ifdef INET6 + if (is_ipv6) { + inp->inp_laddr6 = ipv6->ipv6_dst; + inp->inp_fflowinfo = htonl(0x0fffffff) & + ipv6->ipv6_versfl; + + /*inp->inp_options = ipv6_srcroute();*/ /* soon. */ + /* still need to tweak outbound options + processing to include this mbuf in + the right place and put the correct + NextHdr values in the right places. + XXX rja */ + } else { + if (inp->inp_flags & INP_IPV6) {/* v4 to v6 socket */ + CREATE_IPV6_MAPPED(inp->inp_laddr6, + ti->ti_dst.s_addr); + } else { +#endif /* INET6 */ + inp->inp_laddr = ti->ti_dst; + inp->inp_options = ip_srcroute(); +#if INET6 + } + }; +#endif /* INET6 */ in_pcbrehash(inp); -#if BSD>=43 - inp->inp_options = ip_srcroute(); -#endif tp = intotcpcb(inp); tp->t_state = TCPS_LISTEN; @@ -611,7 +758,7 @@ findpcb: #ifdef TCP_SACK if (!tp->sack_disable) - tcp_del_sackholes(tp, ti); /* Delete stale SACK holes */ + tcp_del_sackholes(tp, th); /* Delete stale SACK holes */ #endif /* TCP_SACK */ /* @@ -619,13 +766,13 @@ findpcb: * else do it below (after getting remote address). */ if (optp && tp->t_state != TCPS_LISTEN) - tcp_dooptions(tp, optp, optlen, ti, + tcp_dooptions(tp, optp, optlen, th, &ts_present, &ts_val, &ts_ecr); #ifdef TCP_SACK if (!tp->sack_disable) { - tp->rcv_laststart = ti->ti_seq; /* last rec'vd segment*/ - tp->rcv_lastend = ti->ti_seq + ti->ti_len; + tp->rcv_laststart = th->th_seq; /* last rec'vd segment*/ + tp->rcv_lastend = th->th_seq + tlen; } #endif /* TCP_SACK */ /* @@ -645,7 +792,7 @@ findpcb: if (tp->t_state == TCPS_ESTABLISHED && (tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) == TH_ACK && (!ts_present || TSTMP_GEQ(ts_val, tp->ts_recent)) && - ti->ti_seq == tp->rcv_nxt && + th->th_seq == tp->rcv_nxt && tiwin && tiwin == tp->snd_wnd && tp->snd_nxt == tp->snd_max) { @@ -654,14 +801,14 @@ findpcb: * record the timestamp. * Fix from Braden, see Stevens p. 870 */ - if (ts_present && SEQ_LEQ(ti->ti_seq, tp->last_ack_sent)) { + if (ts_present && SEQ_LEQ(th->th_seq, tp->last_ack_sent)) { tp->ts_recent_age = tcp_now; tp->ts_recent = ts_val; } - if (ti->ti_len == 0) { - if (SEQ_GT(ti->ti_ack, tp->snd_una) && - SEQ_LEQ(ti->ti_ack, tp->snd_max) && + if (tlen == 0) { + if (SEQ_GT(th->th_ack, tp->snd_una) && + SEQ_LEQ(th->th_ack, tp->snd_max) && tp->snd_cwnd >= tp->snd_wnd && tp->t_dupacks == 0) { /* @@ -671,13 +818,13 @@ findpcb: if (ts_present) tcp_xmit_timer(tp, tcp_now-ts_ecr+1); else if (tp->t_rtt && - SEQ_GT(ti->ti_ack, tp->t_rtseq)) + SEQ_GT(th->th_ack, tp->t_rtseq)) tcp_xmit_timer(tp, tp->t_rtt); - acked = ti->ti_ack - tp->snd_una; + acked = th->th_ack - tp->snd_una; tcpstat.tcps_rcvackpack++; tcpstat.tcps_rcvackbyte += acked; sbdrop(&so->so_snd, acked); - tp->snd_una = ti->ti_ack; + tp->snd_una = th->th_ack; #if defined(TCP_SACK) && defined(TCP_FACK) tp->snd_fack = tp->snd_una; tp->retran_data = 0; @@ -704,11 +851,11 @@ findpcb: (void) tcp_output(tp); return; } - } else if (ti->ti_ack == tp->snd_una && + } else if (th->th_ack == tp->snd_una && tp->segq.lh_first == NULL && - ti->ti_len <= sbspace(&so->so_rcv)) { + tlen <= sbspace(&so->so_rcv)) { /* - * this is a pure, in-sequence data packet + * This is a pure, in-sequence data packet * with nothing on the reassembly queue and * we have enough buffer space to take it. */ @@ -718,18 +865,18 @@ findpcb: tcp_clean_sackreport(tp); #endif /* TCP_SACK */ ++tcpstat.tcps_preddat; - tp->rcv_nxt += ti->ti_len; + tp->rcv_nxt += tlen; tcpstat.tcps_rcvpack++; - tcpstat.tcps_rcvbyte += ti->ti_len; + tcpstat.tcps_rcvbyte += tlen; /* * Drop TCP, IP headers and TCP options then add data * to socket buffer. */ - m->m_data += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr); - m->m_len -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr); + m->m_data += iphlen + off; + m->m_len -= iphlen + off; sbappend(&so->so_rcv, m); sorwakeup(so); - if (ti->ti_flags & TH_PUSH) + if (th->th_flags & TH_PUSH) tp->t_flags |= TF_ACKNOW; else tp->t_flags |= TF_DELACK; @@ -740,8 +887,8 @@ findpcb: /* * Drop TCP, IP headers and TCP options. */ - m->m_data += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr); - m->m_len -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr); + m->m_data += iphlen + off; + m->m_len -= iphlen + off; /* * Calculate amount of space in receive window, @@ -776,6 +923,9 @@ findpcb: case TCPS_LISTEN: { struct mbuf *am; register struct sockaddr_in *sin; +#ifdef INET6 + register struct sockaddr_in6 *sin6; +#endif /* INET6 */ if (tiflags & TH_RST) goto drop; @@ -783,9 +933,19 @@ findpcb: goto dropwithreset; if ((tiflags & TH_SYN) == 0) goto drop; - if ((ti->ti_dport == ti->ti_sport) && - (ti->ti_dst.s_addr == ti->ti_src.s_addr)) - goto drop; + if (th->th_dport == th->th_sport) { +#ifdef INET6 + if (is_ipv6) { + if (IN6_ARE_ADDR_EQUAL(&ipv6->ipv6_src, &ipv6->ipv6_dst)) + goto drop; + } else { +#endif /* INET6 */ + if (ti->ti_dst.s_addr == ti->ti_src.s_addr) + goto drop; +#ifdef INET6 + } +#endif /* INET6 */ + } #ifdef TCPCOOKIE /* @@ -811,12 +971,91 @@ findpcb: * in_broadcast() should never return true on a received * packet with M_BCAST not set. */ - if (m->m_flags & (M_BCAST|M_MCAST) || - IN_MULTICAST(ti->ti_dst.s_addr)) + if (m->m_flags & (M_BCAST|M_MCAST)) + goto drop; +#ifdef INET6 + if (is_ipv6) { + /* XXX What about IPv6 Anycasting ?? :-( rja */ + if (IN6_IS_ADDR_MULTICAST(&ipv6->ipv6_dst)) + goto drop; + } else +#endif /* INET6 */ + if (IN_MULTICAST(ntohl(ti->ti_dst.s_addr))) goto drop; am = m_get(M_DONTWAIT, MT_SONAME); /* XXX */ if (am == NULL) goto drop; +#ifdef INET6 + if (is_ipv6) { + /* + * This is probably the place to set the tp->pf value. + * (Don't forget to do it in the v4 code as well!) + * + * Also, remember to blank out things like flowlabel, or + * set flowlabel for accepted sockets in v6. + * + * FURTHERMORE, this is PROBABLY the place where the whole + * business of key munging is set up for passive + * connections. + */ + am->m_len = sizeof(struct sockaddr_in6); + sin6 = mtod(am, struct sockaddr_in6 *); + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(struct sockaddr_in6); + sin6->sin6_addr = ipv6->ipv6_src; + sin6->sin6_port = th->th_sport; + sin6->sin6_flowinfo = htonl(0x0fffffff) & + inp->inp_ipv6.ipv6_versfl; + laddr6 = inp->inp_laddr6; + if (IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6)) + inp->inp_laddr6 = ipv6->ipv6_dst; + /* This is a good optimization. */ + if (in6_pcbconnect(inp, am)) { + inp->inp_laddr6 = laddr6; + (void) m_free(am); + goto drop; + } /* endif in6_pcbconnect() */ + tp->pf = PF_INET6; + } else { + /* + * Letting v4 incoming datagrams to reach valid + * PF_INET6 sockets causes some overhead here. + */ + if (inp->inp_flags & INP_IPV6) { + if (!(inp->inp_flags & (INP_IPV6_UNDEC|INP_IPV6_MAPPED))) { + (void) m_free(am); + goto drop; + } + + am->m_len = sizeof(struct sockaddr_in6); + + sin6 = mtod(am, struct sockaddr_in6 *); + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(*sin6); + CREATE_IPV6_MAPPED(sin6->sin6_addr, ti->ti_src.s_addr); + sin6->sin6_port = th->th_sport; + sin6->sin6_flowinfo = 0; + + laddr6 = inp->inp_laddr6; + if (inp->inp_laddr.s_addr == INADDR_ANY) + CREATE_IPV6_MAPPED(inp->inp_laddr6, ti->ti_dst.s_addr); + + /* + * The pcb initially has the v6 default hoplimit + * set. We're sending v4 packets so we need to set + * the v4 ttl and tos. + */ + inp->inp_ip.ip_ttl = ip_defttl; + inp->inp_ip.ip_tos = 0; + + if (in6_pcbconnect(inp, am)) { + inp->inp_laddr6 = laddr6; + (void) m_freem(am); + goto drop; + } + tp->pf = PF_INET; + } else { +#endif /* INET6 */ am->m_len = sizeof (struct sockaddr_in); sin = mtod(am, struct sockaddr_in *); sin->sin_family = AF_INET; @@ -833,6 +1072,10 @@ findpcb: goto drop; } (void) m_free(am); +#ifdef INET6 + } /* if (inp->inp_flags & INP_IPV6) */ + } /* if (is_ipv6) */ +#endif /* INET6 */ tp->t_template = tcp_template(tp); if (tp->t_template == 0) { tp = tcp_drop(tp, ENOBUFS); @@ -840,7 +1083,7 @@ findpcb: goto drop; } if (optp) - tcp_dooptions(tp, optp, optlen, ti, + tcp_dooptions(tp, optp, optlen, th, &ts_present, &ts_val, &ts_ecr); #ifdef TCP_SACK /* @@ -862,7 +1105,7 @@ findpcb: #else /* TCP_COMPAT_42 */ tcp_iss += arc4random() % (TCP_ISSINCR / 2) + 1; #endif /* !TCP_COMPAT_42 */ - tp->irs = ti->ti_seq; + tp->irs = th->th_seq; tcp_sendseqinit(tp); #if defined (TCP_SACK) || defined (TCP_NEWRENO) tp->snd_last = tp->snd_una; @@ -893,8 +1136,8 @@ findpcb: tcpstat.tcps_badsyn++; goto dropwithreset; } - if (SEQ_LEQ(ti->ti_ack, tp->snd_una) || - SEQ_GT(ti->ti_ack, tp->snd_max)) + if (SEQ_LEQ(th->th_ack, tp->snd_una) || + SEQ_GT(th->th_ack, tp->snd_max)) goto dropwithreset; } break; @@ -913,8 +1156,8 @@ findpcb: */ case TCPS_SYN_SENT: if ((tiflags & TH_ACK) && - (SEQ_LEQ(ti->ti_ack, tp->iss) || - SEQ_GT(ti->ti_ack, tp->snd_max))) + (SEQ_LEQ(th->th_ack, tp->iss) || + SEQ_GT(th->th_ack, tp->snd_max))) goto dropwithreset; if (tiflags & TH_RST) { if (tiflags & TH_ACK) @@ -924,12 +1167,12 @@ findpcb: if ((tiflags & TH_SYN) == 0) goto drop; if (tiflags & TH_ACK) { - tp->snd_una = ti->ti_ack; + tp->snd_una = th->th_ack; if (SEQ_LT(tp->snd_nxt, tp->snd_una)) tp->snd_nxt = tp->snd_una; } tp->t_timer[TCPT_REXMT] = 0; - tp->irs = ti->ti_seq; + tp->irs = th->th_seq; tcp_rcvseqinit(tp); tp->t_flags |= TF_ACKNOW; #ifdef TCP_SACK @@ -952,8 +1195,8 @@ findpcb: tp->snd_scale = tp->requested_s_scale; tp->rcv_scale = tp->request_r_scale; } - (void) tcp_reass(tp, (struct tcpiphdr *)0, - (struct mbuf *)0); + (void) tcp_reass(tp, (struct tcphdr *)0, + (struct mbuf *)0, &tlen); /* * if we didn't have to retransmit the SYN, * use its rtt as our initial srtt & rtt var. @@ -978,17 +1221,17 @@ trimthenstep6: * If data, trim to stay within window, * dropping FIN if necessary. */ - ti->ti_seq++; - if (ti->ti_len > tp->rcv_wnd) { - todrop = ti->ti_len - tp->rcv_wnd; + th->th_seq++; + if (tlen > tp->rcv_wnd) { + todrop = tlen - tp->rcv_wnd; m_adj(m, -todrop); - ti->ti_len = tp->rcv_wnd; + tlen = tp->rcv_wnd; tiflags &= ~TH_FIN; tcpstat.tcps_rcvpackafterwin++; tcpstat.tcps_rcvbyteafterwin += todrop; } - tp->snd_wl1 = ti->ti_seq - 1; - tp->rcv_up = ti->ti_seq; + tp->snd_wl1 = th->th_seq - 1; + tp->rcv_up = th->th_seq; goto step6; } @@ -1021,25 +1264,25 @@ trimthenstep6: tp->ts_recent = 0; } else { tcpstat.tcps_rcvduppack++; - tcpstat.tcps_rcvdupbyte += ti->ti_len; + tcpstat.tcps_rcvdupbyte += tlen; tcpstat.tcps_pawsdrop++; goto dropafterack; } } - todrop = tp->rcv_nxt - ti->ti_seq; + todrop = tp->rcv_nxt - th->th_seq; if (todrop > 0) { if (tiflags & TH_SYN) { tiflags &= ~TH_SYN; - ti->ti_seq++; - if (ti->ti_urp > 1) - ti->ti_urp--; + th->th_seq++; + if (th->th_urp > 1) + th->th_urp--; else tiflags &= ~TH_URG; todrop--; } - if (todrop >= ti->ti_len || - (todrop == ti->ti_len && (tiflags & TH_FIN) == 0)) { + if (todrop >= tlen || + (todrop == tlen && (tiflags & TH_FIN) == 0)) { /* * Any valid FIN must be to the left of the * window. At this point, FIN must be a @@ -1051,20 +1294,20 @@ trimthenstep6: * but keep on processing for RST or ACK. */ tp->t_flags |= TF_ACKNOW; - tcpstat.tcps_rcvdupbyte += todrop = ti->ti_len; + tcpstat.tcps_rcvdupbyte += todrop = tlen; tcpstat.tcps_rcvduppack++; } else { tcpstat.tcps_rcvpartduppack++; tcpstat.tcps_rcvpartdupbyte += todrop; } m_adj(m, todrop); - ti->ti_seq += todrop; - ti->ti_len -= todrop; - if (ti->ti_urp > todrop) - ti->ti_urp -= todrop; + th->th_seq += todrop; + tlen -= todrop; + if (th->th_urp > todrop) + th->th_urp -= todrop; else { tiflags &= ~TH_URG; - ti->ti_urp = 0; + th->th_urp = 0; } } @@ -1073,7 +1316,7 @@ trimthenstep6: * user processes are gone, then RST the other end. */ if ((so->so_state & SS_NOFDREF) && - tp->t_state > TCPS_CLOSE_WAIT && ti->ti_len) { + tp->t_state > TCPS_CLOSE_WAIT && tlen) { tp = tcp_close(tp); tcpstat.tcps_rcvafterclose++; goto dropwithreset; @@ -1083,11 +1326,11 @@ trimthenstep6: * If segment ends after window, drop trailing data * (and PUSH and FIN); if nothing left, just ACK. */ - todrop = (ti->ti_seq+ti->ti_len) - (tp->rcv_nxt+tp->rcv_wnd); + todrop = (th->th_seq + tlen) - (tp->rcv_nxt+tp->rcv_wnd); if (todrop > 0) { tcpstat.tcps_rcvpackafterwin++; - if (todrop >= ti->ti_len) { - tcpstat.tcps_rcvbyteafterwin += ti->ti_len; + if (todrop >= tlen) { + tcpstat.tcps_rcvbyteafterwin += tlen; /* * If a new connection request is received * while in TIME_WAIT, drop the old connection @@ -1096,7 +1339,7 @@ trimthenstep6: */ if (tiflags & TH_SYN && tp->t_state == TCPS_TIME_WAIT && - SEQ_GT(ti->ti_seq, tp->rcv_nxt)) { + SEQ_GT(th->th_seq, tp->rcv_nxt)) { iss = tp->rcv_nxt + TCP_ISSINCR; tp = tcp_close(tp); goto findpcb; @@ -1108,7 +1351,7 @@ trimthenstep6: * remember to ack. Otherwise, drop segment * and ack. */ - if (tp->rcv_wnd == 0 && ti->ti_seq == tp->rcv_nxt) { + if (tp->rcv_wnd == 0 && th->th_seq == tp->rcv_nxt) { tp->t_flags |= TF_ACKNOW; tcpstat.tcps_rcvwinprobe++; } else @@ -1116,7 +1359,7 @@ trimthenstep6: } else tcpstat.tcps_rcvbyteafterwin += todrop; m_adj(m, -todrop); - ti->ti_len -= todrop; + tlen -= todrop; tiflags &= ~(TH_PUSH|TH_FIN); } @@ -1126,7 +1369,7 @@ trimthenstep6: * Fix from Braden, see Stevens p. 870 */ if (ts_present && TSTMP_GEQ(ts_val, tp->ts_recent) && - SEQ_LEQ(ti->ti_seq, tp->last_ack_sent)) { + SEQ_LEQ(th->th_seq, tp->last_ack_sent)) { tp->ts_recent_age = tcp_now; tp->ts_recent = ts_val; } @@ -1142,10 +1385,9 @@ trimthenstep6: * Close the tcb. */ if (tiflags & TH_RST) { - - if ((ti->ti_seq != tp->rcv_nxt) && - (ti->ti_ack && ((SEQ_GT(ti->ti_ack, tp->snd_nxt) || - SEQ_LT(ti->ti_ack, (tp->snd_nxt - tp->snd_wnd)))))) + if ((th->th_seq != tp->rcv_nxt) && + (th->th_ack && ((SEQ_GT(th->th_ack, tp->snd_nxt) || + SEQ_LT(th->th_ack, (tp->snd_nxt - tp->snd_wnd)))))) goto drop; switch (tp->t_state) { @@ -1206,8 +1448,9 @@ trimthenstep6: tp->snd_scale = tp->requested_s_scale; tp->rcv_scale = tp->request_r_scale; } - (void) tcp_reass(tp, (struct tcpiphdr *)0, (struct mbuf *)0); - tp->snd_wl1 = ti->ti_seq - 1; + (void) tcp_reass(tp, (struct tcphdr *)0, (struct mbuf *)0, + &tlen); + tp->snd_wl1 = th->th_seq - 1; /* fall into ... */ /* @@ -1225,8 +1468,7 @@ trimthenstep6: case TCPS_CLOSING: case TCPS_LAST_ACK: case TCPS_TIME_WAIT: - - if (SEQ_LEQ(ti->ti_ack, tp->snd_una)) { + if (SEQ_LEQ(th->th_ack, tp->snd_una)) { /* * Duplicate/old ACK processing. * Increments t_dupacks: @@ -1239,7 +1481,7 @@ trimthenstep6: * Window shrinks * Old ACK */ - if (ti->ti_len) + if (tlen) break; /* * If we get an old ACK, there is probably packet @@ -1247,7 +1489,7 @@ trimthenstep6: * t_dupacks so that we are less agressive in * doing a fast retransmit. */ - if (ti->ti_ack != tp->snd_una) { + if (th->th_ack != tp->snd_una) { tp->t_dupacks = 0; break; } @@ -1297,7 +1539,7 @@ trimthenstep6: 2 / tp->t_maxseg; #if defined(TCP_SACK) || defined(TCP_NEWRENO) - if (SEQ_LT(ti->ti_ack, tp->snd_last)){ + if (SEQ_LT(th->th_ack, tp->snd_last)){ /* * False fast retx after * timeout. Do not cut window. @@ -1353,7 +1595,7 @@ trimthenstep6: #endif /* TCP_SACK */ tp->t_timer[TCPT_REXMT] = 0; tp->t_rtt = 0; - tp->snd_nxt = ti->ti_ack; + tp->snd_nxt = th->th_ack; tp->snd_cwnd = tp->t_maxseg; tcpstat.tcps_sndrexmitfast++; (void) tcp_output(tp); @@ -1395,7 +1637,7 @@ trimthenstep6: * for the other side's cached packets, retract it. */ #ifdef TCP_NEWRENO - if (tp->t_dupacks >= tcprexmtthresh && !tcp_newreno(tp, ti)) { + if (tp->t_dupacks >= tcprexmtthresh && !tcp_newreno(tp, th)) { /* Out of fast recovery */ tp->snd_cwnd = tp->snd_ssthresh; /* @@ -1404,17 +1646,17 @@ trimthenstep6: * would be inclined to send a burst, better to do * it via the slow start mechanism. */ - if (tcp_seq_subtract(tp->snd_max, ti->ti_ack) < + if (tcp_seq_subtract(tp->snd_max, th->th_ack) < tp->snd_ssthresh) tp->snd_cwnd = tcp_seq_subtract(tp->snd_max, - ti->ti_ack) + tp->t_maxseg; + th->th_ack) + tp->t_maxseg; tp->t_dupacks = 0; } #elif defined(TCP_SACK) if (!tp->sack_disable) { if (tp->t_dupacks >= tcprexmtthresh) { /* Check for a partial ACK */ - if (tcp_sack_partialack(tp, ti)) { + if (tcp_sack_partialack(tp, th)) { #if defined(TCP_SACK) && defined(TCP_FACK) /* Force call to tcp_output */ if (tp->snd_awnd < tp->snd_cwnd) @@ -1427,27 +1669,27 @@ trimthenstep6: /* Out of fast recovery */ tp->snd_cwnd = tp->snd_ssthresh; if (tcp_seq_subtract(tp->snd_max, - ti->ti_ack) < tp->snd_ssthresh) + th->th_ack) < tp->snd_ssthresh) tp->snd_cwnd = tcp_seq_subtract(tp->snd_max, - ti->ti_ack) + tp->t_maxseg; + th->th_ack) + tp->t_maxseg; tp->t_dupacks = 0; #if defined(TCP_SACK) && defined(TCP_FACK) - if (SEQ_GT(ti->ti_ack, tp->snd_fack)) - tp->snd_fack = ti->ti_ack; + if (SEQ_GT(th->th_ack, tp->snd_fack)) + tp->snd_fack = th->th_ack; #endif /* TCP_FACK */ } } } else { if (tp->t_dupacks >= tcprexmtthresh && - !tcp_newreno(tp, ti)) { + !tcp_newreno(tp, th)) { /* Out of fast recovery */ tp->snd_cwnd = tp->snd_ssthresh; - if (tcp_seq_subtract(tp->snd_max, ti->ti_ack) < + if (tcp_seq_subtract(tp->snd_max, th->th_ack) < tp->snd_ssthresh) tp->snd_cwnd = tcp_seq_subtract(tp->snd_max, - ti->ti_ack) + tp->t_maxseg; + th->th_ack) + tp->t_maxseg; tp->t_dupacks = 0; } } @@ -1457,11 +1699,11 @@ trimthenstep6: tp->snd_cwnd = tp->snd_ssthresh; tp->t_dupacks = 0; #endif - if (SEQ_GT(ti->ti_ack, tp->snd_max)) { + if (SEQ_GT(th->th_ack, tp->snd_max)) { tcpstat.tcps_rcvacktoomuch++; goto dropafterack; } - acked = ti->ti_ack - tp->snd_una; + acked = th->th_ack - tp->snd_una; tcpstat.tcps_rcvackpack++; tcpstat.tcps_rcvackbyte += acked; @@ -1476,7 +1718,7 @@ trimthenstep6: */ if (ts_present) tcp_xmit_timer(tp, tcp_now-ts_ecr+1); - else if (tp->t_rtt && SEQ_GT(ti->ti_ack, tp->t_rtseq)) + else if (tp->t_rtt && SEQ_GT(th->th_ack, tp->t_rtseq)) tcp_xmit_timer(tp,tp->t_rtt); /* @@ -1485,7 +1727,7 @@ trimthenstep6: * If there is more data to be acked, restart retransmit * timer, using current (possibly backed-off) value. */ - if (ti->ti_ack == tp->snd_max) { + if (th->th_ack == tp->snd_max) { tp->t_timer[TCPT_REXMT] = 0; needoutput = 1; } else if (tp->t_timer[TCPT_PERSIST] == 0) @@ -1504,7 +1746,7 @@ trimthenstep6: if (cw > tp->snd_ssthresh) incr = incr * incr / cw; #if defined (TCP_NEWRENO) || defined (TCP_SACK) - if (SEQ_GEQ(ti->ti_ack, tp->snd_last)) + if (SEQ_GEQ(th->th_ack, tp->snd_last)) #endif tp->snd_cwnd = min(cw + incr, TCP_MAXWIN<<tp->snd_scale); } @@ -1519,7 +1761,7 @@ trimthenstep6: } if (sb_notify(&so->so_snd)) sowwakeup(so); - tp->snd_una = ti->ti_ack; + tp->snd_una = th->th_ack; if (SEQ_LT(tp->snd_nxt, tp->snd_una)) tp->snd_nxt = tp->snd_una; #if defined (TCP_SACK) && defined (TCP_FACK) @@ -1595,16 +1837,16 @@ step6: * Update window information. * Don't look at window if no ACK: TAC's send garbage on first SYN. */ - if (((tiflags & TH_ACK) && SEQ_LT(tp->snd_wl1, ti->ti_seq)) || - (tp->snd_wl1 == ti->ti_seq && SEQ_LT(tp->snd_wl2, ti->ti_ack)) || - (tp->snd_wl2 == ti->ti_ack && tiwin > tp->snd_wnd)) { + if (((tiflags & TH_ACK) && SEQ_LT(tp->snd_wl1, th->th_seq)) || + (tp->snd_wl1 == th->th_seq && SEQ_LT(tp->snd_wl2, th->th_ack)) || + (tp->snd_wl2 == th->th_ack && tiwin > tp->snd_wnd)) { /* keep track of pure window updates */ - if (ti->ti_len == 0 && - tp->snd_wl2 == ti->ti_ack && tiwin > tp->snd_wnd) + if (tlen == 0 && + tp->snd_wl2 == th->th_ack && tiwin > tp->snd_wnd) tcpstat.tcps_rcvwinupd++; tp->snd_wnd = tiwin; - tp->snd_wl1 = ti->ti_seq; - tp->snd_wl2 = ti->ti_ack; + tp->snd_wl1 = th->th_seq; + tp->snd_wl2 = th->th_ack; if (tp->snd_wnd > tp->max_sndwnd) tp->max_sndwnd = tp->snd_wnd; needoutput = 1; @@ -1613,7 +1855,7 @@ step6: /* * Process segments with URG. */ - if ((tiflags & TH_URG) && ti->ti_urp && + if ((tiflags & TH_URG) && th->th_urp && TCPS_HAVERCVDFIN(tp->t_state) == 0) { /* * This is a kludge, but if we receive and accept @@ -1621,8 +1863,8 @@ step6: * soreceive. It's hard to imagine someone * actually wanting to send this much urgent data. */ - if (ti->ti_urp + so->so_rcv.sb_cc > sb_max) { - ti->ti_urp = 0; /* XXX */ + if (th->th_urp + so->so_rcv.sb_cc > sb_max) { + th->th_urp = 0; /* XXX */ tiflags &= ~TH_URG; /* XXX */ goto dodata; /* XXX */ } @@ -1640,8 +1882,8 @@ step6: * of data past the urgent section as the original * spec states (in one of two places). */ - if (SEQ_GT(ti->ti_seq+ti->ti_urp, tp->rcv_up)) { - tp->rcv_up = ti->ti_seq + ti->ti_urp; + if (SEQ_GT(th->th_seq+th->th_urp, tp->rcv_up)) { + tp->rcv_up = th->th_seq + th->th_urp; so->so_oobmark = so->so_rcv.sb_cc + (tp->rcv_up - tp->rcv_nxt) - 1; if (so->so_oobmark == 0) @@ -1655,12 +1897,12 @@ step6: * but if two URG's are pending at once, some out-of-band * data may creep in... ick. */ - if (ti->ti_urp <= (u_int16_t) ti->ti_len + if (th->th_urp <= (u_int16_t) tlen #ifdef SO_OOBINLINE && (so->so_options & SO_OOBINLINE) == 0 #endif ) - tcp_pulloutofband(so, ti, m); + tcp_pulloutofband(so, th->th_urp, m); /* XXX? */ } else /* * If no out of band data is expected, @@ -1679,19 +1921,41 @@ dodata: /* XXX */ * case PRU_RCVD). If a FIN has already been received on this * connection then we just ignore the text. */ - if ((ti->ti_len || (tiflags & TH_FIN)) && + if ((tlen || (tiflags & TH_FIN)) && TCPS_HAVERCVDFIN(tp->t_state) == 0) { - TCP_REASS(tp, ti, m, so, tiflags); + if (th->th_seq == tp->rcv_nxt && tp->segq.lh_first == NULL && + tp->t_state == TCPS_ESTABLISHED) { + if (th->th_flags & TH_PUSH) + tp->t_flags |= TF_ACKNOW; + else + tp->t_flags |= TF_DELACK; + (tp)->rcv_nxt += tlen; + tiflags = th->th_flags & TH_FIN; + tcpstat.tcps_rcvpack++; + tcpstat.tcps_rcvbyte += tlen; + sbappend(&so->so_rcv, m); + sorwakeup(so); + } else { + tcp_reass(tp, th, m, &tlen); + tp->t_flags |= TF_ACKNOW; + } #ifdef TCP_SACK if (!tp->sack_disable) tcp_update_sack_list(tp); #endif + + /* + * variable len never referenced again in modern BSD, + * so why bother computing it ?? + */ +#if 0 /* * Note the amount of data that peer has sent into * our window, in order to estimate the sender's * buffer size. */ len = so->so_rcv.sb_hiwat - (tp->rcv_adv - tp->rcv_nxt); +#endif /* 0 */ } else { m_freem(m); tiflags &= ~TH_FIN; @@ -1745,8 +2009,14 @@ dodata: /* XXX */ break; } } - if (so->so_options & SO_DEBUG) - tcp_trace(TA_INPUT, ostate, tp, &tcp_saveti, 0); + if (so->so_options & SO_DEBUG) { +#ifdef INET6 + if (tp->pf == PF_INET6) + tcp_trace(TA_INPUT, ostate, tp, (struct tcpiphdr *) &tcp_saveti6, 0, tlen); + else +#endif /* INET6 */ + tcp_trace(TA_INPUT, ostate, tp, &tcp_saveti, 0, tlen); + } /* * Return any desired output. @@ -1785,15 +2055,27 @@ dropwithreset: * Make ACK acceptable to originator of segment. * Don't bother to respond if destination was broadcast/multicast. */ - if ((tiflags & TH_RST) || m->m_flags & (M_BCAST|M_MCAST) || - IN_MULTICAST(ti->ti_dst.s_addr)) - goto drop; + if ((tiflags & TH_RST) || m->m_flags & (M_BCAST|M_MCAST)) + goto drop; +#ifdef INET6 + if (is_ipv6) { + /* For following calls to tcp_respond */ + ti = mtod(m, struct tcpiphdr *); + if (IN6_IS_ADDR_MULTICAST(&ipv6->ipv6_dst)) + goto drop; + } else { +#endif /* INET6 */ + if (IN_MULTICAST(ntohl(ti->ti_dst.s_addr))) + goto drop; +#ifdef INET6 + } +#endif /* INET6 */ if (tiflags & TH_ACK) - tcp_respond(tp, ti, m, (tcp_seq)0, ti->ti_ack, TH_RST); + tcp_respond(tp, ti, m, (tcp_seq)0, th->th_ack, TH_RST); else { if (tiflags & TH_SYN) - ti->ti_len++; - tcp_respond(tp, ti, m, ti->ti_seq+ti->ti_len, (tcp_seq)0, + tlen++; + tcp_respond(tp, ti, m, th->th_seq+tlen, (tcp_seq)0, TH_RST|TH_ACK); } /* destroy temporarily created socket */ @@ -1805,8 +2087,15 @@ drop: /* * Drop space held by incoming segment and return. */ - if (tp && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) - tcp_trace(TA_DROP, ostate, tp, &tcp_saveti, 0); + if (tp && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) { +#ifdef INET6 + if (tp->pf == PF_INET6) + tcp_trace(TA_DROP, ostate, tp, (struct tcpiphdr *)&tcp_saveti6, 0, tlen); + else +#endif /* INET6 */ + tcp_trace(TA_DROP, ostate, tp, &tcp_saveti, 0, tlen); + } + m_freem(m); /* destroy temporarily created socket */ if (dropsocket) @@ -1816,11 +2105,11 @@ drop: } void -tcp_dooptions(tp, cp, cnt, ti, ts_present, ts_val, ts_ecr) +tcp_dooptions(tp, cp, cnt, th, ts_present, ts_val, ts_ecr) struct tcpcb *tp; u_char *cp; int cnt; - struct tcpiphdr *ti; + struct tcphdr *th; int *ts_present; u_int32_t *ts_val, *ts_ecr; { @@ -1846,7 +2135,7 @@ tcp_dooptions(tp, cp, cnt, ti, ts_present, ts_val, ts_ecr) case TCPOPT_MAXSEG: if (optlen != TCPOLEN_MAXSEG) continue; - if (!(ti->ti_flags & TH_SYN)) + if (!(th->th_flags & TH_SYN)) continue; bcopy((char *) cp + 2, (char *) &mss, sizeof(mss)); NTOHS(mss); @@ -1855,7 +2144,7 @@ tcp_dooptions(tp, cp, cnt, ti, ts_present, ts_val, ts_ecr) case TCPOPT_WINDOW: if (optlen != TCPOLEN_WINDOW) continue; - if (!(ti->ti_flags & TH_SYN)) + if (!(th->th_flags & TH_SYN)) continue; tp->t_flags |= TF_RCVD_SCALE; tp->requested_s_scale = min(cp[2], TCP_MAX_WINSHIFT); @@ -1874,7 +2163,7 @@ tcp_dooptions(tp, cp, cnt, ti, ts_present, ts_val, ts_ecr) * A timestamp received in a SYN makes * it ok to send timestamp requests and replies. */ - if (ti->ti_flags & TH_SYN) { + if (th->th_flags & TH_SYN) { tp->t_flags |= TF_RCVD_TSTMP; tp->ts_recent = *ts_val; tp->ts_recent_age = tcp_now; @@ -1884,20 +2173,20 @@ tcp_dooptions(tp, cp, cnt, ti, ts_present, ts_val, ts_ecr) #ifdef TCP_SACK case TCPOPT_SACK_PERMITTED: if (tp->sack_disable || optlen!=TCPOLEN_SACK_PERMITTED) - continue; - if (ti->ti_flags & TH_SYN) + continue; + if (th->th_flags & TH_SYN) /* MUST only be set on SYN */ tp->t_flags |= TF_SACK_PERMIT; break; case TCPOPT_SACK: - if (tcp_sack_option(tp, ti, cp, optlen)) + if (tcp_sack_option(tp, th, cp, optlen)) continue; break; #endif } } /* Update t_maxopd and t_maxseg after all options are processed */ - if (ti->ti_flags & TH_SYN) + if (th->th_flags & TH_SYN) (void) tcp_mss(tp, mss); /* sets t_maxseg */ } @@ -2012,10 +2301,10 @@ tcp_update_sack_list(tp) * and 0 otherwise, if the option was fine. tp->snd_holes is an ordered list * of holes (oldest to newest, in terms of the sequence space). */ -int -tcp_sack_option(tp, ti, cp, optlen) +int +tcp_sack_option(tp, th, cp, optlen) struct tcpcb *tp; - struct tcpiphdr *ti; + struct tcphdr *th; u_char *cp; int optlen; { @@ -2058,7 +2347,7 @@ tcp_sack_option(tp, ti, cp, optlen) tp->snd_holes = (struct sackhole *) malloc(sizeof(struct sackhole), M_PCB, M_NOWAIT); cur = tp->snd_holes; - cur->start = ti->ti_ack; + cur->start = th->th_ack; cur->end = sack.start; cur->rxmit = cur->start; cur->next = 0; @@ -2223,13 +2512,13 @@ tcp_sack_option(tp, ti, cp, optlen) * tcp_dooptions(), will fix up the hole. */ void -tcp_del_sackholes(tp, ti) +tcp_del_sackholes(tp, th) struct tcpcb *tp; - struct tcpiphdr *ti; + struct tcpiphdr *th; { if (!tp->sack_disable && tp->t_state != TCPS_LISTEN) { /* max because this could be an older ack just arrived */ - tcp_seq lastack = max(ti->ti_ack, tp->snd_una); + tcp_seq lastack = max(th->th_ack, tp->snd_una); struct sackhole *cur = tp->snd_holes; struct sackhole *prev = cur; while (cur) @@ -2268,11 +2557,11 @@ tcp_clean_sackreport(tp) * If the ack advances at least to tp->snd_last, return 0. */ int -tcp_sack_partialack(tp, ti) +tcp_sack_partialack(tp, th) struct tcpcb *tp; - struct tcpiphdr *ti; + struct tcpiphdr *th; { - if (SEQ_LT(ti->ti_ack, tp->snd_last)) { + if (SEQ_LT(th->ti_ack, tp->snd_last)) { /* Turn off retx. timer (will start again next segment) */ tp->t_timer[TCPT_REXMT] = 0; tp->t_rtt = 0; @@ -2282,7 +2571,7 @@ tcp_sack_partialack(tp, ti) * fact that tp->snd_una has not been updated yet. In FACK * hold snd_cwnd constant during fast recovery. */ - tp->snd_cwnd -= (ti->ti_ack - tp->snd_una - tp->t_maxseg); + tp->snd_cwnd -= (th->th_ack - tp->snd_una - tp->t_maxseg); #endif return 1; } @@ -2297,12 +2586,12 @@ tcp_sack_partialack(tp, ti) * sequencing purposes. */ void -tcp_pulloutofband(so, ti, m) +tcp_pulloutofband(so, urgent, m) struct socket *so; - struct tcpiphdr *ti; + u_int urgent; register struct mbuf *m; { - int cnt = ti->ti_urp - 1; + int cnt = urgent - 1; while (cnt >= 0) { if (m->m_len > cnt) { @@ -2434,9 +2723,33 @@ tcp_mss(tp, offer) inp = tp->t_inpcb; ro = &inp->inp_route; + so = inp->inp_socket; if ((rt = ro->ro_rt) == (struct rtentry *)0) { /* No route yet, so try to acquire one */ +#ifdef INET6 + /* + * Get a new IPv6 route if an IPv6 destination, otherwise, get + * and IPv4 route (including those pesky IPv4-mapped addresses). + */ + bzero(ro,sizeof(struct route6)); + if (sotopf(so) == AF_INET6) { + if (IN6_IS_ADDR_V4MAPPED(&inp->inp_faddr6)) { + /* Get an IPv4 route. */ + ro->ro_dst.sa_family = AF_INET; + ro->ro_dst.sa_len = sizeof(ro->ro_dst); + ((struct sockaddr_in *) &ro->ro_dst)->sin_addr = + inp->inp_faddr; + rtalloc(ro); + } else { + ro->ro_dst.sa_family = AF_INET6; + ro->ro_dst.sa_len = sizeof(struct sockaddr_in6); + ((struct sockaddr_in6 *) &ro->ro_dst)->sin6_addr = + inp->inp_faddr6; + rtalloc(ro); + } + } else +#endif /* INET6 */ if (inp->inp_faddr.s_addr != INADDR_ANY) { ro->ro_dst.sa_family = AF_INET; ro->ro_dst.sa_len = sizeof(ro->ro_dst); @@ -2449,7 +2762,6 @@ tcp_mss(tp, offer) } } ifp = rt->rt_ifp; - so = inp->inp_socket; #ifdef RTV_MTU /* if route characteristics exist ... */ /* @@ -2480,11 +2792,32 @@ tcp_mss(tp, offer) * if there's an mtu associated with the route, use it */ if (rt->rt_rmx.rmx_mtu) +#ifdef INET6 + { + /* + * One may wish to lower MSS to take into account options, + * especially security-related options. + */ + if (tp->pf == AF_INET6) + mss = rt->rt_rmx.rmx_mtu - sizeof(struct tcpipv6hdr); + else +#endif /* INET6 */ mss = rt->rt_rmx.rmx_mtu - sizeof(struct tcpiphdr); +#ifdef INET6 + } +#endif /* INET6 */ else #endif /* RTV_MTU */ { - mss = ifp->if_mtu - sizeof(struct tcpiphdr); + /* + * ifp may be null and rmx_mtu may be zero in certain + * v6 cases (e.g., if ND wasn't able to resolve the + * destination host. + */ + mss = ifp ? ifp->if_mtu - sizeof(struct tcpiphdr) : 0; +#ifdef INET6 + if (tp->pf == AF_INET) +#endif /* INET6 */ if (!in_localaddr(inp->inp_faddr)) mss = min(mss, tcp_mssdflt); } @@ -2574,19 +2907,19 @@ tcp_mss(tp, offer) * be started again. If the ack advances at least to tp->snd_last, return 0. */ int -tcp_newreno(tp, ti) +tcp_newreno(tp, th) struct tcpcb *tp; -struct tcpiphdr *ti; +struct tcphdr *th; { - if (SEQ_LT(ti->ti_ack, tp->snd_last)) { + if (SEQ_LT(th->th_ack, tp->snd_last)) { tcp_seq onxt = tp->snd_nxt; tcp_seq ouna = tp->snd_una; /* snd_una not yet updated */ u_long ocwnd = tp->snd_cwnd; tp->t_timer[TCPT_REXMT] = 0; tp->t_rtt = 0; - tp->snd_nxt = ti->ti_ack; + tp->snd_nxt = th->th_ack; tp->snd_cwnd = tp->t_maxseg; - tp->snd_una = ti->ti_ack; + tp->snd_una = th->th_ack; (void) tcp_output(tp); tp->snd_cwnd = ocwnd; tp->snd_una = ouna; @@ -2596,7 +2929,7 @@ struct tcpiphdr *ti; * Partial window deflation. Relies on fact that tp->snd_una * not updated yet. */ - tp->snd_cwnd -= (ti->ti_ack - tp->snd_una - tp->t_maxseg); + tp->snd_cwnd -= (th->th_ack - tp->snd_una - tp->t_maxseg); return 1; } return 0; diff --git a/sys/netinet/tcp_output.c b/sys/netinet/tcp_output.c index 6e88e2dbc9b..8b3c5c57683 100644 --- a/sys/netinet/tcp_output.c +++ b/sys/netinet/tcp_output.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tcp_output.c,v 1.15 1998/11/25 05:44:37 millert Exp $ */ +/* $OpenBSD: tcp_output.c,v 1.16 1999/01/11 02:01:36 deraadt Exp $ */ /* $NetBSD: tcp_output.c,v 1.16 1997/06/03 16:17:09 kml Exp $ */ /* @@ -36,6 +36,18 @@ * @(#)tcp_output.c 8.3 (Berkeley) 12/30/93 */ +/* +%%% portions-copyright-nrl-95 +Portions of this software are Copyright 1995-1998 by Randall Atkinson, +Ronald Lee, Daniel McDonald, Bao Phan, and Chris Winters. All Rights +Reserved. All rights under this copyright have been assigned to the US +Naval Research Laboratory (NRL). The NRL Copyright Notice and License +Agreement Version 1.1 (January 17, 1995) applies to these portions of the +software. +You should have received a copy of the license with this software. If you +didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>. +*/ + #include <sys/param.h> #include <sys/systm.h> #include <sys/malloc.h> @@ -44,6 +56,7 @@ #include <sys/socket.h> #include <sys/socketvar.h> #include <sys/errno.h> +#include <sys/domain.h> #include <net/route.h> @@ -66,6 +79,10 @@ #include <netiso/tuba_table.h> #endif +#ifdef INET6 +#include <netinet6/tcpipv6.h> +#endif /* INET6 */ + #ifdef notyet extern struct mbuf *m_copypack(); #endif @@ -165,6 +182,10 @@ tcp_output(tp) int off, flags, error; register struct mbuf *m; register struct tcpiphdr *ti; + register struct tcphdr *th; +#ifdef INET6 + register struct tcpipv6hdr *ti6 = NULL; +#endif /* INET6 */ u_char opt[MAX_TCPOPTLEN]; unsigned int optlen, hdrlen; int idle, sendalot; @@ -419,7 +440,20 @@ send: * link header, i.e. * max_linkhdr + sizeof (struct tcpiphdr) + optlen <= MHLEN */ +#ifdef INET6 + /* + * For IPv6, this has changed to be: + * max_linkhdr + sizeof(struct tcphdr) + optlen + + * sizeof(struct ipv6) <= MHLEN + * This MIGHT be harder... + */ +#endif /* INET6 */ optlen = 0; +#ifdef INET6 + if (tp->pf == PF_INET6) /* if tp->pf is 0, then assume IPv4. */ + hdrlen = sizeof(struct tcphdr) + sizeof(struct ipv6); + else +#endif /* INET6 */ hdrlen = sizeof (struct tcpiphdr); if (flags & TH_SYN) { tp->snd_nxt = tp->iss; @@ -596,10 +630,31 @@ send: m->m_len = hdrlen; } m->m_pkthdr.rcvif = (struct ifnet *)0; - ti = mtod(m, struct tcpiphdr *); - if (tp->t_template == 0) - panic("tcp_output"); - bcopy((caddr_t)tp->t_template, (caddr_t)ti, sizeof (struct tcpiphdr)); +#ifdef INET6 + if (tp->pf == PF_INET6) { + ti6 = mtod(m, struct tcpipv6hdr *); + ti = NULL; + + if (!tp->t_template) + panic("tcp_output"); + + bcopy((caddr_t)tp->t_template, (caddr_t)ti6, + sizeof (struct tcpipv6hdr)); + + th = &ti6->ti6_t; + } else +#endif /* INET6 */ + { + ti = mtod(m, struct tcpiphdr *); + + if (tp->t_template == 0) + panic("tcp_output"); + + bcopy((caddr_t)tp->t_template, (caddr_t)ti, + sizeof (struct tcpiphdr)); + + th = &ti->ti_t; + }; /* * Fill in fields, remembering maximum advertised @@ -623,9 +678,10 @@ send: * (retransmit and persist are mutually exclusive...) */ if (len || (flags & (TH_SYN|TH_FIN)) || tp->t_timer[TCPT_PERSIST]) - ti->ti_seq = htonl(tp->snd_nxt); + th->th_seq = htonl(tp->snd_nxt); else - ti->ti_seq = htonl(tp->snd_max); + th->th_seq = htonl(tp->snd_max); + #ifdef TCP_SACK if (sack_rxmit) { /* @@ -635,7 +691,7 @@ send: */ if (sendalot) sendalot = 0; - ti->ti_seq = htonl(p->rxmit); + th->th_seq = htonl(p->rxmit); p->rxmit += len; #if defined(TCP_SACK) && defined(TCP_FACK) tp->retran_data += len; @@ -643,12 +699,13 @@ send: } #endif /* TCP_SACK */ - ti->ti_ack = htonl(tp->rcv_nxt); + th->th_ack = htonl(tp->rcv_nxt); if (optlen) { - bcopy((caddr_t)opt, (caddr_t)(ti + 1), optlen); - ti->ti_off = (sizeof (struct tcphdr) + optlen) >> 2; + bcopy((caddr_t)opt, (caddr_t)(th + 1), optlen); + th->th_off = (sizeof (struct tcphdr) + optlen) >> 2; } - ti->ti_flags = flags; + th->th_flags = flags; + /* * Calculate receive window. Don't shrink window, * but avoid silly window syndrome. @@ -661,13 +718,13 @@ send: win = (long)(tp->rcv_adv - tp->rcv_nxt); if (flags & TH_RST) win = 0; - ti->ti_win = htons((u_int16_t) (win>>tp->rcv_scale)); + th->th_win = htons((u_int16_t) (win>>tp->rcv_scale)); if (SEQ_GT(tp->snd_up, tp->snd_nxt)) { u_int32_t urp = tp->snd_up - tp->snd_nxt; if (urp > IP_MAXPACKET) urp = IP_MAXPACKET; - ti->ti_urp = htons((u_int16_t)urp); - ti->ti_flags |= TH_URG; + th->th_urp = htons((u_int16_t)urp); + th->th_flags |= TH_URG; } else /* * If no urgent pointer to send, then we pull @@ -681,10 +738,19 @@ send: * Put TCP length in extended header, and then * checksum extended header and data. */ - if (len + optlen) - ti->ti_len = htons((u_int16_t)(sizeof (struct tcphdr) + - optlen + len)); - ti->ti_sum = in_cksum(m, (int)(hdrlen + len)); +#ifdef INET6 + if (tp->pf == PF_INET6) { + th->th_sum = in6_cksum(m, IPPROTO_TCP, + sizeof(struct tcphdr) + optlen + len, + sizeof(struct ipv6)); + } else +#endif /* INET6 */ + { + if (len + optlen) + ti->ti_len = htons((u_int16_t)(sizeof (struct tcphdr) + + optlen + len)); + ti->ti_sum = in_cksum(m, (int)(hdrlen + len)); + } /* * In transmit state, time the transmission and arrange for @@ -762,7 +828,12 @@ send: * Trace. */ if (so->so_options & SO_DEBUG) - tcp_trace(TA_OUTPUT, tp->t_state, tp, ti, 0); +#if INET6 + tcp_trace(TA_OUTPUT, tp->t_state, tp, + (tp->pf == AF_INET6) ? (struct tcpiphdr *)ti6 : ti, 0, len); +#else + tcp_trace(TA_OUTPUT, tp->t_state, tp, ti, 0, len); +#endif /* * Fill in IP length and desired time to live and @@ -776,17 +847,25 @@ send: error = tuba_output(m, tp); else #endif +#ifdef INET6 + if (tp->pf == PF_INET6) { + ((struct ipv6 *)ti6)->ipv6_length = m->m_pkthdr.len - sizeof(struct ipv6); + + /* Following fields are already grabbed from the tcp_template. */ + /* ((struct ipv6 *)ti6)->ipv6_versfl = ntohl(0x60000000); + ((struct ipv6 *)ti6)->ipv6_nexthdr = IPPROTO_TCP; + ((struct ipv6 *)ti6)->ipv6_hoplimit = + tp->t_inpcb->inp_ipv6.ipv6_hoplimit;*/ + + error = ipv6_output(m, &tp->t_inpcb->inp_route6, (so->so_options & SO_DONTROUTE), NULL, NULL, tp->t_inpcb->inp_socket); + } else +#endif /* INET6 */ { ((struct ip *)ti)->ip_len = m->m_pkthdr.len; ((struct ip *)ti)->ip_ttl = tp->t_inpcb->inp_ip.ip_ttl; /* XXX */ ((struct ip *)ti)->ip_tos = tp->t_inpcb->inp_ip.ip_tos; /* XXX */ -#if BSD >= 43 error = ip_output(m, tp->t_inpcb->inp_options, &tp->t_inpcb->inp_route, so->so_options & SO_DONTROUTE, 0, tp->t_inpcb); -#else - error = ip_output(m, (struct mbuf *)0, &tp->t_inpcb->inp_route, - so->so_options & SO_DONTROUTE); -#endif #if defined(TCP_SACK) && defined(TCP_FACK) /* Update snd_awnd to reflect the new data that was sent. */ tp->snd_awnd = tcp_seq_subtract(tp->snd_max, tp->snd_fack) + diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c index 255f94e8519..bd08bc4550b 100644 --- a/sys/netinet/tcp_subr.c +++ b/sys/netinet/tcp_subr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tcp_subr.c,v 1.12 1998/11/17 19:23:02 provos Exp $ */ +/* $OpenBSD: tcp_subr.c,v 1.13 1999/01/11 02:01:36 deraadt Exp $ */ /* $NetBSD: tcp_subr.c,v 1.22 1996/02/13 23:44:00 christos Exp $ */ /* @@ -36,6 +36,18 @@ * @(#)tcp_subr.c 8.1 (Berkeley) 6/10/93 */ +/* +%%% portions-copyright-nrl-95 +Portions of this software are Copyright 1995-1998 by Randall Atkinson, +Ronald Lee, Daniel McDonald, Bao Phan, and Chris Winters. All Rights +Reserved. All rights under this copyright have been assigned to the US +Naval Research Laboratory (NRL). The NRL Copyright Notice and License +Agreement Version 1.1 (January 17, 1995) applies to these portions of the +software. +You should have received a copy of the license with this software. If you +didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>. +*/ + #include <sys/param.h> #include <sys/proc.h> #include <sys/systm.h> @@ -63,6 +75,12 @@ #include <netinet/tcpip.h> #include <dev/rndvar.h> +#ifdef INET6 +#include <netinet6/ipv6_var.h> +#include <netinet6/tcpipv6.h> +#include <sys/domain.h> +#endif /* INET6 */ + /* patchable/settable parameters for tcp */ int tcp_mssdflt = TCP_MSS; int tcp_rttdflt = TCPTV_SRTTDFLT / PR_SLOWHZ; @@ -95,6 +113,10 @@ int tcp_do_sack = TCP_DO_SACK; /* RFC 2018 selective ACKs */ #endif int tcbhashsize = TCBHASHSIZE; +#ifdef INET6 +extern int ipv6_defhoplmt; +#endif /* INET6 */ + /* * Tcp initialization */ @@ -107,10 +129,18 @@ tcp_init() tcp_iss = arc4random() + 1; #endif /* !TCP_COMPAT_42 */ in_pcbinit(&tcbtable, tcbhashsize); - if (max_protohdr < sizeof(struct tcpiphdr)) - max_protohdr = sizeof(struct tcpiphdr); - if (max_linkhdr + sizeof(struct tcpiphdr) > MHLEN) + +#ifdef INET6 + /* + * Since sizeof(struct ipv6) > sizeof(struct ip), we + * do max length checks/computations only on the former. + */ + if (max_protohdr < (sizeof(struct ipv6) + sizeof(struct tcphdr))) + max_protohdr = (sizeof(struct ipv6) + sizeof(struct tcphdr)); + if ((max_linkhdr + sizeof(struct ipv6) + sizeof(struct tcphdr)) > + MHLEN) panic("tcp_init"); +#endif /* INET6 */ } /* @@ -119,6 +149,14 @@ tcp_init() * in a skeletal tcp/ip header, minimizing the amount of work * necessary when the connection is used. */ +#ifdef INET6 +/* + * To support IPv6 in addition to IPv4 and considering that the sizes of + * the IPv4 and IPv6 headers are not the same, we now use a separate pointer + * for the TCP header. Also, we made the former tcpiphdr header pointer + * into just an IP overlay pointer, with casting as appropriate for v6. rja + */ +#endif /* INET6 */ struct tcpiphdr * tcp_template(tp) struct tcpcb *tp; @@ -126,29 +164,60 @@ tcp_template(tp) register struct inpcb *inp = tp->t_inpcb; register struct mbuf *m; register struct tcpiphdr *n; + register struct tcphdr *th; +#ifdef INET6 + register struct tcpipv6hdr *ti6; + register struct ipv6 *ipv6; +#endif /* INET6 */ if ((n = tp->t_template) == 0) { m = m_get(M_DONTWAIT, MT_HEADER); if (m == NULL) return (0); - m->m_len = sizeof (struct tcpiphdr); +#ifdef INET6 + if (tp->pf == PF_INET6) + m->m_len = sizeof (struct tcphdr) + sizeof(struct ipv6); + else +#endif /* INET6 */ + m->m_len = sizeof (struct tcpiphdr); n = mtod(m, struct tcpiphdr *); } - bzero(n->ti_x1, sizeof n->ti_x1); - n->ti_pr = IPPROTO_TCP; - n->ti_len = htons(sizeof (struct tcpiphdr) - sizeof (struct ip)); - n->ti_src = inp->inp_laddr; - n->ti_dst = inp->inp_faddr; - n->ti_sport = inp->inp_lport; - n->ti_dport = inp->inp_fport; - n->ti_seq = 0; - n->ti_ack = 0; - n->ti_x2 = 0; - n->ti_off = 5; - n->ti_flags = 0; - n->ti_win = 0; - n->ti_sum = 0; - n->ti_urp = 0; +#ifdef INET6 + if (tp->pf == PF_INET6) { + + ti6 = (struct tcpipv6hdr *)n; + ipv6 = (struct ipv6 *)n; + th = &ti6->ti6_t; + + ipv6->ipv6_src = inp->inp_laddr6; + ipv6->ipv6_dst = inp->inp_faddr6; + ipv6->ipv6_versfl = htonl(0x60000000) | + (inp->inp_ipv6.ipv6_versfl & htonl(0x0fffffff)); + + ipv6->ipv6_nexthdr = IPPROTO_TCP; + ipv6->ipv6_length = htons(sizeof(struct tcphdr)); /*XXX*/ + ipv6->ipv6_hoplimit = inp->inp_ipv6.ipv6_hoplimit; + } else +#endif /* INET6 */ + { + th = &n->ti_t; + bzero(n->ti_x1, sizeof n->ti_x1); + n->ti_pr = IPPROTO_TCP; + n->ti_len = htons(sizeof (struct tcpiphdr) - sizeof (struct ip)); + n->ti_src = inp->inp_laddr; + n->ti_dst = inp->inp_faddr; + } + + th->th_sport = inp->inp_lport; + th->th_dport = inp->inp_fport; + th->th_seq = 0; + th->th_ack = 0; + th->th_x2 = 0; + th->th_off = 5; + th->th_flags = 0; + th->th_win = 0; + th->th_sum = 0; + th->th_urp = 0; return (n); } @@ -165,6 +234,9 @@ tcp_template(tp) * In any case the ack and sequence number of the transmitted * segment are as specified by the parameters. */ +#ifdef INET6 +/* This function looks hairy, because it was so IPv4-dependent. */ +#endif /* INET6 */ void tcp_respond(tp, ti, m, ack, seq, flags) struct tcpcb *tp; @@ -176,11 +248,31 @@ tcp_respond(tp, ti, m, ack, seq, flags) register int tlen; int win = 0; struct route *ro = 0; + register struct tcphdr *th; +#ifdef INET6 + int is_ipv6 = 0; /* true iff IPv6 */ +#endif /* INET6 */ if (tp) { win = sbspace(&tp->t_inpcb->inp_socket->so_rcv); +#ifdef INET6 + /* + * If this is called with an unconnected + * socket/tp/pcb (tp->pf is 0), we lose. + */ + is_ipv6 = (tp->pf == PF_INET6); + + /* + * The route/route6 distinction is meaningless + * unless you're allocating space or passing parameters. + */ +#endif /* INET6 */ ro = &tp->t_inpcb->inp_route; } +#ifdef INET6 + else + is_ipv6 = (((struct ip *)ti)->ip_v == 6); +#endif /* INET6 */ if (m == 0) { m = m_gethdr(M_DONTWAIT, MT_HEADER); if (m == NULL) @@ -191,41 +283,87 @@ tcp_respond(tp, ti, m, ack, seq, flags) tlen = 0; #endif m->m_data += max_linkhdr; - *mtod(m, struct tcpiphdr *) = *ti; +#ifdef INET6 + if (is_ipv6) + bcopy(ti, mtod(m, caddr_t), sizeof(struct tcphdr) + + sizeof(struct ipv6)); + else +#endif /* INET6 */ + bcopy(ti, mtod(m, caddr_t), sizeof(struct tcphdr) + + sizeof(struct ip)); + ti = mtod(m, struct tcpiphdr *); flags = TH_ACK; } else { m_freem(m->m_next); m->m_next = 0; m->m_data = (caddr_t)ti; - m->m_len = sizeof (struct tcpiphdr); tlen = 0; #define xchg(a,b,type) { type t; t=a; a=b; b=t; } - xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, u_int32_t); - xchg(ti->ti_dport, ti->ti_sport, u_int16_t); +#ifdef INET6 + if (is_ipv6) { + m->m_len = sizeof(struct tcphdr) + sizeof(struct ipv6); + xchg(((struct ipv6 *)ti)->ipv6_dst,\ + ((struct ipv6 *)ti)->ipv6_src,\ + struct in6_addr); + th = (void *)ti + sizeof(struct ipv6); + } else +#endif /* INET6 */ + { + m->m_len = sizeof (struct tcpiphdr); + xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, u_int32_t); + th = (void *)ti + sizeof(struct ip); + } + xchg(th->th_dport, th->th_sport, u_int16_t); #undef xchg } - ti->ti_len = htons((u_int16_t)(sizeof (struct tcphdr) + tlen)); - tlen += sizeof (struct tcpiphdr); +#ifdef INET6 + if (is_ipv6) { + tlen += sizeof(struct tcphdr) + sizeof(struct ipv6); + th = (struct tcphdr *)((caddr_t)ti + sizeof(struct ipv6)); + } else +#endif /* INET6 */ + { + ti->ti_len = htons((u_int16_t)(sizeof (struct tcphdr) + tlen)); + tlen += sizeof (struct tcpiphdr); + th = (struct tcphdr *)((caddr_t)ti + sizeof(struct ip)); + } + m->m_len = tlen; m->m_pkthdr.len = tlen; m->m_pkthdr.rcvif = (struct ifnet *) 0; - bzero(ti->ti_x1, sizeof ti->ti_x1); - ti->ti_seq = htonl(seq); - ti->ti_ack = htonl(ack); - ti->ti_x2 = 0; - ti->ti_off = sizeof (struct tcphdr) >> 2; - ti->ti_flags = flags; + th->th_seq = htonl(seq); + th->th_ack = htonl(ack); + th->th_x2 = 0; + th->th_off = sizeof (struct tcphdr) >> 2; + th->th_flags = flags; if (tp) - ti->ti_win = htons((u_int16_t) (win >> tp->rcv_scale)); + th->th_win = htons((u_int16_t) (win >> tp->rcv_scale)); else - ti->ti_win = htons((u_int16_t)win); - ti->ti_urp = 0; - ti->ti_sum = 0; - ti->ti_sum = in_cksum(m, tlen); - ((struct ip *)ti)->ip_len = tlen; - ((struct ip *)ti)->ip_ttl = ip_defttl; - (void) ip_output(m, NULL, ro, 0, NULL, tp ? tp->t_inpcb : NULL); + th->th_win = htons((u_int16_t)win); + th->th_urp = 0; + +#ifdef INET6 + if (is_ipv6) { + ((struct ipv6 *)ti)->ipv6_versfl = htonl(0x60000000); + ((struct ipv6 *)ti)->ipv6_nexthdr = IPPROTO_TCP; + ((struct ipv6 *)ti)->ipv6_hoplimit = MAXHOPLIMIT; + ((struct ipv6 *)ti)->ipv6_length = tlen - sizeof(struct ipv6); + th->th_sum = 0; + th->th_sum = in6_cksum(m, IPPROTO_TCP, + ((struct ipv6 *)ti)->ipv6_length, sizeof(struct ipv6)); + HTONS(((struct ipv6 *)ti)->ipv6_length); + ipv6_output(m, (struct route6 *)ro, 0, NULL, NULL, NULL); + } else +#endif /* INET6 */ + { + bzero(ti->ti_x1, sizeof ti->ti_x1); + ti->ti_len = htons((u_short)tlen - sizeof(struct ip)); + th->th_sum = in_cksum(m, tlen); + ((struct ip *)ti)->ip_len = tlen; + ((struct ip *)ti)->ip_ttl = ip_defttl; + ip_output(m, NULL, ro, 0, NULL, tp ? tp->t_inpcb : NULL); + } } /* @@ -263,7 +401,23 @@ tcp_newtcpcb(inp) TCPTV_MIN, TCPTV_REXMTMAX); tp->snd_cwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT; tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT; - inp->inp_ip.ip_ttl = ip_defttl; +#ifdef INET6 + /* + * If we want to use tp->pf for a quick-n-easy way to determine + * the outbound dgram type, we cannot make this decision + * until a connection is established! Bzero() sets pf to zero, and + * that's the way we want it, unless, of course, it's an AF_INET + * socket... + */ + if ((inp->inp_flags & INP_IPV6) == 0) + tp->pf = PF_INET; /* If AF_INET socket, we can't do v6 from it. */ + + if (inp->inp_flags & INP_IPV6) + inp->inp_ipv6.ipv6_hoplimit = ipv6_defhoplmt; + else +#endif /* INET6 */ + inp->inp_ip.ip_ttl = ip_defttl; + inp->inp_ppcb = (caddr_t)tp; return (tp); } @@ -310,6 +464,30 @@ tcp_close(tp) #endif #ifdef RTV_RTT register struct rtentry *rt; +#ifdef INET6 + register int bound_to_specific = 0; /* I.e. non-default */ + + /* + * This code checks the nature of the route for this connection. + * Normally this is done by two simple checks in the next + * INET/INET6 ifdef block, but because of two possible lower layers, + * that check is done here. + * + * Perhaps should be doing this only for a RTF_HOST route. + */ + rt = inp->inp_route.ro_rt; /* Same for route or route6. */ + if (tp->pf == PF_INET6) { + if (rt) + bound_to_specific = + !(IN6_IS_ADDR_UNSPECIFIED(& + ((struct sockaddr_in6 *)rt_key(rt))->sin6_addr)); + } else { + if (rt) + bound_to_specific = + (((struct sockaddr_in *)rt_key(rt))-> + sin_addr.s_addr != INADDR_ANY); + } +#endif /* INET6 */ /* * If we sent enough data to get some meaningful characteristics, @@ -323,9 +501,17 @@ tcp_close(tp) * Don't update the default route's characteristics and don't * update anything that the user "locked". */ +#ifdef INET6 + /* + * Note that rt and bound_to_specific are set above. + */ + if (SEQ_LT(tp->iss + so->so_snd.sb_hiwat * 16, tp->snd_max) && + rt && bound_to_specific) { +#else /* INET6 */ if (SEQ_LT(tp->iss + so->so_snd.sb_hiwat * 16, tp->snd_max) && (rt = inp->inp_route.ro_rt) && satosin(rt_key(rt))->sin_addr.s_addr != INADDR_ANY) { +#endif /* INET6 */ register u_long i = 0; if ((rt->rt_rmx.rmx_locks & RTV_RTT) == 0) { @@ -369,7 +555,15 @@ tcp_close(tp) i = (i + tp->t_maxseg / 2) / tp->t_maxseg; if (i < 2) i = 2; - i *= (u_long)(tp->t_maxseg + sizeof (struct tcpiphdr)); +#ifdef INET6 + if (tp->pf == PF_INET6) + i *= (u_long)(tp->t_maxseg + sizeof (struct tcphdr) + + sizeof(struct ipv6)); + else +#endif /* INET6 */ + i *= (u_long)(tp->t_maxseg + + sizeof (struct tcpiphdr)); + if (rt->rt_rmx.rmx_ssthresh) rt->rt_rmx.rmx_ssthresh = (rt->rt_rmx.rmx_ssthresh + i) / 2; @@ -378,12 +572,27 @@ tcp_close(tp) } } #endif /* RTV_RTT */ + /* free the reassembly queue, if any */ - while ((qe = tp->segq.lh_first) != NULL) { - LIST_REMOVE(qe, ipqe_q); - m_freem(qe->ipqe_m); - FREE(qe, M_IPQ); - } +#ifdef INET6 + /* Reassembling TCP segments in v6 might be sufficiently different + * to merit two codepaths to free the reasssembly queue. + * If an undecided TCP socket, then the IPv4 codepath will be used + * because it won't matter much anyway. + */ + if (tp->pf == AF_INET6) { + while ((qe = tp->segq.lh_first) != NULL) { + LIST_REMOVE(qe, ipqe_q); + m_freem(qe->ipqe_m); + FREE(qe, M_IPQ); + } + } else +#endif /* INET6 */ + while ((qe = tp->segq.lh_first) != NULL) { + LIST_REMOVE(qe, ipqe_q); + m_freem(qe->ipqe_m); + FREE(qe, M_IPQ); + } #ifdef TCP_SACK /* Free SACK holes. */ q = p = tp->snd_holes; @@ -466,12 +675,28 @@ tcp_ctlinput(cmd, sa, v) ip = 0; else if (errno == 0) return NULL; - 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); + +#ifdef INET6 + if (sa->sa_family == AF_INET6) { + if (ip) { + struct ipv6 *ipv6 = (struct ipv6 *)ip; + + th = (struct tcphdr *)(ipv6 + 1); + in6_pcbnotify(&tcbtable, sa, th->th_dport, + &ipv6->ipv6_src, th->th_sport, cmd, notify); + } else + in6_pcbnotify(&tcbtable, sa, 0, + (struct in6_addr *)&in6addr_any, 0, cmd, notify); } else - in_pcbnotifyall(&tcbtable, sa, errno, notify); +#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); + } return NULL; } diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c index 02cb808c772..953ebef8e9d 100644 --- a/sys/netinet/tcp_usrreq.c +++ b/sys/netinet/tcp_usrreq.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tcp_usrreq.c,v 1.31 1999/01/07 06:05:05 deraadt Exp $ */ +/* $OpenBSD: tcp_usrreq.c,v 1.32 1999/01/11 02:01:36 deraadt Exp $ */ /* $NetBSD: tcp_usrreq.c,v 1.20 1996/02/13 23:44:16 christos Exp $ */ /* @@ -36,6 +36,18 @@ * @(#)tcp_usrreq.c 8.2 (Berkeley) 1/3/94 */ +/* +%%% portions-copyright-nrl-95 +Portions of this software are Copyright 1995-1998 by Randall Atkinson, +Ronald Lee, Daniel McDonald, Bao Phan, and Chris Winters. All Rights +Reserved. All rights under this copyright have been assigned to the US +Naval Research Laboratory (NRL). The NRL Copyright Notice and License +Agreement Version 1.1 (January 17, 1995) applies to these portions of the +software. +You should have received a copy of the license with this software. If you +didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>. +*/ + #include <sys/param.h> #include <sys/systm.h> #include <sys/kernel.h> @@ -73,6 +85,10 @@ extern int check_ipsec_policy __P((struct inpcb *, u_int32_t)); #endif +#ifdef INET6 +#include <sys/domain.h> +#endif /* INET6 */ + /* * TCP protocol interface to socket abstraction. */ @@ -103,9 +119,16 @@ tcp_usrreq(so, req, m, nam, control) int error = 0; int ostate; - if (req == PRU_CONTROL) - return (in_control(so, (u_long)m, (caddr_t)nam, - (struct ifnet *)control)); + if (req == PRU_CONTROL) { +#ifdef INET6 + if (sotopf(so) == PF_INET6) + return in6_control(so, (u_long)m, (caddr_t)nam, + (struct ifnet *)control, 0); + else +#endif /* INET6 */ + return (in_control(so, (u_long)m, (caddr_t)nam, + (struct ifnet *)control)); + } if (control && control->m_len) { m_freem(control); if (m) @@ -176,6 +199,20 @@ tcp_usrreq(so, req, m, nam, control) error = in_pcbbind(inp, nam); if (error) break; +#ifdef INET6 + /* + * If we bind to an address, set up the tp->pf accordingly! + */ + if (inp->inp_flags & INP_IPV6) { + /* If a PF_INET6 socket... */ + if (inp->inp_flags & INP_IPV6_MAPPED) + tp->pf = AF_INET; + else if ((inp->inp_flags & INP_IPV6_UNDEC) == 0) + tp->pf = AF_INET6; + /* else tp->pf is still 0. */ + } + /* else socket is PF_INET, and tp->pf is PF_INET. */ +#endif /* INET6 */ break; /* @@ -184,6 +221,8 @@ tcp_usrreq(so, req, m, nam, control) case PRU_LISTEN: if (inp->inp_lport == 0) error = in_pcbbind(inp, NULL); + /* If the in_pcbbind() above is called, the tp->pf + should still be whatever it was before. */ if (error == 0) tp->t_state = TCPS_LISTEN; break; @@ -198,11 +237,28 @@ tcp_usrreq(so, req, m, nam, control) case PRU_CONNECT: sin = mtod(nam, struct sockaddr_in *); - /* Disallow connects to a multicast address */ - if (IN_MULTICAST(sin->sin_addr.s_addr)) { - error = EINVAL; - break; - } +#ifdef INET6 + if (sin->sin_family == AF_INET6) { + struct in6_addr *in6_addr = &mtod(nam, + struct sockaddr_in6 *)->sin6_addr; + + if (IN6_IS_ADDR_UNSPECIFIED(in6_addr) || + IN6_IS_ADDR_MULTICAST(in6_addr) || + (IN6_IS_ADDR_V4MAPPED(in6_addr) && + ((in6_addr->in6a_words[3] == INADDR_ANY) || + IN_MULTICAST(in6_addr->in6a_words[3]) || + in_broadcast(sin->sin_addr, NULL)))) { + error = EINVAL; + break; + } + } else if (sin->sin_family == AF_INET) +#endif /* INET6 */ + if ((sin->sin_addr.s_addr == INADDR_ANY) || + IN_MULTICAST(sin->sin_addr.s_addr) || + in_broadcast(sin->sin_addr, NULL)) { + error = EINVAL; + break; + } /* Trying to connect to some broadcast address */ if (in_broadcast(sin->sin_addr, NULL)) { @@ -219,12 +275,34 @@ tcp_usrreq(so, req, m, nam, control) if (error) break; +#ifdef INET6 + /* + * With a connection, I now know the version of IP + * is in use and hence can set tp->pf with authority. + */ + if (inp->inp_flags & INP_IPV6) { + if (inp->inp_flags & INP_IPV6_MAPPED) + tp->pf = PF_INET; + else + tp->pf = PF_INET6; + } + /* else I'm a PF_INET socket, and hence tp->pf is PF_INET. */ +#endif /* INET6 */ + tp->t_template = tcp_template(tp); if (tp->t_template == 0) { in_pcbdisconnect(inp); error = ENOBUFS; break; } + +#ifdef INET6 + if ((inp->inp_flags & INP_IPV6) && (tp->pf == PF_INET)) { + inp->inp_ip.ip_ttl = ip_defttl; + inp->inp_ip.ip_tos = 0; + } +#endif /* INET6 */ + so->so_state |= SS_CONNECTOUT; /* Compute window scaling to request. */ while (tp->request_r_scale < TCP_MAX_WINSHIFT && @@ -388,7 +466,7 @@ tcp_usrreq(so, req, m, nam, control) panic("tcp_usrreq"); } if (tp && (so->so_options & SO_DEBUG)) - tcp_trace(TA_USER, ostate, tp, (struct tcpiphdr *)0, req); + tcp_trace(TA_USER, ostate, tp, (struct tcpiphdr *)0, req, 0); splx(s); return (error); } @@ -414,12 +492,28 @@ tcp_ctloutput(op, so, level, optname, mp) (void) m_free(*mp); return (ECONNRESET); } +#ifdef INET6 + tp = intotcpcb(inp); +#endif /* INET6 */ if (level != IPPROTO_TCP) { - error = ip_ctloutput(op, so, level, optname, mp); +#ifdef INET6 + /* + * Not sure if this is the best approach. + * It seems to be, but we don't set tp->pf until the connection + * is established, which may lead to confusion in the case of + * AF_INET6 sockets which get SET/GET options for IPv4. + */ + if (tp->pf == PF_INET6) + error = ipv6_ctloutput(op, so, level, optname, mp); + else +#endif /* INET6 */ + error = ip_ctloutput(op, so, level, optname, mp); splx(s); return (error); } +#ifndef INET6 tp = intotcpcb(inp); +#endif /* !INET6 */ switch (op) { diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h index e7949e60a84..9d136abf83f 100644 --- a/sys/netinet/tcp_var.h +++ b/sys/netinet/tcp_var.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tcp_var.h,v 1.14 1998/11/18 17:42:22 deraadt Exp $ */ +/* $OpenBSD: tcp_var.h,v 1.15 1999/01/11 02:01:36 deraadt Exp $ */ /* $NetBSD: tcp_var.h,v 1.17 1996/02/13 23:44:24 christos Exp $ */ /* @@ -77,7 +77,8 @@ struct tcpcb { #define TF_RCVD_TSTMP 0x0100 /* a timestamp was received in SYN */ #define TF_SACK_PERMIT 0x0200 /* other side said I could SACK */ - struct tcpiphdr *t_template; /* skeletal packet for transmit */ + struct tcpiphdr *t_template; /* skeletal packet for transmit, will + * be either tcpiphdr or tcpipv6hdr */ struct inpcb *t_inpcb; /* back pointer to internet pcb */ /* * The following fields are used as in the protocol specification. @@ -165,6 +166,8 @@ struct tcpcb { /* TUBA stuff */ caddr_t t_tuba_pcb; /* next level down pcb for TCP over z */ + + int pf; }; #define intotcpcb(ip) ((struct tcpcb *)(ip)->inp_ppcb) @@ -324,7 +327,7 @@ struct tcpcb * struct tcpcb * tcp_drop __P((struct tcpcb *, int)); void tcp_dooptions __P((struct tcpcb *, - u_char *, int, struct tcpiphdr *, int *, u_int32_t *, u_int32_t *)); + u_char *, int, struct tcphdr *, int *, u_int32_t *, u_int32_t *)); void tcp_drain __P((void)); void tcp_fasttimo __P((void)); void tcp_init __P((void)); @@ -334,10 +337,9 @@ struct tcpcb * tcp_newtcpcb __P((struct inpcb *)); void tcp_notify __P((struct inpcb *, int)); int tcp_output __P((struct tcpcb *)); -void tcp_pulloutofband __P((struct socket *, - struct tcpiphdr *, struct mbuf *)); +void tcp_pulloutofband __P((struct socket *, u_int, struct mbuf *)); void tcp_quench __P((struct inpcb *, int)); -int tcp_reass __P((struct tcpcb *, struct tcpiphdr *, struct mbuf *)); +int tcp_reass __P((struct tcpcb *, struct tcphdr *, struct mbuf *, int *)); void tcp_respond __P((struct tcpcb *, struct tcpiphdr *, struct mbuf *, tcp_seq, tcp_seq, int)); void tcp_setpersist __P((struct tcpcb *)); @@ -346,7 +348,7 @@ struct tcpiphdr * tcp_template __P((struct tcpcb *)); struct tcpcb * tcp_timers __P((struct tcpcb *, int)); -void tcp_trace __P((int, int, struct tcpcb *, struct tcpiphdr *, int)); +void tcp_trace __P((int, int, struct tcpcb *, struct tcpiphdr *, int, int)); struct tcpcb * tcp_usrclosed __P((struct tcpcb *)); int tcp_sysctl __P((int *, u_int, void *, size_t *, void *, size_t)); @@ -371,4 +373,4 @@ int tcp_newreno __P((struct tcpcb *, struct tcpiphdr *)); u_long tcp_seq_subtract __P((u_long, u_long )); #endif /* TCP_NEWRENO || TCP_SACK */ -#endif /* KERNEL */ +#endif /* _KERNEL */ diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c index e3f173c79e3..89bf839fdec 100644 --- a/sys/netinet/udp_usrreq.c +++ b/sys/netinet/udp_usrreq.c @@ -1,4 +1,4 @@ -/* $OpenBSD: udp_usrreq.c,v 1.18 1999/01/07 06:05:05 deraadt Exp $ */ +/* $OpenBSD: udp_usrreq.c,v 1.19 1999/01/11 02:01:36 deraadt Exp $ */ /* $NetBSD: udp_usrreq.c,v 1.28 1996/03/16 23:54:03 christos Exp $ */ /* @@ -36,6 +36,18 @@ * @(#)udp_usrreq.c 8.4 (Berkeley) 1/21/94 */ +/* +%%% portions-copyright-nrl-95 +Portions of this software are Copyright 1995-1998 by Randall Atkinson, +Ronald Lee, Daniel McDonald, Bao Phan, and Chris Winters. All Rights +Reserved. All rights under this copyright have been assigned to the US +Naval Research Laboratory (NRL). The NRL Copyright Notice and License +Agreement Version 1.1 (January 17, 1995) applies to these portions of the +software. +You should have received a copy of the license with this software. If you +didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>. +*/ + #include <sys/param.h> #include <sys/malloc.h> #include <sys/mbuf.h> @@ -69,13 +81,23 @@ extern int check_ipsec_policy __P((struct inpcb *, u_int32_t)); #include <machine/stdarg.h> +#ifdef INET6 +#include <netinet6/in6.h> +#include <netinet6/ipv6.h> +#include <netinet6/in6_var.h> +#include <netinet6/ipv6_var.h> +#include <netinet6/ipv6_icmp.h> + +extern int ipv6_defhoplmt; + +#endif /* INET6 */ + /* * UDP protocol implementation. * Per RFC 768, August, 1980. */ int udpcksum = 1; -struct sockaddr_in udp_in = { sizeof(udp_in), AF_INET }; static void udp_detach __P((struct inpcb *)); static void udp_notify __P((struct inpcb *, int)); @@ -114,6 +136,17 @@ udp_input(m, va_alist) int iphlen; va_list ap; u_int16_t savesum; + union { + struct sockaddr sa; + struct sockaddr_in sin; +#ifdef INET6 + struct sockaddr_in6 sin6; +#endif /* INET6 */ + } srcsa, dstsa; +#ifdef INET6 + struct ipv6 *ipv6; + struct sockaddr_in6 src_v4mapped; +#endif /* INET6 */ va_start(ap, m); iphlen = va_arg(ap, int); @@ -121,12 +154,39 @@ udp_input(m, va_alist) udpstat.udps_ipackets++; + switch (mtod(m, struct ip *)->ip_v) { + case 4: + ip = mtod(m, struct ip *); +#ifdef INET6 + ipv6 = NULL; +#endif /* INET6 */ + srcsa.sa.sa_family = AF_INET; + break; +#ifdef INET6 + case 6: + ip = NULL; + ipv6 = mtod(m, struct ipv6 *); + srcsa.sa.sa_family = AF_INET6; + break; +#endif /* INET6 */ + default: + printf("udp_input: received unknown IP version %d", mtod(m, struct ip *)->ip_v); + goto bad; + } + /* * Strip IP options, if any; should skip this, * make available to user, and use on returned packets, * but we don't yet have a way to check the checksum * with options still present. */ + /* + * (contd. from above...) Furthermore, we may want to strip options + * for such things as ICMP errors, where options just get in the way. + */ +#ifdef INET6 + if (ip) +#endif /* INET6 */ if (iphlen > sizeof (struct ip)) { ip_stripoptions(m, (struct mbuf *)0); iphlen = sizeof(struct ip); @@ -135,34 +195,39 @@ udp_input(m, va_alist) /* * Get IP and UDP header together in first mbuf. */ - ip = mtod(m, struct ip *); if (m->m_len < iphlen + sizeof(struct udphdr)) { - if ((m = m_pullup(m, iphlen + sizeof(struct udphdr))) == 0) { + if ((m = m_pullup2(m, iphlen + sizeof(struct udphdr))) == 0) { udpstat.udps_hdrops++; return; } - ip = mtod(m, struct ip *); +#ifdef INET6 + if (ipv6) + ipv6 = mtod(m, struct ipv6 *); + else +#endif /* INET6 */ + ip = mtod(m, struct ip *); } - uh = (struct udphdr *)((caddr_t)ip + iphlen); + uh = (struct udphdr *)(mtod(m, caddr_t) + iphlen); /* * Make mbuf data length reflect UDP length. * If not enough data to reflect UDP length, drop. */ len = ntohs((u_int16_t)uh->uh_ulen); - if (ip->ip_len != len) { - if (len > ip->ip_len || len < sizeof(struct udphdr)) { + if (m->m_pkthdr.len - iphlen != len) { + if (len > (m->m_pkthdr.len - iphlen) || + len < sizeof(struct udphdr)) { udpstat.udps_badlen++; goto bad; } - m_adj(m, len - ip->ip_len); - /* ip->ip_len = len; */ + m_adj(m, len - (m->m_pkthdr.len - iphlen)); } /* * Save a copy of the IP header in case we want restore it * for sending an ICMP error message in response. */ - save_ip = *ip; + if (ip) + save_ip = *ip; /* * Checksum extended UDP header and data. @@ -170,6 +235,17 @@ udp_input(m, va_alist) * udpcksum is not set. */ savesum = uh->uh_sum; +#ifdef INET6 + if (ipv6) { + /* + * In IPv6, the UDP checksum is ALWAYS used. + */ + if ((uh->uh_sum = in6_cksum(m, IPPROTO_UDP, len, iphlen))) { + udpstat.udps_badsum++; + goto bad; + } + } else +#endif /* INET6 */ if (uh->uh_sum) { bzero(((struct ipovly *)ip)->ih_x1, sizeof ((struct ipovly *)ip)->ih_x1); @@ -181,8 +257,54 @@ udp_input(m, va_alist) } } + switch (srcsa.sa.sa_family) { + case AF_INET: + memset(&srcsa, 0, sizeof(struct sockaddr_in)); + srcsa.sin.sin_len = sizeof(struct sockaddr_in); + srcsa.sin.sin_family = AF_INET; + srcsa.sin.sin_port = uh->uh_sport; + srcsa.sin.sin_addr = ip->ip_src; + +#ifdef INET6 + memset(&src_v4mapped, 0, sizeof(struct sockaddr_in6)); + src_v4mapped.sin6_len = sizeof(struct sockaddr_in6); + src_v4mapped.sin6_family = AF_INET6; + src_v4mapped.sin6_port = uh->uh_sport; + CREATE_IPV6_MAPPED(src_v4mapped.sin6_addr, ip->ip_src.s_addr); +#endif /* INET6 */ + + memset(&dstsa, 0, sizeof(struct sockaddr_in)); + dstsa.sin.sin_len = sizeof(struct sockaddr_in); + dstsa.sin.sin_family = AF_INET; + dstsa.sin.sin_port = uh->uh_dport; + dstsa.sin.sin_addr = ip->ip_dst; + break; +#ifdef INET6 + case AF_INET6: + memset(&srcsa, 0, sizeof(struct sockaddr_in6)); + srcsa.sin6.sin6_len = sizeof(struct sockaddr_in6); + srcsa.sin6.sin6_family = AF_INET6; + srcsa.sin6.sin6_port = uh->uh_sport; + srcsa.sin6.sin6_flowinfo = htonl(0x0fffffff) & ipv6->ipv6_versfl; + srcsa.sin6.sin6_addr = ipv6->ipv6_src; + + memset(&dstsa, 0, sizeof(struct sockaddr_in6)); + dstsa.sin6.sin6_len = sizeof(struct sockaddr_in6); + dstsa.sin6.sin6_family = AF_INET6; + dstsa.sin6.sin6_port = uh->uh_dport; + dstsa.sin6.sin6_addr = ipv6->ipv6_dst; + break; +#endif /* INET6 */ + } + +#ifdef INET6 + if ((ipv6 && IN6_IS_ADDR_MULTICAST(&ipv6->ipv6_dst)) || + (ip && IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) || + (ip && in_broadcast(ip->ip_dst, m->m_pkthdr.rcvif))) { +#else /* INET6 */ if (IN_MULTICAST(ip->ip_dst.s_addr) || in_broadcast(ip->ip_dst, m->m_pkthdr.rcvif)) { +#endif /* INET6 */ struct socket *last; /* * Deliver a multicast or broadcast datagram to *all* sockets @@ -200,11 +322,6 @@ udp_input(m, va_alist) * fixing the interface. Maybe 4.5BSD will remedy this?) */ - /* - * Construct sockaddr format source address. - */ - udp_in.sin_port = uh->uh_sport; - udp_in.sin_addr = ip->ip_src; iphlen += sizeof(struct udphdr); m->m_len -= iphlen; m->m_pkthdr.len -= iphlen; @@ -219,11 +336,27 @@ udp_input(m, va_alist) inp = inp->inp_queue.cqe_next) { if (inp->inp_lport != uh->uh_dport) continue; +#ifdef INET6 + if (ipv6) { + if (!(inp->inp_flags & INP_IPV6)) + continue; + if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6)) + if (!IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, &ipv6->ipv6_dst)) + continue; + } else +#endif /* INET6 */ if (inp->inp_laddr.s_addr != INADDR_ANY) { if (inp->inp_laddr.s_addr != ip->ip_dst.s_addr) continue; } +#ifdef INET6 + if (ipv6) { + if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6)) + if (!IN6_ARE_ADDR_EQUAL(&inp->inp_faddr6, &ipv6->ipv6_src) || inp->inp_fport != uh->uh_sport) + continue; + } else +#endif /* INET6 */ if (inp->inp_faddr.s_addr != INADDR_ANY) { if (inp->inp_faddr.s_addr != ip->ip_src.s_addr || @@ -235,9 +368,18 @@ udp_input(m, va_alist) struct mbuf *n; if ((n = m_copy(m, 0, M_COPYALL)) != NULL) { +#ifdef INET6 + if (ipv6) + opts = ipv6_headertocontrol(m, iphlen, ((struct inpcb *)last->so_pcb)->inp_flags); +#endif /* INET6 */ if (sbappendaddr(&last->so_rcv, - sintosa(&udp_in), n, - (struct mbuf *)0) == 0) { +#ifdef INET6 + /* This cruft is needed in (the rare) case I deliver a {multi,broad}cast + IPv4 packet to an AF_INET6 socket. */ + ((((struct inpcb *)last->so_pcb)->inp_flags & INP_IPV6) && ip) ? + (struct sockaddr *)&src_v4mapped : +#endif /* INET6 */ + &srcsa.sa, n, (struct mbuf *)0) == 0) { m_freem(n); udpstat.udps_fullsock++; } else @@ -266,8 +408,19 @@ udp_input(m, va_alist) udpstat.udps_noportbcast++; goto bad; } - if (sbappendaddr(&last->so_rcv, sintosa(&udp_in), m, - (struct mbuf *)0) == 0) { + +#ifdef INET6 + if (ipv6) + opts = ipv6_headertocontrol(m, iphlen, ((struct inpcb *)last->so_pcb)->inp_flags); +#endif /* INET6 */ + if (sbappendaddr(&last->so_rcv, +#ifdef INET6 + /* This cruft is needed in (the rare) case I deliver a + {multi,broad}cast IPv4 packet to an AF_INET6 socket. */ + ((((struct inpcb *)last->so_pcb)->inp_flags & INP_IPV6) && ip) ? + (struct sockaddr *)&src_v4mapped : +#endif /* INET6 */ + &srcsa.sa, m, (struct mbuf *)0) == 0) { udpstat.udps_fullsock++; goto bad; } @@ -277,10 +430,23 @@ udp_input(m, va_alist) /* * Locate pcb for datagram. */ +#ifdef INET6 + if (ipv6) + inp = in6_pcbhashlookup(&udbtable, &ipv6->ipv6_src, uh->uh_sport, + &ipv6->ipv6_dst, uh->uh_dport); + else +#endif /* INET6 */ inp = in_pcbhashlookup(&udbtable, ip->ip_src, uh->uh_sport, ip->ip_dst, uh->uh_dport); if (inp == 0) { ++udpstat.udps_pcbhashmiss; +#ifdef INET6 + if (ipv6) { + inp = in_pcblookup(&udbtable, (struct in_addr *)&(ipv6->ipv6_src), + uh->uh_sport, (struct in_addr *)&(ipv6->ipv6_dst), + uh->uh_dport, INPLOOKUP_WILDCARD | INPLOOKUP_IPV6); + } else +#endif /* INET6 */ inp = in_pcblookup(&udbtable, &ip->ip_src, uh->uh_sport, &ip->ip_dst, uh->uh_dport, INPLOOKUP_WILDCARD); if (inp == 0) { @@ -294,20 +460,26 @@ udp_input(m, va_alist) HTONS(ip->ip_id); HTONS(ip->ip_off); uh->uh_sum = savesum; +#ifdef INET6 + if (ipv6) + ipv6_icmp_error(m, ICMPV6_UNREACH,ICMPV6_UNREACH_PORT,0); + else +#endif /* INET6 */ icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT, 0, 0); return; } } - /* - * Construct sockaddr format source address. - * Stuff source address and datagram in user buffer. - */ - udp_in.sin_port = uh->uh_sport; - udp_in.sin_addr = ip->ip_src; if (inp->inp_flags & INP_CONTROLOPTS) { struct mbuf **mp = &opts; +#ifdef INET6 + if (ipv6) { + if (inp->inp_flags & INP_IPV6) + opts = ipv6_headertocontrol(m, iphlen, inp->inp_flags); + } else + if (ip) +#endif /* INET6 */ if (inp->inp_flags & INP_RECVDSTADDR) { *mp = udp_saveopt((caddr_t) &ip->ip_dst, sizeof(struct in_addr), IP_RECVDSTADDR); @@ -335,8 +507,14 @@ udp_input(m, va_alist) m->m_len -= iphlen; m->m_pkthdr.len -= iphlen; m->m_data += iphlen; - if (sbappendaddr(&inp->inp_socket->so_rcv, sintosa(&udp_in), m, - opts) == 0) { + + if (sbappendaddr(&inp->inp_socket->so_rcv, +#ifdef INET6 + /* This cruft is needed to deliver a IPv4 packet to an AF_INET6 socket. */ + ((((struct inpcb *)inp->inp_socket->so_pcb)->inp_flags & INP_IPV6) && ip) ? + (struct sockaddr *)&src_v4mapped : +#endif /* INET6 */ + &srcsa.sa, m, opts) == 0) { udpstat.udps_fullsock++; goto bad; } @@ -408,6 +586,17 @@ udp_ctlinput(cmd, sa, v) ip = 0; else if (errno == 0) return NULL; +#ifdef INET6 + if (sa->sa_family == AF_INET6) { + if (ip) { + struct ipv6 *ipv6 = (struct ipv6 *)ip; + + uh = (struct udphdr *)((caddr_t)ipv6 + sizeof(struct ipv6)); + in6_pcbnotify(&udbtable, sa, uh->uh_dport, &(ipv6->ipv6_src), uh->uh_sport, cmd, udp_notify); + } else + in6_pcbnotify(&udbtable, sa, 0, (struct in6_addr *)&in6addr_any, 0, cmd, udp_notify); + } else +#endif /* INET6 */ if (ip) { uh = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2)); in_pcbnotify(&udbtable, sa, uh->uh_dport, ip->ip_src, @@ -433,6 +622,12 @@ udp_output(m, va_alist) struct in_addr laddr; int s = 0, error = 0; va_list ap; +#ifdef INET6 + register struct in6_addr laddr6; + int v6packet = 0; + struct ifnet *forceif = NULL; +#endif /* INET6 */ + int pcbflags = 0; va_start(ap, m); inp = va_arg(ap, struct inpcb *); @@ -440,12 +635,32 @@ udp_output(m, va_alist) control = va_arg(ap, struct mbuf *); va_end(ap); +#ifndef INET6 if (control) m_freem(control); /* XXX */ +#endif /* INET6 */ if (addr) { + /* + * Save current PCB flags because they may change during + * temporary connection, particularly the INP_IPV6_UNDEC + * flag. + */ + pcbflags = inp->inp_flags; + +#ifdef INET6 + if (inp->inp_flags & INP_IPV6) + laddr6 = inp->inp_laddr6; + else +#endif /* INET6 */ laddr = inp->inp_laddr; +#ifdef INET6 + if (((inp->inp_flags & INP_IPV6) && + !IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6)) + || (inp->inp_faddr.s_addr != INADDR_ANY)) { +#else /* INET6 */ if (inp->inp_faddr.s_addr != INADDR_ANY) { +#endif /* INET6 */ error = EISCONN; goto release; } @@ -459,7 +674,13 @@ udp_output(m, va_alist) goto release; } } else { +#ifdef INET6 + if (((inp->inp_flags & INP_IPV6) && + IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6)) + || (inp->inp_faddr.s_addr == INADDR_ANY)) { +#else /* INET6 */ if (inp->inp_faddr.s_addr == INADDR_ANY) { +#endif /* INET6 */ error = ENOTCONN; goto release; } @@ -468,7 +689,21 @@ udp_output(m, va_alist) * Calculate data length and get a mbuf * for UDP and IP headers. */ +#ifdef INET6 + /* + * Handles IPv4-mapped IPv6 address because temporary connect sets + * the right flag. + */ + v6packet = ((inp->inp_flags & INP_IPV6) && + !(inp->inp_flags & INP_IPV6_MAPPED)); + + if (!v6packet && control) + m_freem(control); + + M_PREPEND(m, v6packet ? (sizeof(struct udphdr) + sizeof(struct ipv6)) : sizeof(struct udpiphdr), M_DONTWAIT); +#else /* INET6 */ M_PREPEND(m, sizeof(struct udpiphdr), M_DONTWAIT); +#endif /* INET6 */ if (m == 0) { error = ENOBUFS; goto bail; @@ -487,6 +722,36 @@ udp_output(m, va_alist) * Fill in mbuf with extended UDP header * and addresses and length put into network format. */ +#ifdef INET6 + if (v6packet) { + struct ipv6 *ipv6 = mtod(m, struct ipv6 *); + struct udphdr *uh = (struct udphdr *)(mtod(m, caddr_t) + sizeof(struct ipv6)); + int payload = sizeof(struct ipv6); + + ipv6->ipv6_versfl = htonl(0x60000000) | (inp->inp_ipv6.ipv6_versfl & htonl(0x0fffffff)); + + ipv6->ipv6_hoplimit = inp->inp_ipv6.ipv6_hoplimit; + ipv6->ipv6_nexthdr = IPPROTO_UDP; + ipv6->ipv6_src = inp->inp_laddr6; + ipv6->ipv6_dst = inp->inp_faddr6; + ipv6->ipv6_length = (u_short)len + sizeof(struct udphdr); + + uh->uh_sport = inp->inp_lport; + uh->uh_dport = inp->inp_fport; + uh->uh_ulen = htons(ipv6->ipv6_length); + uh->uh_sum = 0; + + if (control) + if ((error = ipv6_controltoheader(&m, control, &forceif, &payload))) + goto release; + + /* + * Always calculate udp checksum for IPv6 datagrams + */ + if (!(uh->uh_sum = in6_cksum(m, IPPROTO_UDP, len + sizeof(struct udphdr), payload))) + uh->uh_sum = 0xffff; + } else { +#endif /* INET6 */ ui = mtod(m, struct udpiphdr *); bzero(ui->ui_x1, sizeof ui->ui_x1); ui->ui_pr = IPPROTO_UDP; @@ -496,19 +761,59 @@ udp_output(m, va_alist) ui->ui_sport = inp->inp_lport; ui->ui_dport = inp->inp_fport; ui->ui_ulen = ui->ui_len; +#ifdef INET6 + } +#endif /* INET6 */ /* * Stuff checksum and output datagram. */ +#ifdef INET6 + if (!v6packet) { +#endif /* INET6 */ ui->ui_sum = 0; if (udpcksum) { if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0) ui->ui_sum = 0xffff; } ((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len; +#ifdef INET6 + /* + * For now, we use the default values for ttl and tos for + * v4 packets sent using a v6 pcb. We probably want to + * later allow v4 setsockopt operations on a v6 socket to + * modify the ttl and tos for v4 packets sent using + * the mapped address format. We really ought to + * save the v4 ttl and v6 hoplimit in separate places + * instead of craming both in the inp_hu union. + */ + if (inp->inp_flags & INP_IPV6) { + ((struct ip *)ui)->ip_ttl = ip_defttl; + ((struct ip *)ui)->ip_tos = 0; + } else { +#endif /* INET6 */ + ((struct ip *)ui)->ip_ttl = inp->inp_ip.ip_ttl; /* XXX */ + ((struct ip *)ui)->ip_tos = inp->inp_ip.ip_tos; /* XXX */ +#ifdef INET6 + }; + }; +#endif /* INET6 */ ((struct ip *)ui)->ip_ttl = inp->inp_ip.ip_ttl; /* XXX */ ((struct ip *)ui)->ip_tos = inp->inp_ip.ip_tos; /* XXX */ udpstat.udps_opackets++; +#ifdef INET6 + if (v6packet) + error = ipv6_output(m, &inp->inp_route6, + inp->inp_socket->so_options & SO_DONTROUTE, + (inp->inp_flags & INP_IPV6_MCAST)?inp->inp_moptions6:NULL, + forceif, inp->inp_socket); + else + if (inp->inp_flags & INP_IPV6_MCAST) + error = ip_output(m, inp->inp_options, &inp->inp_route, + inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST), + NULL, NULL, inp->inp_socket); + else +#endif /* INET6 */ error = ip_output(m, inp->inp_options, &inp->inp_route, inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST), inp->inp_moptions, inp); @@ -516,6 +821,12 @@ udp_output(m, va_alist) bail: if (addr) { in_pcbdisconnect(inp); + inp->inp_flags = pcbflags; +#ifdef INET6 + if (inp->inp_flags & INP_IPV6) + inp->inp_laddr6 = laddr6; + else +#endif /* INET6 */ inp->inp_laddr = laddr; splx(s); } @@ -541,9 +852,16 @@ udp_usrreq(so, req, m, addr, control) int error = 0; int s; - if (req == PRU_CONTROL) + if (req == PRU_CONTROL) { +#ifdef INET6 + if (inp->inp_flags & INP_IPV6) + return (in6_control(so, (u_long)m, (caddr_t)addr, + (struct ifnet *)control, 0)); + else +#endif /* INET6 */ return (in_control(so, (u_long)m, (caddr_t)addr, (struct ifnet *)control)); + } if (inp == NULL && req != PRU_ATTACH) { error = EINVAL; goto release; @@ -567,6 +885,12 @@ udp_usrreq(so, req, m, addr, control) error = soreserve(so, udp_sendspace, udp_recvspace); if (error) break; +#ifdef INET6 + if (((struct inpcb *)so->so_pcb)->inp_flags & INP_IPV6) + ((struct inpcb *) so->so_pcb)->inp_ipv6.ipv6_hoplimit = + ipv6_defhoplmt; + else +#endif /* INET6 */ ((struct inpcb *) so->so_pcb)->inp_ip.ip_ttl = ip_defttl; break; @@ -585,6 +909,14 @@ udp_usrreq(so, req, m, addr, control) break; case PRU_CONNECT: +#ifdef INET6 + if (inp->inp_flags & INP_IPV6) { + if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6)) { + error = EISCONN; + break; + } + } else +#endif /* INET6 */ if (inp->inp_faddr.s_addr != INADDR_ANY) { error = EISCONN; break; @@ -605,12 +937,25 @@ udp_usrreq(so, req, m, addr, control) break; case PRU_DISCONNECT: +#ifdef INET6 + if (inp->inp_flags & INP_IPV6) { + if (IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6)) { + error = ENOTCONN; + break; + } + } else +#endif /* INET6 */ if (inp->inp_faddr.s_addr == INADDR_ANY) { error = ENOTCONN; break; } s = splsoftnet(); in_pcbdisconnect(inp); +#ifdef INET6 + if (inp->inp_flags & INP_IPV6) + inp->inp_laddr6 = in6addr_any; + else +#endif /* INET6 */ inp->inp_laddr.s_addr = INADDR_ANY; splx(s); so->so_state &= ~SS_ISCONNECTED; /* XXX */ @@ -645,6 +990,10 @@ udp_usrreq(so, req, m, addr, control) /* * stat: don't bother with a blocksize. */ + /* + * Perhaps Path MTU might be returned for a connected + * UDP socket in this case. + */ return (0); case PRU_SENDOOB: |