Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 25 Feb 2001 19:01:23 +0100
From:      Jesper Skriver <jesper@FreeBSD.org>
To:        audit@FreeBSD.org, Jonathan Lemon <jlemon@FreeBSD.org>
Subject:   please review: MFC of "react to ICMP unreachables"
Message-ID:  <20010225190123.A33539@skriver.dk>

next in thread | raw e-mail | index | archive | help
The below is a combination of what PHK originally committed (based on my
PR's), my and jlemon's later extensions.

I hope to get this in before 4.3-RELEASE

Suggestion to commit message

MFC:

src/sys/sys/protosw.h:        rev 1.32
src/sys/netinet/ip_icmp.c:    rev 1.48, 1.52 & 1.53
src/sys/netinet/ip_input.c:   rev 1.155 
src/sys/netinet/tcp_subr.c:   rev 1.95, 1.86, 1.88 & 1.92
src/sys/netinet/tcp_var.h:    rev 1.62 & 1.63 
src/sys/netinet/udp_usrreq.c: rev 1.80
src/sys/netinet/in_pcb.c:     rev 1.70, 1.71 & 1.76
src/sys/netinet/in_pcb.h:     rev 1.35

Allow ICMP unreachables which map into PRC_UNREACH_ADMIN_PROHIB to
reset TCP connections which are in the SYN_SENT state, if the sequence
number in the echoed ICMP reply is correct.  This behavior can be
controlled by the sysctl net.inet.tcp.icmp_may_rst.
  
Currently, only subtypes 2,3,10,11,12 are treated as such
(port, protocol and administrative unreachables).
  
Assocaiate an error code with these resets which is reported to the
user application: ENETRESET.

/Jesper

-- 
Jesper Skriver, jesper(at)skriver(dot)dk  -  CCIE #5456
Work:    Network manager   @ AS3292 (Tele Danmark DataNetworks)
Private: FreeBSD committer @ AS2109 (A much smaller network ;-)

One Unix to rule them all, One Resolver to find them,
One IP to bring them all and in the zone to bind them.




Index: src/sys/netinet/in_pcb.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/in_pcb.c,v
retrieving revision 1.59.2.6
diff -u -r1.59.2.6 in_pcb.c
--- src/sys/netinet/in_pcb.c	2001/02/24 18:36:01	1.59.2.6
+++ src/sys/netinet/in_pcb.c	2001/02/25 16:48:53
@@ -62,6 +62,8 @@
 #include <netinet/in_pcb.h>
 #include <netinet/in_var.h>
 #include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_var.h>
 #ifdef INET6
 #include <netinet/ip6.h>
 #include <netinet6/ip6_var.h>
@@ -642,15 +644,20 @@
  * 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.
+ *
+ * If tcp_seq_check != 0 it also checks if tcp_sequence is
+ * a valid TCP sequence number for the session.
  */
 void
-in_pcbnotify(head, dst, fport_arg, laddr, lport_arg, cmd, notify)
+in_pcbnotify(head, dst, fport_arg, laddr, lport_arg, cmd, notify, tcp_sequence, tcp_seq_check)
 	struct inpcbhead *head;
 	struct sockaddr *dst;
 	u_int fport_arg, lport_arg;
 	struct in_addr laddr;
 	int cmd;
 	void (*notify) __P((struct inpcb *, int));
+	u_int32_t tcp_sequence;
+	int tcp_seq_check;
 {
 	register struct inpcb *inp, *oinp;
 	struct in_addr faddr;
@@ -679,6 +686,17 @@
 			inp = inp->inp_list.le_next;
 			continue;
 		}
+		/*
+		 * If tcp_seq_check is set, then skip sessions where
+		 * the sequence number is not one of a unacknowledged
+		 * packet.
+		 *
+		 * If it doesn't match, we break the loop, as only a
+		 * single session can match on src/dst ip addresses
+		 * and TCP port numbers.
+		 */
+		if ((tcp_seq_check == 1) && (tcp_seq_vs_sess(inp, tcp_sequence) == 0))
+			break;
 		oinp = inp;
 		inp = inp->inp_list.le_next;
 		if (notify)
Index: src/sys/netinet/in_pcb.h
===================================================================
RCS file: /home/ncvs/src/sys/netinet/in_pcb.h,v
retrieving revision 1.32.2.1
diff -u -r1.32.2.1 in_pcb.h
--- src/sys/netinet/in_pcb.h	2001/02/24 18:36:01	1.32.2.1
+++ src/sys/netinet/in_pcb.h	2001/02/25 16:53:39
@@ -291,7 +291,8 @@
 			       struct in_addr, u_int, struct in_addr, u_int,
 			       int, struct ifnet *));
 void	in_pcbnotify __P((struct inpcbhead *, struct sockaddr *,
-	    u_int, struct in_addr, u_int, int, void (*)(struct inpcb *, int)));
+	    u_int, struct in_addr, u_int, int, void (*)(struct inpcb *, int),
+	    u_int32_t, int));
 void	in_pcbnotifyall __P((struct inpcbhead *, struct sockaddr *,
 	    int, void (*)(struct inpcb *, int)));
 void	in_pcbrehash __P((struct inpcb *));
Index: src/sys/netinet/ip_icmp.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/ip_icmp.c,v
retrieving revision 1.39.2.4
diff -u -r1.39.2.4 ip_icmp.c
--- src/sys/netinet/ip_icmp.c	2001/02/24 21:35:18	1.39.2.4
+++ src/sys/netinet/ip_icmp.c	2001/02/25 17:10:29
@@ -324,33 +324,34 @@
 		switch (code) {
 			case ICMP_UNREACH_NET:
 			case ICMP_UNREACH_HOST:
-			case ICMP_UNREACH_PROTOCOL:
-			case ICMP_UNREACH_PORT:
 			case ICMP_UNREACH_SRCFAIL:
-				code += PRC_UNREACH_NET;
+			case ICMP_UNREACH_NET_UNKNOWN:
+			case ICMP_UNREACH_HOST_UNKNOWN:
+			case ICMP_UNREACH_ISOLATED:
+			case ICMP_UNREACH_TOSNET:
+			case ICMP_UNREACH_TOSHOST:
+			case ICMP_UNREACH_HOST_PRECEDENCE:
+			case ICMP_UNREACH_PRECEDENCE_CUTOFF:
+				code = PRC_UNREACH_NET;
 				break;
 
 			case ICMP_UNREACH_NEEDFRAG:
 				code = PRC_MSGSIZE;
 				break;
 
-			case ICMP_UNREACH_NET_UNKNOWN:
-			case ICMP_UNREACH_NET_PROHIB:
-			case ICMP_UNREACH_TOSNET:
-				code = PRC_UNREACH_NET;
+			/*
+			 * RFC 1122, Sections 3.2.2.1 and 4.2.3.9.
+			 * Treat subcodes 2,3 as immediate RST
+			 */
+			case ICMP_UNREACH_PROTOCOL:
+			case ICMP_UNREACH_PORT:
+				code = PRC_UNREACH_ADMIN_PROHIB;
 				break;
 
-			case ICMP_UNREACH_HOST_UNKNOWN:
-			case ICMP_UNREACH_ISOLATED:
+			case ICMP_UNREACH_NET_PROHIB:
 			case ICMP_UNREACH_HOST_PROHIB:
-			case ICMP_UNREACH_TOSHOST:
-				code = PRC_UNREACH_HOST;
-				break;
-
 			case ICMP_UNREACH_FILTER_PROHIB:
-			case ICMP_UNREACH_HOST_PRECEDENCE:
-			case ICMP_UNREACH_PRECEDENCE_CUTOFF:
-				code = PRC_UNREACH_PORT;
+				code = PRC_UNREACH_ADMIN_PROHIB;
 				break;
 
 			default:
Index: src/sys/netinet/ip_input.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/ip_input.c,v
retrieving revision 1.130.2.13
diff -u -r1.130.2.13 ip_input.c
--- src/sys/netinet/ip_input.c	2001/02/07 01:03:13	1.130.2.13
+++ src/sys/netinet/ip_input.c	2001/02/25 17:10:29
@@ -1427,7 +1427,7 @@
 	EHOSTUNREACH,	EHOSTUNREACH,	ECONNREFUSED,	ECONNREFUSED,
 	EMSGSIZE,	EHOSTUNREACH,	0,		0,
 	0,		0,		0,		0,
-	ENOPROTOOPT
+	ENOPROTOOPT,	ENETRESET
 };
 
 /*
Index: src/sys/netinet/tcp_subr.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/tcp_subr.c,v
retrieving revision 1.73.2.7
diff -u -r1.73.2.7 tcp_subr.c
--- src/sys/netinet/tcp_subr.c	2001/02/24 18:36:01	1.73.2.7
+++ src/sys/netinet/tcp_subr.c	2001/02/25 17:10:30
@@ -135,6 +135,10 @@
 SYSCTL_INT(_net_inet_tcp, OID_AUTO, pcbcount, CTLFLAG_RD, 
     &tcbinfo.ipi_count, 0, "Number of active PCBs");
 
+static int	icmp_may_rst = 1;
+SYSCTL_INT(_net_inet_tcp, OID_AUTO, icmp_may_rst, CTLFLAG_RW, &icmp_may_rst, 0, 
+    "Certain ICMP unreachable messages may abort connections in SYN_SENT");
+
 static void	tcp_cleartaocache __P((void));
 static void	tcp_notify __P((struct inpcb *, int));
 
@@ -956,13 +960,18 @@
 	struct sockaddr *sa;
 	void *vip;
 {
-	register struct ip *ip = vip;
-	register struct tcphdr *th;
+	struct ip *ip = vip;
+	struct tcphdr *th;
 	void (*notify) __P((struct inpcb *, int)) = tcp_notify;
+	tcp_seq tcp_sequence = 0;
+	int tcp_seq_check = 0;
 
 	if (cmd == PRC_QUENCH)
 		notify = tcp_quench;
-	else if (cmd == PRC_MSGSIZE)
+	else if (icmp_may_rst && cmd == PRC_UNREACH_ADMIN_PROHIB && ip) {
+		tcp_seq_check = 1;
+		notify = tcp_drop_syn_sent;
+	} else if (cmd == PRC_MSGSIZE)
 		notify = tcp_mtudisc;
 	else if (PRC_IS_REDIRECT(cmd)) {
 		/*
@@ -981,8 +990,10 @@
 	if (ip) {
 		th = (struct tcphdr *)((caddr_t)ip 
 				       + (IP_VHL_HL(ip->ip_vhl) << 2));
+		if (tcp_seq_check == 1)
+			tcp_sequence = ntohl(th->th_seq);
 		in_pcbnotify(&tcb, sa, th->th_dport, ip->ip_src, th->th_sport,
-			cmd, notify);
+			cmd, notify, tcp_sequence, tcp_seq_check);
 	} else
 		in_pcbnotifyall(&tcb, sa, cmd, notify);
 }
@@ -1071,6 +1082,30 @@
 #endif /* INET6 */
 
 /*
+ * Check if the supplied TCP sequence number is a sequence number
+ * for a sent but unacknowledged packet on the given TCP session.
+ */
+int
+tcp_seq_vs_sess(inp, tcp_sequence)
+	struct inpcb *inp;
+	tcp_seq tcp_sequence;
+{
+	struct tcpcb *tp = intotcpcb(inp);
+	/*
+	 * If the sequence number is less than that of the last 
+	 * unacknowledged packet, or greater than that of the 
+	 * last sent, the given sequence number is not that
+	 * of a sent but unacknowledged packet for this session.
+	 */
+	if (SEQ_LT(tcp_sequence, tp->snd_una) ||
+			SEQ_GT(tcp_sequence, tp->snd_max)) {
+		return(0);
+	} else {
+		return(1);
+	}
+}
+
+/*
  * When a source quench is received, close congestion window
  * to one segment.  We will gradually open it again as we proceed.
  */
@@ -1083,6 +1118,22 @@
 
 	if (tp)
 		tp->snd_cwnd = tp->t_maxseg;
+}
+
+/*
+ * When a specific ICMP unreachable message is received and the
+ * connection state is SYN-SENT, drop the connection.  This behavior
+ * is controlled by the icmp_may_rst sysctl.
+ */
+void
+tcp_drop_syn_sent(inp, errno)
+	struct inpcb *inp;
+	int errno;
+{
+	struct tcpcb *tp = intotcpcb(inp);
+
+	if (tp && tp->t_state == TCPS_SYN_SENT)
+		tcp_drop(tp, errno);
 }
 
 /*
Index: src/sys/netinet/tcp_var.h
===================================================================
RCS file: /home/ncvs/src/sys/netinet/tcp_var.h,v
retrieving revision 1.56.2.2
diff -u -r1.56.2.2 tcp_var.h
--- src/sys/netinet/tcp_var.h	2000/08/16 06:14:23	1.56.2.2
+++ src/sys/netinet/tcp_var.h	2001/02/25 17:21:49
@@ -384,10 +384,12 @@
 void	 tcp_input __P((struct mbuf *, int, int));
 void	 tcp_mss __P((struct tcpcb *, int));
 int	 tcp_mssopt __P((struct tcpcb *));
+void	 tcp_drop_syn_sent __P((struct inpcb *, int));
 void	 tcp_mtudisc __P((struct inpcb *, int));
 struct tcpcb *
 	 tcp_newtcpcb __P((struct inpcb *));
 int	 tcp_output __P((struct tcpcb *));
+int	 tcp_seq_vs_sess __P((struct inpcb *, tcp_seq));
 void	 tcp_quench __P((struct inpcb *, int));
 void	 tcp_respond __P((struct tcpcb *, void *,
 	    struct tcphdr *, struct mbuf *, tcp_seq, tcp_seq, int));
Index: src/sys/netinet/udp_usrreq.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/udp_usrreq.c,v
retrieving revision 1.64.2.7
diff -u -r1.64.2.7 udp_usrreq.c
--- src/sys/netinet/udp_usrreq.c	2001/02/24 21:35:18	1.64.2.7
+++ src/sys/netinet/udp_usrreq.c	2001/02/25 16:52:45
@@ -526,7 +526,7 @@
 	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, notify);
+			cmd, notify, 0, 0);
 	} else
 		in_pcbnotifyall(&udb, sa, cmd, notify);
 }
Index: src/sys/sys/protosw.h
===================================================================
RCS file: /home/ncvs/src/sys/sys/protosw.h,v
retrieving revision 1.28
diff -u -r1.28 protosw.h
--- src/sys/sys/protosw.h	1999/12/29 04:24:45	1.28
+++ src/sys/sys/protosw.h	2001/02/25 16:48:53
@@ -269,8 +269,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)
@@ -282,7 +283,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


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




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