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.

Disallow resetting TCP sessions which are not in a SYN_SENT state.

Reviewed by: jesper, -net
This commit is contained in:
Jonathan Lemon 2001-02-23 20:51:46 +00:00
parent 89bbe051bb
commit e4bb5b0572
4 changed files with 38 additions and 182 deletions

View File

@ -315,69 +315,37 @@ icmp_input(m, off, proto)
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_HOST;
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:
code = PRC_UNREACH_HOST;
/*
* 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_NET_PROHIB:
code = PRC_UNREACH_ADMIN_PROHIB;
break;
case ICMP_UNREACH_TOSNET:
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:
code = PRC_UNREACH_ADMIN_PROHIB;
break;
case ICMP_UNREACH_TOSHOST:
code = PRC_UNREACH_HOST;
break;
case ICMP_UNREACH_FILTER_PROHIB:
code = PRC_UNREACH_ADMIN_PROHIB;
break;
case ICMP_UNREACH_HOST_PRECEDENCE:
code = PRC_UNREACH_HOST;
break;
case ICMP_UNREACH_PRECEDENCE_CUTOFF:
code = PRC_UNREACH_HOST;
break;
default:
goto badcode;
}

View File

@ -1429,7 +1429,7 @@ u_char inetctlerrmap[PRC_NCMDS] = {
EHOSTUNREACH, EHOSTUNREACH, ECONNREFUSED, ECONNREFUSED,
EMSGSIZE, EHOSTUNREACH, 0, 0,
0, 0, 0, 0,
ENOPROTOOPT
ENOPROTOOPT, ENETRESET
};
/*

View File

@ -134,32 +134,9 @@ 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 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_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");
/*
* 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_unreach_like_rst is enabled, only act on sessions in SYN-SENT state");
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));
@ -993,35 +970,17 @@ tcp_ctlinput(cmd, sa, vip)
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 ((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.
*/
else if (icmp_may_rst && cmd == PRC_UNREACH_ADMIN_PROHIB && ip) {
tcp_seq_check = 1;
if (cmd == PRC_UNREACH_ADMIN_PROHIB) {
notify = tcp_drop_all_states;
} else {
notify = tcp_drop_syn_sent;
}
notify = tcp_drop_syn_sent;
} else if (cmd == PRC_MSGSIZE)
notify = tcp_mtudisc;
else if (PRC_IS_REDIRECT(cmd)) {
@ -1173,10 +1132,9 @@ tcp_quench(inp, errno)
}
/*
* When a ICMP unreachable is recieved, drop the
* TCP connection, depending on the sysctl
* icmp_like_rst_syn_sent_only, it only drops
* the session if it's in SYN-SENT state
* 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)
@ -1184,22 +1142,8 @@ tcp_drop_syn_sent(inp, errno)
int errno;
{
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)
if (tp && tp->t_state == TCPS_SYN_SENT)
tcp_drop(tp, errno);
}

View File

@ -134,32 +134,9 @@ 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 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_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");
/*
* 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_unreach_like_rst is enabled, only act on sessions in SYN-SENT state");
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));
@ -993,35 +970,17 @@ tcp_ctlinput(cmd, sa, vip)
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 ((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.
*/
else if (icmp_may_rst && cmd == PRC_UNREACH_ADMIN_PROHIB && ip) {
tcp_seq_check = 1;
if (cmd == PRC_UNREACH_ADMIN_PROHIB) {
notify = tcp_drop_all_states;
} else {
notify = tcp_drop_syn_sent;
}
notify = tcp_drop_syn_sent;
} else if (cmd == PRC_MSGSIZE)
notify = tcp_mtudisc;
else if (PRC_IS_REDIRECT(cmd)) {
@ -1173,10 +1132,9 @@ tcp_quench(inp, errno)
}
/*
* When a ICMP unreachable is recieved, drop the
* TCP connection, depending on the sysctl
* icmp_like_rst_syn_sent_only, it only drops
* the session if it's in SYN-SENT state
* 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)
@ -1184,22 +1142,8 @@ tcp_drop_syn_sent(inp, errno)
int errno;
{
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)
if (tp && tp->t_state == TCPS_SYN_SENT)
tcp_drop(tp, errno);
}