Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

[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
Loading
Loading
Loading
Loading
+4 −0
Original line number Original line Diff line number Diff line
@@ -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 {
+10 −0
Original line number Original line Diff line number Diff line
@@ -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 {
};
};
#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,
+3 −0
Original line number Original line Diff line number Diff line
@@ -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,
		/* 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;
}
}
+123 −1
Original line number Original line Diff line number Diff line
@@ -254,6 +254,55 @@ ctnetlink_dump_mark(struct sk_buff *skb, const struct nf_conn *ct)
#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,
	    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,
		    (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[])
	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[])
		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;
}
}