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 } /*