[NETFILTER]: SNMP NAT: fix memory corruption
[safe/jmp/linux-2.6] / net / ipv4 / netfilter / ip_conntrack_proto_tcp.c
index 809dfed..062b252 100644 (file)
@@ -32,7 +32,6 @@
 
 #include <net/tcp.h>
 
-#include <linux/netfilter.h>
 #include <linux/netfilter_ipv4.h>
 #include <linux/netfilter_ipv4/ip_conntrack.h>
 #include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
@@ -85,21 +84,21 @@ static const char *tcp_conntrack_names[] = {
 #define HOURS * 60 MINS
 #define DAYS * 24 HOURS
 
-unsigned long ip_ct_tcp_timeout_syn_sent =      2 MINS;
-unsigned long ip_ct_tcp_timeout_syn_recv =     60 SECS;
-unsigned long ip_ct_tcp_timeout_established =   5 DAYS;
-unsigned long ip_ct_tcp_timeout_fin_wait =      2 MINS;
-unsigned long ip_ct_tcp_timeout_close_wait =   60 SECS;
-unsigned long ip_ct_tcp_timeout_last_ack =     30 SECS;
-unsigned long ip_ct_tcp_timeout_time_wait =     2 MINS;
-unsigned long ip_ct_tcp_timeout_close =        10 SECS;
+unsigned int ip_ct_tcp_timeout_syn_sent =      2 MINS;
+unsigned int ip_ct_tcp_timeout_syn_recv =     60 SECS;
+unsigned int ip_ct_tcp_timeout_established =   5 DAYS;
+unsigned int ip_ct_tcp_timeout_fin_wait =      2 MINS;
+unsigned int ip_ct_tcp_timeout_close_wait =   60 SECS;
+unsigned int ip_ct_tcp_timeout_last_ack =     30 SECS;
+unsigned int ip_ct_tcp_timeout_time_wait =     2 MINS;
+unsigned int ip_ct_tcp_timeout_close =        10 SECS;
 
 /* RFC1122 says the R2 limit should be at least 100 seconds.
    Linux uses 15 packets as limit, which corresponds 
    to ~13-30min depending on RTO. */
-unsigned long ip_ct_tcp_timeout_max_retrans =     5 MINS;
+unsigned int ip_ct_tcp_timeout_max_retrans =     5 MINS;
  
-static unsigned long * tcp_timeouts[]
+static const unsigned int * tcp_timeouts[]
 = { NULL,                              /*      TCP_CONNTRACK_NONE */
     &ip_ct_tcp_timeout_syn_sent,       /*      TCP_CONNTRACK_SYN_SENT, */
     &ip_ct_tcp_timeout_syn_recv,       /*      TCP_CONNTRACK_SYN_RECV, */
@@ -170,7 +169,7 @@ enum tcp_bit_set {
  *     if they are invalid
  *     or we do not support the request (simultaneous open)
  */
-static enum tcp_conntrack tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = {
+static const enum tcp_conntrack tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = {
        {
 /* ORIGINAL */
 /*          sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI   */
@@ -272,9 +271,9 @@ static enum tcp_conntrack tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = {
  *     sCL -> sCL
  */
 /*          sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI   */
-/*ack*/           { sIV, sIV, sSR, sES, sCW, sCW, sTW, sTW, sCL, sIV },
+/*ack*/           { sIV, sIG, sSR, sES, sCW, sCW, sTW, sTW, sCL, sIV },
 /*
- *     sSS -> sIV      Might be a half-open connection.
+ *     sSS -> sIG      Might be a half-open connection.
  *     sSR -> sSR      Might answer late resent SYN.
  *     sES -> sES      :-)
  *     sFW -> sCW      Normal close request answered by ACK.
@@ -336,6 +335,59 @@ static int tcp_print_conntrack(struct seq_file *s,
        return seq_printf(s, "%s ", tcp_conntrack_names[state]);
 }
 
+#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
+    defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
+static int tcp_to_nfattr(struct sk_buff *skb, struct nfattr *nfa,
+                        const struct ip_conntrack *ct)
+{
+       struct nfattr *nest_parms;
+       
+       read_lock_bh(&tcp_lock);
+       nest_parms = NFA_NEST(skb, CTA_PROTOINFO_TCP);
+       NFA_PUT(skb, CTA_PROTOINFO_TCP_STATE, sizeof(u_int8_t),
+               &ct->proto.tcp.state);
+       read_unlock_bh(&tcp_lock);
+
+       NFA_NEST_END(skb, nest_parms);
+
+       return 0;
+
+nfattr_failure:
+       read_unlock_bh(&tcp_lock);
+       return -1;
+}
+
+static const size_t cta_min_tcp[CTA_PROTOINFO_TCP_MAX] = {
+       [CTA_PROTOINFO_TCP_STATE-1]     = sizeof(u_int8_t),
+};
+
+static int nfattr_to_tcp(struct nfattr *cda[], struct ip_conntrack *ct)
+{
+       struct nfattr *attr = cda[CTA_PROTOINFO_TCP-1];
+       struct nfattr *tb[CTA_PROTOINFO_TCP_MAX];
+
+       /* updates could not contain anything about the private
+        * protocol info, in that case skip the parsing */
+       if (!attr)
+               return 0;
+
+        nfattr_parse_nested(tb, CTA_PROTOINFO_TCP_MAX, attr);
+
+       if (nfattr_bad_size(tb, CTA_PROTOINFO_TCP_MAX, cta_min_tcp))
+               return -EINVAL;
+
+       if (!tb[CTA_PROTOINFO_TCP_STATE-1])
+               return -EINVAL;
+
+       write_lock_bh(&tcp_lock);
+       ct->proto.tcp.state = 
+               *(u_int8_t *)NFA_DATA(tb[CTA_PROTOINFO_TCP_STATE-1]);
+       write_unlock_bh(&tcp_lock);
+
+       return 0;
+}
+#endif
+
 static unsigned int get_conntrack_index(const struct tcphdr *tcph)
 {
        if (tcph->rst) return TCP_RST_SET;
@@ -699,7 +751,7 @@ static int tcp_in_window(struct ip_ct_tcp *state,
                res = 1;
        } else {
                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: %s ",
                        before(seq, sender->td_maxend + 1) ?
                        after(end, sender->td_end - receiver->td_maxwin - 1) ?
@@ -765,10 +817,11 @@ void ip_conntrack_tcp_update(struct sk_buff *skb,
 #define        TH_CWR  0x80
 
 /* table of valid flag combinations - ECE and CWR are always valid */
-static u8 tcp_valid_flags[(TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG) + 1] =
+static const u8 tcp_valid_flags[(TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG) + 1] =
 {
        [TH_SYN]                        = 1,
        [TH_SYN|TH_ACK]                 = 1,
+       [TH_SYN|TH_PUSH]                = 1,
        [TH_SYN|TH_ACK|TH_PUSH]         = 1,
        [TH_RST]                        = 1,
        [TH_RST|TH_ACK]                 = 1,
@@ -798,7 +851,7 @@ static int tcp_error(struct sk_buff *skb,
                                sizeof(_tcph), &_tcph);
        if (th == NULL) {
                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: short packet ");
                return -NF_ACCEPT;
        }
@@ -806,7 +859,7 @@ static int tcp_error(struct sk_buff *skb,
        /* Not whole TCP header or malformed packet */
        if (th->doff*4 < sizeof(struct tcphdr) || tcplen < th->doff*4) {
                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: truncated/malformed packet ");
                return -NF_ACCEPT;
        }
@@ -817,13 +870,10 @@ static int tcp_error(struct sk_buff *skb,
         * and moreover root might send raw packets.
         */
        /* FIXME: Source route IP option packets --RR */
-       if (hooknum == NF_IP_PRE_ROUTING
-           && skb->ip_summed != CHECKSUM_UNNECESSARY
-           && csum_tcpudp_magic(iph->saddr, iph->daddr, tcplen, IPPROTO_TCP,
-                                skb->ip_summed == CHECKSUM_HW ? skb->csum
-                                : skb_checksum(skb, iph->ihl*4, tcplen, 0))) {
+       if (hooknum == NF_IP_PRE_ROUTING &&
+           nf_ip_checksum(skb, hooknum, iph->ihl * 4, 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: bad TCP checksum ");
                return -NF_ACCEPT;
        }
@@ -832,7 +882,7 @@ static int tcp_error(struct sk_buff *skb,
        tcpflags = (((u_int8_t *)th)[13] & ~(TH_ECE|TH_CWR));
        if (!tcp_valid_flags[tcpflags]) {
                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: invalid TCP flag combination ");
                return -NF_ACCEPT;
        }
@@ -864,8 +914,12 @@ static int tcp_packet(struct ip_conntrack *conntrack,
 
        switch (new_state) {
        case TCP_CONNTRACK_IGNORE:
-               /* Either SYN in ORIGINAL
-                * or SYN/ACK in REPLY. */
+               /* Ignored packets:
+                * 
+                * a) SYN in ORIGINAL
+                * b) SYN/ACK in REPLY
+                * c) ACK in reply direction after initial SYN in original.
+                */
                if (index == TCP_SYNACK_SET
                    && conntrack->proto.tcp.last_index == TCP_SYN_SET
                    && conntrack->proto.tcp.last_dir != dir
@@ -880,8 +934,9 @@ static int tcp_packet(struct ip_conntrack *conntrack,
                         */
                        write_unlock_bh(&tcp_lock);
                        if (LOG_INVALID(IPPROTO_TCP))
-                               nf_log_packet(PF_INET, 0, skb, NULL, NULL, 
-                                         "ip_ct_tcp: killing out of sync session ");
+                               nf_log_packet(PF_INET, 0, skb, NULL, NULL,
+                                             NULL, "ip_ct_tcp: "
+                                             "killing out of sync session ");
                        if (del_timer(&conntrack->timeout))
                                conntrack->timeout.function((unsigned long)
                                                            conntrack);
@@ -895,7 +950,7 @@ static int tcp_packet(struct ip_conntrack *conntrack,
                
                write_unlock_bh(&tcp_lock);
                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: invalid packet ignored ");
                return NF_ACCEPT;
        case TCP_CONNTRACK_MAX:
@@ -905,7 +960,7 @@ static int tcp_packet(struct ip_conntrack *conntrack,
                       old_state);
                write_unlock_bh(&tcp_lock);
                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: invalid state ");
                return -NF_ACCEPT;
        case TCP_CONNTRACK_SYN_SENT:
@@ -926,21 +981,28 @@ static int tcp_packet(struct ip_conntrack *conntrack,
                        write_unlock_bh(&tcp_lock);
                        if (LOG_INVALID(IPPROTO_TCP))
                                nf_log_packet(PF_INET, 0, skb, NULL, NULL,
-                                             "ip_ct_tcp: invalid SYN");
+                                             NULL, "ip_ct_tcp: invalid SYN");
                        return -NF_ACCEPT;
                }
        case TCP_CONNTRACK_CLOSE:
                if (index == TCP_RST_SET
-                   && test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)
-                   && conntrack->proto.tcp.last_index == TCP_SYN_SET
+                   && ((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))
                    && ntohl(th->ack_seq) == conntrack->proto.tcp.last_end) {
-                       /* RST sent to invalid SYN we had let trough
-                        * SYN was in window then, tear down connection.
+                       /* RST sent to invalid SYN or ACK we had let through
+                        * at a) and c) above:
+                        *
+                        * a) SYN was in window then
+                        * c) we hold a half-open connection.
+                        *
+                        * Delete our connection entry.
                         * We skip window checking, because packet might ACK
-                        * segments we ignored in the SYN. */
+                        * segments we ignored. */
                        goto in_window;
                }
-               /* Just fall trough */
+               /* Just fall through */
        default:
                /* Keep compilers happy. */
                break;
@@ -973,6 +1035,10 @@ static int tcp_packet(struct ip_conntrack *conntrack,
                  ? ip_ct_tcp_timeout_max_retrans : *tcp_timeouts[new_state];
        write_unlock_bh(&tcp_lock);
 
+       ip_conntrack_event_cache(IPCT_PROTOINFO_VOLATILE, skb);
+       if (new_state != old_state)
+               ip_conntrack_event_cache(IPCT_PROTOINFO, skb);
+
        if (!test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)) {
                /* If only reply is a RST, we can consider ourselves not to
                   have an established connection: this is a fairly common
@@ -991,7 +1057,8 @@ static int tcp_packet(struct ip_conntrack *conntrack,
                /* Set ASSURED if we see see valid ack in ESTABLISHED 
                   after SYN_RECV or a valid answer for a picked up 
                   connection. */
-                       set_bit(IPS_ASSURED_BIT, &conntrack->status);
+               set_bit(IPS_ASSURED_BIT, &conntrack->status);
+               ip_conntrack_event_cache(IPCT_STATUS, skb);
        }
        ip_ct_refresh_acct(conntrack, ctinfo, skb, timeout);
 
@@ -1096,4 +1163,11 @@ struct ip_conntrack_protocol ip_conntrack_protocol_tcp =
        .packet                 = tcp_packet,
        .new                    = tcp_new,
        .error                  = tcp_error,
+#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
+    defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
+       .to_nfattr              = tcp_to_nfattr,
+       .from_nfattr            = nfattr_to_tcp,
+       .tuple_to_nfattr        = ip_ct_port_tuple_to_nfattr,
+       .nfattr_to_tuple        = ip_ct_port_nfattr_to_tuple,
+#endif
 };