Subject: ICMP errors break TCP connections (#181)
Index:	sys/netinet/{ip_icmp,tcp_subr,udp_usrreq,in_pcb} 2.11BSD

Description:
	ICMP Port Unreachable errors cause existing connections to be
	broken.

Repeat-By:
	From the 'tcp_wrapper' README by Wietse Venema (wietse@wzv.win.tue.nl):

"On some systems, the optional RFC 931 remote username lookups may
trigger a kernel bug.  When a client host connects to your system, and
the RFC 931 connection from your system to that client is rejected by a
router, your kernel may drop all connections with that client.  This is
not a bug in the wrapper programs: complain to your vendor, and don't
enable remote user name lookups until the bug has been fixed.

	...

The following procedure can be used (from outside the tue.nl domain) to
find out if your kernel has the bug. From the system under test, do:

	% ftp 131.155.70.100

This command attempts to make an ftp connection to our anonymous ftp
server (ftp.win.tue.nl).  When the connection has been established, run
the following command from the same system under test, while keeping
the ftp connection open:

	% telnet 131.155.70.100 111

Do not forget the `111' at the end of the command. This telnet command
attempts to connect to our portmap process.  The telnet command should
fail with:  "host not reachable", or something like that. If your ftp
connection gets messed up, you have the bug. If the telnet command does
not fail, please let me know a.s.a.p.!

For those who care, the bug is that the BSD kernel code was not careful
enough with incoming ICMP UNREACHABLE control messages (it ignored the
local and remote port numbers). The bug is still present in the BSD
NET/1 source release (1989) but apparently has been fixed in BSD NET/2
(1991). You can see it with your own eyes, if you have the courage."

Fix:
	All updates are available via anonymous FTP to ftp.iipo.gtegsc.com
	in the directory /pub/2.11BSD.

	The 1987/1988 TCP/IP 4.3BSD release (which is what was ported
	to 2.11BSD) was not selective enough when passing the ICMP
	errors to the next level (TCP, UDP).

	The fix was to pass a pointer to the IP header to the protocol
	pr_ctlinput() function from icmp_input() and to retrieve the port
	numbers in udp_ctlinput().  The calling convention for in_pcbnotify()
	was much changed (the port numbers needed to be passed thru) too.

	The following files were modified:

/usr/src/sys/h/protosw.h
/usr/src/sys/netinet/ip_icmp.c
/usr/src/sys/netinet/tcp_subr.c
/usr/src/sys/netinet/udp_usrreq.c
/usr/src/sys/netinet/in_pcb.c
/usr/src/sys/netinet/udp_var.h

	and

/usr/src/ucb/netstat/inet.c

	The udp_usrreq.c file underwent more revision than strictly 
	necessary to fix the ICMP problem - this seemed like a good time
	to collect some additional statistics and in general reorganize
	the module to follow the current 4BSD porting base.

	The UDP statistics structure is augmented to include several
	new statistics.  The only application program affected that I know
	of is 'netstat' and a patch for that program is included below.

	1) Save where indicated below to /tmp/patchfile
	2) cd /tmp
	3) patch -p0 < patchfile
	4) cd /sys/YOURKERNEL
	5) make clean
	6) make all
	7) make install
	8) reboot
	9) cd /usr/src/ucb/netstat
	10) make 
	11) make install
	12) make clean

=======cut here
*** /usr/src/sys/h/protosw.h.old	Thu Dec 24 16:42:53 1992
--- /usr/src/sys/h/protosw.h	Sun Feb 20 14:33:53 1994
***************
*** 9,15 ****
   * software without specific prior written permission. This software
   * is provided ``as is'' without express or implied warranty.
   *
!  *	@(#)protosw.h	7.2 (Berkeley) 12/30/87
   */
  
  /*
--- 9,15 ----
   * software without specific prior written permission. This software
   * is provided ``as is'' without express or implied warranty.
   *
!  *	@(#)protosw.h	7.2.1 (2.11BSD GTE) 2/20/94
   */
  
  /*
***************
*** 117,131 ****
  
  /*
   * The arguments to the ctlinput routine are
!  *	(*protosw[].pr_ctlinput)(cmd, arg);
!  * where cmd is one of the commands below, and arg is
!  * an optional argument (caddr_t).
!  *
!  * N.B. The IMP code, in particular, pressumes the values
!  *      of some of the commands; change with extreme care.
!  * TODO:
!  *	spread out codes so new ICMP codes can be
!  *	accomodated more easily
   */
  #define	PRC_IFDOWN		0	/* interface transition */
  #define	PRC_ROUTEDEAD		1	/* select new route if possible */
--- 117,125 ----
  
  /*
   * The arguments to the ctlinput routine are
!  *	(*protosw[].pr_ctlinput)(cmd, sa, arg);
!  * where cmd is one of the commands below, sa is a pointer to a sockaddr,
!  * and arg is an optional caddr_t argument used within a protocol family.
   */
  #define	PRC_IFDOWN		0	/* interface transition */
  #define	PRC_ROUTEDEAD		1	/* select new route if possible */
***************
*** 148,153 ****
--- 142,150 ----
  #define	PRC_PARAMPROB		20	/* header incorrect */
  
  #define	PRC_NCMDS		21
+ 
+ #define	PRC_IS_REDIRECT(cmd)	\
+ 	((cmd) >= PRC_REDIRECT_NET && (cmd) <= PRC_REDIRECT_TOSHOST)
  
  #if	defined(PRCREQUESTS) && defined(SUPERVISOR)
  char	*prcrequests[] = {
*** /usr/src/sys/netinet/ip_icmp.c.old	Fri Sep  2 20:40:45 1988
--- /usr/src/sys/netinet/ip_icmp.c	Sun Feb 20 13:54:20 1994
***************
*** 9,15 ****
   * software without specific prior written permission. This software
   * is provided ``as is'' without express or implied warranty.
   *
!  *	@(#)ip_icmp.c	7.7 (Berkeley) 12/7/87
   */
  
  #include "param.h"
--- 9,15 ----
   * software without specific prior written permission. This software
   * is provided ``as is'' without express or implied warranty.
   *
!  *	@(#)ip_icmp.c	7.7.1 (2.11BSD GTE) 2/20/94
   */
  
  #include "param.h"
***************
*** 186,192 ****
  		goto raw;
  	icmpstat.icps_inhist[icp->icmp_type]++;
  	code = icp->icmp_code;
! 	switch (UCHAR(icp->icmp_type)) {
  
  	case ICMP_UNREACH:
  		if (code > 5)
--- 186,192 ----
  		goto raw;
  	icmpstat.icps_inhist[icp->icmp_type]++;
  	code = icp->icmp_code;
! 	switch (icp->icmp_type) {
  
  	case ICMP_UNREACH:
  		if (code > 5)
***************
*** 224,231 ****
  			printf("deliver to protocol %d\n", icp->icmp_ip.ip_p);
  #endif
  		icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
! 		if (ctlfunc = inetsw[ip_protox[UCHAR(icp->icmp_ip.ip_p)]].pr_ctlinput)
! 			(*ctlfunc)(code, (struct sockaddr *)&icmpsrc);
  		break;
  
  	badcode:
--- 224,232 ----
  			printf("deliver to protocol %d\n", icp->icmp_ip.ip_p);
  #endif
  		icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
! 		if (ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput)
! 			(*ctlfunc)(code, (struct sockaddr *)&icmpsrc,
! 			    (caddr_t)&icp->icmp_ip);
  		break;
  
  	badcode:
*** /usr/src/sys/netinet/tcp_subr.c.old	Mon Jul  3 11:03:08 1989
--- /usr/src/sys/netinet/tcp_subr.c	Sun Feb 20 14:03:43 1994
***************
*** 9,15 ****
   * software without specific prior written permission. This software
   * is provided ``as is'' without express or implied warranty.
   *
!  *	@(#)tcp_subr.c	7.13.1.1 (Berkeley) 2/7/88
   */
  
  #include "param.h"
--- 9,15 ----
   * software without specific prior written permission. This software
   * is provided ``as is'' without express or implied warranty.
   *
!  *	@(#)tcp_subr.c	7.13.2 (2.11BSD GTE) 2/20/94
   */
  
  #include "param.h"
***************
*** 276,338 ****
   * Notify a tcp user of an asynchronous error;
   * just wake up so that he can collect error status.
   */
! tcp_notify(inp)
  	register struct inpcb *inp;
  {
  
  	WAKEUP((caddr_t) &inp->inp_socket->so_timeo);
  	sorwakeup(inp->inp_socket);
  	sowwakeup(inp->inp_socket);
  }
! tcp_ctlinput(cmd, sa)
! 	int cmd;
  	struct sockaddr *sa;
  {
  	extern u_char inetctlerrmap[];
! 	struct sockaddr_in *sin;
! 	int tcp_quench(), in_rtchange();
  
! 	if ((unsigned)cmd > PRC_NCMDS)
  		return;
! 	if (sa->sa_family != AF_INET && sa->sa_family != AF_IMPLINK)
! 		return;
! 	sin = (struct sockaddr_in *)sa;
! 	if (sin->sin_addr.s_addr == INADDR_ANY)
! 		return;
! 
! 	switch (cmd) {
! 
! 	case PRC_QUENCH:
! 		in_pcbnotify(&tcb, &sin->sin_addr, 0, tcp_quench);
! 		break;
! 
! 	case PRC_ROUTEDEAD:
! 	case PRC_REDIRECT_NET:
! 	case PRC_REDIRECT_HOST:
! 	case PRC_REDIRECT_TOSNET:
! 	case PRC_REDIRECT_TOSHOST:
! #if BSD>=43
! 		in_pcbnotify(&tcb, &sin->sin_addr, 0, in_rtchange);
! #endif
! 		break;
! 
! 	default:
! 		if (inetctlerrmap[cmd] == 0)
! 			return;		/* XXX */
! 		in_pcbnotify(&tcb, &sin->sin_addr, (int)inetctlerrmap[cmd],
! 			tcp_notify);
! 	}
  }
  
- #if BSD<43
- /* XXX fake routine */
- tcp_abort(inp)
- 	struct inpcb *inp;
- {
- 	return;
- }
- #endif
- 
  /*
   * When a source quench is received, close congestion window
   * to one segment.  We will gradually open it again as we proceed.
--- 276,314 ----
   * Notify a tcp user of an asynchronous error;
   * just wake up so that he can collect error status.
   */
! tcp_notify(inp, error)
  	register struct inpcb *inp;
+ 	int error;
  {
  
+ 	inp->inp_socket->so_error = error;
  	WAKEUP((caddr_t) &inp->inp_socket->so_timeo);
  	sorwakeup(inp->inp_socket);
  	sowwakeup(inp->inp_socket);
  }
! 
! tcp_ctlinput(cmd, sa, ip)
! 	register int cmd;
  	struct sockaddr *sa;
+ 	register struct ip *ip;
  {
+ 	register struct tcphdr *th;
+ 	extern struct in_addr zeroin_addr;
  	extern u_char inetctlerrmap[];
! 	int (*notify)() = tcp_notify, tcp_quench();
  
! 	if (cmd == PRC_QUENCH)
! 		notify = tcp_quench;
! 	else if ((unsigned)cmd > PRC_NCMDS || inetctlerrmap[cmd] == 0)
  		return;
! 	if (ip) {
! 		th = (struct tcphdr *)((caddr_t)ip + (ip->ip_hl << 2));
! 		in_pcbnotify(&tcb, sa, th->th_dport, ip->ip_src, th->th_sport,
! 			cmd, notify);
! 	} else
! 		in_pcbnotify(&tcb, sa, 0, zeroin_addr, 0, cmd, notify);
  }
  
  /*
   * When a source quench is received, close congestion window
   * to one segment.  We will gradually open it again as we proceed.
***************
*** 345,348 ****
  	if (tp)
  		tp->snd_cwnd = tp->t_maxseg;
  }
- 
--- 321,323 ----
*** /usr/src/sys/netinet/udp_usrreq.c.old	Thu Apr 28 16:29:07 1988
--- /usr/src/sys/netinet/udp_usrreq.c	Sun Feb 20 16:02:28 1994
***************
*** 9,15 ****
   * software without specific prior written permission. This software
   * is provided ``as is'' without express or implied warranty.
   *
!  *	@(#)udp_usrreq.c	7.5 (Berkeley) 3/11/88
   */
  
  #include "param.h"
--- 9,15 ----
   * software without specific prior written permission. This software
   * is provided ``as is'' without express or implied warranty.
   *
!  *	@(#)udp_usrreq.c	7.5.1 (2.11BSD GTE) 2/20/94
   */
  
  #include "param.h"
***************
*** 33,38 ****
--- 33,40 ----
  #include "udp.h"
  #include "udp_var.h"
  
+ struct	inpcb *udp_last_inpcb = &udb;
+ 
  /*
   * UDP protocol implementation.
   * Per RFC 768, August, 1980.
***************
*** 62,67 ****
--- 64,71 ----
  	int len;
  	struct ip ip;
  
+ 	udpstat.udps_ipackets++;
+ 
  	/*
  	 * Get IP and UDP header together in first mbuf.
  	 */
***************
*** 111,123 ****
  	/*
  	 * Locate pcb for datagram.
  	 */
! 	inp = in_pcblookup(&udb,
! 	    ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport,
! 		INPLOOKUP_WILDCARD);
  	if (inp == 0) {
  		/* don't send ICMP response for broadcast packet */
! 		if (in_broadcast(ui->ui_dst))
  			goto bad;
  		*(struct ip *)ui = ip;
  		icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT,
  		    ifp);
--- 115,138 ----
  	/*
  	 * Locate pcb for datagram.
  	 */
! 	inp = udp_last_inpcb;
! 	if (inp->inp_lport != ui->ui_dport ||
! 	    inp->inp_fport != ui->ui_sport ||
! 	    inp->inp_faddr.s_addr != ui->ui_src.s_addr ||
! 	    inp->inp_laddr.s_addr != ui->ui_dst.s_addr) {
! 		inp = in_pcblookup(&udb, ui->ui_src, ui->ui_sport,
! 		    ui->ui_dst, ui->ui_dport, INPLOOKUP_WILDCARD);
! 		if (inp)
! 			udp_last_inpcb = inp;
! 		udpstat.udpps_pcbcachemiss++;
! 	}
  	if (inp == 0) {
+ 		udpstat.udps_noport++;
  		/* don't send ICMP response for broadcast packet */
! 		if (in_broadcast(ui->ui_dst)) {
! 			udpstat.udps_noportbcast++;
  			goto bad;
+ 		}
  		*(struct ip *)ui = ip;
  		icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT,
  		    ifp);
***************
*** 133,140 ****
  	m->m_len -= sizeof (struct udpiphdr);
  	m->m_off += sizeof (struct udpiphdr);
  	if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in,
! 	    m, (struct mbuf *)0) == 0)
  		goto bad;
  	sorwakeup(inp->inp_socket);
  	return;
  bad:
--- 148,157 ----
  	m->m_len -= sizeof (struct udpiphdr);
  	m->m_off += sizeof (struct udpiphdr);
  	if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in,
! 	    m, (struct mbuf *)0) == 0) {
! 		udpstat.udps_fullsock++;
  		goto bad;
+ 	}
  	sorwakeup(inp->inp_socket);
  	return;
  bad:
***************
*** 145,203 ****
   * Notify a udp user of an asynchronous error;
   * just wake up so that he can collect error status.
   */
! udp_notify(inp)
  	register struct inpcb *inp;
  {
  
  	sorwakeup(inp->inp_socket);
  	sowwakeup(inp->inp_socket);
  }
  
! udp_ctlinput(cmd, sa)
! 	int cmd;
  	struct sockaddr *sa;
  {
  	extern u_char inetctlerrmap[];
- 	struct sockaddr_in *sin;
- 	int in_rtchange();
  
! 	if ((unsigned)cmd > PRC_NCMDS)
  		return;
! 	if (sa->sa_family != AF_INET && sa->sa_family != AF_IMPLINK)
! 		return;
! 	sin = (struct sockaddr_in *)sa;
! 	if (sin->sin_addr.s_addr == INADDR_ANY)
! 		return;
! 
! 	switch (cmd) {
! 
! 	case PRC_QUENCH:
! 		break;
! 
! 	case PRC_ROUTEDEAD:
! 	case PRC_REDIRECT_NET:
! 	case PRC_REDIRECT_HOST:
! 	case PRC_REDIRECT_TOSNET:
! 	case PRC_REDIRECT_TOSHOST:
! 		in_pcbnotify(&udb, &sin->sin_addr, 0, in_rtchange);
! 		break;
! 
! 	default:
! 		if (inetctlerrmap[cmd] == 0)
! 			return;		/* XXX */
! 		in_pcbnotify(&udb, &sin->sin_addr, (int)inetctlerrmap[cmd],
! 			udp_notify);
! 	}
  }
  
! udp_output(inp, m0)
  	register struct inpcb *inp;
  	struct mbuf *m0;
  {
! 	register struct mbuf *m;
  	register struct udpiphdr *ui;
  	register int len = 0;
  
  	/*
  	 * Calculate data length and get a mbuf
  	 * for UDP and IP headers.
--- 162,229 ----
   * Notify a udp user of an asynchronous error;
   * just wake up so that he can collect error status.
   */
! udp_notify(inp, errno)
  	register struct inpcb *inp;
+ 	int errno;
  {
  
+ 	inp->inp_socket->so_error = errno;
  	sorwakeup(inp->inp_socket);
  	sowwakeup(inp->inp_socket);
  }
  
! udp_ctlinput(cmd, sa, ip)
! 	register int cmd;
  	struct sockaddr *sa;
+ 	register struct ip *ip;
  {
+ 	register struct udphdr *uh;
+ 	extern struct in_addr zeroin_addr;
  	extern u_char inetctlerrmap[];
  
! 	if ((unsigned)cmd > PRC_NCMDS || inetctlerrmap[cmd] == 0)
  		return;
! 	if (ip) {
! 		uh = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2));
! 		in_pcbnotify(&udb, sa, uh->uh_dport, ip->ip_src, uh->uh_sport,
! 			cmd, udp_notify);
! 	} else
! 		in_pcbnotify(&udb, sa, 0, zeroin_addr, 0, cmd, udp_notify);
  }
  
! udp_output(inp, m0, addr, control)
  	register struct inpcb *inp;
  	struct mbuf *m0;
+ 	struct mbuf *addr, *control;
  {
! 	register struct mbuf *m = m0;
  	register struct udpiphdr *ui;
  	register int len = 0;
+ 	struct in_addr laddr;
+ 	int s, error = 0;
  
+ 	if (addr) {
+ 		laddr = inp->inp_laddr;
+ 		if (inp->inp_faddr.s_addr != INADDR_ANY) {
+ 			error = EISCONN;
+ 			goto release;
+ 		}
+ 		/*
+ 		 * Must block input while temporarily connected.
+ 		 */
+ 		s = splnet();
+ 		error = in_pcbconnect(inp, addr);
+ 		if (error) {
+ 			splx(s);
+ 			goto release;
+ 		}
+ 	} else {
+ 		if (inp->inp_faddr.s_addr == INADDR_ANY) {
+ 			error = ENOTCONN;
+ 			goto release;
+ 		}
+ 	}
+ 
  	/*
  	 * Calculate data length and get a mbuf
  	 * for UDP and IP headers.
***************
*** 204,214 ****
  	 */
  	for (m = m0; m; m = m->m_next)
  		len += m->m_len;
! 	MGET(m, M_DONTWAIT, MT_HEADER);
! 	if (m == 0) {
! 		m_freem(m0);
! 		return (ENOBUFS);
! 	}
  
  	/*
  	 * Fill in mbuf with extended UDP header
--- 230,236 ----
  	 */
  	for (m = m0; m; m = m->m_next)
  		len += m->m_len;
! 	MGET(m, M_WAIT, MT_HEADER);
  
  	/*
  	 * Fill in mbuf with extended UDP header
***************
*** 239,246 ****
  	}
  	((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
  	((struct ip *)ui)->ip_ttl = udp_ttl;
! 	return (ip_output(m, inp->inp_options, &inp->inp_route,
! 	    inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST)));
  }
  
  int	udp_sendspace = 2048;		/* really max datagram size */
--- 261,280 ----
  	}
  	((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
  	((struct ip *)ui)->ip_ttl = udp_ttl;
! 	udpstat.udps_opackets++;
! 	error = ip_output(m, inp->inp_options, &inp->inp_route,
! 	    inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST));
! 
! 	if (addr) {
! 		in_pcbdisconnect(inp);
! 		inp->inp_laddr = laddr;
! 		splx(s);
! 	}
! 	return(error);
! 
! release:
! 	m_freem(m);
! 	return(error);
  }
  
  int	udp_sendspace = 2048;		/* really max datagram size */
***************
*** 252,259 ****
  	int req;
  	struct mbuf *m, *nam, *rights;
  {
! 	struct inpcb *inp = sotoinpcb(so);
  	int error = 0;
  
  	if (req == PRU_CONTROL)
  		return (in_control(so, (int)m, (caddr_t)nam,
--- 286,294 ----
  	int req;
  	struct mbuf *m, *nam, *rights;
  {
! 	register struct inpcb *inp = sotoinpcb(so);
  	int error = 0;
+ 	register int s;
  
  	if (req == PRU_CONTROL)
  		return (in_control(so, (int)m, (caddr_t)nam,
***************
*** 273,279 ****
--- 308,316 ----
  			error = EINVAL;
  			break;
  		}
+ 		s = splnet();
  		error = in_pcballoc(so, &udb);
+ 		splx(s);
  		if (error)
  			break;
  		error = soreserve(so, udp_sendspace, udp_recvspace);
***************
*** 282,292 ****
  		break;
  
  	case PRU_DETACH:
! 		in_pcbdetach(inp);
  		break;
  
  	case PRU_BIND:
  		error = in_pcbbind(inp, nam);
  		break;
  
  	case PRU_LISTEN:
--- 319,331 ----
  		break;
  
  	case PRU_DETACH:
! 		udp_detach(inp);
  		break;
  
  	case PRU_BIND:
+ 		s = splnet();
  		error = in_pcbbind(inp, nam);
+ 		splx(s);
  		break;
  
  	case PRU_LISTEN:
***************
*** 298,304 ****
--- 337,345 ----
  			error = EISCONN;
  			break;
  		}
+ 		s = splnet();
  		error = in_pcbconnect(inp, nam);
+ 		splx(s);
  		if (error == 0)
  			soisconnected(so);
  		break;
***************
*** 316,322 ****
--- 357,366 ----
  			error = ENOTCONN;
  			break;
  		}
+ 		s = splnet();
  		in_pcbdisconnect(inp);
+ 		inp->inp_laddr.s_addr = INADDR_ANY;
+ 		splx(s);
  		so->so_state &= ~SS_ISCONNECTED;		/* XXX */
  		break;
  
***************
*** 324,367 ****
  		socantsendmore(so);
  		break;
  
! 	case PRU_SEND: {
! 		struct in_addr laddr;
! 		int s;
  
- 		if (nam) {
- 			laddr = inp->inp_laddr;
- 			if (inp->inp_faddr.s_addr != INADDR_ANY) {
- 				error = EISCONN;
- 				break;
- 			}
- 			/*
- 			 * Must block input while temporarily connected.
- 			 */
- 			s = splnet();
- 			error = in_pcbconnect(inp, nam);
- 			if (error) {
- 				splx(s);
- 				break;
- 			}
- 		} else {
- 			if (inp->inp_faddr.s_addr == INADDR_ANY) {
- 				error = ENOTCONN;
- 				break;
- 			}
- 		}
- 		error = udp_output(inp, m);
- 		m = NULL;
- 		if (nam) {
- 			in_pcbdisconnect(inp);
- 			inp->inp_laddr = laddr;
- 			splx(s);
- 		}
- 		}
- 		break;
- 
  	case PRU_ABORT:
  		soisdisconnected(so);
! 		in_pcbdetach(inp);
  		break;
  
  	case PRU_SOCKADDR:
--- 368,379 ----
  		socantsendmore(so);
  		break;
  
! 	case PRU_SEND:
! 		return(udp_output(inp, m, nam, rights));
  
  	case PRU_ABORT:
  		soisdisconnected(so);
! 		udp_detach(inp);
  		break;
  
  	case PRU_SOCKADDR:
***************
*** 397,400 ****
--- 409,423 ----
  	if (m != NULL)
  		m_freem(m);
  	return (error);
+ }
+ 
+ udp_detach(inp)
+ 	register struct inpcb *inp;
+ {
+ 	register int s = splnet();
+ 
+ 	if (inp == udp_last_inpcb)
+ 		udp_last_inpcb = &udb;
+ 	in_pcbdetach(inp);
+ 	splx(s);
  }
*** /usr/src/sys/netinet/in_pcb.c.old	Thu Apr 28 15:58:04 1988
--- /usr/src/sys/netinet/in_pcb.c	Sun Feb 20 14:28:53 1994
***************
*** 9,15 ****
   * software without specific prior written permission. This software
   * is provided ``as is'' without express or implied warranty.
   *
!  *	@(#)in_pcb.c	7.6 (Berkeley) 12/7/87
   */
  
  #include "param.h"
--- 9,15 ----
   * software without specific prior written permission. This software
   * is provided ``as is'' without express or implied warranty.
   *
!  *	@(#)in_pcb.c	7.6.1 (2.11BSD GTE) 2/20/94
   */
  
  #include "param.h"
***************
*** 57,67 ****
--- 57,73 ----
  	register struct inpcb *head = inp->inp_head;
  	register struct sockaddr_in *sin;
  	u_short lport = 0;
+ 	int wild = 0;
  
  	if (in_ifaddr == 0)
  		return (EADDRNOTAVAIL);
  	if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY)
  		return (EINVAL);
+ 	if ((so->so_options & SO_REUSEADDR) == 0 &&
+ 	    ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
+ 	     (so->so_options & SO_ACCEPTCONN) == 0))
+ 		wild = INPLOOKUP_WILDCARD;
+ 
  	if (nam == 0)
  		goto noname;
  	sin = mtod(nam, struct sockaddr_in *);
***************
*** 78,93 ****
  	lport = sin->sin_port;
  	if (lport) {
  		u_short aport = ntohs(lport);
- 		int wild = 0;
  
  		/* GROSS */
  		if (aport < IPPORT_RESERVED && u.u_uid != 0)
  			return (EACCES);
- 		/* even GROSSER, but this is the Internet */
- 		if ((so->so_options & SO_REUSEADDR) == 0 &&
- 		    ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
- 		     (so->so_options & SO_ACCEPTCONN) == 0))
- 			wild = INPLOOKUP_WILDCARD;
  		if (in_pcblookup(head,
  		    zeroin_addr, 0, sin->sin_addr, lport, wild))
  			return (EADDRINUSE);
--- 84,93 ----
***************
*** 266,296 ****
  
  /*
   * Pass some notification to all connections of a protocol
!  * associated with address dst.  Call the protocol specific
!  * routine (if any) to handle each connection.
   */
! in_pcbnotify(head, dst, errno, notify)
  	struct inpcb *head;
! 	register struct in_addr *dst;
! 	int errno, (*notify)();
  {
  	register struct inpcb *inp, *oinp;
! 	int s = splimp();
  
  	for (inp = head->inp_next; inp != head;) {
! 		if (inp->inp_faddr.s_addr != dst->s_addr ||
! 		    inp->inp_socket == 0) {
  			inp = inp->inp_next;
  			continue;
  		}
- 		if (errno) 
- 			inp->inp_socket->so_error = errno;
  		oinp = inp;
  		inp = inp->inp_next;
  		if (notify)
! 			(*notify)(oinp);
  	}
- 	splx(s);
  }
  
  /*
--- 266,328 ----
  
  /*
   * Pass some notification to all connections of a protocol
!  * associated with address dst.  The local address and/or port numbers
!  * may be specified to limit the search.  The "usual action" will be
!  * taken, depending on the ctlinput cmd.  The caller must filter any
!  * cmds that are uninteresting (e.g., no error in the map).
!  * Call the protocol specific routine (if any) to report
!  * any errors for each matching socket.
!  *
!  * Must be called at splnet.
   */
! in_pcbnotify(head, dst, fport, laddr, lport, cmd, notify)
  	struct inpcb *head;
! 	struct sockaddr *dst;
! 	u_short fport, lport;
! 	struct in_addr laddr;
! 	int cmd, (*notify)();
  {
  	register struct inpcb *inp, *oinp;
! 	struct in_addr faddr;
! 	int errno;
! 	int in_rtchange();
! 	extern u_char inetctlerrmap[];
  
+ 	if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET)
+ 		return;
+ 	faddr = ((struct sockaddr_in *)dst)->sin_addr;
+ 	if (faddr.s_addr == INADDR_ANY)
+ 		return;
+ 
+ 	/*
+ 	 * Redirects go to all references to the destination,
+ 	 * and use in_rtchange to invalidate the route cache.
+ 	 * Dead host indications: notify all references to the destination.
+ 	 * Otherwise, if we have knowledge of the local port and address,
+ 	 * deliver only to that socket.
+ 	 */
+ 	if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) {
+ 		fport = 0;
+ 		lport = 0;
+ 		laddr.s_addr = 0;
+ 		if (cmd != PRC_HOSTDEAD)
+ 			notify = in_rtchange;
+ 	}
+ 	errno = inetctlerrmap[cmd];
  	for (inp = head->inp_next; inp != head;) {
! 		if (inp->inp_faddr.s_addr != faddr.s_addr ||
! 		    inp->inp_socket == 0 ||
! 		    (lport && inp->inp_lport != lport) ||
! 		    (laddr.s_addr && inp->inp_laddr.s_addr != laddr.s_addr) ||
! 		    (fport && inp->inp_fport != fport)) {
  			inp = inp->inp_next;
  			continue;
  		}
  		oinp = inp;
  		inp = inp->inp_next;
  		if (notify)
! 			(*notify)(oinp, errno);
  	}
  }
  
  /*
*** /usr/src/sys/netinet/udp_var.h.old	Sun Sep  4 18:23:23 1988
--- /usr/src/sys/netinet/udp_var.h	Sun Feb 20 15:51:58 1994
***************
*** 9,15 ****
   * software without specific prior written permission. This software
   * is provided ``as is'' without express or implied warranty.
   *
!  *	@(#)udp_var.h	7.3 (Berkeley) 12/7/87
   */
  
  /*
--- 9,15 ----
   * software without specific prior written permission. This software
   * is provided ``as is'' without express or implied warranty.
   *
!  *	@(#)udp_var.h	7.3.1 (2.11BSD GTE) 2/20/94
   */
  
  /*
***************
*** 33,41 ****
  #define	ui_sum		ui_u.uh_sum
  
  struct	udpstat {
! 	long	udps_hdrops;
! 	long	udps_badsum;
! 	long	udps_badlen;
  };
  
  #define	UDP_TTL		30		/* deflt time to live for UDP packets */
--- 33,49 ----
  #define	ui_sum		ui_u.uh_sum
  
  struct	udpstat {
! 				/* input statistics: */
! 	long	udps_ipackets;		/* total input packets */
! 	long	udps_hdrops;		/* packet shorter than header */
! 	long	udps_badsum;		/* checksum error */
! 	long	udps_badlen;		/* data length larger than packet */
! 	long	udps_noport;		/* no socket on port */
! 	long	udps_noportbcast;	/* of above, arrived as broadcast */
! 	long	udps_fullsock;		/* not delivered, input socket full */
! 	long	udpps_pcbcachemiss;	/* input packets missing pcb cache */
! 				/* output statistics: */
! 	long	udps_opackets;		/* total output packets */
  };
  
  #define	UDP_TTL		30		/* deflt time to live for UDP packets */
*** /usr/src/ucb/netstat/inet.c.old	Mon Jan 10 22:25:19 1994
--- /usr/src/ucb/netstat/inet.c	Sun Feb 20 20:45:15 1994
***************
*** 53,58 ****
--- 53,59 ----
  extern	int Aflag;
  extern	int aflag;
  extern	int nflag;
+ extern	int sflag;
  extern	char *plural();
  
  #ifdef pdp11
***************
*** 219,234 ****
  		return;
  	klseek(kmem, off, 0);
  	read(kmem, (char *)&udpstat, sizeof (udpstat));
! 	printf("%s:\n\t%lu incomplete header%s\n", name,
! 		udpstat.udps_hdrops, plural(udpstat.udps_hdrops));
! 	printf("\t%lu bad data length field%s\n",
! 		udpstat.udps_badlen, plural(udpstat.udps_badlen));
! 	printf("\t%lu bad checksum%s\n",
! 		udpstat.udps_badsum, plural(udpstat.udps_badsum));
! #ifdef sun
! 	printf("\t%ld socket overflow%s\n",
! 		udpstat.udps_fullsock, plural(udpstat.udps_fullsock));
! #endif
  }
  
  /*
--- 220,233 ----
  		return;
  	klseek(kmem, off, 0);
  	read(kmem, (char *)&udpstat, sizeof (udpstat));
! 	printf("%s:\n", name);
! #define	p(f, m) printf(m, udpstat.f, plural(udpstat.f))
! 	p(udps_hdrops, "\t%lu incomplete header%s\n");
! 	p(udps_badlen, "\t%lu bad data length field%s\n");
! 	p(udps_badsum, "\t%lu bad checksum%s\n");
! 	p(udps_noport, "\t%lu no port%s\n");
! 	p(udps_noportbcast, "\t%lu (arrived as bcast) no port%s\n");
! #undef p
  }
  
  /*
