Commit 41d73ec0 authored by Patrick McHardy's avatar Patrick McHardy Committed by Pablo Neira Ayuso

netfilter: nf_conntrack: make sequence number adjustments usuable without NAT

Split out sequence number adjustments from NAT and move them to the conntrack
core to make them usable for SYN proxying. The sequence number adjustment
information is moved to a seperate extend. The extend is added to new
conntracks when a NAT mapping is set up for a connection using a helper.

As a side effect, this saves 24 bytes per connection with NAT in the common
case that a connection does not have a helper assigned.
Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
Tested-by: default avatarMartin Topholm <mph@one.com>
Signed-off-by: default avatarJesper Dangaard Brouer <brouer@redhat.com>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 706f5151
...@@ -319,6 +319,7 @@ extern void nf_ct_attach(struct sk_buff *, const struct sk_buff *); ...@@ -319,6 +319,7 @@ extern void nf_ct_attach(struct sk_buff *, const struct sk_buff *);
extern void (*nf_ct_destroy)(struct nf_conntrack *) __rcu; extern void (*nf_ct_destroy)(struct nf_conntrack *) __rcu;
struct nf_conn; struct nf_conn;
enum ip_conntrack_info;
struct nlattr; struct nlattr;
struct nfq_ct_hook { struct nfq_ct_hook {
...@@ -327,14 +328,10 @@ struct nfq_ct_hook { ...@@ -327,14 +328,10 @@ struct nfq_ct_hook {
int (*parse)(const struct nlattr *attr, struct nf_conn *ct); int (*parse)(const struct nlattr *attr, struct nf_conn *ct);
int (*attach_expect)(const struct nlattr *attr, struct nf_conn *ct, int (*attach_expect)(const struct nlattr *attr, struct nf_conn *ct,
u32 portid, u32 report); u32 portid, u32 report);
};
extern struct nfq_ct_hook __rcu *nfq_ct_hook;
struct nfq_ct_nat_hook {
void (*seq_adjust)(struct sk_buff *skb, struct nf_conn *ct, void (*seq_adjust)(struct sk_buff *skb, struct nf_conn *ct,
u32 ctinfo, s32 off); enum ip_conntrack_info ctinfo, s32 off);
}; };
extern struct nfq_ct_nat_hook __rcu *nfq_ct_nat_hook; extern struct nfq_ct_hook __rcu *nfq_ct_hook;
#else #else
static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {} static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {}
#endif #endif
......
...@@ -9,6 +9,7 @@ enum nf_ct_ext_id { ...@@ -9,6 +9,7 @@ enum nf_ct_ext_id {
NF_CT_EXT_HELPER, NF_CT_EXT_HELPER,
#if defined(CONFIG_NF_NAT) || defined(CONFIG_NF_NAT_MODULE) #if defined(CONFIG_NF_NAT) || defined(CONFIG_NF_NAT_MODULE)
NF_CT_EXT_NAT, NF_CT_EXT_NAT,
NF_CT_EXT_SEQADJ,
#endif #endif
NF_CT_EXT_ACCT, NF_CT_EXT_ACCT,
#ifdef CONFIG_NF_CONNTRACK_EVENTS #ifdef CONFIG_NF_CONNTRACK_EVENTS
...@@ -31,6 +32,7 @@ enum nf_ct_ext_id { ...@@ -31,6 +32,7 @@ enum nf_ct_ext_id {
#define NF_CT_EXT_HELPER_TYPE struct nf_conn_help #define NF_CT_EXT_HELPER_TYPE struct nf_conn_help
#define NF_CT_EXT_NAT_TYPE struct nf_conn_nat #define NF_CT_EXT_NAT_TYPE struct nf_conn_nat
#define NF_CT_EXT_SEQADJ_TYPE struct nf_conn_seqadj
#define NF_CT_EXT_ACCT_TYPE struct nf_conn_counter #define NF_CT_EXT_ACCT_TYPE struct nf_conn_counter
#define NF_CT_EXT_ECACHE_TYPE struct nf_conntrack_ecache #define NF_CT_EXT_ECACHE_TYPE struct nf_conntrack_ecache
#define NF_CT_EXT_ZONE_TYPE struct nf_conntrack_zone #define NF_CT_EXT_ZONE_TYPE struct nf_conntrack_zone
......
#ifndef _NF_CONNTRACK_SEQADJ_H
#define _NF_CONNTRACK_SEQADJ_H
#include <net/netfilter/nf_conntrack_extend.h>
/**
* struct nf_ct_seqadj - sequence number adjustment information
*
* @correction_pos: position of the last TCP sequence number modification
* @offset_before: sequence number offset before last modification
* @offset_after: sequence number offset after last modification
*/
struct nf_ct_seqadj {
u32 correction_pos;
s32 offset_before;
s32 offset_after;
};
struct nf_conn_seqadj {
struct nf_ct_seqadj seq[IP_CT_DIR_MAX];
};
static inline struct nf_conn_seqadj *nfct_seqadj(const struct nf_conn *ct)
{
return nf_ct_ext_find(ct, NF_CT_EXT_SEQADJ);
}
static inline struct nf_conn_seqadj *nfct_seqadj_ext_add(struct nf_conn *ct)
{
return nf_ct_ext_add(ct, NF_CT_EXT_SEQADJ, GFP_ATOMIC);
}
extern int nf_ct_seqadj_set(struct nf_conn *ct, enum ip_conntrack_info ctinfo,
__be32 seq, s32 off);
extern void nf_ct_tcp_seqadj_set(struct sk_buff *skb,
struct nf_conn *ct,
enum ip_conntrack_info ctinfo,
s32 off);
extern int nf_ct_seq_adjust(struct sk_buff *skb,
struct nf_conn *ct, enum ip_conntrack_info ctinfo,
unsigned int protoff);
extern s32 nf_ct_seq_offset(const struct nf_conn *ct, enum ip_conntrack_dir,
u32 seq);
extern int nf_conntrack_seqadj_init(void);
extern void nf_conntrack_seqadj_fini(void);
#endif /* _NF_CONNTRACK_SEQADJ_H */
...@@ -13,15 +13,6 @@ enum nf_nat_manip_type { ...@@ -13,15 +13,6 @@ enum nf_nat_manip_type {
#define HOOK2MANIP(hooknum) ((hooknum) != NF_INET_POST_ROUTING && \ #define HOOK2MANIP(hooknum) ((hooknum) != NF_INET_POST_ROUTING && \
(hooknum) != NF_INET_LOCAL_IN) (hooknum) != NF_INET_LOCAL_IN)
/* NAT sequence number modifications */
struct nf_nat_seq {
/* position of the last TCP sequence number modification (if any) */
u_int32_t correction_pos;
/* sequence number offset before and after last modification */
int32_t offset_before, offset_after;
};
#include <linux/list.h> #include <linux/list.h>
#include <linux/netfilter/nf_conntrack_pptp.h> #include <linux/netfilter/nf_conntrack_pptp.h>
#include <net/netfilter/nf_conntrack_extend.h> #include <net/netfilter/nf_conntrack_extend.h>
...@@ -39,7 +30,6 @@ struct nf_conn; ...@@ -39,7 +30,6 @@ struct nf_conn;
/* The structure embedded in the conntrack structure. */ /* The structure embedded in the conntrack structure. */
struct nf_conn_nat { struct nf_conn_nat {
struct hlist_node bysource; struct hlist_node bysource;
struct nf_nat_seq seq[IP_CT_DIR_MAX];
struct nf_conn *ct; struct nf_conn *ct;
union nf_conntrack_nat_help help; union nf_conntrack_nat_help help;
#if defined(CONFIG_IP_NF_TARGET_MASQUERADE) || \ #if defined(CONFIG_IP_NF_TARGET_MASQUERADE) || \
......
...@@ -39,28 +39,9 @@ extern int nf_nat_mangle_udp_packet(struct sk_buff *skb, ...@@ -39,28 +39,9 @@ extern int nf_nat_mangle_udp_packet(struct sk_buff *skb,
const char *rep_buffer, const char *rep_buffer,
unsigned int rep_len); unsigned int rep_len);
extern void nf_nat_set_seq_adjust(struct nf_conn *ct,
enum ip_conntrack_info ctinfo,
__be32 seq, s32 off);
extern int nf_nat_seq_adjust(struct sk_buff *skb,
struct nf_conn *ct,
enum ip_conntrack_info ctinfo,
unsigned int protoff);
extern int (*nf_nat_seq_adjust_hook)(struct sk_buff *skb,
struct nf_conn *ct,
enum ip_conntrack_info ctinfo,
unsigned int protoff);
/* Setup NAT on this expected conntrack so it follows master, but goes /* Setup NAT on this expected conntrack so it follows master, but goes
* to port ct->master->saved_proto. */ * to port ct->master->saved_proto. */
extern void nf_nat_follow_master(struct nf_conn *ct, extern void nf_nat_follow_master(struct nf_conn *ct,
struct nf_conntrack_expect *this); struct nf_conntrack_expect *this);
extern s32 nf_nat_get_offset(const struct nf_conn *ct,
enum ip_conntrack_dir dir,
u32 seq);
extern void nf_nat_tcp_seq_adjust(struct sk_buff *skb, struct nf_conn *ct,
u32 dir, s32 off);
#endif #endif
...@@ -99,7 +99,8 @@ enum ip_conntrack_events { ...@@ -99,7 +99,8 @@ enum ip_conntrack_events {
IPCT_PROTOINFO, /* protocol information has changed */ IPCT_PROTOINFO, /* protocol information has changed */
IPCT_HELPER, /* new helper has been set */ IPCT_HELPER, /* new helper has been set */
IPCT_MARK, /* new mark has been set */ IPCT_MARK, /* new mark has been set */
IPCT_NATSEQADJ, /* NAT is doing sequence adjustment */ IPCT_SEQADJ, /* sequence adjustment has changed */
IPCT_NATSEQADJ = IPCT_SEQADJ,
IPCT_SECMARK, /* new security mark has been set */ IPCT_SECMARK, /* new security mark has been set */
IPCT_LABEL, /* new connlabel has been set */ IPCT_LABEL, /* new connlabel has been set */
}; };
......
...@@ -42,8 +42,10 @@ enum ctattr_type { ...@@ -42,8 +42,10 @@ 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_SEQ_ADJ_ORIG,
CTA_NAT_SEQ_ADJ_REPLY, CTA_NAT_SEQ_ADJ_ORIG = CTA_SEQ_ADJ_ORIG,
CTA_SEQ_ADJ_REPLY,
CTA_NAT_SEQ_ADJ_REPLY = CTA_SEQ_ADJ_REPLY,
CTA_SECMARK, /* obsolete */ CTA_SECMARK, /* obsolete */
CTA_ZONE, CTA_ZONE,
CTA_SECCTX, CTA_SECCTX,
...@@ -165,6 +167,15 @@ enum ctattr_protonat { ...@@ -165,6 +167,15 @@ enum ctattr_protonat {
}; };
#define CTA_PROTONAT_MAX (__CTA_PROTONAT_MAX - 1) #define CTA_PROTONAT_MAX (__CTA_PROTONAT_MAX - 1)
enum ctattr_seqadj {
CTA_SEQADJ_UNSPEC,
CTA_SEQADJ_CORRECTION_POS,
CTA_SEQADJ_OFFSET_BEFORE,
CTA_SEQADJ_OFFSET_AFTER,
__CTA_SEQADJ_MAX
};
#define CTA_SEQADJ_MAX (__CTA_SEQADJ_MAX - 1)
enum ctattr_natseq { enum ctattr_natseq {
CTA_NAT_SEQ_UNSPEC, CTA_NAT_SEQ_UNSPEC,
CTA_NAT_SEQ_CORRECTION_POS, CTA_NAT_SEQ_CORRECTION_POS,
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <net/netfilter/nf_conntrack_l3proto.h> #include <net/netfilter/nf_conntrack_l3proto.h>
#include <net/netfilter/nf_conntrack_zones.h> #include <net/netfilter/nf_conntrack_zones.h>
#include <net/netfilter/nf_conntrack_core.h> #include <net/netfilter/nf_conntrack_core.h>
#include <net/netfilter/nf_conntrack_seqadj.h>
#include <net/netfilter/ipv4/nf_conntrack_ipv4.h> #include <net/netfilter/ipv4/nf_conntrack_ipv4.h>
#include <net/netfilter/nf_nat_helper.h> #include <net/netfilter/nf_nat_helper.h>
#include <net/netfilter/ipv4/nf_defrag_ipv4.h> #include <net/netfilter/ipv4/nf_defrag_ipv4.h>
...@@ -136,11 +137,7 @@ static unsigned int ipv4_confirm(unsigned int hooknum, ...@@ -136,11 +137,7 @@ static unsigned int ipv4_confirm(unsigned int hooknum,
/* adjust seqs for loopback traffic only in outgoing direction */ /* adjust seqs for loopback traffic only in outgoing direction */
if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) && if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) &&
!nf_is_loopback_packet(skb)) { !nf_is_loopback_packet(skb)) {
typeof(nf_nat_seq_adjust_hook) seq_adjust; if (!nf_ct_seq_adjust(skb, ct, ctinfo, ip_hdrlen(skb))) {
seq_adjust = rcu_dereference(nf_nat_seq_adjust_hook);
if (!seq_adjust ||
!seq_adjust(skb, ct, ctinfo, ip_hdrlen(skb))) {
NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop); NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop);
return NF_DROP; return NF_DROP;
} }
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <net/netfilter/nf_conntrack_l3proto.h> #include <net/netfilter/nf_conntrack_l3proto.h>
#include <net/netfilter/nf_conntrack_core.h> #include <net/netfilter/nf_conntrack_core.h>
#include <net/netfilter/nf_conntrack_zones.h> #include <net/netfilter/nf_conntrack_zones.h>
#include <net/netfilter/nf_conntrack_seqadj.h>
#include <net/netfilter/ipv6/nf_conntrack_ipv6.h> #include <net/netfilter/ipv6/nf_conntrack_ipv6.h>
#include <net/netfilter/nf_nat_helper.h> #include <net/netfilter/nf_nat_helper.h>
#include <net/netfilter/ipv6/nf_defrag_ipv6.h> #include <net/netfilter/ipv6/nf_defrag_ipv6.h>
...@@ -158,11 +159,7 @@ static unsigned int ipv6_confirm(unsigned int hooknum, ...@@ -158,11 +159,7 @@ static unsigned int ipv6_confirm(unsigned int hooknum,
/* adjust seqs for loopback traffic only in outgoing direction */ /* adjust seqs for loopback traffic only in outgoing direction */
if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) && if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) &&
!nf_is_loopback_packet(skb)) { !nf_is_loopback_packet(skb)) {
typeof(nf_nat_seq_adjust_hook) seq_adjust; if (!nf_ct_seq_adjust(skb, ct, ctinfo, protoff)) {
seq_adjust = rcu_dereference(nf_nat_seq_adjust_hook);
if (!seq_adjust ||
!seq_adjust(skb, ct, ctinfo, protoff)) {
NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop); NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop);
return NF_DROP; return NF_DROP;
} }
......
netfilter-objs := core.o nf_log.o nf_queue.o nf_sockopt.o netfilter-objs := core.o nf_log.o nf_queue.o nf_sockopt.o
nf_conntrack-y := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_expect.o nf_conntrack_helper.o nf_conntrack_proto.o nf_conntrack_l3proto_generic.o nf_conntrack_proto_generic.o nf_conntrack_proto_tcp.o nf_conntrack_proto_udp.o nf_conntrack_extend.o nf_conntrack_acct.o nf_conntrack-y := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_expect.o nf_conntrack_helper.o nf_conntrack_proto.o nf_conntrack_l3proto_generic.o nf_conntrack_proto_generic.o nf_conntrack_proto_tcp.o nf_conntrack_proto_udp.o nf_conntrack_extend.o nf_conntrack_acct.o nf_conntrack_seqadj.o
nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMEOUT) += nf_conntrack_timeout.o nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMEOUT) += nf_conntrack_timeout.o
nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMESTAMP) += nf_conntrack_timestamp.o nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMESTAMP) += nf_conntrack_timestamp.o
nf_conntrack-$(CONFIG_NF_CONNTRACK_EVENTS) += nf_conntrack_ecache.o nf_conntrack-$(CONFIG_NF_CONNTRACK_EVENTS) += nf_conntrack_ecache.o
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#include <net/netfilter/nf_conntrack_l4proto.h> #include <net/netfilter/nf_conntrack_l4proto.h>
#include <net/netfilter/nf_conntrack_expect.h> #include <net/netfilter/nf_conntrack_expect.h>
#include <net/netfilter/nf_conntrack_helper.h> #include <net/netfilter/nf_conntrack_helper.h>
#include <net/netfilter/nf_conntrack_seqadj.h>
#include <net/netfilter/nf_conntrack_core.h> #include <net/netfilter/nf_conntrack_core.h>
#include <net/netfilter/nf_conntrack_extend.h> #include <net/netfilter/nf_conntrack_extend.h>
#include <net/netfilter/nf_conntrack_acct.h> #include <net/netfilter/nf_conntrack_acct.h>
...@@ -1326,6 +1327,7 @@ void nf_conntrack_cleanup_end(void) ...@@ -1326,6 +1327,7 @@ void nf_conntrack_cleanup_end(void)
nf_ct_extend_unregister(&nf_ct_zone_extend); nf_ct_extend_unregister(&nf_ct_zone_extend);
#endif #endif
nf_conntrack_proto_fini(); nf_conntrack_proto_fini();
nf_conntrack_seqadj_fini();
nf_conntrack_labels_fini(); nf_conntrack_labels_fini();
nf_conntrack_helper_fini(); nf_conntrack_helper_fini();
nf_conntrack_timeout_fini(); nf_conntrack_timeout_fini();
...@@ -1531,6 +1533,10 @@ int nf_conntrack_init_start(void) ...@@ -1531,6 +1533,10 @@ int nf_conntrack_init_start(void)
if (ret < 0) if (ret < 0)
goto err_labels; goto err_labels;
ret = nf_conntrack_seqadj_init();
if (ret < 0)
goto err_seqadj;
#ifdef CONFIG_NF_CONNTRACK_ZONES #ifdef CONFIG_NF_CONNTRACK_ZONES
ret = nf_ct_extend_register(&nf_ct_zone_extend); ret = nf_ct_extend_register(&nf_ct_zone_extend);
if (ret < 0) if (ret < 0)
...@@ -1555,6 +1561,8 @@ int nf_conntrack_init_start(void) ...@@ -1555,6 +1561,8 @@ int nf_conntrack_init_start(void)
nf_ct_extend_unregister(&nf_ct_zone_extend); nf_ct_extend_unregister(&nf_ct_zone_extend);
err_extend: err_extend:
#endif #endif
nf_conntrack_seqadj_fini();
err_seqadj:
nf_conntrack_labels_fini(); nf_conntrack_labels_fini();
err_labels: err_labels:
nf_conntrack_helper_fini(); nf_conntrack_helper_fini();
...@@ -1577,9 +1585,6 @@ void nf_conntrack_init_end(void) ...@@ -1577,9 +1585,6 @@ void nf_conntrack_init_end(void)
/* For use by REJECT target */ /* For use by REJECT target */
RCU_INIT_POINTER(ip_ct_attach, nf_conntrack_attach); RCU_INIT_POINTER(ip_ct_attach, nf_conntrack_attach);
RCU_INIT_POINTER(nf_ct_destroy, destroy_conntrack); RCU_INIT_POINTER(nf_ct_destroy, destroy_conntrack);
/* Howto get NAT offsets */
RCU_INIT_POINTER(nf_ct_nat_offset, NULL);
} }
/* /*
...@@ -1666,8 +1671,3 @@ int nf_conntrack_init_net(struct net *net) ...@@ -1666,8 +1671,3 @@ int nf_conntrack_init_net(struct net *net)
err_stat: err_stat:
return ret; return ret;
} }
s32 (*nf_ct_nat_offset)(const struct nf_conn *ct,
enum ip_conntrack_dir dir,
u32 seq);
EXPORT_SYMBOL_GPL(nf_ct_nat_offset);
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include <net/netfilter/nf_conntrack_core.h> #include <net/netfilter/nf_conntrack_core.h>
#include <net/netfilter/nf_conntrack_expect.h> #include <net/netfilter/nf_conntrack_expect.h>
#include <net/netfilter/nf_conntrack_helper.h> #include <net/netfilter/nf_conntrack_helper.h>
#include <net/netfilter/nf_conntrack_seqadj.h>
#include <net/netfilter/nf_conntrack_l3proto.h> #include <net/netfilter/nf_conntrack_l3proto.h>
#include <net/netfilter/nf_conntrack_l4proto.h> #include <net/netfilter/nf_conntrack_l4proto.h>
#include <net/netfilter/nf_conntrack_tuple.h> #include <net/netfilter/nf_conntrack_tuple.h>
...@@ -381,9 +382,8 @@ ctnetlink_dump_master(struct sk_buff *skb, const struct nf_conn *ct) ...@@ -381,9 +382,8 @@ ctnetlink_dump_master(struct sk_buff *skb, const struct nf_conn *ct)
return -1; return -1;
} }
#ifdef CONFIG_NF_NAT_NEEDED
static int static int
dump_nat_seq_adj(struct sk_buff *skb, const struct nf_nat_seq *natseq, int type) dump_ct_seq_adj(struct sk_buff *skb, const struct nf_ct_seqadj *seq, int type)
{ {
struct nlattr *nest_parms; struct nlattr *nest_parms;
...@@ -391,12 +391,12 @@ dump_nat_seq_adj(struct sk_buff *skb, const struct nf_nat_seq *natseq, int type) ...@@ -391,12 +391,12 @@ dump_nat_seq_adj(struct sk_buff *skb, const struct nf_nat_seq *natseq, int type)
if (!nest_parms) if (!nest_parms)
goto nla_put_failure; goto nla_put_failure;
if (nla_put_be32(skb, CTA_NAT_SEQ_CORRECTION_POS, if (nla_put_be32(skb, CTA_SEQADJ_CORRECTION_POS,
htonl(natseq->correction_pos)) || htonl(seq->correction_pos)) ||
nla_put_be32(skb, CTA_NAT_SEQ_OFFSET_BEFORE, nla_put_be32(skb, CTA_SEQADJ_OFFSET_BEFORE,
htonl(natseq->offset_before)) || htonl(seq->offset_before)) ||
nla_put_be32(skb, CTA_NAT_SEQ_OFFSET_AFTER, nla_put_be32(skb, CTA_SEQADJ_OFFSET_AFTER,
htonl(natseq->offset_after))) htonl(seq->offset_after)))
goto nla_put_failure; goto nla_put_failure;
nla_nest_end(skb, nest_parms); nla_nest_end(skb, nest_parms);
...@@ -408,27 +408,24 @@ dump_nat_seq_adj(struct sk_buff *skb, const struct nf_nat_seq *natseq, int type) ...@@ -408,27 +408,24 @@ dump_nat_seq_adj(struct sk_buff *skb, const struct nf_nat_seq *natseq, int type)
} }
static inline int static inline int
ctnetlink_dump_nat_seq_adj(struct sk_buff *skb, const struct nf_conn *ct) ctnetlink_dump_ct_seq_adj(struct sk_buff *skb, const struct nf_conn *ct)
{ {
struct nf_nat_seq *natseq; struct nf_conn_seqadj *seqadj = nfct_seqadj(ct);
struct nf_conn_nat *nat = nfct_nat(ct); struct nf_ct_seqadj *seq;
if (!(ct->status & IPS_SEQ_ADJUST) || !nat) if (!(ct->status & IPS_SEQ_ADJUST) || !seqadj)
return 0; return 0;
natseq = &nat->seq[IP_CT_DIR_ORIGINAL]; seq = &seqadj->seq[IP_CT_DIR_ORIGINAL];
if (dump_nat_seq_adj(skb, natseq, CTA_NAT_SEQ_ADJ_ORIG) == -1) if (dump_ct_seq_adj(skb, seq, CTA_SEQ_ADJ_ORIG) == -1)
return -1; return -1;
natseq = &nat->seq[IP_CT_DIR_REPLY]; seq = &seqadj->seq[IP_CT_DIR_REPLY];
if (dump_nat_seq_adj(skb, natseq, CTA_NAT_SEQ_ADJ_REPLY) == -1) if (dump_ct_seq_adj(skb, seq, CTA_SEQ_ADJ_REPLY) == -1)
return -1; return -1;
return 0; 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)
...@@ -502,7 +499,7 @@ ctnetlink_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, ...@@ -502,7 +499,7 @@ ctnetlink_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type,
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_master(skb, ct) < 0 || ctnetlink_dump_master(skb, ct) < 0 ||
ctnetlink_dump_nat_seq_adj(skb, ct) < 0) ctnetlink_dump_ct_seq_adj(skb, ct) < 0)
goto nla_put_failure; goto nla_put_failure;
nlmsg_end(skb, nlh); nlmsg_end(skb, nlh);
...@@ -707,8 +704,8 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item) ...@@ -707,8 +704,8 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item)
ctnetlink_dump_master(skb, ct) < 0) ctnetlink_dump_master(skb, ct) < 0)
goto nla_put_failure; goto nla_put_failure;
if (events & (1 << IPCT_NATSEQADJ) && if (events & (1 << IPCT_SEQADJ) &&
ctnetlink_dump_nat_seq_adj(skb, ct) < 0) ctnetlink_dump_ct_seq_adj(skb, ct) < 0)
goto nla_put_failure; goto nla_put_failure;
} }
...@@ -1439,66 +1436,65 @@ ctnetlink_change_protoinfo(struct nf_conn *ct, const struct nlattr * const cda[] ...@@ -1439,66 +1436,65 @@ ctnetlink_change_protoinfo(struct nf_conn *ct, const struct nlattr * const cda[]
return err; return err;
} }
#ifdef CONFIG_NF_NAT_NEEDED static const struct nla_policy seqadj_policy[CTA_SEQADJ_MAX+1] = {
static const struct nla_policy nat_seq_policy[CTA_NAT_SEQ_MAX+1] = { [CTA_SEQADJ_CORRECTION_POS] = { .type = NLA_U32 },
[CTA_NAT_SEQ_CORRECTION_POS] = { .type = NLA_U32 }, [CTA_SEQADJ_OFFSET_BEFORE] = { .type = NLA_U32 },
[CTA_NAT_SEQ_OFFSET_BEFORE] = { .type = NLA_U32 }, [CTA_SEQADJ_OFFSET_AFTER] = { .type = NLA_U32 },
[CTA_NAT_SEQ_OFFSET_AFTER] = { .type = NLA_U32 },
}; };
static inline int static inline int
change_nat_seq_adj(struct nf_nat_seq *natseq, const struct nlattr * const attr) change_seq_adj(struct nf_ct_seqadj *seq, const struct nlattr * const attr)
{ {
int err; int err;
struct nlattr *cda[CTA_NAT_SEQ_MAX+1]; struct nlattr *cda[CTA_SEQADJ_MAX+1];
err = nla_parse_nested(cda, CTA_NAT_SEQ_MAX, attr, nat_seq_policy); err = nla_parse_nested(cda, CTA_SEQADJ_MAX, attr, seqadj_policy);
if (err < 0) if (err < 0)
return err; return err;
if (!cda[CTA_NAT_SEQ_CORRECTION_POS]) if (!cda[CTA_SEQADJ_CORRECTION_POS])
return -EINVAL; return -EINVAL;
natseq->correction_pos = seq->correction_pos =
ntohl(nla_get_be32(cda[CTA_NAT_SEQ_CORRECTION_POS])); ntohl(nla_get_be32(cda[CTA_SEQADJ_CORRECTION_POS]));
if (!cda[CTA_NAT_SEQ_OFFSET_BEFORE]) if (!cda[CTA_SEQADJ_OFFSET_BEFORE])
return -EINVAL; return -EINVAL;
natseq->offset_before = seq->offset_before =
ntohl(nla_get_be32(cda[CTA_NAT_SEQ_OFFSET_BEFORE])); ntohl(nla_get_be32(cda[CTA_SEQADJ_OFFSET_BEFORE]));
if (!cda[CTA_NAT_SEQ_OFFSET_AFTER]) if (!cda[CTA_SEQADJ_OFFSET_AFTER])
return -EINVAL; return -EINVAL;
natseq->offset_after = seq->offset_after =
ntohl(nla_get_be32(cda[CTA_NAT_SEQ_OFFSET_AFTER])); ntohl(nla_get_be32(cda[CTA_SEQADJ_OFFSET_AFTER]));
return 0; return 0;
} }
static int static int
ctnetlink_change_nat_seq_adj(struct nf_conn *ct, ctnetlink_change_seq_adj(struct nf_conn *ct,
const struct nlattr * const cda[]) const struct nlattr * const cda[])
{ {
struct nf_conn_seqadj *seqadj = nfct_seqadj(ct);
int ret = 0; int ret = 0;
struct nf_conn_nat *nat = nfct_nat(ct);
if (!nat) if (!seqadj)
return 0; return 0;
if (cda[CTA_NAT_SEQ_ADJ_ORIG]) { if (cda[CTA_SEQ_ADJ_ORIG]) {
ret = change_nat_seq_adj(&nat->seq[IP_CT_DIR_ORIGINAL], ret = change_seq_adj(&seqadj->seq[IP_CT_DIR_ORIGINAL],
cda[CTA_NAT_SEQ_ADJ_ORIG]); cda[CTA_SEQ_ADJ_ORIG]);
if (ret < 0) if (ret < 0)
return ret; return ret;
ct->status |= IPS_SEQ_ADJUST; ct->status |= IPS_SEQ_ADJUST;
} }
if (cda[CTA_NAT_SEQ_ADJ_REPLY]) { if (cda[CTA_SEQ_ADJ_REPLY]) {
ret = change_nat_seq_adj(&nat->seq[IP_CT_DIR_REPLY], ret = change_seq_adj(&seqadj->seq[IP_CT_DIR_REPLY],
cda[CTA_NAT_SEQ_ADJ_REPLY]); cda[CTA_SEQ_ADJ_REPLY]);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -1507,7 +1503,6 @@ ctnetlink_change_nat_seq_adj(struct nf_conn *ct, ...@@ -1507,7 +1503,6 @@ ctnetlink_change_nat_seq_adj(struct nf_conn *ct,
return 0; return 0;
} }
#endif
static int static int
ctnetlink_attach_labels(struct nf_conn *ct, const struct nlattr * const cda[]) ctnetlink_attach_labels(struct nf_conn *ct, const struct nlattr * const cda[])
...@@ -1573,13 +1568,12 @@ ctnetlink_change_conntrack(struct nf_conn *ct, ...@@ -1573,13 +1568,12 @@ ctnetlink_change_conntrack(struct nf_conn *ct,
ct->mark = ntohl(nla_get_be32(cda[CTA_MARK])); ct->mark = ntohl(nla_get_be32(cda[CTA_MARK]));
#endif #endif
#ifdef CONFIG_NF_NAT_NEEDED if (cda[CTA_SEQ_ADJ_ORIG] || cda[CTA_SEQ_ADJ_REPLY]) {
if (cda[CTA_NAT_SEQ_ADJ_ORIG] || cda[CTA_NAT_SEQ_ADJ_REPLY]) { err = ctnetlink_change_seq_adj(ct, cda);
err = ctnetlink_change_nat_seq_adj(ct, cda);
if (err < 0) if (err < 0)
return err; return err;
} }
#endif
if (cda[CTA_LABELS]) { if (cda[CTA_LABELS]) {
err = ctnetlink_attach_labels(ct, cda); err = ctnetlink_attach_labels(ct, cda);
if (err < 0) if (err < 0)
...@@ -1684,13 +1678,11 @@ ctnetlink_create_conntrack(struct net *net, u16 zone, ...@@ -1684,13 +1678,11 @@ ctnetlink_create_conntrack(struct net *net, u16 zone,
goto err2; goto err2;
} }
#ifdef CONFIG_NF_NAT_NEEDED if (cda[CTA_SEQ_ADJ_ORIG] || cda[CTA_SEQ_ADJ_REPLY]) {
if (cda[CTA_NAT_SEQ_ADJ_ORIG] || cda[CTA_NAT_SEQ_ADJ_REPLY]) { err = ctnetlink_change_seq_adj(ct, cda);
err = ctnetlink_change_nat_seq_adj(ct, cda);
if (err < 0) if (err < 0)
goto err2; goto err2;
} }
#endif
memset(&ct->proto, 0, sizeof(ct->proto)); memset(&ct->proto, 0, sizeof(ct->proto));
if (cda[CTA_PROTOINFO]) { if (cda[CTA_PROTOINFO]) {
...@@ -1804,7 +1796,7 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, ...@@ -1804,7 +1796,7 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
(1 << IPCT_ASSURED) | (1 << IPCT_ASSURED) |
(1 << IPCT_HELPER) | (1 << IPCT_HELPER) |
(1 << IPCT_PROTOINFO) | (1 << IPCT_PROTOINFO) |
(1 << IPCT_NATSEQADJ) | (1 << IPCT_SEQADJ) |
(1 << IPCT_MARK) | events, (1 << IPCT_MARK) | events,
ct, NETLINK_CB(skb).portid, ct, NETLINK_CB(skb).portid,
nlmsg_report(nlh)); nlmsg_report(nlh));
...@@ -1827,7 +1819,7 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, ...@@ -1827,7 +1819,7 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
(1 << IPCT_HELPER) | (1 << IPCT_HELPER) |
(1 << IPCT_LABEL) | (1 << IPCT_LABEL) |
(1 << IPCT_PROTOINFO) | (1 << IPCT_PROTOINFO) |
(1 << IPCT_NATSEQADJ) | (1 << IPCT_SEQADJ) |
(1 << IPCT_MARK), (1 << IPCT_MARK),
ct, NETLINK_CB(skb).portid, ct, NETLINK_CB(skb).portid,
nlmsg_report(nlh)); nlmsg_report(nlh));
...@@ -2082,7 +2074,7 @@ ctnetlink_nfqueue_build(struct sk_buff *skb, struct nf_conn *ct) ...@@ -2082,7 +2074,7 @@ ctnetlink_nfqueue_build(struct sk_buff *skb, struct nf_conn *ct)
goto nla_put_failure; goto nla_put_failure;
if ((ct->status & IPS_SEQ_ADJUST) && if ((ct->status & IPS_SEQ_ADJUST) &&
ctnetlink_dump_nat_seq_adj(skb, ct) < 0) ctnetlink_dump_ct_seq_adj(skb, ct) < 0)
goto nla_put_failure; goto nla_put_failure;
#ifdef CONFIG_NF_CONNTRACK_MARK #ifdef CONFIG_NF_CONNTRACK_MARK
...@@ -2211,6 +2203,7 @@ static struct nfq_ct_hook ctnetlink_nfqueue_hook = { ...@@ -2211,6 +2203,7 @@ static struct nfq_ct_hook ctnetlink_nfqueue_hook = {
.build = ctnetlink_nfqueue_build, .build = ctnetlink_nfqueue_build,
.parse = ctnetlink_nfqueue_parse, .parse = ctnetlink_nfqueue_parse,
.attach_expect = ctnetlink_nfqueue_attach_expect, .attach_expect = ctnetlink_nfqueue_attach_expect,
.seq_adjust = nf_ct_tcp_seqadj_set,
}; };
#endif /* CONFIG_NETFILTER_NETLINK_QUEUE_CT */ #endif /* CONFIG_NETFILTER_NETLINK_QUEUE_CT */
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <net/netfilter/nf_conntrack.h> #include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_l4proto.h> #include <net/netfilter/nf_conntrack_l4proto.h>
#include <net/netfilter/nf_conntrack_ecache.h> #include <net/netfilter/nf_conntrack_ecache.h>
#include <net/netfilter/nf_conntrack_seqadj.h>
#include <net/netfilter/nf_log.h> #include <net/netfilter/nf_log.h>
#include <net/netfilter/ipv4/nf_conntrack_ipv4.h> #include <net/netfilter/ipv4/nf_conntrack_ipv4.h>
#include <net/netfilter/ipv6/nf_conntrack_ipv6.h> #include <net/netfilter/ipv6/nf_conntrack_ipv6.h>
...@@ -495,21 +496,6 @@ static void tcp_sack(const struct sk_buff *skb, unsigned int dataoff, ...@@ -495,21 +496,6 @@ static void tcp_sack(const struct sk_buff *skb, unsigned int dataoff,
} }
} }
#ifdef CONFIG_NF_NAT_NEEDED
static inline s32 nat_offset(const struct nf_conn *ct,
enum ip_conntrack_dir dir,
u32 seq)
{
typeof(nf_ct_nat_offset) get_offset = rcu_dereference(nf_ct_nat_offset);
return get_offset != NULL ? get_offset(ct, dir, seq) : 0;
}
#define NAT_OFFSET(ct, dir, seq) \
(nat_offset(ct, dir, seq))
#else
#define NAT_OFFSET(ct, dir, seq) 0
#endif
static bool tcp_in_window(const struct nf_conn *ct, static bool tcp_in_window(const struct nf_conn *ct,
struct ip_ct_tcp *state, struct ip_ct_tcp *state,
enum ip_conntrack_dir dir, enum ip_conntrack_dir dir,
...@@ -540,7 +526,7 @@ static bool tcp_in_window(const struct nf_conn *ct, ...@@ -540,7 +526,7 @@ static bool tcp_in_window(const struct nf_conn *ct,
tcp_sack(skb, dataoff, tcph, &sack); tcp_sack(skb, dataoff, tcph, &sack);
/* Take into account NAT sequence number mangling */ /* Take into account NAT sequence number mangling */
receiver_offset = NAT_OFFSET(ct, !dir, ack - 1); receiver_offset = nf_ct_seq_offset(ct, !dir, ack - 1);
ack -= receiver_offset; ack -= receiver_offset;
sack -= receiver_offset; sack -= receiver_offset;
......
#include <linux/types.h>
#include <linux/netfilter.h>
#include <net/tcp.h>
#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_extend.h>
#include <net/netfilter/nf_conntrack_seqadj.h>
int nf_ct_seqadj_set(struct nf_conn *ct, enum ip_conntrack_info ctinfo,
__be32 seq, s32 off)
{
struct nf_conn_seqadj *seqadj = nfct_seqadj(ct);
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
struct nf_ct_seqadj *this_way;
if (off == 0)
return 0;
set_bit(IPS_SEQ_ADJUST_BIT, &ct->status);
spin_lock_bh(&ct->lock);
this_way = &seqadj->seq[dir];
if (this_way->offset_before == this_way->offset_after ||
before(this_way->correction_pos, seq)) {
this_way->correction_pos = seq;
this_way->offset_before = this_way->offset_after;
this_way->offset_after += off;
}
spin_unlock_bh(&ct->lock);
return 0;
}
EXPORT_SYMBOL_GPL(nf_ct_seqadj_set);
void nf_ct_tcp_seqadj_set(struct sk_buff *skb,
struct nf_conn *ct, enum ip_conntrack_info ctinfo,
s32 off)
{
const struct tcphdr *th;
if (nf_ct_protonum(ct) != IPPROTO_TCP)
return;
th = (struct tcphdr *)(skb_network_header(skb) + ip_hdrlen(skb));
nf_ct_seqadj_set(ct, ctinfo, th->seq, off);
}
EXPORT_SYMBOL_GPL(nf_ct_tcp_seqadj_set);
/* Adjust one found SACK option including checksum correction */
static void nf_ct_sack_block_adjust(struct sk_buff *skb,
struct tcphdr *tcph,
unsigned int sackoff,
unsigned int sackend,
struct nf_ct_seqadj *seq)
{
while (sackoff < sackend) {
struct tcp_sack_block_wire *sack;
__be32 new_start_seq, new_end_seq;
sack = (void *)skb->data + sackoff;
if (after(ntohl(sack->start_seq) - seq->offset_before,
seq->correction_pos))
new_start_seq = htonl(ntohl(sack->start_seq) -
seq->offset_after);
else
new_start_seq = htonl(ntohl(sack->start_seq) -
seq->offset_before);
if (after(ntohl(sack->end_seq) - seq->offset_before,
seq->correction_pos))
new_end_seq = htonl(ntohl(sack->end_seq) -
seq->offset_after);
else
new_end_seq = htonl(ntohl(sack->end_seq) -
seq->offset_before);
pr_debug("sack_adjust: start_seq: %d->%d, end_seq: %d->%d\n",
ntohl(sack->start_seq), new_start_seq,
ntohl(sack->end_seq), new_end_seq);
inet_proto_csum_replace4(&tcph->check, skb,
sack->start_seq, new_start_seq, 0);
inet_proto_csum_replace4(&tcph->check, skb,
sack->end_seq, new_end_seq, 0);
sack->start_seq = new_start_seq;
sack->end_seq = new_end_seq;
sackoff += sizeof(*sack);
}
}
/* TCP SACK sequence number adjustment */
static unsigned int nf_ct_sack_adjust(struct sk_buff *skb,
unsigned int protoff,
struct tcphdr *tcph,
struct nf_conn *ct,
enum ip_conntrack_info ctinfo)
{
unsigned int dir, optoff, optend;
struct nf_conn_seqadj *seqadj = nfct_seqadj(ct);
optoff = protoff + sizeof(struct tcphdr);
optend = protoff + tcph->doff * 4;
if (!skb_make_writable(skb, optend))
return 0;
dir = CTINFO2DIR(ctinfo);
while (optoff < optend) {
/* Usually: option, length. */
unsigned char *op = skb->data + optoff;
switch (op[0]) {
case TCPOPT_EOL:
return 1;
case TCPOPT_NOP:
optoff++;
continue;
default:
/* no partial options */
if (optoff + 1 == optend ||
optoff + op[1] > optend ||
op[1] < 2)
return 0;
if (op[0] == TCPOPT_SACK &&
op[1] >= 2+TCPOLEN_SACK_PERBLOCK &&
((op[1] - 2) % TCPOLEN_SACK_PERBLOCK) == 0)
nf_ct_sack_block_adjust(skb, tcph, optoff + 2,
optoff+op[1],
&seqadj->seq[!dir]);
optoff += op[1];
}
}
return 1;
}
/* TCP sequence number adjustment. Returns 1 on success, 0 on failure */
int nf_ct_seq_adjust(struct sk_buff *skb,
struct nf_conn *ct, enum ip_conntrack_info ctinfo,
unsigned int protoff)
{
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
struct tcphdr *tcph;
__be32 newseq, newack;
s32 seqoff, ackoff;
struct nf_conn_seqadj *seqadj = nfct_seqadj(ct);
struct nf_ct_seqadj *this_way, *other_way;
int res;
this_way = &seqadj->seq[dir];
other_way = &seqadj->seq[!dir];
if (!skb_make_writable(skb, protoff + sizeof(*tcph)))
return 0;
tcph = (void *)skb->data + protoff;
spin_lock_bh(&ct->lock);
if (after(ntohl(tcph->seq), this_way->correction_pos))
seqoff = this_way->offset_after;
else
seqoff = this_way->offset_before;
if (after(ntohl(tcph->ack_seq) - other_way->offset_before,
other_way->correction_pos))
ackoff = other_way->offset_after;
else
ackoff = other_way->offset_before;
newseq = htonl(ntohl(tcph->seq) + seqoff);
newack = htonl(ntohl(tcph->ack_seq) - ackoff);
inet_proto_csum_replace4(&tcph->check, skb, tcph->seq, newseq, 0);
inet_proto_csum_replace4(&tcph->check, skb, tcph->ack_seq, newack, 0);
pr_debug("Adjusting sequence number from %u->%u, ack from %u->%u\n",
ntohl(tcph->seq), ntohl(newseq), ntohl(tcph->ack_seq),
ntohl(newack));
tcph->seq = newseq;
tcph->ack_seq = newack;
res = nf_ct_sack_adjust(skb, protoff, tcph, ct, ctinfo);
spin_unlock_bh(&ct->lock);
return res;
}
EXPORT_SYMBOL_GPL(nf_ct_seq_adjust);
s32 nf_ct_seq_offset(const struct nf_conn *ct,
enum ip_conntrack_dir dir,
u32 seq)
{
struct nf_conn_seqadj *seqadj = nfct_seqadj(ct);
struct nf_ct_seqadj *this_way;
if (!seqadj)
return 0;
this_way = &seqadj->seq[dir];
return after(seq, this_way->correction_pos) ?
this_way->offset_after : this_way->offset_before;
}
EXPORT_SYMBOL_GPL(nf_ct_seq_offset);
static struct nf_ct_ext_type nf_ct_seqadj_extend __read_mostly = {
.len = sizeof(struct nf_conn_seqadj),
.align = __alignof__(struct nf_conn_seqadj),
.id = NF_CT_EXT_SEQADJ,
};
int nf_conntrack_seqadj_init(void)
{
return nf_ct_extend_register(&nf_ct_seqadj_extend);
}
void nf_conntrack_seqadj_fini(void)
{
nf_ct_extend_unregister(&nf_ct_seqadj_extend);
}
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <net/netfilter/nf_nat_core.h> #include <net/netfilter/nf_nat_core.h>
#include <net/netfilter/nf_nat_helper.h> #include <net/netfilter/nf_nat_helper.h>
#include <net/netfilter/nf_conntrack_helper.h> #include <net/netfilter/nf_conntrack_helper.h>
#include <net/netfilter/nf_conntrack_seqadj.h>
#include <net/netfilter/nf_conntrack_l3proto.h> #include <net/netfilter/nf_conntrack_l3proto.h>
#include <net/netfilter/nf_conntrack_zones.h> #include <net/netfilter/nf_conntrack_zones.h>
#include <linux/netfilter/nf_nat.h> #include <linux/netfilter/nf_nat.h>
...@@ -402,6 +403,9 @@ nf_nat_setup_info(struct nf_conn *ct, ...@@ -402,6 +403,9 @@ nf_nat_setup_info(struct nf_conn *ct,
ct->status |= IPS_SRC_NAT; ct->status |= IPS_SRC_NAT;
else else
ct->status |= IPS_DST_NAT; ct->status |= IPS_DST_NAT;
if (nfct_help(ct))
nfct_seqadj_ext_add(ct);
} }
if (maniptype == NF_NAT_MANIP_SRC) { if (maniptype == NF_NAT_MANIP_SRC) {
...@@ -764,10 +768,6 @@ static struct nf_ct_helper_expectfn follow_master_nat = { ...@@ -764,10 +768,6 @@ static struct nf_ct_helper_expectfn follow_master_nat = {
.expectfn = nf_nat_follow_master, .expectfn = nf_nat_follow_master,
}; };
static struct nfq_ct_nat_hook nfq_ct_nat = {
.seq_adjust = nf_nat_tcp_seq_adjust,
};
static int __init nf_nat_init(void) static int __init nf_nat_init(void)
{ {
int ret; int ret;
...@@ -787,14 +787,9 @@ static int __init nf_nat_init(void) ...@@ -787,14 +787,9 @@ static int __init nf_nat_init(void)
/* Initialize fake conntrack so that NAT will skip it */ /* Initialize fake conntrack so that NAT will skip it */
nf_ct_untracked_status_or(IPS_NAT_DONE_MASK); nf_ct_untracked_status_or(IPS_NAT_DONE_MASK);
BUG_ON(nf_nat_seq_adjust_hook != NULL);
RCU_INIT_POINTER(nf_nat_seq_adjust_hook, nf_nat_seq_adjust);
BUG_ON(nfnetlink_parse_nat_setup_hook != NULL); BUG_ON(nfnetlink_parse_nat_setup_hook != NULL);
RCU_INIT_POINTER(nfnetlink_parse_nat_setup_hook, RCU_INIT_POINTER(nfnetlink_parse_nat_setup_hook,
nfnetlink_parse_nat_setup); nfnetlink_parse_nat_setup);
BUG_ON(nf_ct_nat_offset != NULL);
RCU_INIT_POINTER(nf_ct_nat_offset, nf_nat_get_offset);
RCU_INIT_POINTER(nfq_ct_nat_hook, &nfq_ct_nat);
#ifdef CONFIG_XFRM #ifdef CONFIG_XFRM
BUG_ON(nf_nat_decode_session_hook != NULL); BUG_ON(nf_nat_decode_session_hook != NULL);
RCU_INIT_POINTER(nf_nat_decode_session_hook, __nf_nat_decode_session); RCU_INIT_POINTER(nf_nat_decode_session_hook, __nf_nat_decode_session);
...@@ -813,10 +808,7 @@ static void __exit nf_nat_cleanup(void) ...@@ -813,10 +808,7 @@ static void __exit nf_nat_cleanup(void)
unregister_pernet_subsys(&nf_nat_net_ops); unregister_pernet_subsys(&nf_nat_net_ops);
nf_ct_extend_unregister(&nat_extend); nf_ct_extend_unregister(&nat_extend);
nf_ct_helper_expectfn_unregister(&follow_master_nat); nf_ct_helper_expectfn_unregister(&follow_master_nat);
RCU_INIT_POINTER(nf_nat_seq_adjust_hook, NULL);
RCU_INIT_POINTER(nfnetlink_parse_nat_setup_hook, NULL); RCU_INIT_POINTER(nfnetlink_parse_nat_setup_hook, NULL);
RCU_INIT_POINTER(nf_ct_nat_offset, NULL);
RCU_INIT_POINTER(nfq_ct_nat_hook, NULL);
#ifdef CONFIG_XFRM #ifdef CONFIG_XFRM
RCU_INIT_POINTER(nf_nat_decode_session_hook, NULL); RCU_INIT_POINTER(nf_nat_decode_session_hook, NULL);
#endif #endif
......
...@@ -20,67 +20,13 @@ ...@@ -20,67 +20,13 @@
#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_ecache.h>
#include <net/netfilter/nf_conntrack_expect.h> #include <net/netfilter/nf_conntrack_expect.h>
#include <net/netfilter/nf_conntrack_seqadj.h>
#include <net/netfilter/nf_nat.h> #include <net/netfilter/nf_nat.h>
#include <net/netfilter/nf_nat_l3proto.h> #include <net/netfilter/nf_nat_l3proto.h>
#include <net/netfilter/nf_nat_l4proto.h> #include <net/netfilter/nf_nat_l4proto.h>
#include <net/netfilter/nf_nat_core.h> #include <net/netfilter/nf_nat_core.h>
#include <net/netfilter/nf_nat_helper.h> #include <net/netfilter/nf_nat_helper.h>
#define DUMP_OFFSET(x) \
pr_debug("offset_before=%d, offset_after=%d, correction_pos=%u\n", \
x->offset_before, x->offset_after, x->correction_pos);
/* Setup TCP sequence correction given this change at this sequence */
static inline void
adjust_tcp_sequence(u32 seq,
int sizediff,
struct nf_conn *ct,
enum ip_conntrack_info ctinfo)
{
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
struct nf_conn_nat *nat = nfct_nat(ct);
struct nf_nat_seq *this_way = &nat->seq[dir];
pr_debug("adjust_tcp_sequence: seq = %u, sizediff = %d\n",
seq, sizediff);
pr_debug("adjust_tcp_sequence: Seq_offset before: ");
DUMP_OFFSET(this_way);
spin_lock_bh(&ct->lock);
/* SYN adjust. If it's uninitialized, or this is after last
* correction, record it: we don't handle more than one
* adjustment in the window, but do deal with common case of a
* retransmit */
if (this_way->offset_before == this_way->offset_after ||
before(this_way->correction_pos, seq)) {
this_way->correction_pos = seq;
this_way->offset_before = this_way->offset_after;
this_way->offset_after += sizediff;
}
spin_unlock_bh(&ct->lock);
pr_debug("adjust_tcp_sequence: Seq_offset after: ");
DUMP_OFFSET(this_way);
}
/* Get the offset value, for conntrack. Caller must have the conntrack locked */
s32 nf_nat_get_offset(const struct nf_conn *ct,
enum ip_conntrack_dir dir,
u32 seq)
{
struct nf_conn_nat *nat = nfct_nat(ct);
struct nf_nat_seq *this_way;
if (!nat)
return 0;
this_way = &nat->seq[dir];
return after(seq, this_way->correction_pos)
? this_way->offset_after : this_way->offset_before;
}
/* Frobs data inside this packet, which is linear. */ /* Frobs data inside this packet, which is linear. */
static void mangle_contents(struct sk_buff *skb, static void mangle_contents(struct sk_buff *skb,
unsigned int dataoff, unsigned int dataoff,
...@@ -135,30 +81,6 @@ static int enlarge_skb(struct sk_buff *skb, unsigned int extra) ...@@ -135,30 +81,6 @@ static int enlarge_skb(struct sk_buff *skb, unsigned int extra)
return 1; return 1;
} }
void nf_nat_set_seq_adjust(struct nf_conn *ct, enum ip_conntrack_info ctinfo,
__be32 seq, s32 off)
{
if (!off)
return;
set_bit(IPS_SEQ_ADJUST_BIT, &ct->status);
adjust_tcp_sequence(ntohl(seq), off, ct, ctinfo);
nf_conntrack_event_cache(IPCT_NATSEQADJ, ct);
}
EXPORT_SYMBOL_GPL(nf_nat_set_seq_adjust);
void nf_nat_tcp_seq_adjust(struct sk_buff *skb, struct nf_conn *ct,
u32 ctinfo, int off)
{
const struct tcphdr *th;
if (nf_ct_protonum(ct) != IPPROTO_TCP)
return;
th = (struct tcphdr *)(skb_network_header(skb)+ ip_hdrlen(skb));
nf_nat_set_seq_adjust(ct, ctinfo, th->seq, off);
}
EXPORT_SYMBOL_GPL(nf_nat_tcp_seq_adjust);
/* Generic function for mangling variable-length address changes inside /* Generic function for mangling variable-length address changes inside
* NATed TCP connections (like the PORT XXX,XXX,XXX,XXX,XXX,XXX * NATed TCP connections (like the PORT XXX,XXX,XXX,XXX,XXX,XXX
* command in FTP). * command in FTP).
...@@ -203,8 +125,8 @@ int __nf_nat_mangle_tcp_packet(struct sk_buff *skb, ...@@ -203,8 +125,8 @@ int __nf_nat_mangle_tcp_packet(struct sk_buff *skb,
datalen, oldlen); datalen, oldlen);
if (adjust && rep_len != match_len) if (adjust && rep_len != match_len)
nf_nat_set_seq_adjust(ct, ctinfo, tcph->seq, nf_ct_seqadj_set(ct, ctinfo, tcph->seq,
(int)rep_len - (int)match_len); (int)rep_len - (int)match_len);
return 1; return 1;
} }
...@@ -264,150 +186,6 @@ nf_nat_mangle_udp_packet(struct sk_buff *skb, ...@@ -264,150 +186,6 @@ nf_nat_mangle_udp_packet(struct sk_buff *skb,
} }
EXPORT_SYMBOL(nf_nat_mangle_udp_packet); EXPORT_SYMBOL(nf_nat_mangle_udp_packet);
/* Adjust one found SACK option including checksum correction */
static void
sack_adjust(struct sk_buff *skb,
struct tcphdr *tcph,
unsigned int sackoff,
unsigned int sackend,
struct nf_nat_seq *natseq)
{
while (sackoff < sackend) {
struct tcp_sack_block_wire *sack;
__be32 new_start_seq, new_end_seq;
sack = (void *)skb->data + sackoff;
if (after(ntohl(sack->start_seq) - natseq->offset_before,
natseq->correction_pos))
new_start_seq = htonl(ntohl(sack->start_seq)
- natseq->offset_after);
else
new_start_seq = htonl(ntohl(sack->start_seq)
- natseq->offset_before);
if (after(ntohl(sack->end_seq) - natseq->offset_before,
natseq->correction_pos))
new_end_seq = htonl(ntohl(sack->end_seq)
- natseq->offset_after);
else
new_end_seq = htonl(ntohl(sack->end_seq)
- natseq->offset_before);
pr_debug("sack_adjust: start_seq: %d->%d, end_seq: %d->%d\n",
ntohl(sack->start_seq), new_start_seq,
ntohl(sack->end_seq), new_end_seq);
inet_proto_csum_replace4(&tcph->check, skb,
sack->start_seq, new_start_seq, 0);
inet_proto_csum_replace4(&tcph->check, skb,
sack->end_seq, new_end_seq, 0);
sack->start_seq = new_start_seq;
sack->end_seq = new_end_seq;
sackoff += sizeof(*sack);
}
}
/* TCP SACK sequence number adjustment */
static inline unsigned int
nf_nat_sack_adjust(struct sk_buff *skb,
unsigned int protoff,
struct tcphdr *tcph,
struct nf_conn *ct,
enum ip_conntrack_info ctinfo)
{
unsigned int dir, optoff, optend;
struct nf_conn_nat *nat = nfct_nat(ct);
optoff = protoff + sizeof(struct tcphdr);
optend = protoff + tcph->doff * 4;
if (!skb_make_writable(skb, optend))
return 0;
dir = CTINFO2DIR(ctinfo);
while (optoff < optend) {
/* Usually: option, length. */
unsigned char *op = skb->data + optoff;
switch (op[0]) {
case TCPOPT_EOL:
return 1;
case TCPOPT_NOP:
optoff++;
continue;
default:
/* no partial options */
if (optoff + 1 == optend ||
optoff + op[1] > optend ||
op[1] < 2)
return 0;
if (op[0] == TCPOPT_SACK &&
op[1] >= 2+TCPOLEN_SACK_PERBLOCK &&
((op[1] - 2) % TCPOLEN_SACK_PERBLOCK) == 0)
sack_adjust(skb, tcph, optoff+2,
optoff+op[1], &nat->seq[!dir]);
optoff += op[1];
}
}
return 1;
}
/* TCP sequence number adjustment. Returns 1 on success, 0 on failure */
int
nf_nat_seq_adjust(struct sk_buff *skb,
struct nf_conn *ct,
enum ip_conntrack_info ctinfo,
unsigned int protoff)
{
struct tcphdr *tcph;
int dir;
__be32 newseq, newack;
s32 seqoff, ackoff;
struct nf_conn_nat *nat = nfct_nat(ct);
struct nf_nat_seq *this_way, *other_way;
int res;
dir = CTINFO2DIR(ctinfo);
this_way = &nat->seq[dir];
other_way = &nat->seq[!dir];
if (!skb_make_writable(skb, protoff + sizeof(*tcph)))
return 0;
tcph = (void *)skb->data + protoff;
spin_lock_bh(&ct->lock);
if (after(ntohl(tcph->seq), this_way->correction_pos))
seqoff = this_way->offset_after;
else
seqoff = this_way->offset_before;
if (after(ntohl(tcph->ack_seq) - other_way->offset_before,
other_way->correction_pos))
ackoff = other_way->offset_after;
else
ackoff = other_way->offset_before;
newseq = htonl(ntohl(tcph->seq) + seqoff);
newack = htonl(ntohl(tcph->ack_seq) - ackoff);
inet_proto_csum_replace4(&tcph->check, skb, tcph->seq, newseq, 0);
inet_proto_csum_replace4(&tcph->check, skb, tcph->ack_seq, newack, 0);
pr_debug("Adjusting sequence number from %u->%u, ack from %u->%u\n",
ntohl(tcph->seq), ntohl(newseq), ntohl(tcph->ack_seq),
ntohl(newack));
tcph->seq = newseq;
tcph->ack_seq = newack;
res = nf_nat_sack_adjust(skb, protoff, tcph, ct, ctinfo);
spin_unlock_bh(&ct->lock);
return res;
}
/* Setup NAT on this expected conntrack so it follows master. */ /* Setup NAT on this expected conntrack so it follows master. */
/* If we fail to get a free NAT slot, we'll get dropped on confirm */ /* If we fail to get a free NAT slot, we'll get dropped on confirm */
void nf_nat_follow_master(struct nf_conn *ct, void nf_nat_follow_master(struct nf_conn *ct,
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <net/netfilter/nf_nat_helper.h> #include <net/netfilter/nf_nat_helper.h>
#include <net/netfilter/nf_conntrack_helper.h> #include <net/netfilter/nf_conntrack_helper.h>
#include <net/netfilter/nf_conntrack_expect.h> #include <net/netfilter/nf_conntrack_expect.h>
#include <net/netfilter/nf_conntrack_seqadj.h>
#include <linux/netfilter/nf_conntrack_sip.h> #include <linux/netfilter/nf_conntrack_sip.h>
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -308,7 +309,7 @@ static void nf_nat_sip_seq_adjust(struct sk_buff *skb, unsigned int protoff, ...@@ -308,7 +309,7 @@ static void nf_nat_sip_seq_adjust(struct sk_buff *skb, unsigned int protoff,
return; return;
th = (struct tcphdr *)(skb->data + protoff); th = (struct tcphdr *)(skb->data + protoff);
nf_nat_set_seq_adjust(ct, ctinfo, th->seq, off); nf_ct_seqadj_set(ct, ctinfo, th->seq, off);
} }
/* Handles expected signalling connections and media streams */ /* Handles expected signalling connections and media streams */
......
...@@ -87,14 +87,14 @@ int nfqnl_ct_put(struct sk_buff *skb, struct nf_conn *ct, ...@@ -87,14 +87,14 @@ int nfqnl_ct_put(struct sk_buff *skb, struct nf_conn *ct,
void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct,
enum ip_conntrack_info ctinfo, int diff) enum ip_conntrack_info ctinfo, int diff)
{ {
struct nfq_ct_nat_hook *nfq_nat_ct; struct nfq_ct_hook *nfq_ct;
nfq_nat_ct = rcu_dereference(nfq_ct_nat_hook); nfq_ct = rcu_dereference(nfq_ct_hook);
if (nfq_nat_ct == NULL) if (nfq_ct == NULL)
return; return;
if ((ct->status & IPS_NAT_MASK) && diff) if ((ct->status & IPS_NAT_MASK) && diff)
nfq_nat_ct->seq_adjust(skb, ct, ctinfo, diff); nfq_ct->seq_adjust(skb, ct, ctinfo, diff);
} }
int nfqnl_attach_expect(struct nf_conn *ct, const struct nlattr *attr, int nfqnl_attach_expect(struct nf_conn *ct, const struct nlattr *attr,
......
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