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>
