Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 31 Dec 2000 21:26:01 +0100 (CET)
From:      Jesper Skriver <jesper@skriver.dk>
To:        FreeBSD-gnats-submit@freebsd.org
Cc:        Jesper Skriver <jesper@skriver.dk>
Subject:   kern/23986: Update of "react to ICMP unreachables" code
Message-ID:  <200012312026.eBVKQ1c00576@tam.skriver.dk>
Resent-Message-ID: <200012312030.eBVKU4r02449@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         23986
>Category:       kern
>Synopsis:       Update of "react to ICMP unreachables" code
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          update
>Submitter-Id:   current-users
>Arrival-Date:   Sun Dec 31 12:30:04 PST 2000
>Closed-Date:
>Last-Modified:
>Originator:     Jesper Skriver <jesper@skriver.dk>
>Release:        FreeBSD 5.0-CURRENT i386
>Organization:
none
>Environment:
System: FreeBSD tam 5.0-CURRENT FreeBSD 5.0-CURRENT #0: Sun Dec 31 20:37:53 CET 2000 root@tam:/usr/obj/usr/src/sys/TAM2 i386


	
>Description:
	Update of the code to react on ICMP unreachables.
PHK has previously committed this code, see kern/23086 & kern/23655

We now let ICMP unreachables kill TCP sessions regardless of state, and all
other unreachables (except "need to fragment, but DF bit set") will kill 
sessions in SYN-SENT state.

Broken down the changes are:

- remove unneeded loop increment in src/sys/netinet/in_pcb.c:in_pcbnotify
- add new PRC_UNREACH_ADMIN_PROHIB in sys/sys/protosw.h
- remove condition on TCP in src/sys/netinet/ip_icmp.c:icmp_input
- in src/sys/netinet/ip_icmp.c:icmp_input set code = PRC_UNREACH_ADMIN_PROHIB
  or PRC_UNREACH_HOST for all unreachables except ICMP_UNREACH_NEEDFRAG
- rename sysctl icmp_admin_prohib_like_rst to icmp_unreach_like_rst to reflect
  the fact that we also react on ICMP unreachables that are not administrative
  prohibited.
  Also update the comments to reflect this.
- in sys/netinet/tcp_subr.c:tcp_ctlinput add code to treat PRC_UNREACH_ADMIN_PROHIB
  and PRC_UNREACH_HOST different.

>How-To-Repeat:
	
>Fix:

	Apply this diff:

diff -ru sys/sys.old/protosw.h sys/sys/protosw.h
--- sys/sys.old/protosw.h	Sun Dec 31 18:45:14 2000
+++ sys/sys/protosw.h	Sun Dec 31 20:05:52 2000
@@ -275,8 +275,9 @@
 #define	PRC_TIMXCEED_INTRANS	18	/* packet lifetime expired in transit */
 #define	PRC_TIMXCEED_REASS	19	/* lifetime expired on reass q */
 #define	PRC_PARAMPROB		20	/* header incorrect */
+#define	PRC_UNREACH_ADMIN_PROHIB	21	/* packet administrativly prohibited */
 
-#define	PRC_NCMDS		21
+#define	PRC_NCMDS		22
 
 #define	PRC_IS_REDIRECT(cmd)	\
 	((cmd) >= PRC_REDIRECT_NET && (cmd) <= PRC_REDIRECT_TOSHOST)
@@ -288,7 +289,7 @@
 	"NET-UNREACH", "HOST-UNREACH", "PROTO-UNREACH", "PORT-UNREACH",
 	"#12", "SRCFAIL-UNREACH", "NET-REDIRECT", "HOST-REDIRECT",
 	"TOSNET-REDIRECT", "TOSHOST-REDIRECT", "TX-INTRANS", "TX-REASS",
-	"PARAMPROB"
+	"PARAMPROB", "ADMIN-UNREACH"
 };
 #endif
diff -ru sys/netinet.old/in_pcb.c sys/netinet/in_pcb.c
--- sys/netinet.old/in_pcb.c	Sun Dec 31 16:07:05 2000
+++ sys/netinet/in_pcb.c	Sun Dec 31 21:10:25 2000
@@ -732,7 +732,6 @@
 		 * and TCP port numbers.
 		 */
 		if ((tcp_seq_check == 1) && (tcp_seq_vs_sess(inp, tcp_sequence) == 0)) {
-			inp = inp->inp_list.le_next;
 			break;
 		}
 		oinp = inp;
diff -ru sys/netinet.old/ip_icmp.c sys/netinet/ip_icmp.c
--- sys/netinet.old/ip_icmp.c	Sun Dec 31 16:07:05 2000
+++ sys/netinet/ip_icmp.c	Sun Dec 31 19:58:31 2000
@@ -315,11 +315,23 @@
 	case ICMP_UNREACH:
 		switch (code) {
 			case ICMP_UNREACH_NET:
+				code = PRC_UNREACH_HOST;
+				break;
+
 			case ICMP_UNREACH_HOST:
+				code = PRC_UNREACH_HOST;
+				break;
+
 			case ICMP_UNREACH_PROTOCOL:
+				code = PRC_UNREACH_HOST;
+				break;
+
 			case ICMP_UNREACH_PORT:
+				code = PRC_UNREACH_HOST;
+				break;
+
 			case ICMP_UNREACH_SRCFAIL:
-				code += PRC_UNREACH_NET;
+				code = PRC_UNREACH_HOST;
 				break;
 
 			case ICMP_UNREACH_NEEDFRAG:
@@ -327,37 +339,43 @@
 				break;
 
 			case ICMP_UNREACH_NET_UNKNOWN:
+				code = PRC_UNREACH_HOST;
+				break;
+
 			case ICMP_UNREACH_NET_PROHIB:
-				if (icp->icmp_ip.ip_p == IPPROTO_TCP) {
-					code = PRC_UNREACH_PORT;
-					break;
-				}
+				code = PRC_UNREACH_ADMIN_PROHIB;
+				break;
 
 			case ICMP_UNREACH_TOSNET:
-				code = PRC_UNREACH_NET;
+				code = PRC_UNREACH_HOST;
 				break;
 
 			case ICMP_UNREACH_HOST_UNKNOWN:
+				code = PRC_UNREACH_HOST;
+				break;
+
 			case ICMP_UNREACH_ISOLATED:
+				code = PRC_UNREACH_HOST;
+				break;
+
 			case ICMP_UNREACH_HOST_PROHIB:
-				if (icp->icmp_ip.ip_p == IPPROTO_TCP) {
-					code = PRC_UNREACH_PORT;
-					break;
-				}
+				code = PRC_UNREACH_ADMIN_PROHIB;
+				break;
 
 			case ICMP_UNREACH_TOSHOST:
 				code = PRC_UNREACH_HOST;
 				break;
 
 			case ICMP_UNREACH_FILTER_PROHIB:
-				if (icp->icmp_ip.ip_p == IPPROTO_TCP) {
-					code = PRC_UNREACH_PORT;
-					break;
-				}
+				code = PRC_UNREACH_ADMIN_PROHIB;
+				break;
 
 			case ICMP_UNREACH_HOST_PRECEDENCE:
+				code = PRC_UNREACH_HOST;
+				break;
+
 			case ICMP_UNREACH_PRECEDENCE_CUTOFF:
-				code = PRC_UNREACH_PORT;
+				code = PRC_UNREACH_HOST;
 				break;
 
 			default:
diff -ru sys/netinet.old/tcp_subr.c sys/netinet/tcp_subr.c
--- sys/netinet.old/tcp_subr.c	Sun Dec 31 16:07:04 2000
+++ sys/netinet/tcp_subr.c	Sun Dec 31 20:18:15 2000
@@ -135,24 +135,31 @@
     &tcbinfo.ipi_count, 0, "Number of active PCBs");
 
 /*
- * Treat ICMP administratively prohibited like a TCP RST
- * as required by rfc1122 section 3.2.2.1
+ * Treat ICMP unreachables like a TCP RST as required by rfc1122 section 3.2.2.1
+ *
+ * Administatively prohibited kill's sessions regardless of
+ * their current state, other unreachable by default only kill 
+ * sessions if they are in SYN-SENT state, this ensure temporary 
+ * routing problems doesn't kill existing TCP sessions.
+ * This can be overridden by icmp_like_rst_syn_sent_only.
  */
  
-static int	icmp_admin_prohib_like_rst = 1;
-SYSCTL_INT(_net_inet_tcp, OID_AUTO, icmp_admin_prohib_like_rst, CTLFLAG_RW,
-	&icmp_admin_prohib_like_rst, 0, 
-	"Treat ICMP administratively prohibited messages like TCP RST, rfc1122 section 3.2.2.1");
+static int	icmp_unreach_like_rst = 1;
+SYSCTL_INT(_net_inet_tcp, OID_AUTO, icmp_unreach_like_rst, CTLFLAG_RW,
+	&icmp_unreach_like_rst, 0, 
+	"Treat ICMP unreachable messages like TCP RST, rfc1122 section 3.2.2.1");
 
 /*
- * When icmp_admin_prohib_like_rst is enabled, only act on
- * sessions in SYN-SENT state
+ * Control if ICMP unreachable messages other that administratively prohibited
+ * ones will kill sessions not in SYN-SENT state.
+ *
+ * Has no effect unless icmp_unreach_like_rst is enabled.
  */
 
 static int	icmp_like_rst_syn_sent_only = 1;
 SYSCTL_INT(_net_inet_tcp, OID_AUTO, icmp_like_rst_syn_sent_only, CTLFLAG_RW,
 	&icmp_like_rst_syn_sent_only, 0, 
-	"When icmp_admin_prohib_like_rst is enabled, only act on sessions in SYN-SENT state");
+	"When icmp_unreach_like_rst is enabled, only act on sessions in SYN-SENT state");
 
 static void	tcp_cleartaocache __P((void));
 static void	tcp_notify __P((struct inpcb *, int));
@@ -983,17 +990,27 @@
 
 	if (cmd == PRC_QUENCH)
 		notify = tcp_quench;
-	else if ((icmp_admin_prohib_like_rst == 1) && (cmd == PRC_UNREACH_PORT) && 
-			(ip) && ((IP_VHL_HL(ip->ip_vhl) << 2) == sizeof(struct ip))) {
+	else if ((icmp_unreach_like_rst == 1) && ((cmd == PRC_UNREACH_HOST) ||
+			(cmd == PRC_UNREACH_ADMIN_PROHIB)) && (ip) && 
+			((IP_VHL_HL(ip->ip_vhl) << 2) == sizeof(struct ip))) {
 		/*
 		 * Only go here if the length of the IP header in the ICMP packet
 		 * is 20 bytes, that is it doesn't have options, if it does have
 		 * options, we will not have the first 8 bytes of the TCP header,
 		 * and thus we cannot match against TCP source/destination port
 		 * numbers and TCP sequence number.
+		 *
+		 * If PRC_UNREACH_ADMIN_PROHIB drop session regardsless of current
+		 * state, else we check the sysctl icmp_like_rst_syn_sent_only to
+		 * see if we should drop the session only in SYN-SENT state, or
+		 * in all states.
 		 */
 		tcp_seq_check = 1;
-		notify = tcp_drop_syn_sent;
+		if (cmd == PRC_UNREACH_ADMIN_PROHIB) {
+			notify = tcp_drop_all_states;
+		} else {
+			notify = tcp_drop_syn_sent;
+		}
 	} else if (cmd == PRC_MSGSIZE)
 		notify = tcp_mtudisc;
 	else if (!PRC_IS_REDIRECT(cmd) &&
@@ -1146,6 +1163,20 @@
 	struct tcpcb *tp = intotcpcb(inp);
 	if((tp) && ((icmp_like_rst_syn_sent_only == 0) || 
 			(tp->t_state == TCPS_SYN_SENT)))
+		tcp_drop(tp, errno);
+}
+
+/*
+ * When a ICMP unreachable is recieved, drop the
+ * TCP connection, regardless of the state.
+ */
+void
+tcp_drop_all_states(inp, errno)
+	struct inpcb *inp;
+	int errno;
+{
+	struct tcpcb *tp = intotcpcb(inp);
+	if(tp)
 		tcp_drop(tp, errno);
 }
 
diff -ru sys/netinet.old/tcp_var.h sys/netinet/tcp_var.h
--- sys/netinet.old/tcp_var.h	Sun Dec 31 16:07:04 2000
+++ sys/netinet/tcp_var.h	Sun Dec 31 20:30:12 2000
@@ -388,6 +388,7 @@
 void	 tcp_mss __P((struct tcpcb *, int));
 int	 tcp_mssopt __P((struct tcpcb *));
 void	 tcp_drop_syn_sent __P((struct inpcb *, int));
+void	 tcp_drop_all_states __P((struct inpcb *, int));
 void	 tcp_mtudisc __P((struct inpcb *, int));
 struct tcpcb *
 	 tcp_newtcpcb __P((struct inpcb *));

>Release-Note:
>Audit-Trail:
>Unformatted:


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200012312026.eBVKQ1c00576>