Date: Fri, 24 Nov 2000 23:20:33 +0100 (CET) From: jesper@skriver.dk To: FreeBSD-gnats-submit@freebsd.org Cc: jesper@skriver.dk Subject: kern/23086: implement rfc1122 3.2.2.1 Message-ID: <20001124222033.2BBFF3E4F@freesbee.wheel.dk>
next in thread | raw e-mail | index | archive | help
>Number: 23086
>Category: kern
>Synopsis: implement rfc1122 3.2.2.1, react to ICMP administratively prohibited
>Confidential: no
>Severity: non-critical
>Priority: medium
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: change-request
>Submitter-Id: current-users
>Arrival-Date: Fri Nov 24 14:30:01 PST 2000
>Closed-Date:
>Last-Modified:
>Originator: Jesper Skriver
>Release: FreeBSD 5.0-CURRENT i386
>Organization:
Tele Danmark
>Environment:
System: FreeBSD tam 5.0-CURRENT FreeBSD 5.0-CURRENT #0: Mon Nov 20 22:19:22 CET 2000 root@tam:/usr/obj/usr/src/sys/TAM2 i386
Any FreeBSD machine.
>Description:
We currently does not react to ICMP administratively prohibited messages
send by routers when they deny our traffic, this causes a timeout when trying to
connect to TCP ports/services on a remote host, which is blocked by routers or
firewalls.
rfc1122 (Requirements for Internet Hosts) section 3.2.2.1 actually require
that we treat such a message for a TCP session, that we treat it like if we had
recieved a RST.
quote begin.
A Destination Unreachable message that is received MUST be
reported to the transport layer. The transport layer SHOULD
use the information appropriately; for example, see Sections
4.1.3.3, 4.2.3.9, and 4.2.4 below. A transport protocol
that has its own mechanism for notifying the sender that a
port is unreachable (e.g., TCP, which sends RST segments)
MUST nevertheless accept an ICMP Port Unreachable for the
same purpose.
quote end.
I've written a small extension that implement this, it also create a sysctl
"net.inet.tcp.icmp_admin_prohib_like_rst" to control if this new behaviour
is activated.
When it's activated (set to 1) we'll treat a ICMP administratively prohibited
message (icmp type 3 code 9, 10 and 13) for a TCP sessions, as if we recived
a TCP RST, but only if the TCP session is in SYN_SENT state.
The reason for only reacting when in SYN_SENT state, is that this will solve
the problem, and at the same time minimize the risk of this being abused.
I suggest that we enable this new behaviour by default, but it would be a
change of current behaviour, so if people prefer to leave it disabled by
default, at least for now, this would be ok for me, the attached diff
actually have the sysctl set to 0 by default.
>How-To-Repeat:
Try to telnet to any host where this is blocked by a router/firewall
that sends ICMP administratively prohibited messages, and do this with this
sysctl set to 0 and to 1, like below:
tam# sysctl -w net.inet.tcp.icmp_admin_prohib_like_rst=0
net.inet.tcp.icmp_admin_prohib_like_rst: 1 -> 0
tam# time telnet 193.89.247.125 25
Trying 193.89.247.125...
telnet: connect to address 193.89.247.125: Connection refused
telnet: Unable to connect to remote host
0.012u 0.000s 0:45.06 0.0% 88+164k 0+0io 0pf+0w
tam# sysctl -w net.inet.tcp.icmp_admin_prohib_like_rst=1
net.inet.tcp.icmp_admin_prohib_like_rst: 0 -> 1
tam# time telnet 193.89.247.125 25
Trying 193.89.247.125...
telnet: connect to address 193.89.247.125: Connection refused
telnet: Unable to connect to remote host
0.012u 0.000s 0:00.06 16.6% 88+152k 0+0io 0pf+0w
This saved 45 seconds ...
>Fix:
Apply the below diff
diff -ru sys/netinet.old/ip_icmp.c sys/netinet/ip_icmp.c
--- sys/netinet.old/ip_icmp.c Thu Nov 2 10:46:23 2000
+++ sys/netinet/ip_icmp.c Mon Nov 20 22:33:43 2000
@@ -328,6 +328,11 @@
case ICMP_UNREACH_NET_UNKNOWN:
case ICMP_UNREACH_NET_PROHIB:
+ if (icp->icmp_ip.ip_p == IPPROTO_TCP) {
+ code = PRC_UNREACH_PORT;
+ break;
+ }
+
case ICMP_UNREACH_TOSNET:
code = PRC_UNREACH_NET;
break;
@@ -335,11 +340,21 @@
case ICMP_UNREACH_HOST_UNKNOWN:
case ICMP_UNREACH_ISOLATED:
case ICMP_UNREACH_HOST_PROHIB:
+ if (icp->icmp_ip.ip_p == IPPROTO_TCP) {
+ code = PRC_UNREACH_PORT;
+ 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;
+ }
+
case ICMP_UNREACH_HOST_PRECEDENCE:
case ICMP_UNREACH_PRECEDENCE_CUTOFF:
code = PRC_UNREACH_PORT;
diff -ru sys/netinet.old/tcp_subr.c sys/netinet/tcp_subr.c
--- sys/netinet.old/tcp_subr.c Fri Oct 27 13:45:41 2000
+++ sys/netinet/tcp_subr.c Tue Nov 21 21:16:27 2000
@@ -134,6 +134,15 @@
SYSCTL_INT(_net_inet_tcp, OID_AUTO, pcbcount, CTLFLAG_RD,
&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
+ */
+
+static int icmp_admin_prohib_like_rst = 0;
+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 void tcp_cleartaocache __P((void));
static void tcp_notify __P((struct inpcb *, int));
@@ -961,6 +970,8 @@
if (cmd == PRC_QUENCH)
notify = tcp_quench;
+ else if ((icmp_admin_prohib_like_rst == 1) && (cmd == PRC_UNREACH_PORT) && (ip))
+ notify = tcp_drop_syn_sent;
else if (cmd == PRC_MSGSIZE)
notify = tcp_mtudisc;
else if (!PRC_IS_REDIRECT(cmd) &&
@@ -1071,6 +1082,20 @@
if (tp)
tp->snd_cwnd = tp->t_maxseg;
+}
+
+/*
+ * When a ICMP unreachable is recieved, drop the
+ * TCP connection, but only if in SYN_SENT
+ */
+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);
}
/*
diff -ru sys/netinet.old/tcp_var.h sys/netinet/tcp_var.h
--- sys/netinet.old/tcp_var.h Sat Jul 22 01:26:37 2000
+++ sys/netinet/tcp_var.h Sun Nov 19 21:17:55 2000
@@ -387,6 +387,7 @@
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 *));
>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?20001124222033.2BBFF3E4F>
