diff options
author | Pablo Neira Ayuso <pablo@netfilter.org> | 2007-12-17 22:28:00 -0800 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-01-28 14:58:50 -0800 |
commit | 13eae15a244bb29beaa47bf86a24fd29ca7f8a4c (patch) | |
tree | 27a1a1e6498033c5aa440ced5242016fd808c560 /net/netfilter | |
parent | 170080645dac61816455afad807ffeb326ce79e8 (diff) |
[NETFILTER]: ctnetlink: add support for NAT sequence adjustments
The combination of NAT and helpers may produce TCP sequence adjustments.
In failover setups, this information needs to be replicated in order to
achieve a successful recovery of mangled, related connections. This patch is
particularly useful for conntrackd, see:
http://people.netfilter.org/pablo/conntrack-tools/
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/netfilter')
-rw-r--r-- | net/netfilter/nf_conntrack_netlink.c | 124 |
1 files changed, 123 insertions, 1 deletions
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index a15971e9923..d7da167ef1b 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -254,6 +254,55 @@ nla_put_failure: #define ctnetlink_dump_mark(a, b) (0) #endif +#ifdef CONFIG_NF_NAT_NEEDED +static inline int +dump_nat_seq_adj(struct sk_buff *skb, const struct nf_nat_seq *natseq, int type) +{ + __be32 tmp; + struct nlattr *nest_parms; + + nest_parms = nla_nest_start(skb, type | NLA_F_NESTED); + if (!nest_parms) + goto nla_put_failure; + + tmp = htonl(natseq->correction_pos); + NLA_PUT(skb, CTA_NAT_SEQ_CORRECTION_POS, sizeof(tmp), &tmp); + tmp = htonl(natseq->offset_before); + NLA_PUT(skb, CTA_NAT_SEQ_OFFSET_BEFORE, sizeof(tmp), &tmp); + tmp = htonl(natseq->offset_after); + NLA_PUT(skb, CTA_NAT_SEQ_OFFSET_AFTER, sizeof(tmp), &tmp); + + 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) { @@ -321,7 +370,8 @@ ctnetlink_fill_info(struct sk_buff *skb, u32 pid, u32 seq, ctnetlink_dump_helpinfo(skb, ct) < 0 || ctnetlink_dump_mark(skb, ct) < 0 || ctnetlink_dump_id(skb, ct) < 0 || - ctnetlink_dump_use(skb, ct) < 0) + ctnetlink_dump_use(skb, ct) < 0 || + ctnetlink_dump_nat_seq_adj(skb, ct) < 0) goto nla_put_failure; nlh->nlmsg_len = skb_tail_pointer(skb) - b; @@ -424,6 +474,10 @@ static int ctnetlink_conntrack_event(struct notifier_block *this, (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_NATSEQADJ && + ctnetlink_dump_nat_seq_adj(skb, ct) < 0) + goto nla_put_failure; } nlh->nlmsg_len = skb->tail - b; @@ -935,6 +989,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(*(__be32 *)nla_data(cda[CTA_NAT_SEQ_CORRECTION_POS])); + + if (!cda[CTA_NAT_SEQ_OFFSET_BEFORE]) + return -EINVAL; + + natseq->offset_before = + ntohl(*(__be32 *)nla_data(cda[CTA_NAT_SEQ_OFFSET_BEFORE])); + + if (!cda[CTA_NAT_SEQ_OFFSET_AFTER]) + return -EINVAL; + + natseq->offset_after = + ntohl(*(__be32 *)nla_data(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[]) { @@ -969,6 +1083,14 @@ ctnetlink_change_conntrack(struct nf_conn *ct, struct nlattr *cda[]) ct->mark = ntohl(*(__be32 *)nla_data(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; } |