]> err.no Git - linux-2.6/blobdiff - net/netfilter/nf_conntrack_netlink.c
[NETFILTER]: nf_log: move logging stuff to seperate header
[linux-2.6] / net / netfilter / nf_conntrack_netlink.c
index 9f9bef2446a13a5409981326909cf0f8147a4dcc..3a065f43ddd8b514208af7c1846600727de12c7e 100644 (file)
@@ -4,7 +4,7 @@
  * (C) 2001 by Jay Schulist <jschlst@samba.org>
  * (C) 2002-2006 by Harald Welte <laforge@gnumonks.org>
  * (C) 2003 by Patrick Mchardy <kaber@trash.net>
- * (C) 2005-2006 by Pablo Neira Ayuso <pablo@eurodev.net>
+ * (C) 2005-2007 by Pablo Neira Ayuso <pablo@netfilter.org>
  *
  * Initial connection tracking via netlink development funded and
  * generally made possible by Network Robots, Inc. (www.networkrobots.com)
@@ -59,7 +59,7 @@ ctnetlink_dump_tuples_proto(struct sk_buff *skb,
        nest_parms = nla_nest_start(skb, CTA_TUPLE_PROTO | NLA_F_NESTED);
        if (!nest_parms)
                goto nla_put_failure;
-       NLA_PUT(skb, CTA_PROTO_NUM, sizeof(u_int8_t), &tuple->dst.protonum);
+       NLA_PUT_U8(skb, CTA_PROTO_NUM, tuple->dst.protonum);
 
        if (likely(l4proto->tuple_to_nlattr))
                ret = l4proto->tuple_to_nlattr(skb, tuple);
@@ -120,8 +120,7 @@ ctnetlink_dump_tuples(struct sk_buff *skb,
 static inline int
 ctnetlink_dump_status(struct sk_buff *skb, const struct nf_conn *ct)
 {
-       __be32 status = htonl((u_int32_t) ct->status);
-       NLA_PUT(skb, CTA_STATUS, sizeof(status), &status);
+       NLA_PUT_BE32(skb, CTA_STATUS, htonl(ct->status));
        return 0;
 
 nla_put_failure:
@@ -131,15 +130,12 @@ nla_put_failure:
 static inline int
 ctnetlink_dump_timeout(struct sk_buff *skb, const struct nf_conn *ct)
 {
-       long timeout_l = ct->timeout.expires - jiffies;
-       __be32 timeout;
+       long timeout = (ct->timeout.expires - jiffies) / HZ;
 
-       if (timeout_l < 0)
+       if (timeout < 0)
                timeout = 0;
-       else
-               timeout = htonl(timeout_l / HZ);
 
-       NLA_PUT(skb, CTA_TIMEOUT, sizeof(timeout), &timeout);
+       NLA_PUT_BE32(skb, CTA_TIMEOUT, htonl(timeout));
        return 0;
 
 nla_put_failure:
@@ -193,7 +189,7 @@ ctnetlink_dump_helpinfo(struct sk_buff *skb, const struct nf_conn *ct)
        nest_helper = nla_nest_start(skb, CTA_HELP | NLA_F_NESTED);
        if (!nest_helper)
                goto nla_put_failure;
-       NLA_PUT(skb, CTA_HELP_NAME, strlen(helper->name), helper->name);
+       NLA_PUT_STRING(skb, CTA_HELP_NAME, helper->name);
 
        if (helper->to_nlattr)
                helper->to_nlattr(skb, ct);
@@ -215,17 +211,15 @@ ctnetlink_dump_counters(struct sk_buff *skb, const struct nf_conn *ct,
 {
        enum ctattr_type type = dir ? CTA_COUNTERS_REPLY: CTA_COUNTERS_ORIG;
        struct nlattr *nest_count;
-       __be32 tmp;
 
        nest_count = nla_nest_start(skb, type | NLA_F_NESTED);
        if (!nest_count)
                goto nla_put_failure;
 
-       tmp = htonl(ct->counters[dir].packets);
-       NLA_PUT(skb, CTA_COUNTERS32_PACKETS, sizeof(u_int32_t), &tmp);
-
-       tmp = htonl(ct->counters[dir].bytes);
-       NLA_PUT(skb, CTA_COUNTERS32_BYTES, sizeof(u_int32_t), &tmp);
+       NLA_PUT_BE32(skb, CTA_COUNTERS32_PACKETS,
+                    htonl(ct->counters[dir].packets));
+       NLA_PUT_BE32(skb, CTA_COUNTERS32_BYTES,
+                    htonl(ct->counters[dir].bytes));
 
        nla_nest_end(skb, nest_count);
 
@@ -242,9 +236,7 @@ nla_put_failure:
 static inline int
 ctnetlink_dump_mark(struct sk_buff *skb, const struct nf_conn *ct)
 {
-       __be32 mark = htonl(ct->mark);
-
-       NLA_PUT(skb, CTA_MARK, sizeof(u_int32_t), &mark);
+       NLA_PUT_BE32(skb, CTA_MARK, htonl(ct->mark));
        return 0;
 
 nla_put_failure:
@@ -254,11 +246,95 @@ nla_put_failure:
 #define ctnetlink_dump_mark(a, b) (0)
 #endif
 
+#ifdef CONFIG_NF_CONNTRACK_SECMARK
+static inline int
+ctnetlink_dump_secmark(struct sk_buff *skb, const struct nf_conn *ct)
+{
+       NLA_PUT_BE32(skb, CTA_SECMARK, htonl(ct->secmark));
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+#else
+#define ctnetlink_dump_secmark(a, b) (0)
+#endif
+
+#define master_tuple(ct) &(ct->master->tuplehash[IP_CT_DIR_ORIGINAL].tuple)
+
+static inline int
+ctnetlink_dump_master(struct sk_buff *skb, const struct nf_conn *ct)
+{
+       struct nlattr *nest_parms;
+
+       if (!(ct->status & IPS_EXPECTED))
+               return 0;
+
+       nest_parms = nla_nest_start(skb, CTA_TUPLE_MASTER | NLA_F_NESTED);
+       if (!nest_parms)
+               goto nla_put_failure;
+       if (ctnetlink_dump_tuples(skb, master_tuple(ct)) < 0)
+               goto nla_put_failure;
+       nla_nest_end(skb, nest_parms);
+
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+
+#ifdef CONFIG_NF_NAT_NEEDED
+static inline int
+dump_nat_seq_adj(struct sk_buff *skb, const struct nf_nat_seq *natseq, int type)
+{
+       struct nlattr *nest_parms;
+
+       nest_parms = nla_nest_start(skb, type | NLA_F_NESTED);
+       if (!nest_parms)
+               goto nla_put_failure;
+
+       NLA_PUT_BE32(skb, CTA_NAT_SEQ_CORRECTION_POS,
+                    htonl(natseq->correction_pos));
+       NLA_PUT_BE32(skb, CTA_NAT_SEQ_OFFSET_BEFORE,
+                    htonl(natseq->offset_before));
+       NLA_PUT_BE32(skb, CTA_NAT_SEQ_OFFSET_AFTER,
+                    htonl(natseq->offset_after));
+
+       nla_nest_end(skb, nest_parms);
+
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+
+static inline int
+ctnetlink_dump_nat_seq_adj(struct sk_buff *skb, const struct nf_conn *ct)
+{
+       struct nf_nat_seq *natseq;
+       struct nf_conn_nat *nat = nfct_nat(ct);
+
+       if (!(ct->status & IPS_SEQ_ADJUST) || !nat)
+               return 0;
+
+       natseq = &nat->seq[IP_CT_DIR_ORIGINAL];
+       if (dump_nat_seq_adj(skb, natseq, CTA_NAT_SEQ_ADJ_ORIG) == -1)
+               return -1;
+
+       natseq = &nat->seq[IP_CT_DIR_REPLY];
+       if (dump_nat_seq_adj(skb, natseq, CTA_NAT_SEQ_ADJ_REPLY) == -1)
+               return -1;
+
+       return 0;
+}
+#else
+#define ctnetlink_dump_nat_seq_adj(a, b) (0)
+#endif
+
 static inline int
 ctnetlink_dump_id(struct sk_buff *skb, const struct nf_conn *ct)
 {
-       __be32 id = htonl(ct->id);
-       NLA_PUT(skb, CTA_ID, sizeof(u_int32_t), &id);
+       NLA_PUT_BE32(skb, CTA_ID, htonl((unsigned long)ct));
        return 0;
 
 nla_put_failure:
@@ -268,9 +344,7 @@ nla_put_failure:
 static inline int
 ctnetlink_dump_use(struct sk_buff *skb, const struct nf_conn *ct)
 {
-       __be32 use = htonl(atomic_read(&ct->ct_general.use));
-
-       NLA_PUT(skb, CTA_USE, sizeof(u_int32_t), &use);
+       NLA_PUT_BE32(skb, CTA_USE, htonl(atomic_read(&ct->ct_general.use)));
        return 0;
 
 nla_put_failure:
@@ -320,8 +394,11 @@ ctnetlink_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
            ctnetlink_dump_protoinfo(skb, ct) < 0 ||
            ctnetlink_dump_helpinfo(skb, ct) < 0 ||
            ctnetlink_dump_mark(skb, ct) < 0 ||
+           ctnetlink_dump_secmark(skb, ct) < 0 ||
            ctnetlink_dump_id(skb, ct) < 0 ||
-           ctnetlink_dump_use(skb, ct) < 0)
+           ctnetlink_dump_use(skb, ct) < 0 ||
+           ctnetlink_dump_master(skb, ct) < 0 ||
+           ctnetlink_dump_nat_seq_adj(skb, ct) < 0)
                goto nla_put_failure;
 
        nlh->nlmsg_len = skb_tail_pointer(skb) - b;
@@ -419,11 +496,24 @@ static int ctnetlink_conntrack_event(struct notifier_block *this,
                    && ctnetlink_dump_mark(skb, ct) < 0)
                        goto nla_put_failure;
 #endif
+#ifdef CONFIG_NF_CONNTRACK_SECMARK
+               if ((events & IPCT_SECMARK || ct->secmark)
+                   && ctnetlink_dump_secmark(skb, ct) < 0)
+                       goto nla_put_failure;
+#endif
 
                if (events & IPCT_COUNTER_FILLING &&
                    (ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 ||
                     ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0))
                        goto nla_put_failure;
+
+               if (events & IPCT_RELATED &&
+                   ctnetlink_dump_master(skb, ct) < 0)
+                       goto nla_put_failure;
+
+               if (events & IPCT_NATSEQADJ &&
+                   ctnetlink_dump_nat_seq_adj(skb, ct) < 0)
+                       goto nla_put_failure;
        }
 
        nlh->nlmsg_len = skb->tail - b;
@@ -512,16 +602,20 @@ ctnetlink_parse_tuple_ip(struct nlattr *attr, struct nf_conntrack_tuple *tuple)
 
        l3proto = nf_ct_l3proto_find_get(tuple->src.l3num);
 
-       if (likely(l3proto->nlattr_to_tuple))
-               ret = l3proto->nlattr_to_tuple(tb, tuple);
+       if (likely(l3proto->nlattr_to_tuple)) {
+               ret = nla_validate_nested(attr, CTA_IP_MAX,
+                                         l3proto->nla_policy);
+               if (ret == 0)
+                       ret = l3proto->nlattr_to_tuple(tb, tuple);
+       }
 
        nf_ct_l3proto_put(l3proto);
 
        return ret;
 }
 
-static const size_t cta_min_proto[CTA_PROTO_MAX+1] = {
-       [CTA_PROTO_NUM] = sizeof(u_int8_t),
+static const struct nla_policy proto_nla_policy[CTA_PROTO_MAX+1] = {
+       [CTA_PROTO_NUM] = { .type = NLA_U8 },
 };
 
 static inline int
@@ -532,19 +626,22 @@ ctnetlink_parse_tuple_proto(struct nlattr *attr,
        struct nf_conntrack_l4proto *l4proto;
        int ret = 0;
 
-       nla_parse_nested(tb, CTA_PROTO_MAX, attr, NULL);
-
-       if (nlattr_bad_size(tb, CTA_PROTO_MAX, cta_min_proto))
-               return -EINVAL;
+       ret = nla_parse_nested(tb, CTA_PROTO_MAX, attr, proto_nla_policy);
+       if (ret < 0)
+               return ret;
 
        if (!tb[CTA_PROTO_NUM])
                return -EINVAL;
-       tuple->dst.protonum = *(u_int8_t *)nla_data(tb[CTA_PROTO_NUM]);
+       tuple->dst.protonum = nla_get_u8(tb[CTA_PROTO_NUM]);
 
        l4proto = nf_ct_l4proto_find_get(tuple->src.l3num, tuple->dst.protonum);
 
-       if (likely(l4proto->nlattr_to_tuple))
-               ret = l4proto->nlattr_to_tuple(tb, tuple);
+       if (likely(l4proto->nlattr_to_tuple)) {
+               ret = nla_validate_nested(attr, CTA_PROTO_MAX,
+                                         l4proto->nla_policy);
+               if (ret == 0)
+                       ret = l4proto->nlattr_to_tuple(tb, tuple);
+       }
 
        nf_ct_l4proto_put(l4proto);
 
@@ -588,9 +685,9 @@ ctnetlink_parse_tuple(struct nlattr *cda[], struct nf_conntrack_tuple *tuple,
 }
 
 #ifdef CONFIG_NF_NAT_NEEDED
-static const size_t cta_min_protonat[CTA_PROTONAT_MAX+1] = {
-       [CTA_PROTONAT_PORT_MIN] = sizeof(u_int16_t),
-       [CTA_PROTONAT_PORT_MAX] = sizeof(u_int16_t),
+static const struct nla_policy protonat_nla_policy[CTA_PROTONAT_MAX+1] = {
+       [CTA_PROTONAT_PORT_MIN] = { .type = NLA_U16 },
+       [CTA_PROTONAT_PORT_MAX] = { .type = NLA_U16 },
 };
 
 static int nfnetlink_parse_nat_proto(struct nlattr *attr,
@@ -598,12 +695,12 @@ static int nfnetlink_parse_nat_proto(struct nlattr *attr,
                                     struct nf_nat_range *range)
 {
        struct nlattr *tb[CTA_PROTONAT_MAX+1];
-       struct nf_nat_protocol *npt;
-
-       nla_parse_nested(tb, CTA_PROTONAT_MAX, attr, NULL);
+       const struct nf_nat_protocol *npt;
+       int err;
 
-       if (nlattr_bad_size(tb, CTA_PROTONAT_MAX, cta_min_protonat))
-               return -EINVAL;
+       err = nla_parse_nested(tb, CTA_PROTONAT_MAX, attr, protonat_nla_policy);
+       if (err < 0)
+               return err;
 
        npt = nf_nat_proto_find_get(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum);
 
@@ -621,9 +718,9 @@ static int nfnetlink_parse_nat_proto(struct nlattr *attr,
        return 0;
 }
 
-static const size_t cta_min_nat[CTA_NAT_MAX+1] = {
-       [CTA_NAT_MINIP]         = sizeof(u_int32_t),
-       [CTA_NAT_MAXIP]         = sizeof(u_int32_t),
+static const struct nla_policy nat_nla_policy[CTA_NAT_MAX+1] = {
+       [CTA_NAT_MINIP]         = { .type = NLA_U32 },
+       [CTA_NAT_MAXIP]         = { .type = NLA_U32 },
 };
 
 static inline int
@@ -635,18 +732,17 @@ nfnetlink_parse_nat(struct nlattr *nat,
 
        memset(range, 0, sizeof(*range));
 
-       nla_parse_nested(tb, CTA_NAT_MAX, nat, NULL);
-
-       if (nlattr_bad_size(tb, CTA_NAT_MAX, cta_min_nat))
-               return -EINVAL;
+       err = nla_parse_nested(tb, CTA_NAT_MAX, nat, nat_nla_policy);
+       if (err < 0)
+               return err;
 
        if (tb[CTA_NAT_MINIP])
-               range->min_ip = *(__be32 *)nla_data(tb[CTA_NAT_MINIP]);
+               range->min_ip = nla_get_be32(tb[CTA_NAT_MINIP]);
 
        if (!tb[CTA_NAT_MAXIP])
                range->max_ip = range->min_ip;
        else
-               range->max_ip = *(__be32 *)nla_data(tb[CTA_NAT_MAXIP]);
+               range->max_ip = nla_get_be32(tb[CTA_NAT_MAXIP]);
 
        if (range->min_ip)
                range->flags |= IP_NAT_RANGE_MAP_IPS;
@@ -677,12 +773,12 @@ ctnetlink_parse_help(struct nlattr *attr, char **helper_name)
        return 0;
 }
 
-static const size_t cta_min[CTA_MAX+1] = {
-       [CTA_STATUS]            = sizeof(u_int32_t),
-       [CTA_TIMEOUT]           = sizeof(u_int32_t),
-       [CTA_MARK]              = sizeof(u_int32_t),
-       [CTA_USE]               = sizeof(u_int32_t),
-       [CTA_ID]                = sizeof(u_int32_t)
+static const struct nla_policy ct_nla_policy[CTA_MAX+1] = {
+       [CTA_STATUS]            = { .type = NLA_U32 },
+       [CTA_TIMEOUT]           = { .type = NLA_U32 },
+       [CTA_MARK]              = { .type = NLA_U32 },
+       [CTA_USE]               = { .type = NLA_U32 },
+       [CTA_ID]                = { .type = NLA_U32 },
 };
 
 static int
@@ -696,9 +792,6 @@ ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
        u_int8_t u3 = nfmsg->nfgen_family;
        int err = 0;
 
-       if (nlattr_bad_size(cda, CTA_MAX, cta_min))
-               return -EINVAL;
-
        if (cda[CTA_TUPLE_ORIG])
                err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_ORIG, u3);
        else if (cda[CTA_TUPLE_REPLY])
@@ -719,8 +812,8 @@ ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
        ct = nf_ct_tuplehash_to_ctrack(h);
 
        if (cda[CTA_ID]) {
-               u_int32_t id = ntohl(*(__be32 *)nla_data(cda[CTA_ID]));
-               if (ct->id != id) {
+               u_int32_t id = ntohl(nla_get_be32(cda[CTA_ID]));
+               if (id != (u32)(unsigned long)ct) {
                        nf_ct_put(ct);
                        return -ENOENT;
                }
@@ -754,9 +847,6 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb,
                                          ctnetlink_done);
        }
 
-       if (nlattr_bad_size(cda, CTA_MAX, cta_min))
-               return -EINVAL;
-
        if (cda[CTA_TUPLE_ORIG])
                err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_ORIG, u3);
        else if (cda[CTA_TUPLE_REPLY])
@@ -802,7 +892,7 @@ static inline int
 ctnetlink_change_status(struct nf_conn *ct, struct nlattr *cda[])
 {
        unsigned long d;
-       unsigned int status = ntohl(*(__be32 *)nla_data(cda[CTA_STATUS]));
+       unsigned int status = ntohl(nla_get_be32(cda[CTA_STATUS]));
        d = ct->status ^ status;
 
        if (d & (IPS_EXPECTED|IPS_CONFIRMED|IPS_DYING))
@@ -828,19 +918,17 @@ ctnetlink_change_status(struct nf_conn *ct, struct nlattr *cda[])
                        if (nfnetlink_parse_nat(cda[CTA_NAT_DST], ct,
                                                &range) < 0)
                                return -EINVAL;
-                       if (nf_nat_initialized(ct,
-                                              HOOK2MANIP(NF_IP_PRE_ROUTING)))
+                       if (nf_nat_initialized(ct, IP_NAT_MANIP_DST))
                                return -EEXIST;
-                       nf_nat_setup_info(ct, &range, NF_IP_PRE_ROUTING);
+                       nf_nat_setup_info(ct, &range, IP_NAT_MANIP_DST);
                }
                if (cda[CTA_NAT_SRC]) {
                        if (nfnetlink_parse_nat(cda[CTA_NAT_SRC], ct,
                                                &range) < 0)
                                return -EINVAL;
-                       if (nf_nat_initialized(ct,
-                                              HOOK2MANIP(NF_IP_POST_ROUTING)))
+                       if (nf_nat_initialized(ct, IP_NAT_MANIP_SRC))
                                return -EEXIST;
-                       nf_nat_setup_info(ct, &range, NF_IP_POST_ROUTING);
+                       nf_nat_setup_info(ct, &range, IP_NAT_MANIP_SRC);
                }
 #endif
        }
@@ -904,7 +992,7 @@ ctnetlink_change_helper(struct nf_conn *ct, struct nlattr *cda[])
 static inline int
 ctnetlink_change_timeout(struct nf_conn *ct, struct nlattr *cda[])
 {
-       u_int32_t timeout = ntohl(*(__be32 *)nla_data(cda[CTA_TIMEOUT]));
+       u_int32_t timeout = ntohl(nla_get_be32(cda[CTA_TIMEOUT]));
 
        if (!del_timer(&ct->timeout))
                return -ETIME;
@@ -935,6 +1023,66 @@ ctnetlink_change_protoinfo(struct nf_conn *ct, struct nlattr *cda[])
        return err;
 }
 
+#ifdef CONFIG_NF_NAT_NEEDED
+static inline int
+change_nat_seq_adj(struct nf_nat_seq *natseq, struct nlattr *attr)
+{
+       struct nlattr *cda[CTA_NAT_SEQ_MAX+1];
+
+       nla_parse_nested(cda, CTA_NAT_SEQ_MAX, attr, NULL);
+
+       if (!cda[CTA_NAT_SEQ_CORRECTION_POS])
+               return -EINVAL;
+
+       natseq->correction_pos =
+               ntohl(nla_get_be32(cda[CTA_NAT_SEQ_CORRECTION_POS]));
+
+       if (!cda[CTA_NAT_SEQ_OFFSET_BEFORE])
+               return -EINVAL;
+
+       natseq->offset_before =
+               ntohl(nla_get_be32(cda[CTA_NAT_SEQ_OFFSET_BEFORE]));
+
+       if (!cda[CTA_NAT_SEQ_OFFSET_AFTER])
+               return -EINVAL;
+
+       natseq->offset_after =
+               ntohl(nla_get_be32(cda[CTA_NAT_SEQ_OFFSET_AFTER]));
+
+       return 0;
+}
+
+static int
+ctnetlink_change_nat_seq_adj(struct nf_conn *ct, struct nlattr *cda[])
+{
+       int ret = 0;
+       struct nf_conn_nat *nat = nfct_nat(ct);
+
+       if (!nat)
+               return 0;
+
+       if (cda[CTA_NAT_SEQ_ADJ_ORIG]) {
+               ret = change_nat_seq_adj(&nat->seq[IP_CT_DIR_ORIGINAL],
+                                        cda[CTA_NAT_SEQ_ADJ_ORIG]);
+               if (ret < 0)
+                       return ret;
+
+               ct->status |= IPS_SEQ_ADJUST;
+       }
+
+       if (cda[CTA_NAT_SEQ_ADJ_REPLY]) {
+               ret = change_nat_seq_adj(&nat->seq[IP_CT_DIR_REPLY],
+                                        cda[CTA_NAT_SEQ_ADJ_REPLY]);
+               if (ret < 0)
+                       return ret;
+
+               ct->status |= IPS_SEQ_ADJUST;
+       }
+
+       return 0;
+}
+#endif
+
 static int
 ctnetlink_change_conntrack(struct nf_conn *ct, struct nlattr *cda[])
 {
@@ -966,7 +1114,15 @@ ctnetlink_change_conntrack(struct nf_conn *ct, struct nlattr *cda[])
 
 #if defined(CONFIG_NF_CONNTRACK_MARK)
        if (cda[CTA_MARK])
-               ct->mark = ntohl(*(__be32 *)nla_data(cda[CTA_MARK]));
+               ct->mark = ntohl(nla_get_be32(cda[CTA_MARK]));
+#endif
+
+#ifdef CONFIG_NF_NAT_NEEDED
+       if (cda[CTA_NAT_SEQ_ADJ_ORIG] || cda[CTA_NAT_SEQ_ADJ_REPLY]) {
+               err = ctnetlink_change_nat_seq_adj(ct, cda);
+               if (err < 0)
+                       return err;
+       }
 #endif
 
        return 0;
@@ -975,7 +1131,8 @@ ctnetlink_change_conntrack(struct nf_conn *ct, struct nlattr *cda[])
 static int
 ctnetlink_create_conntrack(struct nlattr *cda[],
                           struct nf_conntrack_tuple *otuple,
-                          struct nf_conntrack_tuple *rtuple)
+                          struct nf_conntrack_tuple *rtuple,
+                          struct nf_conn *master_ct)
 {
        struct nf_conn *ct;
        int err = -EINVAL;
@@ -988,7 +1145,7 @@ ctnetlink_create_conntrack(struct nlattr *cda[],
 
        if (!cda[CTA_TIMEOUT])
                goto err;
-       ct->timeout.expires = ntohl(*(__be32 *)nla_data(cda[CTA_TIMEOUT]));
+       ct->timeout.expires = ntohl(nla_get_be32(cda[CTA_TIMEOUT]));
 
        ct->timeout.expires = jiffies + ct->timeout.expires * HZ;
        ct->status |= IPS_CONFIRMED;
@@ -1007,7 +1164,7 @@ ctnetlink_create_conntrack(struct nlattr *cda[],
 
 #if defined(CONFIG_NF_CONNTRACK_MARK)
        if (cda[CTA_MARK])
-               ct->mark = ntohl(*(__be32 *)nla_data(cda[CTA_MARK]));
+               ct->mark = ntohl(nla_get_be32(cda[CTA_MARK]));
 #endif
 
        helper = nf_ct_helper_find_get(rtuple);
@@ -1022,6 +1179,12 @@ ctnetlink_create_conntrack(struct nlattr *cda[],
                rcu_assign_pointer(help->helper, helper);
        }
 
+       /* setup master conntrack: this is a confirmed expectation */
+       if (master_ct) {
+               __set_bit(IPS_EXPECTED_BIT, &ct->status);
+               ct->master = master_ct;
+       }
+
        add_timer(&ct->timeout);
        nf_conntrack_hash_insert(ct);
 
@@ -1045,9 +1208,6 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
        u_int8_t u3 = nfmsg->nfgen_family;
        int err = 0;
 
-       if (nlattr_bad_size(cda, CTA_MAX, cta_min))
-               return -EINVAL;
-
        if (cda[CTA_TUPLE_ORIG]) {
                err = ctnetlink_parse_tuple(cda, &otuple, CTA_TUPLE_ORIG, u3);
                if (err < 0)
@@ -1067,10 +1227,37 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
                h = __nf_conntrack_find(&rtuple, NULL);
 
        if (h == NULL) {
+               struct nf_conntrack_tuple master;
+               struct nf_conntrack_tuple_hash *master_h = NULL;
+               struct nf_conn *master_ct = NULL;
+
+               if (cda[CTA_TUPLE_MASTER]) {
+                       err = ctnetlink_parse_tuple(cda,
+                                                   &master,
+                                                   CTA_TUPLE_MASTER,
+                                                   u3);
+                       if (err < 0)
+                               return err;
+
+                       master_h = __nf_conntrack_find(&master, NULL);
+                       if (master_h == NULL) {
+                               err = -ENOENT;
+                               goto out_unlock;
+                       }
+                       master_ct = nf_ct_tuplehash_to_ctrack(master_h);
+                       atomic_inc(&master_ct->ct_general.use);
+               }
+
                write_unlock_bh(&nf_conntrack_lock);
                err = -ENOENT;
                if (nlh->nlmsg_flags & NLM_F_CREATE)
-                       err = ctnetlink_create_conntrack(cda, &otuple, &rtuple);
+                       err = ctnetlink_create_conntrack(cda,
+                                                        &otuple,
+                                                        &rtuple,
+                                                        master_ct);
+               if (err < 0 && master_ct)
+                       nf_ct_put(master_ct);
+
                return err;
        }
        /* implicit 'else' */
@@ -1084,6 +1271,11 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
                        err = -EINVAL;
                        goto out_unlock;
                }
+               /* can't link an existing conntrack to a master */
+               if (cda[CTA_TUPLE_MASTER]) {
+                       err = -EINVAL;
+                       goto out_unlock;
+               }
                err = ctnetlink_change_conntrack(nf_ct_tuplehash_to_ctrack(h),
                                                 cda);
        }
@@ -1162,8 +1354,10 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb,
                          const struct nf_conntrack_expect *exp)
 {
        struct nf_conn *master = exp->master;
-       __be32 timeout = htonl((exp->timeout.expires - jiffies) / HZ);
-       __be32 id = htonl(exp->id);
+       long timeout = (exp->timeout.expires - jiffies) / HZ;
+
+       if (timeout < 0)
+               timeout = 0;
 
        if (ctnetlink_exp_dump_tuple(skb, &exp->tuple, CTA_EXPECT_TUPLE) < 0)
                goto nla_put_failure;
@@ -1174,8 +1368,8 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb,
                                 CTA_EXPECT_MASTER) < 0)
                goto nla_put_failure;
 
-       NLA_PUT(skb, CTA_EXPECT_TIMEOUT, sizeof(timeout), &timeout);
-       NLA_PUT(skb, CTA_EXPECT_ID, sizeof(u_int32_t), &id);
+       NLA_PUT_BE32(skb, CTA_EXPECT_TIMEOUT, htonl(timeout));
+       NLA_PUT_BE32(skb, CTA_EXPECT_ID, htonl((unsigned long)exp));
 
        return 0;
 
@@ -1313,9 +1507,9 @@ out:
        return skb->len;
 }
 
-static const size_t cta_min_exp[CTA_EXPECT_MAX+1] = {
-       [CTA_EXPECT_TIMEOUT]    = sizeof(u_int32_t),
-       [CTA_EXPECT_ID]         = sizeof(u_int32_t)
+static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = {
+       [CTA_EXPECT_TIMEOUT]    = { .type = NLA_U32 },
+       [CTA_EXPECT_ID]         = { .type = NLA_U32 },
 };
 
 static int
@@ -1329,9 +1523,6 @@ ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb,
        u_int8_t u3 = nfmsg->nfgen_family;
        int err = 0;
 
-       if (nlattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp))
-               return -EINVAL;
-
        if (nlh->nlmsg_flags & NLM_F_DUMP) {
                return netlink_dump_start(ctnl, skb, nlh,
                                          ctnetlink_exp_dump_table,
@@ -1351,8 +1542,8 @@ ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb,
                return -ENOENT;
 
        if (cda[CTA_EXPECT_ID]) {
-               __be32 id = *(__be32 *)nla_data(cda[CTA_EXPECT_ID]);
-               if (exp->id != ntohl(id)) {
+               __be32 id = nla_get_be32(cda[CTA_EXPECT_ID]);
+               if (ntohl(id) != (u32)(unsigned long)exp) {
                        nf_ct_expect_put(exp);
                        return -ENOENT;
                }
@@ -1393,9 +1584,6 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb,
        unsigned int i;
        int err;
 
-       if (nlattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp))
-               return -EINVAL;
-
        if (cda[CTA_EXPECT_TUPLE]) {
                /* delete a single expect by tuple */
                err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE, u3);
@@ -1408,8 +1596,8 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb,
                        return -ENOENT;
 
                if (cda[CTA_EXPECT_ID]) {
-                       __be32 id = *(__be32 *)nla_data(cda[CTA_EXPECT_ID]);
-                       if (exp->id != ntohl(id)) {
+                       __be32 id = nla_get_be32(cda[CTA_EXPECT_ID]);
+                       if (ntohl(id) != (u32)(unsigned long)exp) {
                                nf_ct_expect_put(exp);
                                return -ENOENT;
                        }
@@ -1534,9 +1722,6 @@ ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb,
        u_int8_t u3 = nfmsg->nfgen_family;
        int err = 0;
 
-       if (nlattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp))
-               return -EINVAL;
-
        if (!cda[CTA_EXPECT_TUPLE]
            || !cda[CTA_EXPECT_MASK]
            || !cda[CTA_EXPECT_MASTER])
@@ -1577,22 +1762,29 @@ static struct notifier_block ctnl_notifier_exp = {
 
 static const struct nfnl_callback ctnl_cb[IPCTNL_MSG_MAX] = {
        [IPCTNL_MSG_CT_NEW]             = { .call = ctnetlink_new_conntrack,
-                                           .attr_count = CTA_MAX, },
+                                           .attr_count = CTA_MAX,
+                                           .policy = ct_nla_policy },
        [IPCTNL_MSG_CT_GET]             = { .call = ctnetlink_get_conntrack,
-                                           .attr_count = CTA_MAX, },
+                                           .attr_count = CTA_MAX,
+                                           .policy = ct_nla_policy },
        [IPCTNL_MSG_CT_DELETE]          = { .call = ctnetlink_del_conntrack,
-                                           .attr_count = CTA_MAX, },
+                                           .attr_count = CTA_MAX,
+                                           .policy = ct_nla_policy },
        [IPCTNL_MSG_CT_GET_CTRZERO]     = { .call = ctnetlink_get_conntrack,
-                                           .attr_count = CTA_MAX, },
+                                           .attr_count = CTA_MAX,
+                                           .policy = ct_nla_policy },
 };
 
 static const struct nfnl_callback ctnl_exp_cb[IPCTNL_MSG_EXP_MAX] = {
        [IPCTNL_MSG_EXP_GET]            = { .call = ctnetlink_get_expect,
-                                           .attr_count = CTA_EXPECT_MAX, },
+                                           .attr_count = CTA_EXPECT_MAX,
+                                           .policy = exp_nla_policy },
        [IPCTNL_MSG_EXP_NEW]            = { .call = ctnetlink_new_expect,
-                                           .attr_count = CTA_EXPECT_MAX, },
+                                           .attr_count = CTA_EXPECT_MAX,
+                                           .policy = exp_nla_policy },
        [IPCTNL_MSG_EXP_DELETE]         = { .call = ctnetlink_del_expect,
-                                           .attr_count = CTA_EXPECT_MAX, },
+                                           .attr_count = CTA_EXPECT_MAX,
+                                           .policy = exp_nla_policy },
 };
 
 static const struct nfnetlink_subsystem ctnl_subsys = {