summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorphk <phk@FreeBSD.org>2000-12-16 19:42:06 +0000
committerphk <phk@FreeBSD.org>2000-12-16 19:42:06 +0000
commit7c7359c504e271891625f81ed6c0fa9e7f8e2f7b (patch)
tree335a89b20197e63564e09fa2c29b77118ea8679d /sys
parent10d3e9963aa4451ef5328ff50b0e57d9fe52d71b (diff)
downloadFreeBSD-src-7c7359c504e271891625f81ed6c0fa9e7f8e2f7b.zip
FreeBSD-src-7c7359c504e271891625f81ed6c0fa9e7f8e2f7b.tar.gz
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 requi re 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. PR: 23086 Submitted by: Jesper Skriver <jesper@skriver.dk>
Diffstat (limited to 'sys')
-rw-r--r--sys/netinet/ip_icmp.c15
-rw-r--r--sys/netinet/tcp_subr.c25
-rw-r--r--sys/netinet/tcp_timewait.c25
-rw-r--r--sys/netinet/tcp_var.h1
4 files changed, 66 insertions, 0 deletions
diff --git a/sys/netinet/ip_icmp.c b/sys/netinet/ip_icmp.c
index 5a44807..11cb500 100644
--- a/sys/netinet/ip_icmp.c
+++ b/sys/netinet/ip_icmp.c
@@ -328,6 +328,11 @@ icmp_input(m, off, proto)
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 @@ icmp_input(m, off, proto)
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 --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c
index 64302c6..fe00373 100644
--- a/sys/netinet/tcp_subr.c
+++ b/sys/netinet/tcp_subr.c
@@ -134,6 +134,15 @@ SYSCTL_INT(_net_inet_tcp, OID_AUTO, do_tcpdrain, CTLFLAG_RW, &do_tcpdrain, 0,
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 @@ tcp_ctlinput(cmd, sa, vip)
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) &&
@@ -1074,6 +1085,20 @@ tcp_quench(inp, errno)
}
/*
+ * 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);
+}
+
+/*
* When `need fragmentation' ICMP is received, update our idea of the MSS
* based on the new value in the route. Also nudge TCP to send something,
* since we know the packet we just sent was dropped.
diff --git a/sys/netinet/tcp_timewait.c b/sys/netinet/tcp_timewait.c
index 64302c6..fe00373 100644
--- a/sys/netinet/tcp_timewait.c
+++ b/sys/netinet/tcp_timewait.c
@@ -134,6 +134,15 @@ SYSCTL_INT(_net_inet_tcp, OID_AUTO, do_tcpdrain, CTLFLAG_RW, &do_tcpdrain, 0,
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 @@ tcp_ctlinput(cmd, sa, vip)
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) &&
@@ -1074,6 +1085,20 @@ tcp_quench(inp, errno)
}
/*
+ * 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);
+}
+
+/*
* When `need fragmentation' ICMP is received, update our idea of the MSS
* based on the new value in the route. Also nudge TCP to send something,
* since we know the packet we just sent was dropped.
diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h
index af18c09..e361caf 100644
--- a/sys/netinet/tcp_var.h
+++ b/sys/netinet/tcp_var.h
@@ -387,6 +387,7 @@ void tcp_init __P((void));
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 *));
OpenPOWER on IntegriCloud