When tcp_output() receives an error upon sending a packet it reverts parts

of its internal state to ignore the failed send and try again a bit later.
If the error is EPERM the packet got blocked by the local firewall and the
revert may cause the session to get stuck and retry indefinitely.  This way
we treat it like a packet loss and let the retransmit timer and timeouts
do their work over time.

The correct behavior is to drop a connection that gets an EPERM error.
However this _may_ introduce some POLA problems and a two commit approach
was chosen.

Discussed with:	glebius
PR:		kern/25986
PR:		kern/102653
This commit is contained in:
Andre Oppermann 2006-09-28 18:02:46 +00:00
parent 7699548f1b
commit 2c30ec0a1f

View File

@ -1137,9 +1137,18 @@ tcp_output(struct tcpcb *tp)
/*
* We know that the packet was lost, so back out the
* sequence number advance, if any.
*
* If the error is EPERM the packet got blocked by the
* local firewall. Normally we should terminate the
* connection but the blocking may have been spurious
* due to a firewall reconfiguration cycle. So we treat
* it like a packet loss and let the retransmit timer and
* timeouts do their work over time.
* XXX: It is a POLA question whether calling tcp_drop right
* away would be the really correct behavior instead.
*/
if ((tp->t_flags & TF_FORCEDATA) == 0 ||
!callout_active(tp->tt_persist)) {
if (error != EPERM && ((tp->t_flags & TF_FORCEDATA) == 0 ||
!callout_active(tp->tt_persist))) {
/*
* No need to check for TH_FIN here because
* the TF_SENTFIN flag handles that case.
@ -1155,6 +1164,10 @@ tcp_output(struct tcpcb *tp)
tp->snd_nxt -= len;
}
}
if (error == EPERM) {
tp->t_softerror = error;
return (error);
}
out:
SOCKBUF_UNLOCK_ASSERT(&so->so_snd); /* Check gotos. */