diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/netfilter/nf_conntrack_proto_tcp.c | 51 |
1 files changed, 41 insertions, 10 deletions
diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index 97a82ba75376..9cc6b5cb06af 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c | |||
@@ -908,23 +908,54 @@ static int tcp_packet(struct nf_conn *ct, | |||
908 | /* b) This SYN/ACK acknowledges a SYN that we earlier | 908 | /* b) This SYN/ACK acknowledges a SYN that we earlier |
909 | * ignored as invalid. This means that the client and | 909 | * ignored as invalid. This means that the client and |
910 | * the server are both in sync, while the firewall is | 910 | * the server are both in sync, while the firewall is |
911 | * not. We kill this session and block the SYN/ACK so | 911 | * not. We get in sync from the previously annotated |
912 | * that the client cannot but retransmit its SYN and | 912 | * values. |
913 | * thus initiate a clean new session. | ||
914 | */ | 913 | */ |
915 | spin_unlock_bh(&ct->lock); | 914 | old_state = TCP_CONNTRACK_SYN_SENT; |
916 | if (LOG_INVALID(net, IPPROTO_TCP)) | 915 | new_state = TCP_CONNTRACK_SYN_RECV; |
917 | nf_log_packet(pf, 0, skb, NULL, NULL, NULL, | 916 | ct->proto.tcp.seen[ct->proto.tcp.last_dir].td_end = |
918 | "nf_ct_tcp: killing out of sync session "); | 917 | ct->proto.tcp.last_end; |
919 | nf_ct_kill(ct); | 918 | ct->proto.tcp.seen[ct->proto.tcp.last_dir].td_maxend = |
920 | return NF_DROP; | 919 | ct->proto.tcp.last_end; |
920 | ct->proto.tcp.seen[ct->proto.tcp.last_dir].td_maxwin = | ||
921 | ct->proto.tcp.last_win == 0 ? | ||
922 | 1 : ct->proto.tcp.last_win; | ||
923 | ct->proto.tcp.seen[ct->proto.tcp.last_dir].td_scale = | ||
924 | ct->proto.tcp.last_wscale; | ||
925 | ct->proto.tcp.seen[ct->proto.tcp.last_dir].flags = | ||
926 | ct->proto.tcp.last_flags; | ||
927 | memset(&ct->proto.tcp.seen[dir], 0, | ||
928 | sizeof(struct ip_ct_tcp_state)); | ||
929 | break; | ||
921 | } | 930 | } |
922 | ct->proto.tcp.last_index = index; | 931 | ct->proto.tcp.last_index = index; |
923 | ct->proto.tcp.last_dir = dir; | 932 | ct->proto.tcp.last_dir = dir; |
924 | ct->proto.tcp.last_seq = ntohl(th->seq); | 933 | ct->proto.tcp.last_seq = ntohl(th->seq); |
925 | ct->proto.tcp.last_end = | 934 | ct->proto.tcp.last_end = |
926 | segment_seq_plus_len(ntohl(th->seq), skb->len, dataoff, th); | 935 | segment_seq_plus_len(ntohl(th->seq), skb->len, dataoff, th); |
927 | 936 | ct->proto.tcp.last_win = ntohs(th->window); | |
937 | |||
938 | /* a) This is a SYN in ORIGINAL. The client and the server | ||
939 | * may be in sync but we are not. In that case, we annotate | ||
940 | * the TCP options and let the packet go through. If it is a | ||
941 | * valid SYN packet, the server will reply with a SYN/ACK, and | ||
942 | * then we'll get in sync. Otherwise, the server ignores it. */ | ||
943 | if (index == TCP_SYN_SET && dir == IP_CT_DIR_ORIGINAL) { | ||
944 | struct ip_ct_tcp_state seen = {}; | ||
945 | |||
946 | ct->proto.tcp.last_flags = | ||
947 | ct->proto.tcp.last_wscale = 0; | ||
948 | tcp_options(skb, dataoff, th, &seen); | ||
949 | if (seen.flags & IP_CT_TCP_FLAG_WINDOW_SCALE) { | ||
950 | ct->proto.tcp.last_flags |= | ||
951 | IP_CT_TCP_FLAG_WINDOW_SCALE; | ||
952 | ct->proto.tcp.last_wscale = seen.td_scale; | ||
953 | } | ||
954 | if (seen.flags & IP_CT_TCP_FLAG_SACK_PERM) { | ||
955 | ct->proto.tcp.last_flags |= | ||
956 | IP_CT_TCP_FLAG_SACK_PERM; | ||
957 | } | ||
958 | } | ||
928 | spin_unlock_bh(&ct->lock); | 959 | spin_unlock_bh(&ct->lock); |
929 | if (LOG_INVALID(net, IPPROTO_TCP)) | 960 | if (LOG_INVALID(net, IPPROTO_TCP)) |
930 | nf_log_packet(pf, 0, skb, NULL, NULL, NULL, | 961 | nf_log_packet(pf, 0, skb, NULL, NULL, NULL, |