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:
parent
89bbe051bb
commit
e4bb5b0572
@ -315,69 +315,37 @@ icmp_input(m, off, proto)
|
|||||||
case ICMP_UNREACH:
|
case ICMP_UNREACH:
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case ICMP_UNREACH_NET:
|
case ICMP_UNREACH_NET:
|
||||||
code = PRC_UNREACH_HOST;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ICMP_UNREACH_HOST:
|
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:
|
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;
|
break;
|
||||||
|
|
||||||
case ICMP_UNREACH_NEEDFRAG:
|
case ICMP_UNREACH_NEEDFRAG:
|
||||||
code = PRC_MSGSIZE;
|
code = PRC_MSGSIZE;
|
||||||
break;
|
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;
|
break;
|
||||||
|
|
||||||
case ICMP_UNREACH_NET_PROHIB:
|
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:
|
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:
|
case ICMP_UNREACH_FILTER_PROHIB:
|
||||||
code = PRC_UNREACH_ADMIN_PROHIB;
|
code = PRC_UNREACH_ADMIN_PROHIB;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ICMP_UNREACH_HOST_PRECEDENCE:
|
|
||||||
code = PRC_UNREACH_HOST;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ICMP_UNREACH_PRECEDENCE_CUTOFF:
|
|
||||||
code = PRC_UNREACH_HOST;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
goto badcode;
|
goto badcode;
|
||||||
}
|
}
|
||||||
|
@ -1429,7 +1429,7 @@ u_char inetctlerrmap[PRC_NCMDS] = {
|
|||||||
EHOSTUNREACH, EHOSTUNREACH, ECONNREFUSED, ECONNREFUSED,
|
EHOSTUNREACH, EHOSTUNREACH, ECONNREFUSED, ECONNREFUSED,
|
||||||
EMSGSIZE, EHOSTUNREACH, 0, 0,
|
EMSGSIZE, EHOSTUNREACH, 0, 0,
|
||||||
0, 0, 0, 0,
|
0, 0, 0, 0,
|
||||||
ENOPROTOOPT
|
ENOPROTOOPT, ENETRESET
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -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,
|
SYSCTL_INT(_net_inet_tcp, OID_AUTO, pcbcount, CTLFLAG_RD,
|
||||||
&tcbinfo.ipi_count, 0, "Number of active PCBs");
|
&tcbinfo.ipi_count, 0, "Number of active PCBs");
|
||||||
|
|
||||||
/*
|
static int icmp_may_rst = 1;
|
||||||
* Treat ICMP unreachables like a TCP RST as required by rfc1122 section 3.2.2.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");
|
||||||
* 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 void tcp_cleartaocache __P((void));
|
static void tcp_cleartaocache __P((void));
|
||||||
static void tcp_notify __P((struct inpcb *, int));
|
static void tcp_notify __P((struct inpcb *, int));
|
||||||
@ -993,35 +970,17 @@ tcp_ctlinput(cmd, sa, vip)
|
|||||||
struct sockaddr *sa;
|
struct sockaddr *sa;
|
||||||
void *vip;
|
void *vip;
|
||||||
{
|
{
|
||||||
register struct ip *ip = vip;
|
struct ip *ip = vip;
|
||||||
register struct tcphdr *th;
|
struct tcphdr *th;
|
||||||
void (*notify) __P((struct inpcb *, int)) = tcp_notify;
|
void (*notify) __P((struct inpcb *, int)) = tcp_notify;
|
||||||
tcp_seq tcp_sequence = 0;
|
tcp_seq tcp_sequence = 0;
|
||||||
int tcp_seq_check = 0;
|
int tcp_seq_check = 0;
|
||||||
|
|
||||||
if (cmd == PRC_QUENCH)
|
if (cmd == PRC_QUENCH)
|
||||||
notify = tcp_quench;
|
notify = tcp_quench;
|
||||||
else if ((icmp_unreach_like_rst == 1) && ((cmd == PRC_UNREACH_HOST) ||
|
else if (icmp_may_rst && cmd == PRC_UNREACH_ADMIN_PROHIB && ip) {
|
||||||
(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;
|
tcp_seq_check = 1;
|
||||||
if (cmd == PRC_UNREACH_ADMIN_PROHIB) {
|
notify = tcp_drop_syn_sent;
|
||||||
notify = tcp_drop_all_states;
|
|
||||||
} else {
|
|
||||||
notify = tcp_drop_syn_sent;
|
|
||||||
}
|
|
||||||
} else if (cmd == PRC_MSGSIZE)
|
} else if (cmd == PRC_MSGSIZE)
|
||||||
notify = tcp_mtudisc;
|
notify = tcp_mtudisc;
|
||||||
else if (PRC_IS_REDIRECT(cmd)) {
|
else if (PRC_IS_REDIRECT(cmd)) {
|
||||||
@ -1173,10 +1132,9 @@ tcp_quench(inp, errno)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When a ICMP unreachable is recieved, drop the
|
* When a specific ICMP unreachable message is received and the
|
||||||
* TCP connection, depending on the sysctl
|
* connection state is SYN-SENT, drop the connection. This behavior
|
||||||
* icmp_like_rst_syn_sent_only, it only drops
|
* is controlled by the icmp_may_rst sysctl.
|
||||||
* the session if it's in SYN-SENT state
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
tcp_drop_syn_sent(inp, errno)
|
tcp_drop_syn_sent(inp, errno)
|
||||||
@ -1184,22 +1142,8 @@ tcp_drop_syn_sent(inp, errno)
|
|||||||
int errno;
|
int errno;
|
||||||
{
|
{
|
||||||
struct tcpcb *tp = intotcpcb(inp);
|
struct tcpcb *tp = intotcpcb(inp);
|
||||||
if((tp) && ((icmp_like_rst_syn_sent_only == 0) ||
|
|
||||||
(tp->t_state == TCPS_SYN_SENT)))
|
|
||||||
tcp_drop(tp, errno);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
if (tp && tp->t_state == TCPS_SYN_SENT)
|
||||||
* 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);
|
tcp_drop(tp, errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
SYSCTL_INT(_net_inet_tcp, OID_AUTO, pcbcount, CTLFLAG_RD,
|
||||||
&tcbinfo.ipi_count, 0, "Number of active PCBs");
|
&tcbinfo.ipi_count, 0, "Number of active PCBs");
|
||||||
|
|
||||||
/*
|
static int icmp_may_rst = 1;
|
||||||
* Treat ICMP unreachables like a TCP RST as required by rfc1122 section 3.2.2.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");
|
||||||
* 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 void tcp_cleartaocache __P((void));
|
static void tcp_cleartaocache __P((void));
|
||||||
static void tcp_notify __P((struct inpcb *, int));
|
static void tcp_notify __P((struct inpcb *, int));
|
||||||
@ -993,35 +970,17 @@ tcp_ctlinput(cmd, sa, vip)
|
|||||||
struct sockaddr *sa;
|
struct sockaddr *sa;
|
||||||
void *vip;
|
void *vip;
|
||||||
{
|
{
|
||||||
register struct ip *ip = vip;
|
struct ip *ip = vip;
|
||||||
register struct tcphdr *th;
|
struct tcphdr *th;
|
||||||
void (*notify) __P((struct inpcb *, int)) = tcp_notify;
|
void (*notify) __P((struct inpcb *, int)) = tcp_notify;
|
||||||
tcp_seq tcp_sequence = 0;
|
tcp_seq tcp_sequence = 0;
|
||||||
int tcp_seq_check = 0;
|
int tcp_seq_check = 0;
|
||||||
|
|
||||||
if (cmd == PRC_QUENCH)
|
if (cmd == PRC_QUENCH)
|
||||||
notify = tcp_quench;
|
notify = tcp_quench;
|
||||||
else if ((icmp_unreach_like_rst == 1) && ((cmd == PRC_UNREACH_HOST) ||
|
else if (icmp_may_rst && cmd == PRC_UNREACH_ADMIN_PROHIB && ip) {
|
||||||
(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;
|
tcp_seq_check = 1;
|
||||||
if (cmd == PRC_UNREACH_ADMIN_PROHIB) {
|
notify = tcp_drop_syn_sent;
|
||||||
notify = tcp_drop_all_states;
|
|
||||||
} else {
|
|
||||||
notify = tcp_drop_syn_sent;
|
|
||||||
}
|
|
||||||
} else if (cmd == PRC_MSGSIZE)
|
} else if (cmd == PRC_MSGSIZE)
|
||||||
notify = tcp_mtudisc;
|
notify = tcp_mtudisc;
|
||||||
else if (PRC_IS_REDIRECT(cmd)) {
|
else if (PRC_IS_REDIRECT(cmd)) {
|
||||||
@ -1173,10 +1132,9 @@ tcp_quench(inp, errno)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When a ICMP unreachable is recieved, drop the
|
* When a specific ICMP unreachable message is received and the
|
||||||
* TCP connection, depending on the sysctl
|
* connection state is SYN-SENT, drop the connection. This behavior
|
||||||
* icmp_like_rst_syn_sent_only, it only drops
|
* is controlled by the icmp_may_rst sysctl.
|
||||||
* the session if it's in SYN-SENT state
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
tcp_drop_syn_sent(inp, errno)
|
tcp_drop_syn_sent(inp, errno)
|
||||||
@ -1184,22 +1142,8 @@ tcp_drop_syn_sent(inp, errno)
|
|||||||
int errno;
|
int errno;
|
||||||
{
|
{
|
||||||
struct tcpcb *tp = intotcpcb(inp);
|
struct tcpcb *tp = intotcpcb(inp);
|
||||||
if((tp) && ((icmp_like_rst_syn_sent_only == 0) ||
|
|
||||||
(tp->t_state == TCPS_SYN_SENT)))
|
|
||||||
tcp_drop(tp, errno);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
if (tp && tp->t_state == TCPS_SYN_SENT)
|
||||||
* 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);
|
tcp_drop(tp, errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user