Commit 13eae15a authored by Pablo Neira Ayuso's avatar Pablo Neira Ayuso Committed by David S. Miller

[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: default avatarPablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 17008064
...@@ -129,6 +129,10 @@ enum ip_conntrack_events ...@@ -129,6 +129,10 @@ enum ip_conntrack_events
/* Mark is set */ /* Mark is set */
IPCT_MARK_BIT = 12, IPCT_MARK_BIT = 12,
IPCT_MARK = (1 << IPCT_MARK_BIT), IPCT_MARK = (1 << IPCT_MARK_BIT),
/* NAT sequence adjustment */
IPCT_NATSEQADJ_BIT = 13,
IPCT_NATSEQADJ = (1 << IPCT_NATSEQADJ_BIT),
}; };
enum ip_conntrack_expect_events { enum ip_conntrack_expect_events {
......
...@@ -37,6 +37,8 @@ enum ctattr_type { ...@@ -37,6 +37,8 @@ enum ctattr_type {
CTA_ID, CTA_ID,
CTA_NAT_DST, CTA_NAT_DST,
CTA_TUPLE_MASTER, CTA_TUPLE_MASTER,
CTA_NAT_SEQ_ADJ_ORIG,
CTA_NAT_SEQ_ADJ_REPLY,
__CTA_MAX __CTA_MAX
}; };
#define CTA_MAX (__CTA_MAX - 1) #define CTA_MAX (__CTA_MAX - 1)
...@@ -119,6 +121,14 @@ enum ctattr_protonat { ...@@ -119,6 +121,14 @@ enum ctattr_protonat {
}; };
#define CTA_PROTONAT_MAX (__CTA_PROTONAT_MAX - 1) #define CTA_PROTONAT_MAX (__CTA_PROTONAT_MAX - 1)
enum ctattr_natseq {
CTA_NAT_SEQ_CORRECTION_POS,
CTA_NAT_SEQ_OFFSET_BEFORE,
CTA_NAT_SEQ_OFFSET_AFTER,
__CTA_NAT_SEQ_MAX
};
#define CTA_NAT_SEQ_MAX (__CTA_NAT_SEQ_MAX - 1)
enum ctattr_expect { enum ctattr_expect {
CTA_EXPECT_UNSPEC, CTA_EXPECT_UNSPEC,
CTA_EXPECT_MASTER, CTA_EXPECT_MASTER,
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv4.h>
#include <net/netfilter/nf_conntrack.h> #include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_helper.h> #include <net/netfilter/nf_conntrack_helper.h>
#include <net/netfilter/nf_conntrack_ecache.h>
#include <net/netfilter/nf_conntrack_expect.h> #include <net/netfilter/nf_conntrack_expect.h>
#include <net/netfilter/nf_nat.h> #include <net/netfilter/nf_nat.h>
#include <net/netfilter/nf_nat_protocol.h> #include <net/netfilter/nf_nat_protocol.h>
...@@ -191,6 +192,8 @@ nf_nat_mangle_tcp_packet(struct sk_buff *skb, ...@@ -191,6 +192,8 @@ nf_nat_mangle_tcp_packet(struct sk_buff *skb,
/* Tell TCP window tracking about seq change */ /* Tell TCP window tracking about seq change */
nf_conntrack_tcp_update(skb, ip_hdrlen(skb), nf_conntrack_tcp_update(skb, ip_hdrlen(skb),
ct, CTINFO2DIR(ctinfo)); ct, CTINFO2DIR(ctinfo));
nf_conntrack_event_cache(IPCT_NATSEQADJ, skb);
} }
return 1; return 1;
} }
......
...@@ -254,6 +254,55 @@ nla_put_failure: ...@@ -254,6 +254,55 @@ nla_put_failure:
#define ctnetlink_dump_mark(a, b) (0) #define ctnetlink_dump_mark(a, b) (0)
#endif #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 static inline int
ctnetlink_dump_id(struct sk_buff *skb, const struct nf_conn *ct) 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, ...@@ -321,7 +370,8 @@ ctnetlink_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
ctnetlink_dump_helpinfo(skb, ct) < 0 || ctnetlink_dump_helpinfo(skb, ct) < 0 ||
ctnetlink_dump_mark(skb, ct) < 0 || ctnetlink_dump_mark(skb, ct) < 0 ||
ctnetlink_dump_id(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; goto nla_put_failure;
nlh->nlmsg_len = skb_tail_pointer(skb) - b; nlh->nlmsg_len = skb_tail_pointer(skb) - b;
...@@ -424,6 +474,10 @@ static int ctnetlink_conntrack_event(struct notifier_block *this, ...@@ -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_ORIGINAL) < 0 ||
ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0)) ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0))
goto nla_put_failure; 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; nlh->nlmsg_len = skb->tail - b;
...@@ -935,6 +989,66 @@ ctnetlink_change_protoinfo(struct nf_conn *ct, struct nlattr *cda[]) ...@@ -935,6 +989,66 @@ ctnetlink_change_protoinfo(struct nf_conn *ct, struct nlattr *cda[])
return err; 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 static int
ctnetlink_change_conntrack(struct nf_conn *ct, struct nlattr *cda[]) ctnetlink_change_conntrack(struct nf_conn *ct, struct nlattr *cda[])
{ {
...@@ -969,6 +1083,14 @@ 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])); ct->mark = ntohl(*(__be32 *)nla_data(cda[CTA_MARK]));
#endif #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; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment