|
@@ -45,8 +45,8 @@
|
|
/* Protects conntrack->proto.tcp */
|
|
/* Protects conntrack->proto.tcp */
|
|
static DEFINE_RWLOCK(tcp_lock);
|
|
static DEFINE_RWLOCK(tcp_lock);
|
|
|
|
|
|
-/* "Be conservative in what you do,
|
|
|
|
- be liberal in what you accept from others."
|
|
|
|
|
|
+/* "Be conservative in what you do,
|
|
|
|
+ be liberal in what you accept from others."
|
|
If it's non-zero, we mark only out of window RST segments as INVALID. */
|
|
If it's non-zero, we mark only out of window RST segments as INVALID. */
|
|
int ip_ct_tcp_be_liberal __read_mostly = 0;
|
|
int ip_ct_tcp_be_liberal __read_mostly = 0;
|
|
|
|
|
|
@@ -54,8 +54,8 @@ int ip_ct_tcp_be_liberal __read_mostly = 0;
|
|
connections. */
|
|
connections. */
|
|
int ip_ct_tcp_loose __read_mostly = 1;
|
|
int ip_ct_tcp_loose __read_mostly = 1;
|
|
|
|
|
|
-/* Max number of the retransmitted packets without receiving an (acceptable)
|
|
|
|
- ACK from the destination. If this number is reached, a shorter timer
|
|
|
|
|
|
+/* Max number of the retransmitted packets without receiving an (acceptable)
|
|
|
|
+ ACK from the destination. If this number is reached, a shorter timer
|
|
will be started. */
|
|
will be started. */
|
|
int ip_ct_tcp_max_retrans __read_mostly = 3;
|
|
int ip_ct_tcp_max_retrans __read_mostly = 3;
|
|
|
|
|
|
@@ -74,7 +74,7 @@ static const char *tcp_conntrack_names[] = {
|
|
"CLOSE",
|
|
"CLOSE",
|
|
"LISTEN"
|
|
"LISTEN"
|
|
};
|
|
};
|
|
-
|
|
|
|
|
|
+
|
|
#define SECS * HZ
|
|
#define SECS * HZ
|
|
#define MINS * 60 SECS
|
|
#define MINS * 60 SECS
|
|
#define HOURS * 60 MINS
|
|
#define HOURS * 60 MINS
|
|
@@ -90,10 +90,10 @@ unsigned int ip_ct_tcp_timeout_time_wait __read_mostly = 2 MINS;
|
|
unsigned int ip_ct_tcp_timeout_close __read_mostly = 10 SECS;
|
|
unsigned int ip_ct_tcp_timeout_close __read_mostly = 10 SECS;
|
|
|
|
|
|
/* RFC1122 says the R2 limit should be at least 100 seconds.
|
|
/* RFC1122 says the R2 limit should be at least 100 seconds.
|
|
- Linux uses 15 packets as limit, which corresponds
|
|
|
|
|
|
+ Linux uses 15 packets as limit, which corresponds
|
|
to ~13-30min depending on RTO. */
|
|
to ~13-30min depending on RTO. */
|
|
unsigned int ip_ct_tcp_timeout_max_retrans __read_mostly = 5 MINS;
|
|
unsigned int ip_ct_tcp_timeout_max_retrans __read_mostly = 5 MINS;
|
|
-
|
|
|
|
|
|
+
|
|
static const unsigned int * tcp_timeouts[]
|
|
static const unsigned int * tcp_timeouts[]
|
|
= { NULL, /* TCP_CONNTRACK_NONE */
|
|
= { NULL, /* TCP_CONNTRACK_NONE */
|
|
&ip_ct_tcp_timeout_syn_sent, /* TCP_CONNTRACK_SYN_SENT, */
|
|
&ip_ct_tcp_timeout_syn_sent, /* TCP_CONNTRACK_SYN_SENT, */
|
|
@@ -106,7 +106,7 @@ static const unsigned int * tcp_timeouts[]
|
|
&ip_ct_tcp_timeout_close, /* TCP_CONNTRACK_CLOSE, */
|
|
&ip_ct_tcp_timeout_close, /* TCP_CONNTRACK_CLOSE, */
|
|
NULL, /* TCP_CONNTRACK_LISTEN */
|
|
NULL, /* TCP_CONNTRACK_LISTEN */
|
|
};
|
|
};
|
|
-
|
|
|
|
|
|
+
|
|
#define sNO TCP_CONNTRACK_NONE
|
|
#define sNO TCP_CONNTRACK_NONE
|
|
#define sSS TCP_CONNTRACK_SYN_SENT
|
|
#define sSS TCP_CONNTRACK_SYN_SENT
|
|
#define sSR TCP_CONNTRACK_SYN_RECV
|
|
#define sSR TCP_CONNTRACK_SYN_RECV
|
|
@@ -129,13 +129,13 @@ enum tcp_bit_set {
|
|
TCP_RST_SET,
|
|
TCP_RST_SET,
|
|
TCP_NONE_SET,
|
|
TCP_NONE_SET,
|
|
};
|
|
};
|
|
-
|
|
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* The TCP state transition table needs a few words...
|
|
* The TCP state transition table needs a few words...
|
|
*
|
|
*
|
|
* We are the man in the middle. All the packets go through us
|
|
* We are the man in the middle. All the packets go through us
|
|
* but might get lost in transit to the destination.
|
|
* but might get lost in transit to the destination.
|
|
- * It is assumed that the destinations can't receive segments
|
|
|
|
|
|
+ * It is assumed that the destinations can't receive segments
|
|
* we haven't seen.
|
|
* we haven't seen.
|
|
*
|
|
*
|
|
* The checked segment is in window, but our windows are *not*
|
|
* The checked segment is in window, but our windows are *not*
|
|
@@ -145,11 +145,11 @@ enum tcp_bit_set {
|
|
* The meaning of the states are:
|
|
* The meaning of the states are:
|
|
*
|
|
*
|
|
* NONE: initial state
|
|
* NONE: initial state
|
|
- * SYN_SENT: SYN-only packet seen
|
|
|
|
|
|
+ * SYN_SENT: SYN-only packet seen
|
|
* SYN_RECV: SYN-ACK packet seen
|
|
* SYN_RECV: SYN-ACK packet seen
|
|
* ESTABLISHED: ACK packet seen
|
|
* ESTABLISHED: ACK packet seen
|
|
* FIN_WAIT: FIN packet seen
|
|
* FIN_WAIT: FIN packet seen
|
|
- * CLOSE_WAIT: ACK seen (after FIN)
|
|
|
|
|
|
+ * CLOSE_WAIT: ACK seen (after FIN)
|
|
* LAST_ACK: FIN seen (after FIN)
|
|
* LAST_ACK: FIN seen (after FIN)
|
|
* TIME_WAIT: last ACK seen
|
|
* TIME_WAIT: last ACK seen
|
|
* CLOSE: closed connection
|
|
* CLOSE: closed connection
|
|
@@ -157,8 +157,8 @@ enum tcp_bit_set {
|
|
* LISTEN state is not used.
|
|
* LISTEN state is not used.
|
|
*
|
|
*
|
|
* Packets marked as IGNORED (sIG):
|
|
* Packets marked as IGNORED (sIG):
|
|
- * if they may be either invalid or valid
|
|
|
|
- * and the receiver may send back a connection
|
|
|
|
|
|
+ * if they may be either invalid or valid
|
|
|
|
+ * and the receiver may send back a connection
|
|
* closing RST or a SYN/ACK.
|
|
* closing RST or a SYN/ACK.
|
|
*
|
|
*
|
|
* Packets marked as INVALID (sIV):
|
|
* Packets marked as INVALID (sIV):
|
|
@@ -175,7 +175,7 @@ static const enum tcp_conntrack tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = {
|
|
* sSS -> sSS Retransmitted SYN
|
|
* sSS -> sSS Retransmitted SYN
|
|
* sSR -> sIG Late retransmitted SYN?
|
|
* sSR -> sIG Late retransmitted SYN?
|
|
* sES -> sIG Error: SYNs in window outside the SYN_SENT state
|
|
* sES -> sIG Error: SYNs in window outside the SYN_SENT state
|
|
- * are errors. Receiver will reply with RST
|
|
|
|
|
|
+ * are errors. Receiver will reply with RST
|
|
* and close the connection.
|
|
* and close the connection.
|
|
* Or we are not in sync and hold a dead connection.
|
|
* Or we are not in sync and hold a dead connection.
|
|
* sFW -> sIG
|
|
* sFW -> sIG
|
|
@@ -188,10 +188,10 @@ static const enum tcp_conntrack tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = {
|
|
/*synack*/ { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV },
|
|
/*synack*/ { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV },
|
|
/*
|
|
/*
|
|
* A SYN/ACK from the client is always invalid:
|
|
* A SYN/ACK from the client is always invalid:
|
|
- * - either it tries to set up a simultaneous open, which is
|
|
|
|
|
|
+ * - either it tries to set up a simultaneous open, which is
|
|
* not supported;
|
|
* not supported;
|
|
* - or the firewall has just been inserted between the two hosts
|
|
* - or the firewall has just been inserted between the two hosts
|
|
- * during the session set-up. The SYN will be retransmitted
|
|
|
|
|
|
+ * during the session set-up. The SYN will be retransmitted
|
|
* by the true client (or it'll time out).
|
|
* by the true client (or it'll time out).
|
|
*/
|
|
*/
|
|
/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */
|
|
/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */
|
|
@@ -201,9 +201,9 @@ static const enum tcp_conntrack tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = {
|
|
* sSS -> sIV Client migth not send FIN in this state:
|
|
* sSS -> sIV Client migth not send FIN in this state:
|
|
* we enforce waiting for a SYN/ACK reply first.
|
|
* we enforce waiting for a SYN/ACK reply first.
|
|
* sSR -> sFW Close started.
|
|
* sSR -> sFW Close started.
|
|
- * sES -> sFW
|
|
|
|
|
|
+ * sES -> sFW
|
|
* sFW -> sLA FIN seen in both directions, waiting for
|
|
* sFW -> sLA FIN seen in both directions, waiting for
|
|
- * the last ACK.
|
|
|
|
|
|
+ * the last ACK.
|
|
* Migth be a retransmitted FIN as well...
|
|
* Migth be a retransmitted FIN as well...
|
|
* sCW -> sLA
|
|
* sCW -> sLA
|
|
* sLA -> sLA Retransmitted FIN. Remain in the same state.
|
|
* sLA -> sLA Retransmitted FIN. Remain in the same state.
|
|
@@ -281,7 +281,7 @@ static const enum tcp_conntrack tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = {
|
|
/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */
|
|
/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */
|
|
/*rst*/ { sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sIV },
|
|
/*rst*/ { sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sIV },
|
|
/*none*/ { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }
|
|
/*none*/ { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }
|
|
- }
|
|
|
|
|
|
+ }
|
|
};
|
|
};
|
|
|
|
|
|
static int tcp_pkt_to_tuple(const struct sk_buff *skb,
|
|
static int tcp_pkt_to_tuple(const struct sk_buff *skb,
|
|
@@ -337,7 +337,7 @@ static int tcp_to_nfattr(struct sk_buff *skb, struct nfattr *nfa,
|
|
const struct ip_conntrack *ct)
|
|
const struct ip_conntrack *ct)
|
|
{
|
|
{
|
|
struct nfattr *nest_parms;
|
|
struct nfattr *nest_parms;
|
|
-
|
|
|
|
|
|
+
|
|
read_lock_bh(&tcp_lock);
|
|
read_lock_bh(&tcp_lock);
|
|
nest_parms = NFA_NEST(skb, CTA_PROTOINFO_TCP);
|
|
nest_parms = NFA_NEST(skb, CTA_PROTOINFO_TCP);
|
|
NFA_PUT(skb, CTA_PROTOINFO_TCP_STATE, sizeof(u_int8_t),
|
|
NFA_PUT(skb, CTA_PROTOINFO_TCP_STATE, sizeof(u_int8_t),
|
|
@@ -367,7 +367,7 @@ static int nfattr_to_tcp(struct nfattr *cda[], struct ip_conntrack *ct)
|
|
if (!attr)
|
|
if (!attr)
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
- nfattr_parse_nested(tb, CTA_PROTOINFO_TCP_MAX, attr);
|
|
|
|
|
|
+ nfattr_parse_nested(tb, CTA_PROTOINFO_TCP_MAX, attr);
|
|
|
|
|
|
if (nfattr_bad_size(tb, CTA_PROTOINFO_TCP_MAX, cta_min_tcp))
|
|
if (nfattr_bad_size(tb, CTA_PROTOINFO_TCP_MAX, cta_min_tcp))
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
@@ -376,7 +376,7 @@ static int nfattr_to_tcp(struct nfattr *cda[], struct ip_conntrack *ct)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
write_lock_bh(&tcp_lock);
|
|
write_lock_bh(&tcp_lock);
|
|
- ct->proto.tcp.state =
|
|
|
|
|
|
+ ct->proto.tcp.state =
|
|
*(u_int8_t *)NFA_DATA(tb[CTA_PROTOINFO_TCP_STATE-1]);
|
|
*(u_int8_t *)NFA_DATA(tb[CTA_PROTOINFO_TCP_STATE-1]);
|
|
write_unlock_bh(&tcp_lock);
|
|
write_unlock_bh(&tcp_lock);
|
|
|
|
|
|
@@ -395,30 +395,30 @@ static unsigned int get_conntrack_index(const struct tcphdr *tcph)
|
|
|
|
|
|
/* TCP connection tracking based on 'Real Stateful TCP Packet Filtering
|
|
/* TCP connection tracking based on 'Real Stateful TCP Packet Filtering
|
|
in IP Filter' by Guido van Rooij.
|
|
in IP Filter' by Guido van Rooij.
|
|
-
|
|
|
|
|
|
+
|
|
http://www.nluug.nl/events/sane2000/papers.html
|
|
http://www.nluug.nl/events/sane2000/papers.html
|
|
http://www.iae.nl/users/guido/papers/tcp_filtering.ps.gz
|
|
http://www.iae.nl/users/guido/papers/tcp_filtering.ps.gz
|
|
-
|
|
|
|
|
|
+
|
|
The boundaries and the conditions are changed according to RFC793:
|
|
The boundaries and the conditions are changed according to RFC793:
|
|
the packet must intersect the window (i.e. segments may be
|
|
the packet must intersect the window (i.e. segments may be
|
|
after the right or before the left edge) and thus receivers may ACK
|
|
after the right or before the left edge) and thus receivers may ACK
|
|
segments after the right edge of the window.
|
|
segments after the right edge of the window.
|
|
|
|
|
|
- td_maxend = max(sack + max(win,1)) seen in reply packets
|
|
|
|
|
|
+ td_maxend = max(sack + max(win,1)) seen in reply packets
|
|
td_maxwin = max(max(win, 1)) + (sack - ack) seen in sent packets
|
|
td_maxwin = max(max(win, 1)) + (sack - ack) seen in sent packets
|
|
td_maxwin += seq + len - sender.td_maxend
|
|
td_maxwin += seq + len - sender.td_maxend
|
|
if seq + len > sender.td_maxend
|
|
if seq + len > sender.td_maxend
|
|
td_end = max(seq + len) seen in sent packets
|
|
td_end = max(seq + len) seen in sent packets
|
|
-
|
|
|
|
|
|
+
|
|
I. Upper bound for valid data: seq <= sender.td_maxend
|
|
I. Upper bound for valid data: seq <= sender.td_maxend
|
|
II. Lower bound for valid data: seq + len >= sender.td_end - receiver.td_maxwin
|
|
II. Lower bound for valid data: seq + len >= sender.td_end - receiver.td_maxwin
|
|
III. Upper bound for valid ack: sack <= receiver.td_end
|
|
III. Upper bound for valid ack: sack <= receiver.td_end
|
|
IV. Lower bound for valid ack: ack >= receiver.td_end - MAXACKWINDOW
|
|
IV. Lower bound for valid ack: ack >= receiver.td_end - MAXACKWINDOW
|
|
-
|
|
|
|
|
|
+
|
|
where sack is the highest right edge of sack block found in the packet.
|
|
where sack is the highest right edge of sack block found in the packet.
|
|
-
|
|
|
|
- The upper bound limit for a valid ack is not ignored -
|
|
|
|
- we doesn't have to deal with fragments.
|
|
|
|
|
|
+
|
|
|
|
+ The upper bound limit for a valid ack is not ignored -
|
|
|
|
+ we doesn't have to deal with fragments.
|
|
*/
|
|
*/
|
|
|
|
|
|
static inline __u32 segment_seq_plus_len(__u32 seq,
|
|
static inline __u32 segment_seq_plus_len(__u32 seq,
|
|
@@ -429,25 +429,25 @@ static inline __u32 segment_seq_plus_len(__u32 seq,
|
|
return (seq + len - (iph->ihl + tcph->doff)*4
|
|
return (seq + len - (iph->ihl + tcph->doff)*4
|
|
+ (tcph->syn ? 1 : 0) + (tcph->fin ? 1 : 0));
|
|
+ (tcph->syn ? 1 : 0) + (tcph->fin ? 1 : 0));
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
/* Fixme: what about big packets? */
|
|
/* Fixme: what about big packets? */
|
|
#define MAXACKWINCONST 66000
|
|
#define MAXACKWINCONST 66000
|
|
#define MAXACKWINDOW(sender) \
|
|
#define MAXACKWINDOW(sender) \
|
|
((sender)->td_maxwin > MAXACKWINCONST ? (sender)->td_maxwin \
|
|
((sender)->td_maxwin > MAXACKWINCONST ? (sender)->td_maxwin \
|
|
: MAXACKWINCONST)
|
|
: MAXACKWINCONST)
|
|
-
|
|
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Simplified tcp_parse_options routine from tcp_input.c
|
|
* Simplified tcp_parse_options routine from tcp_input.c
|
|
*/
|
|
*/
|
|
static void tcp_options(const struct sk_buff *skb,
|
|
static void tcp_options(const struct sk_buff *skb,
|
|
struct iphdr *iph,
|
|
struct iphdr *iph,
|
|
- struct tcphdr *tcph,
|
|
|
|
|
|
+ struct tcphdr *tcph,
|
|
struct ip_ct_tcp_state *state)
|
|
struct ip_ct_tcp_state *state)
|
|
{
|
|
{
|
|
unsigned char buff[(15 * 4) - sizeof(struct tcphdr)];
|
|
unsigned char buff[(15 * 4) - sizeof(struct tcphdr)];
|
|
unsigned char *ptr;
|
|
unsigned char *ptr;
|
|
int length = (tcph->doff*4) - sizeof(struct tcphdr);
|
|
int length = (tcph->doff*4) - sizeof(struct tcphdr);
|
|
-
|
|
|
|
|
|
+
|
|
if (!length)
|
|
if (!length)
|
|
return;
|
|
return;
|
|
|
|
|
|
@@ -456,13 +456,13 @@ static void tcp_options(const struct sk_buff *skb,
|
|
length, buff);
|
|
length, buff);
|
|
BUG_ON(ptr == NULL);
|
|
BUG_ON(ptr == NULL);
|
|
|
|
|
|
- state->td_scale =
|
|
|
|
|
|
+ state->td_scale =
|
|
state->flags = 0;
|
|
state->flags = 0;
|
|
-
|
|
|
|
|
|
+
|
|
while (length > 0) {
|
|
while (length > 0) {
|
|
int opcode=*ptr++;
|
|
int opcode=*ptr++;
|
|
int opsize;
|
|
int opsize;
|
|
-
|
|
|
|
|
|
+
|
|
switch (opcode) {
|
|
switch (opcode) {
|
|
case TCPOPT_EOL:
|
|
case TCPOPT_EOL:
|
|
return;
|
|
return;
|
|
@@ -476,13 +476,13 @@ static void tcp_options(const struct sk_buff *skb,
|
|
if (opsize > length)
|
|
if (opsize > length)
|
|
break; /* don't parse partial options */
|
|
break; /* don't parse partial options */
|
|
|
|
|
|
- if (opcode == TCPOPT_SACK_PERM
|
|
|
|
|
|
+ if (opcode == TCPOPT_SACK_PERM
|
|
&& opsize == TCPOLEN_SACK_PERM)
|
|
&& opsize == TCPOLEN_SACK_PERM)
|
|
state->flags |= IP_CT_TCP_FLAG_SACK_PERM;
|
|
state->flags |= IP_CT_TCP_FLAG_SACK_PERM;
|
|
else if (opcode == TCPOPT_WINDOW
|
|
else if (opcode == TCPOPT_WINDOW
|
|
&& opsize == TCPOLEN_WINDOW) {
|
|
&& opsize == TCPOLEN_WINDOW) {
|
|
state->td_scale = *(u_int8_t *)ptr;
|
|
state->td_scale = *(u_int8_t *)ptr;
|
|
-
|
|
|
|
|
|
+
|
|
if (state->td_scale > 14) {
|
|
if (state->td_scale > 14) {
|
|
/* See RFC1323 */
|
|
/* See RFC1323 */
|
|
state->td_scale = 14;
|
|
state->td_scale = 14;
|
|
@@ -517,16 +517,16 @@ static void tcp_sack(const struct sk_buff *skb,
|
|
/* Fast path for timestamp-only option */
|
|
/* Fast path for timestamp-only option */
|
|
if (length == TCPOLEN_TSTAMP_ALIGNED*4
|
|
if (length == TCPOLEN_TSTAMP_ALIGNED*4
|
|
&& *(__be32 *)ptr ==
|
|
&& *(__be32 *)ptr ==
|
|
- __constant_htonl((TCPOPT_NOP << 24)
|
|
|
|
- | (TCPOPT_NOP << 16)
|
|
|
|
- | (TCPOPT_TIMESTAMP << 8)
|
|
|
|
- | TCPOLEN_TIMESTAMP))
|
|
|
|
|
|
+ __constant_htonl((TCPOPT_NOP << 24)
|
|
|
|
+ | (TCPOPT_NOP << 16)
|
|
|
|
+ | (TCPOPT_TIMESTAMP << 8)
|
|
|
|
+ | TCPOLEN_TIMESTAMP))
|
|
return;
|
|
return;
|
|
-
|
|
|
|
|
|
+
|
|
while (length > 0) {
|
|
while (length > 0) {
|
|
int opcode=*ptr++;
|
|
int opcode=*ptr++;
|
|
int opsize, i;
|
|
int opsize, i;
|
|
-
|
|
|
|
|
|
+
|
|
switch (opcode) {
|
|
switch (opcode) {
|
|
case TCPOPT_EOL:
|
|
case TCPOPT_EOL:
|
|
return;
|
|
return;
|
|
@@ -540,16 +540,16 @@ static void tcp_sack(const struct sk_buff *skb,
|
|
if (opsize > length)
|
|
if (opsize > length)
|
|
break; /* don't parse partial options */
|
|
break; /* don't parse partial options */
|
|
|
|
|
|
- if (opcode == TCPOPT_SACK
|
|
|
|
- && opsize >= (TCPOLEN_SACK_BASE
|
|
|
|
- + TCPOLEN_SACK_PERBLOCK)
|
|
|
|
- && !((opsize - TCPOLEN_SACK_BASE)
|
|
|
|
- % TCPOLEN_SACK_PERBLOCK)) {
|
|
|
|
- for (i = 0;
|
|
|
|
- i < (opsize - TCPOLEN_SACK_BASE);
|
|
|
|
- i += TCPOLEN_SACK_PERBLOCK) {
|
|
|
|
|
|
+ if (opcode == TCPOPT_SACK
|
|
|
|
+ && opsize >= (TCPOLEN_SACK_BASE
|
|
|
|
+ + TCPOLEN_SACK_PERBLOCK)
|
|
|
|
+ && !((opsize - TCPOLEN_SACK_BASE)
|
|
|
|
+ % TCPOLEN_SACK_PERBLOCK)) {
|
|
|
|
+ for (i = 0;
|
|
|
|
+ i < (opsize - TCPOLEN_SACK_BASE);
|
|
|
|
+ i += TCPOLEN_SACK_PERBLOCK) {
|
|
tmp = ntohl(*((__be32 *)(ptr+i)+1));
|
|
tmp = ntohl(*((__be32 *)(ptr+i)+1));
|
|
-
|
|
|
|
|
|
+
|
|
if (after(tmp, *sack))
|
|
if (after(tmp, *sack))
|
|
*sack = tmp;
|
|
*sack = tmp;
|
|
}
|
|
}
|
|
@@ -561,18 +561,18 @@ static void tcp_sack(const struct sk_buff *skb,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-static int tcp_in_window(struct ip_ct_tcp *state,
|
|
|
|
- enum ip_conntrack_dir dir,
|
|
|
|
- unsigned int index,
|
|
|
|
- const struct sk_buff *skb,
|
|
|
|
- struct iphdr *iph,
|
|
|
|
- struct tcphdr *tcph)
|
|
|
|
|
|
+static int tcp_in_window(struct ip_ct_tcp *state,
|
|
|
|
+ enum ip_conntrack_dir dir,
|
|
|
|
+ unsigned int index,
|
|
|
|
+ const struct sk_buff *skb,
|
|
|
|
+ struct iphdr *iph,
|
|
|
|
+ struct tcphdr *tcph)
|
|
{
|
|
{
|
|
struct ip_ct_tcp_state *sender = &state->seen[dir];
|
|
struct ip_ct_tcp_state *sender = &state->seen[dir];
|
|
struct ip_ct_tcp_state *receiver = &state->seen[!dir];
|
|
struct ip_ct_tcp_state *receiver = &state->seen[!dir];
|
|
__u32 seq, ack, sack, end, win, swin;
|
|
__u32 seq, ack, sack, end, win, swin;
|
|
int res;
|
|
int res;
|
|
-
|
|
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Get the required data from the packet.
|
|
* Get the required data from the packet.
|
|
*/
|
|
*/
|
|
@@ -580,23 +580,23 @@ static int tcp_in_window(struct ip_ct_tcp *state,
|
|
ack = sack = ntohl(tcph->ack_seq);
|
|
ack = sack = ntohl(tcph->ack_seq);
|
|
win = ntohs(tcph->window);
|
|
win = ntohs(tcph->window);
|
|
end = segment_seq_plus_len(seq, skb->len, iph, tcph);
|
|
end = segment_seq_plus_len(seq, skb->len, iph, tcph);
|
|
-
|
|
|
|
|
|
+
|
|
if (receiver->flags & IP_CT_TCP_FLAG_SACK_PERM)
|
|
if (receiver->flags & IP_CT_TCP_FLAG_SACK_PERM)
|
|
tcp_sack(skb, iph, tcph, &sack);
|
|
tcp_sack(skb, iph, tcph, &sack);
|
|
-
|
|
|
|
|
|
+
|
|
DEBUGP("tcp_in_window: START\n");
|
|
DEBUGP("tcp_in_window: START\n");
|
|
DEBUGP("tcp_in_window: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu "
|
|
DEBUGP("tcp_in_window: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu "
|
|
"seq=%u ack=%u sack=%u win=%u end=%u\n",
|
|
"seq=%u ack=%u sack=%u win=%u end=%u\n",
|
|
- NIPQUAD(iph->saddr), ntohs(tcph->source),
|
|
|
|
|
|
+ NIPQUAD(iph->saddr), ntohs(tcph->source),
|
|
NIPQUAD(iph->daddr), ntohs(tcph->dest),
|
|
NIPQUAD(iph->daddr), ntohs(tcph->dest),
|
|
seq, ack, sack, win, end);
|
|
seq, ack, sack, win, end);
|
|
DEBUGP("tcp_in_window: sender end=%u maxend=%u maxwin=%u scale=%i "
|
|
DEBUGP("tcp_in_window: sender end=%u maxend=%u maxwin=%u scale=%i "
|
|
"receiver end=%u maxend=%u maxwin=%u scale=%i\n",
|
|
"receiver end=%u maxend=%u maxwin=%u scale=%i\n",
|
|
sender->td_end, sender->td_maxend, sender->td_maxwin,
|
|
sender->td_end, sender->td_maxend, sender->td_maxwin,
|
|
- sender->td_scale,
|
|
|
|
- receiver->td_end, receiver->td_maxend, receiver->td_maxwin,
|
|
|
|
|
|
+ sender->td_scale,
|
|
|
|
+ receiver->td_end, receiver->td_maxend, receiver->td_maxwin,
|
|
receiver->td_scale);
|
|
receiver->td_scale);
|
|
-
|
|
|
|
|
|
+
|
|
if (sender->td_end == 0) {
|
|
if (sender->td_end == 0) {
|
|
/*
|
|
/*
|
|
* Initialize sender data.
|
|
* Initialize sender data.
|
|
@@ -605,26 +605,26 @@ static int tcp_in_window(struct ip_ct_tcp *state,
|
|
/*
|
|
/*
|
|
* Outgoing SYN-ACK in reply to a SYN.
|
|
* Outgoing SYN-ACK in reply to a SYN.
|
|
*/
|
|
*/
|
|
- sender->td_end =
|
|
|
|
|
|
+ sender->td_end =
|
|
sender->td_maxend = end;
|
|
sender->td_maxend = end;
|
|
sender->td_maxwin = (win == 0 ? 1 : win);
|
|
sender->td_maxwin = (win == 0 ? 1 : win);
|
|
|
|
|
|
tcp_options(skb, iph, tcph, sender);
|
|
tcp_options(skb, iph, tcph, sender);
|
|
- /*
|
|
|
|
|
|
+ /*
|
|
* RFC 1323:
|
|
* RFC 1323:
|
|
* Both sides must send the Window Scale option
|
|
* Both sides must send the Window Scale option
|
|
* to enable window scaling in either direction.
|
|
* to enable window scaling in either direction.
|
|
*/
|
|
*/
|
|
if (!(sender->flags & IP_CT_TCP_FLAG_WINDOW_SCALE
|
|
if (!(sender->flags & IP_CT_TCP_FLAG_WINDOW_SCALE
|
|
&& receiver->flags & IP_CT_TCP_FLAG_WINDOW_SCALE))
|
|
&& receiver->flags & IP_CT_TCP_FLAG_WINDOW_SCALE))
|
|
- sender->td_scale =
|
|
|
|
|
|
+ sender->td_scale =
|
|
receiver->td_scale = 0;
|
|
receiver->td_scale = 0;
|
|
} else {
|
|
} else {
|
|
/*
|
|
/*
|
|
* We are in the middle of a connection,
|
|
* We are in the middle of a connection,
|
|
* its history is lost for us.
|
|
* its history is lost for us.
|
|
* Let's try to use the data from the packet.
|
|
* Let's try to use the data from the packet.
|
|
- */
|
|
|
|
|
|
+ */
|
|
sender->td_end = end;
|
|
sender->td_end = end;
|
|
sender->td_maxwin = (win == 0 ? 1 : win);
|
|
sender->td_maxwin = (win == 0 ? 1 : win);
|
|
sender->td_maxend = end + sender->td_maxwin;
|
|
sender->td_maxend = end + sender->td_maxwin;
|
|
@@ -632,11 +632,11 @@ static int tcp_in_window(struct ip_ct_tcp *state,
|
|
} else if (((state->state == TCP_CONNTRACK_SYN_SENT
|
|
} else if (((state->state == TCP_CONNTRACK_SYN_SENT
|
|
&& dir == IP_CT_DIR_ORIGINAL)
|
|
&& dir == IP_CT_DIR_ORIGINAL)
|
|
|| (state->state == TCP_CONNTRACK_SYN_RECV
|
|
|| (state->state == TCP_CONNTRACK_SYN_RECV
|
|
- && dir == IP_CT_DIR_REPLY))
|
|
|
|
|
|
+ && dir == IP_CT_DIR_REPLY))
|
|
&& after(end, sender->td_end)) {
|
|
&& after(end, sender->td_end)) {
|
|
/*
|
|
/*
|
|
* RFC 793: "if a TCP is reinitialized ... then it need
|
|
* RFC 793: "if a TCP is reinitialized ... then it need
|
|
- * not wait at all; it must only be sure to use sequence
|
|
|
|
|
|
+ * not wait at all; it must only be sure to use sequence
|
|
* numbers larger than those recently used."
|
|
* numbers larger than those recently used."
|
|
*/
|
|
*/
|
|
sender->td_end =
|
|
sender->td_end =
|
|
@@ -645,14 +645,14 @@ static int tcp_in_window(struct ip_ct_tcp *state,
|
|
|
|
|
|
tcp_options(skb, iph, tcph, sender);
|
|
tcp_options(skb, iph, tcph, sender);
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
if (!(tcph->ack)) {
|
|
if (!(tcph->ack)) {
|
|
/*
|
|
/*
|
|
* If there is no ACK, just pretend it was set and OK.
|
|
* If there is no ACK, just pretend it was set and OK.
|
|
*/
|
|
*/
|
|
ack = sack = receiver->td_end;
|
|
ack = sack = receiver->td_end;
|
|
- } else if (((tcp_flag_word(tcph) & (TCP_FLAG_ACK|TCP_FLAG_RST)) ==
|
|
|
|
- (TCP_FLAG_ACK|TCP_FLAG_RST))
|
|
|
|
|
|
+ } else if (((tcp_flag_word(tcph) & (TCP_FLAG_ACK|TCP_FLAG_RST)) ==
|
|
|
|
+ (TCP_FLAG_ACK|TCP_FLAG_RST))
|
|
&& (ack == 0)) {
|
|
&& (ack == 0)) {
|
|
/*
|
|
/*
|
|
* Broken TCP stacks, that set ACK in RST packets as well
|
|
* Broken TCP stacks, that set ACK in RST packets as well
|
|
@@ -662,8 +662,8 @@ static int tcp_in_window(struct ip_ct_tcp *state,
|
|
}
|
|
}
|
|
|
|
|
|
if (seq == end
|
|
if (seq == end
|
|
- && (!tcph->rst
|
|
|
|
- || (seq == 0 && state->state == TCP_CONNTRACK_SYN_SENT)))
|
|
|
|
|
|
+ && (!tcph->rst
|
|
|
|
+ || (seq == 0 && state->state == TCP_CONNTRACK_SYN_SENT)))
|
|
/*
|
|
/*
|
|
* Packets contains no data: we assume it is valid
|
|
* Packets contains no data: we assume it is valid
|
|
* and check the ack value only.
|
|
* and check the ack value only.
|
|
@@ -672,7 +672,7 @@ static int tcp_in_window(struct ip_ct_tcp *state,
|
|
* SYN.
|
|
* SYN.
|
|
*/
|
|
*/
|
|
seq = end = sender->td_end;
|
|
seq = end = sender->td_end;
|
|
-
|
|
|
|
|
|
+
|
|
DEBUGP("tcp_in_window: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu "
|
|
DEBUGP("tcp_in_window: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu "
|
|
"seq=%u ack=%u sack =%u win=%u end=%u\n",
|
|
"seq=%u ack=%u sack =%u win=%u end=%u\n",
|
|
NIPQUAD(iph->saddr), ntohs(tcph->source),
|
|
NIPQUAD(iph->saddr), ntohs(tcph->source),
|
|
@@ -681,26 +681,26 @@ static int tcp_in_window(struct ip_ct_tcp *state,
|
|
DEBUGP("tcp_in_window: sender end=%u maxend=%u maxwin=%u scale=%i "
|
|
DEBUGP("tcp_in_window: sender end=%u maxend=%u maxwin=%u scale=%i "
|
|
"receiver end=%u maxend=%u maxwin=%u scale=%i\n",
|
|
"receiver end=%u maxend=%u maxwin=%u scale=%i\n",
|
|
sender->td_end, sender->td_maxend, sender->td_maxwin,
|
|
sender->td_end, sender->td_maxend, sender->td_maxwin,
|
|
- sender->td_scale,
|
|
|
|
|
|
+ sender->td_scale,
|
|
receiver->td_end, receiver->td_maxend, receiver->td_maxwin,
|
|
receiver->td_end, receiver->td_maxend, receiver->td_maxwin,
|
|
receiver->td_scale);
|
|
receiver->td_scale);
|
|
-
|
|
|
|
|
|
+
|
|
DEBUGP("tcp_in_window: I=%i II=%i III=%i IV=%i\n",
|
|
DEBUGP("tcp_in_window: I=%i II=%i III=%i IV=%i\n",
|
|
before(seq, sender->td_maxend + 1),
|
|
before(seq, sender->td_maxend + 1),
|
|
- after(end, sender->td_end - receiver->td_maxwin - 1),
|
|
|
|
- before(sack, receiver->td_end + 1),
|
|
|
|
- after(ack, receiver->td_end - MAXACKWINDOW(sender)));
|
|
|
|
-
|
|
|
|
|
|
+ after(end, sender->td_end - receiver->td_maxwin - 1),
|
|
|
|
+ before(sack, receiver->td_end + 1),
|
|
|
|
+ after(ack, receiver->td_end - MAXACKWINDOW(sender)));
|
|
|
|
+
|
|
if (before(seq, sender->td_maxend + 1) &&
|
|
if (before(seq, sender->td_maxend + 1) &&
|
|
after(end, sender->td_end - receiver->td_maxwin - 1) &&
|
|
after(end, sender->td_end - receiver->td_maxwin - 1) &&
|
|
before(sack, receiver->td_end + 1) &&
|
|
before(sack, receiver->td_end + 1) &&
|
|
after(ack, receiver->td_end - MAXACKWINDOW(sender))) {
|
|
after(ack, receiver->td_end - MAXACKWINDOW(sender))) {
|
|
- /*
|
|
|
|
|
|
+ /*
|
|
* Take into account window scaling (RFC 1323).
|
|
* Take into account window scaling (RFC 1323).
|
|
*/
|
|
*/
|
|
if (!tcph->syn)
|
|
if (!tcph->syn)
|
|
win <<= sender->td_scale;
|
|
win <<= sender->td_scale;
|
|
-
|
|
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Update sender data.
|
|
* Update sender data.
|
|
*/
|
|
*/
|
|
@@ -720,7 +720,7 @@ static int tcp_in_window(struct ip_ct_tcp *state,
|
|
receiver->td_maxend++;
|
|
receiver->td_maxend++;
|
|
}
|
|
}
|
|
|
|
|
|
- /*
|
|
|
|
|
|
+ /*
|
|
* Check retransmissions.
|
|
* Check retransmissions.
|
|
*/
|
|
*/
|
|
if (index == TCP_ACK_SET) {
|
|
if (index == TCP_ACK_SET) {
|
|
@@ -756,11 +756,11 @@ static int tcp_in_window(struct ip_ct_tcp *state,
|
|
: "ACK is over the upper bound (ACKed data not seen yet)"
|
|
: "ACK is over the upper bound (ACKed data not seen yet)"
|
|
: "SEQ is under the lower bound (already ACKed data retransmitted)"
|
|
: "SEQ is under the lower bound (already ACKed data retransmitted)"
|
|
: "SEQ is over the upper bound (over the window of the receiver)");
|
|
: "SEQ is over the upper bound (over the window of the receiver)");
|
|
- }
|
|
|
|
-
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
DEBUGP("tcp_in_window: res=%i sender end=%u maxend=%u maxwin=%u "
|
|
DEBUGP("tcp_in_window: res=%i sender end=%u maxend=%u maxwin=%u "
|
|
"receiver end=%u maxend=%u maxwin=%u\n",
|
|
"receiver end=%u maxend=%u maxwin=%u\n",
|
|
- res, sender->td_end, sender->td_maxend, sender->td_maxwin,
|
|
|
|
|
|
+ res, sender->td_end, sender->td_maxend, sender->td_maxwin,
|
|
receiver->td_end, receiver->td_maxend, receiver->td_maxwin);
|
|
receiver->td_end, receiver->td_maxend, receiver->td_maxwin);
|
|
|
|
|
|
return res;
|
|
return res;
|
|
@@ -769,7 +769,7 @@ static int tcp_in_window(struct ip_ct_tcp *state,
|
|
#ifdef CONFIG_IP_NF_NAT_NEEDED
|
|
#ifdef CONFIG_IP_NF_NAT_NEEDED
|
|
/* Update sender->td_end after NAT successfully mangled the packet */
|
|
/* Update sender->td_end after NAT successfully mangled the packet */
|
|
void ip_conntrack_tcp_update(struct sk_buff *skb,
|
|
void ip_conntrack_tcp_update(struct sk_buff *skb,
|
|
- struct ip_conntrack *conntrack,
|
|
|
|
|
|
+ struct ip_conntrack *conntrack,
|
|
enum ip_conntrack_dir dir)
|
|
enum ip_conntrack_dir dir)
|
|
{
|
|
{
|
|
struct iphdr *iph = skb->nh.iph;
|
|
struct iphdr *iph = skb->nh.iph;
|
|
@@ -781,7 +781,7 @@ void ip_conntrack_tcp_update(struct sk_buff *skb,
|
|
#endif
|
|
#endif
|
|
|
|
|
|
end = segment_seq_plus_len(ntohl(tcph->seq), skb->len, iph, tcph);
|
|
end = segment_seq_plus_len(ntohl(tcph->seq), skb->len, iph, tcph);
|
|
-
|
|
|
|
|
|
+
|
|
write_lock_bh(&tcp_lock);
|
|
write_lock_bh(&tcp_lock);
|
|
/*
|
|
/*
|
|
* We have to worry for the ack in the reply packet only...
|
|
* We have to worry for the ack in the reply packet only...
|
|
@@ -793,11 +793,11 @@ void ip_conntrack_tcp_update(struct sk_buff *skb,
|
|
DEBUGP("tcp_update: sender end=%u maxend=%u maxwin=%u scale=%i "
|
|
DEBUGP("tcp_update: sender end=%u maxend=%u maxwin=%u scale=%i "
|
|
"receiver end=%u maxend=%u maxwin=%u scale=%i\n",
|
|
"receiver end=%u maxend=%u maxwin=%u scale=%i\n",
|
|
sender->td_end, sender->td_maxend, sender->td_maxwin,
|
|
sender->td_end, sender->td_maxend, sender->td_maxwin,
|
|
- sender->td_scale,
|
|
|
|
|
|
+ sender->td_scale,
|
|
receiver->td_end, receiver->td_maxend, receiver->td_maxwin,
|
|
receiver->td_end, receiver->td_maxend, receiver->td_maxwin,
|
|
receiver->td_scale);
|
|
receiver->td_scale);
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
#endif
|
|
#endif
|
|
|
|
|
|
#define TH_FIN 0x01
|
|
#define TH_FIN 0x01
|
|
@@ -847,8 +847,8 @@ static int tcp_error(struct sk_buff *skb,
|
|
nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
|
|
nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
|
|
"ip_ct_tcp: short packet ");
|
|
"ip_ct_tcp: short packet ");
|
|
return -NF_ACCEPT;
|
|
return -NF_ACCEPT;
|
|
- }
|
|
|
|
-
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
/* Not whole TCP header or malformed packet */
|
|
/* Not whole TCP header or malformed packet */
|
|
if (th->doff*4 < sizeof(struct tcphdr) || tcplen < th->doff*4) {
|
|
if (th->doff*4 < sizeof(struct tcphdr) || tcplen < th->doff*4) {
|
|
if (LOG_INVALID(IPPROTO_TCP))
|
|
if (LOG_INVALID(IPPROTO_TCP))
|
|
@@ -856,7 +856,7 @@ static int tcp_error(struct sk_buff *skb,
|
|
"ip_ct_tcp: truncated/malformed packet ");
|
|
"ip_ct_tcp: truncated/malformed packet ");
|
|
return -NF_ACCEPT;
|
|
return -NF_ACCEPT;
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
/* Checksum invalid? Ignore.
|
|
/* Checksum invalid? Ignore.
|
|
* We skip checking packets on the outgoing path
|
|
* We skip checking packets on the outgoing path
|
|
* because it is assumed to be correct.
|
|
* because it is assumed to be correct.
|
|
@@ -893,11 +893,11 @@ static int tcp_packet(struct ip_conntrack *conntrack,
|
|
struct tcphdr *th, _tcph;
|
|
struct tcphdr *th, _tcph;
|
|
unsigned long timeout;
|
|
unsigned long timeout;
|
|
unsigned int index;
|
|
unsigned int index;
|
|
-
|
|
|
|
|
|
+
|
|
th = skb_header_pointer(skb, iph->ihl * 4,
|
|
th = skb_header_pointer(skb, iph->ihl * 4,
|
|
sizeof(_tcph), &_tcph);
|
|
sizeof(_tcph), &_tcph);
|
|
BUG_ON(th == NULL);
|
|
BUG_ON(th == NULL);
|
|
-
|
|
|
|
|
|
+
|
|
write_lock_bh(&tcp_lock);
|
|
write_lock_bh(&tcp_lock);
|
|
old_state = conntrack->proto.tcp.state;
|
|
old_state = conntrack->proto.tcp.state;
|
|
dir = CTINFO2DIR(ctinfo);
|
|
dir = CTINFO2DIR(ctinfo);
|
|
@@ -907,7 +907,7 @@ static int tcp_packet(struct ip_conntrack *conntrack,
|
|
switch (new_state) {
|
|
switch (new_state) {
|
|
case TCP_CONNTRACK_IGNORE:
|
|
case TCP_CONNTRACK_IGNORE:
|
|
/* Ignored packets:
|
|
/* Ignored packets:
|
|
- *
|
|
|
|
|
|
+ *
|
|
* a) SYN in ORIGINAL
|
|
* a) SYN in ORIGINAL
|
|
* b) SYN/ACK in REPLY
|
|
* b) SYN/ACK in REPLY
|
|
* c) ACK in reply direction after initial SYN in original.
|
|
* c) ACK in reply direction after initial SYN in original.
|
|
@@ -916,30 +916,30 @@ static int tcp_packet(struct ip_conntrack *conntrack,
|
|
&& conntrack->proto.tcp.last_index == TCP_SYN_SET
|
|
&& conntrack->proto.tcp.last_index == TCP_SYN_SET
|
|
&& conntrack->proto.tcp.last_dir != dir
|
|
&& conntrack->proto.tcp.last_dir != dir
|
|
&& ntohl(th->ack_seq) ==
|
|
&& ntohl(th->ack_seq) ==
|
|
- conntrack->proto.tcp.last_end) {
|
|
|
|
- /* This SYN/ACK acknowledges a SYN that we earlier
|
|
|
|
|
|
+ conntrack->proto.tcp.last_end) {
|
|
|
|
+ /* This SYN/ACK acknowledges a SYN that we earlier
|
|
* ignored as invalid. This means that the client and
|
|
* ignored as invalid. This means that the client and
|
|
* the server are both in sync, while the firewall is
|
|
* the server are both in sync, while the firewall is
|
|
* not. We kill this session and block the SYN/ACK so
|
|
* not. We kill this session and block the SYN/ACK so
|
|
- * that the client cannot but retransmit its SYN and
|
|
|
|
|
|
+ * that the client cannot but retransmit its SYN and
|
|
* thus initiate a clean new session.
|
|
* thus initiate a clean new session.
|
|
*/
|
|
*/
|
|
- write_unlock_bh(&tcp_lock);
|
|
|
|
|
|
+ write_unlock_bh(&tcp_lock);
|
|
if (LOG_INVALID(IPPROTO_TCP))
|
|
if (LOG_INVALID(IPPROTO_TCP))
|
|
nf_log_packet(PF_INET, 0, skb, NULL, NULL,
|
|
nf_log_packet(PF_INET, 0, skb, NULL, NULL,
|
|
NULL, "ip_ct_tcp: "
|
|
NULL, "ip_ct_tcp: "
|
|
"killing out of sync session ");
|
|
"killing out of sync session ");
|
|
- if (del_timer(&conntrack->timeout))
|
|
|
|
- conntrack->timeout.function((unsigned long)
|
|
|
|
- conntrack);
|
|
|
|
- return -NF_DROP;
|
|
|
|
|
|
+ if (del_timer(&conntrack->timeout))
|
|
|
|
+ conntrack->timeout.function((unsigned long)
|
|
|
|
+ conntrack);
|
|
|
|
+ return -NF_DROP;
|
|
}
|
|
}
|
|
conntrack->proto.tcp.last_index = index;
|
|
conntrack->proto.tcp.last_index = index;
|
|
conntrack->proto.tcp.last_dir = dir;
|
|
conntrack->proto.tcp.last_dir = dir;
|
|
conntrack->proto.tcp.last_seq = ntohl(th->seq);
|
|
conntrack->proto.tcp.last_seq = ntohl(th->seq);
|
|
- conntrack->proto.tcp.last_end =
|
|
|
|
|
|
+ conntrack->proto.tcp.last_end =
|
|
segment_seq_plus_len(ntohl(th->seq), skb->len, iph, th);
|
|
segment_seq_plus_len(ntohl(th->seq), skb->len, iph, th);
|
|
-
|
|
|
|
|
|
+
|
|
write_unlock_bh(&tcp_lock);
|
|
write_unlock_bh(&tcp_lock);
|
|
if (LOG_INVALID(IPPROTO_TCP))
|
|
if (LOG_INVALID(IPPROTO_TCP))
|
|
nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
|
|
nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
|
|
@@ -959,16 +959,16 @@ static int tcp_packet(struct ip_conntrack *conntrack,
|
|
if (old_state < TCP_CONNTRACK_TIME_WAIT)
|
|
if (old_state < TCP_CONNTRACK_TIME_WAIT)
|
|
break;
|
|
break;
|
|
if ((conntrack->proto.tcp.seen[dir].flags &
|
|
if ((conntrack->proto.tcp.seen[dir].flags &
|
|
- IP_CT_TCP_FLAG_CLOSE_INIT)
|
|
|
|
|
|
+ IP_CT_TCP_FLAG_CLOSE_INIT)
|
|
|| after(ntohl(th->seq),
|
|
|| after(ntohl(th->seq),
|
|
- conntrack->proto.tcp.seen[dir].td_end)) {
|
|
|
|
- /* Attempt to reopen a closed connection.
|
|
|
|
- * Delete this connection and look up again. */
|
|
|
|
- write_unlock_bh(&tcp_lock);
|
|
|
|
- if (del_timer(&conntrack->timeout))
|
|
|
|
- conntrack->timeout.function((unsigned long)
|
|
|
|
- conntrack);
|
|
|
|
- return -NF_REPEAT;
|
|
|
|
|
|
+ conntrack->proto.tcp.seen[dir].td_end)) {
|
|
|
|
+ /* Attempt to reopen a closed connection.
|
|
|
|
+ * Delete this connection and look up again. */
|
|
|
|
+ write_unlock_bh(&tcp_lock);
|
|
|
|
+ if (del_timer(&conntrack->timeout))
|
|
|
|
+ conntrack->timeout.function((unsigned long)
|
|
|
|
+ conntrack);
|
|
|
|
+ return -NF_REPEAT;
|
|
} else {
|
|
} else {
|
|
write_unlock_bh(&tcp_lock);
|
|
write_unlock_bh(&tcp_lock);
|
|
if (LOG_INVALID(IPPROTO_TCP))
|
|
if (LOG_INVALID(IPPROTO_TCP))
|
|
@@ -979,9 +979,9 @@ static int tcp_packet(struct ip_conntrack *conntrack,
|
|
case TCP_CONNTRACK_CLOSE:
|
|
case TCP_CONNTRACK_CLOSE:
|
|
if (index == TCP_RST_SET
|
|
if (index == TCP_RST_SET
|
|
&& ((test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)
|
|
&& ((test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)
|
|
- && conntrack->proto.tcp.last_index == TCP_SYN_SET)
|
|
|
|
- || (!test_bit(IPS_ASSURED_BIT, &conntrack->status)
|
|
|
|
- && conntrack->proto.tcp.last_index == TCP_ACK_SET))
|
|
|
|
|
|
+ && conntrack->proto.tcp.last_index == TCP_SYN_SET)
|
|
|
|
+ || (!test_bit(IPS_ASSURED_BIT, &conntrack->status)
|
|
|
|
+ && conntrack->proto.tcp.last_index == TCP_ACK_SET))
|
|
&& ntohl(th->ack_seq) == conntrack->proto.tcp.last_end) {
|
|
&& ntohl(th->ack_seq) == conntrack->proto.tcp.last_end) {
|
|
/* RST sent to invalid SYN or ACK we had let through
|
|
/* RST sent to invalid SYN or ACK we had let through
|
|
* at a) and c) above:
|
|
* at a) and c) above:
|
|
@@ -1000,13 +1000,13 @@ static int tcp_packet(struct ip_conntrack *conntrack,
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
- if (!tcp_in_window(&conntrack->proto.tcp, dir, index,
|
|
|
|
|
|
+ if (!tcp_in_window(&conntrack->proto.tcp, dir, index,
|
|
skb, iph, th)) {
|
|
skb, iph, th)) {
|
|
write_unlock_bh(&tcp_lock);
|
|
write_unlock_bh(&tcp_lock);
|
|
return -NF_ACCEPT;
|
|
return -NF_ACCEPT;
|
|
}
|
|
}
|
|
in_window:
|
|
in_window:
|
|
- /* From now on we have got in-window packets */
|
|
|
|
|
|
+ /* From now on we have got in-window packets */
|
|
conntrack->proto.tcp.last_index = index;
|
|
conntrack->proto.tcp.last_index = index;
|
|
|
|
|
|
DEBUGP("tcp_conntracks: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu "
|
|
DEBUGP("tcp_conntracks: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu "
|
|
@@ -1018,9 +1018,9 @@ static int tcp_packet(struct ip_conntrack *conntrack,
|
|
old_state, new_state);
|
|
old_state, new_state);
|
|
|
|
|
|
conntrack->proto.tcp.state = new_state;
|
|
conntrack->proto.tcp.state = new_state;
|
|
- if (old_state != new_state
|
|
|
|
|
|
+ if (old_state != new_state
|
|
&& (new_state == TCP_CONNTRACK_FIN_WAIT
|
|
&& (new_state == TCP_CONNTRACK_FIN_WAIT
|
|
- || new_state == TCP_CONNTRACK_CLOSE))
|
|
|
|
|
|
+ || new_state == TCP_CONNTRACK_CLOSE))
|
|
conntrack->proto.tcp.seen[dir].flags |= IP_CT_TCP_FLAG_CLOSE_INIT;
|
|
conntrack->proto.tcp.seen[dir].flags |= IP_CT_TCP_FLAG_CLOSE_INIT;
|
|
timeout = conntrack->proto.tcp.retrans >= ip_ct_tcp_max_retrans
|
|
timeout = conntrack->proto.tcp.retrans >= ip_ct_tcp_max_retrans
|
|
&& *tcp_timeouts[new_state] > ip_ct_tcp_timeout_max_retrans
|
|
&& *tcp_timeouts[new_state] > ip_ct_tcp_timeout_max_retrans
|
|
@@ -1046,8 +1046,8 @@ static int tcp_packet(struct ip_conntrack *conntrack,
|
|
&& (old_state == TCP_CONNTRACK_SYN_RECV
|
|
&& (old_state == TCP_CONNTRACK_SYN_RECV
|
|
|| old_state == TCP_CONNTRACK_ESTABLISHED)
|
|
|| old_state == TCP_CONNTRACK_ESTABLISHED)
|
|
&& new_state == TCP_CONNTRACK_ESTABLISHED) {
|
|
&& new_state == TCP_CONNTRACK_ESTABLISHED) {
|
|
- /* Set ASSURED if we see see valid ack in ESTABLISHED
|
|
|
|
- after SYN_RECV or a valid answer for a picked up
|
|
|
|
|
|
+ /* Set ASSURED if we see see valid ack in ESTABLISHED
|
|
|
|
+ after SYN_RECV or a valid answer for a picked up
|
|
connection. */
|
|
connection. */
|
|
set_bit(IPS_ASSURED_BIT, &conntrack->status);
|
|
set_bit(IPS_ASSURED_BIT, &conntrack->status);
|
|
ip_conntrack_event_cache(IPCT_STATUS, skb);
|
|
ip_conntrack_event_cache(IPCT_STATUS, skb);
|
|
@@ -1056,7 +1056,7 @@ static int tcp_packet(struct ip_conntrack *conntrack,
|
|
|
|
|
|
return NF_ACCEPT;
|
|
return NF_ACCEPT;
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
/* Called when a new connection for this protocol found. */
|
|
/* Called when a new connection for this protocol found. */
|
|
static int tcp_new(struct ip_conntrack *conntrack,
|
|
static int tcp_new(struct ip_conntrack *conntrack,
|
|
const struct sk_buff *skb)
|
|
const struct sk_buff *skb)
|
|
@@ -1072,7 +1072,7 @@ static int tcp_new(struct ip_conntrack *conntrack,
|
|
th = skb_header_pointer(skb, iph->ihl * 4,
|
|
th = skb_header_pointer(skb, iph->ihl * 4,
|
|
sizeof(_tcph), &_tcph);
|
|
sizeof(_tcph), &_tcph);
|
|
BUG_ON(th == NULL);
|
|
BUG_ON(th == NULL);
|
|
-
|
|
|
|
|
|
+
|
|
/* Don't need lock here: this conntrack not in circulation yet */
|
|
/* Don't need lock here: this conntrack not in circulation yet */
|
|
new_state
|
|
new_state
|
|
= tcp_conntracks[0][get_conntrack_index(th)]
|
|
= tcp_conntracks[0][get_conntrack_index(th)]
|
|
@@ -1113,7 +1113,7 @@ static int tcp_new(struct ip_conntrack *conntrack,
|
|
if (conntrack->proto.tcp.seen[0].td_maxwin == 0)
|
|
if (conntrack->proto.tcp.seen[0].td_maxwin == 0)
|
|
conntrack->proto.tcp.seen[0].td_maxwin = 1;
|
|
conntrack->proto.tcp.seen[0].td_maxwin = 1;
|
|
conntrack->proto.tcp.seen[0].td_maxend =
|
|
conntrack->proto.tcp.seen[0].td_maxend =
|
|
- conntrack->proto.tcp.seen[0].td_end +
|
|
|
|
|
|
+ conntrack->proto.tcp.seen[0].td_end +
|
|
conntrack->proto.tcp.seen[0].td_maxwin;
|
|
conntrack->proto.tcp.seen[0].td_maxwin;
|
|
conntrack->proto.tcp.seen[0].td_scale = 0;
|
|
conntrack->proto.tcp.seen[0].td_scale = 0;
|
|
|
|
|
|
@@ -1123,25 +1123,25 @@ static int tcp_new(struct ip_conntrack *conntrack,
|
|
conntrack->proto.tcp.seen[1].flags = IP_CT_TCP_FLAG_SACK_PERM |
|
|
conntrack->proto.tcp.seen[1].flags = IP_CT_TCP_FLAG_SACK_PERM |
|
|
IP_CT_TCP_FLAG_BE_LIBERAL;
|
|
IP_CT_TCP_FLAG_BE_LIBERAL;
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
conntrack->proto.tcp.seen[1].td_end = 0;
|
|
conntrack->proto.tcp.seen[1].td_end = 0;
|
|
conntrack->proto.tcp.seen[1].td_maxend = 0;
|
|
conntrack->proto.tcp.seen[1].td_maxend = 0;
|
|
conntrack->proto.tcp.seen[1].td_maxwin = 1;
|
|
conntrack->proto.tcp.seen[1].td_maxwin = 1;
|
|
- conntrack->proto.tcp.seen[1].td_scale = 0;
|
|
|
|
|
|
+ conntrack->proto.tcp.seen[1].td_scale = 0;
|
|
|
|
|
|
/* tcp_packet will set them */
|
|
/* tcp_packet will set them */
|
|
conntrack->proto.tcp.state = TCP_CONNTRACK_NONE;
|
|
conntrack->proto.tcp.state = TCP_CONNTRACK_NONE;
|
|
conntrack->proto.tcp.last_index = TCP_NONE_SET;
|
|
conntrack->proto.tcp.last_index = TCP_NONE_SET;
|
|
-
|
|
|
|
|
|
+
|
|
DEBUGP("tcp_new: sender end=%u maxend=%u maxwin=%u scale=%i "
|
|
DEBUGP("tcp_new: sender end=%u maxend=%u maxwin=%u scale=%i "
|
|
"receiver end=%u maxend=%u maxwin=%u scale=%i\n",
|
|
"receiver end=%u maxend=%u maxwin=%u scale=%i\n",
|
|
sender->td_end, sender->td_maxend, sender->td_maxwin,
|
|
sender->td_end, sender->td_maxend, sender->td_maxwin,
|
|
- sender->td_scale,
|
|
|
|
|
|
+ sender->td_scale,
|
|
receiver->td_end, receiver->td_maxend, receiver->td_maxwin,
|
|
receiver->td_end, receiver->td_maxend, receiver->td_maxwin,
|
|
receiver->td_scale);
|
|
receiver->td_scale);
|
|
return 1;
|
|
return 1;
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
struct ip_conntrack_protocol ip_conntrack_protocol_tcp =
|
|
struct ip_conntrack_protocol ip_conntrack_protocol_tcp =
|
|
{
|
|
{
|
|
.proto = IPPROTO_TCP,
|
|
.proto = IPPROTO_TCP,
|