Commit 92bb4f8e authored by Rusty Russell's avatar Rusty Russell Committed by David S. Miller

[NETFILTER]: Call NAT helper modules directly from conntrack modules, fixup FTP

Currently connection tracking and NAT helper modules for a protocol
interact only indirectly (the conntrack module places information in
the conntrack structure, which the NAT module pulls out).

This leads to several issues:
1) Both modules must know what port to watch, and must match.
2) Identifying the particular packet which created the connection
   is cumbersome (TCP) or impossible (UDP).
3) The connection tracking code sets up an expectation which the
   NAT code then has to change.
4) The lack of direct symbol dependencies means we have to contrive
   one, since they are functionally dependent.

Here is the current code flow:
FTP CONTROL PACKET:
NF_IP_PRE_ROUTING:
   ip_conntrack_in
      resolve_normal_ct
         init_conntrack: sets ct->helper to ip_conntrack_ftp.c:help()
   ct->help(): if PORT/PASV command:
      Sets exp->help.exp_ftp_info to tcp seq number of data.
      ip_conntrack_expect(): expects the connection

   ip_nat_setup_info: sets ct->nat.info->helper to ip_nat_ftp.c:help()
   ip_nat_fn:
      proto->exp_matches_pkt: if packet matches expectation
      ct->nat.info->helper(): If packet going client->server,
            and packet data is one in ct_ftp_info:
         ftp_data_fixup():
            ip_conntrack_change_expect(): change the expectation
            Modify packet contents with new address.

NF_IP_POST_ROUTING:
   ip_nat_fn
      ct->nat.info->helper(): If packet going server->client,
            and packet data is one in ct_ftp_info:
         ftp_data_fixup():
            ip_conntrack_change_expect(): change the expectation
            Modify packet contents with new address.

FTP DATA (EXPECTED) CONNECTION FIRST PACKET:
NF_IP_PRE_ROUTING:
   ip_conntrack_in
      resolve_normal_ct
         init_conntrack: set ct->master.
   ip_nat_fn:
      master->nat.info.helper->expect()
         Set up source NAT mapping to match FTP control connection.

NF_IP_PRE_ROUTING:
   ip_nat_fn:
      master->nat.info.helper->expect()
         Set up dest NAT mapping to match FTP control connection.


The new flow looks like this:
FTP CONTROL PACKET:
NF_IP_PRE_ROUTING:
   ip_conntrack_in
      resolve_normal_ct
         init_conntrack: sets ct->helper to ip_conntrack_ftp.c:help()

NF_IP_POST_ROUTING:
   ip_confirm:
      ct->helper->help:
         If !ip_nat_ftp_hook: ip_conntrack_expect().
         ip_nat_ftp: 
            set exp->oldproto to old port.
            ip_conntrack_change_expect(): change the expectation
            set exp->expectfn to ftp_nat_expected.
            Modify packet contents with new address.

FTP DATA (EXPECTED) CONNECTION FIRST PACKET:
NF_IP_PRE_ROUTING:
   ip_conntrack_in
      resolve_normal_ct
         init_conntrack: set ct->master.
         call exp->expectfn (ftp_nat_expected):
             call ip_nat_follow_master().

The big changes are that the ip_nat_ftp module sets ip_conntrack_ftp's
ip_nat_ftp_hook when it initializes, so it calls the NAT code directly
when a packet containing the expect information is found by the
conntrack helper: and this interface can carry all the information
these two want to share.  Also, that conntrack helper is called as the
packet leaves the box, so there are no issues with expectations being
set up before the packet has been filtered.  The NAT helper doesn't
need to register and duplicate the conntrack ports.

The other trick is ip_nat_follow_master(), which does the NAT setup
all at once (source and destination NAT as required) such that the
expected connection is NATed the same way the master connection
was.

We also call ip_conntrack_tcp_update() (which I incidentally neatened)
after mangling a TCP packet; ip_nat_seq_adjust() does this, but now
mangling is done at the last possible moment, after
ip_nat_seq_adjust() was already called.
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 13b9f4df
...@@ -3,13 +3,6 @@ ...@@ -3,13 +3,6 @@
/* Connection state tracking for netfilter. This is separated from, /* Connection state tracking for netfilter. This is separated from,
but required by, the NAT layer; it can also be used by an iptables but required by, the NAT layer; it can also be used by an iptables
extension. */ extension. */
#include <linux/config.h>
#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
#include <linux/bitops.h>
#include <linux/compiler.h>
#include <asm/atomic.h>
enum ip_conntrack_info enum ip_conntrack_info
{ {
/* Part of an established connection (either direction). */ /* Part of an established connection (either direction). */
...@@ -49,6 +42,13 @@ enum ip_conntrack_status { ...@@ -49,6 +42,13 @@ enum ip_conntrack_status {
IPS_CONFIRMED = (1 << IPS_CONFIRMED_BIT), IPS_CONFIRMED = (1 << IPS_CONFIRMED_BIT),
}; };
#ifdef __KERNEL__
#include <linux/config.h>
#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
#include <linux/bitops.h>
#include <linux/compiler.h>
#include <asm/atomic.h>
#include <linux/netfilter_ipv4/ip_conntrack_tcp.h> #include <linux/netfilter_ipv4/ip_conntrack_tcp.h>
#include <linux/netfilter_ipv4/ip_conntrack_icmp.h> #include <linux/netfilter_ipv4/ip_conntrack_icmp.h>
#include <linux/netfilter_ipv4/ip_conntrack_sctp.h> #include <linux/netfilter_ipv4/ip_conntrack_sctp.h>
...@@ -70,20 +70,6 @@ union ip_conntrack_expect_proto { ...@@ -70,20 +70,6 @@ union ip_conntrack_expect_proto {
#include <linux/netfilter_ipv4/ip_conntrack_ftp.h> #include <linux/netfilter_ipv4/ip_conntrack_ftp.h>
#include <linux/netfilter_ipv4/ip_conntrack_irc.h> #include <linux/netfilter_ipv4/ip_conntrack_irc.h>
/* per expectation: application helper private data */
union ip_conntrack_expect_help {
/* insert conntrack helper private data (expect) here */
struct ip_ct_amanda_expect exp_amanda_info;
struct ip_ct_ftp_expect exp_ftp_info;
struct ip_ct_irc_expect exp_irc_info;
#ifdef CONFIG_IP_NF_NAT_NEEDED
union {
/* insert nat helper private data (expect) here */
} nat;
#endif
};
/* per conntrack: application helper private data */ /* per conntrack: application helper private data */
union ip_conntrack_help { union ip_conntrack_help {
/* insert conntrack helper private data (master) here */ /* insert conntrack helper private data (master) here */
...@@ -100,8 +86,6 @@ union ip_conntrack_nat_help { ...@@ -100,8 +86,6 @@ union ip_conntrack_nat_help {
}; };
#endif #endif
#ifdef __KERNEL__
#include <linux/types.h> #include <linux/types.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
...@@ -148,14 +132,17 @@ struct ip_conntrack_expect ...@@ -148,14 +132,17 @@ struct ip_conntrack_expect
struct ip_conntrack_tuple tuple, mask; struct ip_conntrack_tuple tuple, mask;
/* Function to call after setup and insertion */ /* Function to call after setup and insertion */
int (*expectfn)(struct ip_conntrack *new); void (*expectfn)(struct ip_conntrack *new);
/* At which sequence number did this expectation occur */ #ifdef CONFIG_IP_NF_NAT_NEEDED
u_int32_t seq; /* This is the original per-proto part, used to map the
* expected connection the way the recipient expects. */
union ip_conntrack_expect_proto proto; union ip_conntrack_manip_proto saved_proto;
/* Direction relative to the master connection. */
enum ip_conntrack_dir dir;
#endif
union ip_conntrack_expect_help help; union ip_conntrack_expect_proto proto;
}; };
struct ip_conntrack_counter struct ip_conntrack_counter
...@@ -267,9 +254,9 @@ extern void ip_ct_refresh_acct(struct ip_conntrack *ct, ...@@ -267,9 +254,9 @@ extern void ip_ct_refresh_acct(struct ip_conntrack *ct,
/* These are for NAT. Icky. */ /* These are for NAT. Icky. */
/* Update TCP window tracking data when NAT mangles the packet */ /* Update TCP window tracking data when NAT mangles the packet */
extern int ip_conntrack_tcp_update(struct sk_buff *skb, extern void ip_conntrack_tcp_update(struct sk_buff *skb,
struct ip_conntrack *conntrack, struct ip_conntrack *conntrack,
int dir); enum ip_conntrack_dir dir);
/* Call me when a conntrack is destroyed. */ /* Call me when a conntrack is destroyed. */
extern void (*ip_conntrack_destroyed)(struct ip_conntrack *conntrack); extern void (*ip_conntrack_destroyed)(struct ip_conntrack *conntrack);
......
...@@ -34,14 +34,14 @@ struct ip_conntrack_tuple_hash * ...@@ -34,14 +34,14 @@ struct ip_conntrack_tuple_hash *
ip_conntrack_find_get(const struct ip_conntrack_tuple *tuple, ip_conntrack_find_get(const struct ip_conntrack_tuple *tuple,
const struct ip_conntrack *ignored_conntrack); const struct ip_conntrack *ignored_conntrack);
extern int __ip_conntrack_confirm(struct sk_buff *skb); extern int __ip_conntrack_confirm(struct sk_buff **pskb);
/* Confirm a connection: returns NF_DROP if packet must be dropped. */ /* Confirm a connection: returns NF_DROP if packet must be dropped. */
static inline int ip_conntrack_confirm(struct sk_buff *skb) static inline int ip_conntrack_confirm(struct sk_buff **pskb)
{ {
if (skb->nfct if ((*pskb)->nfct
&& !is_confirmed((struct ip_conntrack *)skb->nfct)) && !is_confirmed((struct ip_conntrack *)(*pskb)->nfct))
return __ip_conntrack_confirm(skb); return __ip_conntrack_confirm(pskb);
return NF_ACCEPT; return NF_ACCEPT;
} }
......
...@@ -20,24 +20,25 @@ enum ip_ct_ftp_type ...@@ -20,24 +20,25 @@ enum ip_ct_ftp_type
IP_CT_FTP_EPSV, IP_CT_FTP_EPSV,
}; };
/* This structure is per expected connection */ #define NUM_SEQ_TO_REMEMBER 2
struct ip_ct_ftp_expect
{
/* We record seq number and length of ftp ip/port text here: all in
* host order. */
/* sequence number of IP address in packet is in ip_conntrack_expect */
u_int32_t len; /* length of IP address */
enum ip_ct_ftp_type ftptype; /* PORT or PASV ? */
u_int16_t port; /* TCP port that was to be used */
};
/* This structure exists only once per master */ /* This structure exists only once per master */
struct ip_ct_ftp_master { struct ip_ct_ftp_master {
/* Next valid seq position for cmd matching after newline */ /* Valid seq positions for cmd matching after newline */
u_int32_t seq_aft_nl[IP_CT_DIR_MAX]; u_int32_t seq_aft_nl[IP_CT_DIR_MAX][NUM_SEQ_TO_REMEMBER];
/* 0 means seq_match_aft_nl not set */ /* 0 means seq_match_aft_nl not set */
int seq_aft_nl_set[IP_CT_DIR_MAX]; int seq_aft_nl_num[IP_CT_DIR_MAX];
}; };
struct ip_conntrack_expect;
/* For NAT to hook in when we find a packet which describes what other
* connection we should expect. */
extern unsigned int (*ip_nat_ftp_hook)(struct sk_buff **pskb,
struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo,
enum ip_ct_ftp_type type,
unsigned int matchoff,
unsigned int matchlen,
struct ip_conntrack_expect *exp,
u32 *seq);
#endif /* _IP_CONNTRACK_FTP_H */ #endif /* _IP_CONNTRACK_FTP_H */
...@@ -25,7 +25,7 @@ struct ip_conntrack_helper ...@@ -25,7 +25,7 @@ struct ip_conntrack_helper
/* Function to call when data passes; return verdict, or -1 to /* Function to call when data passes; return verdict, or -1 to
invalidate. */ invalidate. */
int (*help)(struct sk_buff *skb, int (*help)(struct sk_buff **pskb,
struct ip_conntrack *ct, struct ip_conntrack *ct,
enum ip_conntrack_info conntrackinfo); enum ip_conntrack_info conntrackinfo);
}; };
...@@ -42,8 +42,6 @@ extern struct ip_conntrack_expect *ip_conntrack_expect_alloc(void); ...@@ -42,8 +42,6 @@ extern struct ip_conntrack_expect *ip_conntrack_expect_alloc(void);
/* Add an expected connection: can have more than one per connection */ /* Add an expected connection: can have more than one per connection */
extern int ip_conntrack_expect_related(struct ip_conntrack_expect *exp, extern int ip_conntrack_expect_related(struct ip_conntrack_expect *exp,
struct ip_conntrack *related_to); struct ip_conntrack *related_to);
extern int ip_conntrack_change_expect(struct ip_conntrack_expect *expect,
struct ip_conntrack_tuple *newtuple);
extern void ip_conntrack_unexpect_related(struct ip_conntrack_expect *exp); extern void ip_conntrack_unexpect_related(struct ip_conntrack_expect *exp);
#endif /*_IP_CONNTRACK_HELPER_H*/ #endif /*_IP_CONNTRACK_HELPER_H*/
...@@ -44,10 +44,6 @@ struct ip_conntrack_protocol ...@@ -44,10 +44,6 @@ struct ip_conntrack_protocol
/* Called when a conntrack entry is destroyed */ /* Called when a conntrack entry is destroyed */
void (*destroy)(struct ip_conntrack *conntrack); void (*destroy)(struct ip_conntrack *conntrack);
/* Has to decide if a expectation matches one packet or not */
int (*exp_matches_pkt)(struct ip_conntrack_expect *exp,
const struct sk_buff *skb);
int (*error)(struct sk_buff *skb, enum ip_conntrack_info *ctinfo, int (*error)(struct sk_buff *skb, enum ip_conntrack_info *ctinfo,
unsigned int hooknum); unsigned int hooknum);
......
...@@ -30,12 +30,6 @@ struct ip_nat_helper ...@@ -30,12 +30,6 @@ struct ip_nat_helper
enum ip_conntrack_info ctinfo, enum ip_conntrack_info ctinfo,
unsigned int hooknum, unsigned int hooknum,
struct sk_buff **pskb); struct sk_buff **pskb);
/* Returns verdict and sets up NAT for this connection */
unsigned int (*expect)(struct sk_buff **pskb,
unsigned int hooknum,
struct ip_conntrack *ct,
struct ip_nat_info *info);
}; };
extern int ip_nat_helper_register(struct ip_nat_helper *me); extern int ip_nat_helper_register(struct ip_nat_helper *me);
...@@ -65,4 +59,8 @@ extern int ip_nat_mangle_udp_packet(struct sk_buff **skb, ...@@ -65,4 +59,8 @@ extern int ip_nat_mangle_udp_packet(struct sk_buff **skb,
extern int ip_nat_seq_adjust(struct sk_buff **pskb, extern int ip_nat_seq_adjust(struct sk_buff **pskb,
struct ip_conntrack *ct, struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo); enum ip_conntrack_info ctinfo);
/* Setup NAT on this expected conntrack so it follows master, but goes
* to port ct->master->saved_proto. */
extern void ip_nat_follow_master(struct ip_conntrack *ct);
#endif #endif
...@@ -394,13 +394,13 @@ ip_conntrack_find_get(const struct ip_conntrack_tuple *tuple, ...@@ -394,13 +394,13 @@ ip_conntrack_find_get(const struct ip_conntrack_tuple *tuple,
/* Confirm a connection given skb; places it in hash table */ /* Confirm a connection given skb; places it in hash table */
int int
__ip_conntrack_confirm(struct sk_buff *skb) __ip_conntrack_confirm(struct sk_buff **pskb)
{ {
unsigned int hash, repl_hash; unsigned int hash, repl_hash;
struct ip_conntrack *ct; struct ip_conntrack *ct;
enum ip_conntrack_info ctinfo; enum ip_conntrack_info ctinfo;
ct = ip_conntrack_get(skb, &ctinfo); ct = ip_conntrack_get(*pskb, &ctinfo);
/* ipt_REJECT uses ip_conntrack_attach to attach related /* ipt_REJECT uses ip_conntrack_attach to attach related
ICMP/TCP RST packets in other direction. Actual packet ICMP/TCP RST packets in other direction. Actual packet
...@@ -782,16 +782,6 @@ unsigned int ip_conntrack_in(unsigned int hooknum, ...@@ -782,16 +782,6 @@ unsigned int ip_conntrack_in(unsigned int hooknum,
return -ret; return -ret;
} }
if (ret != NF_DROP && ct->helper) {
ret = ct->helper->help(*pskb, ct, ctinfo);
if (ret == -1) {
/* Invalid */
CONNTRACK_STAT_INC(invalid);
nf_conntrack_put((*pskb)->nfct);
(*pskb)->nfct = NULL;
return NF_ACCEPT;
}
}
if (set_reply) if (set_reply)
set_bit(IPS_SEEN_REPLY_BIT, &ct->status); set_bit(IPS_SEEN_REPLY_BIT, &ct->status);
...@@ -947,7 +937,7 @@ int ip_conntrack_expect_related(struct ip_conntrack_expect *expect, ...@@ -947,7 +937,7 @@ int ip_conntrack_expect_related(struct ip_conntrack_expect *expect,
related_to->helper->name, related_to->helper->name,
NIPQUAD(related_to->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip), NIPQUAD(related_to->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip),
NIPQUAD(related_to->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip)); NIPQUAD(related_to->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip));
ip_conntrack_expect_put(expect); ip_conntrack_expect_put(expect);
return -EPERM; return -EPERM;
} }
DEBUGP("ip_conntrack: max number of expected " DEBUGP("ip_conntrack: max number of expected "
...@@ -981,7 +971,7 @@ int ip_conntrack_expect_related(struct ip_conntrack_expect *expect, ...@@ -981,7 +971,7 @@ int ip_conntrack_expect_related(struct ip_conntrack_expect *expect,
WRITE_UNLOCK(&ip_conntrack_lock); WRITE_UNLOCK(&ip_conntrack_lock);
DEBUGP("expect_related: busy!\n"); DEBUGP("expect_related: busy!\n");
ip_conntrack_expect_put(expect); ip_conntrack_expect_put(expect);
return -EBUSY; return -EBUSY;
} }
...@@ -994,47 +984,6 @@ out: ip_conntrack_expect_insert(expect, related_to); ...@@ -994,47 +984,6 @@ out: ip_conntrack_expect_insert(expect, related_to);
return ret; return ret;
} }
/* Change tuple in an existing expectation */
int ip_conntrack_change_expect(struct ip_conntrack_expect *expect,
struct ip_conntrack_tuple *newtuple)
{
int ret;
MUST_BE_READ_LOCKED(&ip_conntrack_lock);
WRITE_LOCK(&ip_conntrack_expect_tuple_lock);
DEBUGP("change_expect:\n");
DEBUGP("exp tuple: "); DUMP_TUPLE(&expect->tuple);
DEBUGP("exp mask: "); DUMP_TUPLE(&expect->mask);
DEBUGP("newtuple: "); DUMP_TUPLE(newtuple);
if (expect->ct_tuple.dst.protonum == 0) {
/* Never seen before */
DEBUGP("change expect: never seen before\n");
if (!ip_ct_tuple_equal(&expect->tuple, newtuple)
&& LIST_FIND(&ip_conntrack_expect_list, expect_clash,
struct ip_conntrack_expect *, newtuple, &expect->mask)) {
/* Force NAT to find an unused tuple */
ret = -1;
} else {
memcpy(&expect->ct_tuple, &expect->tuple, sizeof(expect->tuple));
memcpy(&expect->tuple, newtuple, sizeof(expect->tuple));
ret = 0;
}
} else {
/* Resent packet */
DEBUGP("change expect: resent packet\n");
if (ip_ct_tuple_equal(&expect->tuple, newtuple)) {
ret = 0;
} else {
/* Force NAT to choose again the same port */
ret = -1;
}
}
WRITE_UNLOCK(&ip_conntrack_expect_tuple_lock);
return ret;
}
/* Alter reply tuple (maybe alter helper). This is for NAT, and is /* Alter reply tuple (maybe alter helper). This is for NAT, and is
implicitly racy: see __ip_conntrack_confirm */ implicitly racy: see __ip_conntrack_confirm */
void ip_conntrack_alter_reply(struct ip_conntrack *conntrack, void ip_conntrack_alter_reply(struct ip_conntrack *conntrack,
......
...@@ -39,6 +39,16 @@ module_param_array(ports, int, &ports_c, 0400); ...@@ -39,6 +39,16 @@ module_param_array(ports, int, &ports_c, 0400);
static int loose; static int loose;
module_param(loose, int, 0600); module_param(loose, int, 0600);
unsigned int (*ip_nat_ftp_hook)(struct sk_buff **pskb,
struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo,
enum ip_ct_ftp_type type,
unsigned int matchoff,
unsigned int matchlen,
struct ip_conntrack_expect *exp,
u32 *seq);
EXPORT_SYMBOL_GPL(ip_nat_ftp_hook);
#if 0 #if 0
#define DEBUGP printk #define DEBUGP printk
#else #else
...@@ -243,24 +253,53 @@ static int find_pattern(const char *data, size_t dlen, ...@@ -243,24 +253,53 @@ static int find_pattern(const char *data, size_t dlen,
return 1; return 1;
} }
static int help(struct sk_buff *skb, /* Look up to see if we're just after a \n. */
static int find_nl_seq(u16 seq, const struct ip_ct_ftp_master *info, int dir)
{
unsigned int i;
for (i = 0; i < info->seq_aft_nl_num[dir]; i++)
if (info->seq_aft_nl[dir][i] == seq)
return 1;
return 0;
}
/* We don't update if it's older than what we have. */
static void update_nl_seq(u16 nl_seq, struct ip_ct_ftp_master *info, int dir)
{
unsigned int i, oldest = NUM_SEQ_TO_REMEMBER;
/* Look for oldest: if we find exact match, we're done. */
for (i = 0; i < info->seq_aft_nl_num[dir]; i++) {
if (info->seq_aft_nl[dir][i] == nl_seq)
return;
if (oldest == info->seq_aft_nl_num[dir]
|| before(info->seq_aft_nl[dir][i], oldest))
oldest = i;
}
if (info->seq_aft_nl_num[dir] < NUM_SEQ_TO_REMEMBER)
info->seq_aft_nl[dir][info->seq_aft_nl_num[dir]++] = nl_seq;
else if (oldest != NUM_SEQ_TO_REMEMBER)
info->seq_aft_nl[dir][oldest] = nl_seq;
}
static int help(struct sk_buff **pskb,
struct ip_conntrack *ct, struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo) enum ip_conntrack_info ctinfo)
{ {
unsigned int dataoff, datalen; unsigned int dataoff, datalen;
struct tcphdr _tcph, *th; struct tcphdr _tcph, *th;
char *fb_ptr; char *fb_ptr;
u_int32_t old_seq_aft_nl; int ret;
int old_seq_aft_nl_set, ret; u32 seq, array[6] = { 0 };
u_int32_t array[6] = { 0 };
int dir = CTINFO2DIR(ctinfo); int dir = CTINFO2DIR(ctinfo);
unsigned int matchlen, matchoff; unsigned int matchlen, matchoff;
struct ip_ct_ftp_master *ct_ftp_info = &ct->help.ct_ftp_info; struct ip_ct_ftp_master *ct_ftp_info = &ct->help.ct_ftp_info;
struct ip_conntrack_expect *exp; struct ip_conntrack_expect *exp;
struct ip_ct_ftp_expect *exp_ftp_info;
unsigned int i; unsigned int i;
int found = 0; int found = 0, ends_in_nl;
/* Until there's been traffic both ways, don't look in packets. */ /* Until there's been traffic both ways, don't look in packets. */
if (ctinfo != IP_CT_ESTABLISHED if (ctinfo != IP_CT_ESTABLISHED
...@@ -269,46 +308,35 @@ static int help(struct sk_buff *skb, ...@@ -269,46 +308,35 @@ static int help(struct sk_buff *skb,
return NF_ACCEPT; return NF_ACCEPT;
} }
th = skb_header_pointer(skb, skb->nh.iph->ihl*4, th = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl*4,
sizeof(_tcph), &_tcph); sizeof(_tcph), &_tcph);
if (th == NULL) if (th == NULL)
return NF_ACCEPT; return NF_ACCEPT;
dataoff = skb->nh.iph->ihl*4 + th->doff*4; dataoff = (*pskb)->nh.iph->ihl*4 + th->doff*4;
/* No data? */ /* No data? */
if (dataoff >= skb->len) { if (dataoff >= (*pskb)->len) {
DEBUGP("ftp: skblen = %u\n", skb->len); DEBUGP("ftp: pskblen = %u\n", (*pskb)->len);
return NF_ACCEPT; return NF_ACCEPT;
} }
datalen = skb->len - dataoff; datalen = (*pskb)->len - dataoff;
LOCK_BH(&ip_ftp_lock); LOCK_BH(&ip_ftp_lock);
fb_ptr = skb_header_pointer(skb, dataoff, fb_ptr = skb_header_pointer(*pskb, dataoff,
skb->len - dataoff, ftp_buffer); (*pskb)->len - dataoff, ftp_buffer);
BUG_ON(fb_ptr == NULL); BUG_ON(fb_ptr == NULL);
old_seq_aft_nl_set = ct_ftp_info->seq_aft_nl_set[dir]; ends_in_nl = (fb_ptr[datalen - 1] == '\n');
old_seq_aft_nl = ct_ftp_info->seq_aft_nl[dir]; seq = ntohl(th->seq) + datalen;
DEBUGP("conntrack_ftp: datalen %u\n", datalen);
if (fb_ptr[datalen - 1] == '\n') {
DEBUGP("conntrack_ftp: datalen %u ends in \\n\n", datalen);
if (!old_seq_aft_nl_set
|| after(ntohl(th->seq) + datalen, old_seq_aft_nl)) {
DEBUGP("conntrack_ftp: updating nl to %u\n",
ntohl(th->seq) + datalen);
ct_ftp_info->seq_aft_nl[dir] =
ntohl(th->seq) + datalen;
ct_ftp_info->seq_aft_nl_set[dir] = 1;
}
}
if(!old_seq_aft_nl_set || /* Look up to see if we're just after a \n. */
(ntohl(th->seq) != old_seq_aft_nl)) { if (!find_nl_seq(ntohl(th->seq), ct_ftp_info, dir)) {
DEBUGP("ip_conntrack_ftp_help: wrong seq pos %s(%u)\n", /* Now if this ends in \n, update ftp info. */
DEBUGP("ip_conntrack_ftp_help: wrong seq pos %s(%u) or %s(%u)\n",
ct_ftp_info->seq_aft_nl[0][dir]
old_seq_aft_nl_set ? "":"(UNSET) ", old_seq_aft_nl); old_seq_aft_nl_set ? "":"(UNSET) ", old_seq_aft_nl);
ret = NF_ACCEPT; ret = NF_ACCEPT;
goto out; goto out_update_nl;
} }
/* Initialize IP array to expected address (it's not mentioned /* Initialize IP array to expected address (it's not mentioned
...@@ -321,7 +349,7 @@ static int help(struct sk_buff *skb, ...@@ -321,7 +349,7 @@ static int help(struct sk_buff *skb,
for (i = 0; i < ARRAY_SIZE(search); i++) { for (i = 0; i < ARRAY_SIZE(search); i++) {
if (search[i].dir != dir) continue; if (search[i].dir != dir) continue;
found = find_pattern(fb_ptr, skb->len - dataoff, found = find_pattern(fb_ptr, (*pskb)->len - dataoff,
search[i].pattern, search[i].pattern,
search[i].plen, search[i].plen,
search[i].skip, search[i].skip,
...@@ -344,7 +372,7 @@ static int help(struct sk_buff *skb, ...@@ -344,7 +372,7 @@ static int help(struct sk_buff *skb,
goto out; goto out;
} else if (found == 0) { /* No match */ } else if (found == 0) { /* No match */
ret = NF_ACCEPT; ret = NF_ACCEPT;
goto out; goto out_update_nl;
} }
DEBUGP("conntrack_ftp: match `%.*s' (%u bytes at %u)\n", DEBUGP("conntrack_ftp: match `%.*s' (%u bytes at %u)\n",
...@@ -354,20 +382,17 @@ static int help(struct sk_buff *skb, ...@@ -354,20 +382,17 @@ static int help(struct sk_buff *skb,
/* Allocate expectation which will be inserted */ /* Allocate expectation which will be inserted */
exp = ip_conntrack_expect_alloc(); exp = ip_conntrack_expect_alloc();
if (exp == NULL) { if (exp == NULL) {
ret = NF_ACCEPT; ret = NF_DROP;
goto out; goto out;
} }
exp_ftp_info = &exp->help.exp_ftp_info; /* We refer to the reverse direction ("!dir") tuples here,
* because we're expecting something in the other direction.
* Doesn't matter unless NAT is happening. */
exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip;
/* Update the ftp info */
if (htonl((array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3]) if (htonl((array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3])
== ct->tuplehash[dir].tuple.src.ip) { != ct->tuplehash[dir].tuple.src.ip) {
exp->seq = ntohl(th->seq) + matchoff;
exp_ftp_info->len = matchlen;
exp_ftp_info->ftptype = search[i].ftptype;
exp_ftp_info->port = array[4] << 8 | array[5];
} else {
/* Enrico Scholz's passive FTP to partially RNAT'd ftp /* Enrico Scholz's passive FTP to partially RNAT'd ftp
server: it really wants us to connect to a server: it really wants us to connect to a
different IP address. Simply don't record it for different IP address. Simply don't record it for
...@@ -381,28 +406,42 @@ static int help(struct sk_buff *skb, ...@@ -381,28 +406,42 @@ static int help(struct sk_buff *skb,
problem (DMZ machines opening holes to internal problem (DMZ machines opening holes to internal
networks, or the packet filter itself). */ networks, or the packet filter itself). */
if (!loose) { if (!loose) {
ip_conntrack_expect_put(exp);
ret = NF_ACCEPT; ret = NF_ACCEPT;
goto out; ip_conntrack_expect_put(exp);
goto out_update_nl;
} }
exp->tuple.dst.ip = htonl((array[0] << 24) | (array[1] << 16)
| (array[2] << 8) | array[3]);
} }
exp->tuple = ((struct ip_conntrack_tuple) exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip;
{ { ct->tuplehash[!dir].tuple.src.ip, exp->tuple.dst.u.tcp.port = htons(array[4] << 8 | array[5]);
{ 0 } }, exp->tuple.src.u.tcp.port = 0; /* Don't care. */
{ htonl((array[0] << 24) | (array[1] << 16) exp->tuple.dst.protonum = IPPROTO_TCP;
| (array[2] << 8) | array[3]),
{ .tcp = { htons(array[4] << 8 | array[5]) } },
IPPROTO_TCP }});
exp->mask = ((struct ip_conntrack_tuple) exp->mask = ((struct ip_conntrack_tuple)
{ { 0xFFFFFFFF, { 0 } }, { { 0xFFFFFFFF, { 0 } },
{ 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFFFF }}); { 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFFFF }});
exp->expectfn = NULL; exp->expectfn = NULL;
/* Ignore failure; should only happen with NAT */ /* Now, NAT might want to mangle the packet, and register the
ip_conntrack_expect_related(exp, ct); * (possibly changed) expectation itself. */
ret = NF_ACCEPT; if (ip_nat_ftp_hook)
ret = ip_nat_ftp_hook(pskb, ct, ctinfo, search[i].ftptype,
matchoff, matchlen, exp, &seq);
else {
/* Can't expect this? Best to drop packet now. */
if (ip_conntrack_expect_related(exp, ct) != 0)
ret = NF_DROP;
else
ret = NF_ACCEPT;
}
out_update_nl:
/* Now if this ends in \n, update ftp info. Seq may have been
* adjusted by NAT code. */
if (ends_in_nl)
update_nl_seq(seq, ct_ftp_info,dir);
out: out:
UNLOCK_BH(&ip_ftp_lock); UNLOCK_BH(&ip_ftp_lock);
return ret; return ret;
...@@ -460,7 +499,5 @@ static int __init init(void) ...@@ -460,7 +499,5 @@ static int __init init(void)
return 0; return 0;
} }
PROVIDES_CONNTRACK(ftp);
module_init(init); module_init(init);
module_exit(fini); module_exit(fini);
...@@ -494,13 +494,6 @@ static int sctp_new(struct ip_conntrack *conntrack, ...@@ -494,13 +494,6 @@ static int sctp_new(struct ip_conntrack *conntrack,
return 1; return 1;
} }
static int sctp_exp_matches_pkt(struct ip_conntrack_expect *exp,
const struct sk_buff *skb)
{
/* To be implemented */
return 0;
}
struct ip_conntrack_protocol ip_conntrack_protocol_sctp = { struct ip_conntrack_protocol ip_conntrack_protocol_sctp = {
.proto = IPPROTO_SCTP, .proto = IPPROTO_SCTP,
.name = "sctp", .name = "sctp",
...@@ -511,7 +504,6 @@ struct ip_conntrack_protocol ip_conntrack_protocol_sctp = { ...@@ -511,7 +504,6 @@ struct ip_conntrack_protocol ip_conntrack_protocol_sctp = {
.packet = sctp_packet, .packet = sctp_packet,
.new = sctp_new, .new = sctp_new,
.destroy = NULL, .destroy = NULL,
.exp_matches_pkt = sctp_exp_matches_pkt,
.me = THIS_MODULE .me = THIS_MODULE
}; };
......
...@@ -707,9 +707,9 @@ static int tcp_in_window(struct ip_ct_tcp *state, ...@@ -707,9 +707,9 @@ static int tcp_in_window(struct ip_ct_tcp *state,
#ifdef CONFIG_IP_NF_NAT_NEEDED #ifdef CONFIG_IP_NF_NAT_NEEDED
/* Update sender->td_end after NAT successfully mangled the packet */ /* Update sender->td_end after NAT successfully mangled the packet */
int ip_conntrack_tcp_update(struct sk_buff *skb, void ip_conntrack_tcp_update(struct sk_buff *skb,
struct ip_conntrack *conntrack, struct ip_conntrack *conntrack,
int dir) enum ip_conntrack_dir dir)
{ {
struct iphdr *iph = skb->nh.iph; struct iphdr *iph = skb->nh.iph;
struct tcphdr *tcph = (void *)skb->nh.iph + skb->nh.iph->ihl*4; struct tcphdr *tcph = (void *)skb->nh.iph + skb->nh.iph->ihl*4;
...@@ -735,8 +735,6 @@ int ip_conntrack_tcp_update(struct sk_buff *skb, ...@@ -735,8 +735,6 @@ int ip_conntrack_tcp_update(struct sk_buff *skb,
sender->td_scale, sender->td_scale,
receiver->td_end, receiver->td_maxend, receiver->td_maxwin, receiver->td_end, receiver->td_maxend, receiver->td_maxwin,
receiver->td_scale); receiver->td_scale);
return 1;
} }
#endif #endif
...@@ -1061,22 +1059,6 @@ static int tcp_new(struct ip_conntrack *conntrack, ...@@ -1061,22 +1059,6 @@ static int tcp_new(struct ip_conntrack *conntrack,
return 1; return 1;
} }
static int tcp_exp_matches_pkt(struct ip_conntrack_expect *exp,
const struct sk_buff *skb)
{
const struct iphdr *iph = skb->nh.iph;
struct tcphdr *th, _tcph;
unsigned int datalen;
th = skb_header_pointer(skb, iph->ihl * 4,
sizeof(_tcph), &_tcph);
if (th == NULL)
return 0;
datalen = skb->len - iph->ihl*4 - th->doff*4;
return between(exp->seq, ntohl(th->seq), ntohl(th->seq) + datalen);
}
struct ip_conntrack_protocol ip_conntrack_protocol_tcp = struct ip_conntrack_protocol ip_conntrack_protocol_tcp =
{ {
.proto = IPPROTO_TCP, .proto = IPPROTO_TCP,
...@@ -1087,6 +1069,5 @@ struct ip_conntrack_protocol ip_conntrack_protocol_tcp = ...@@ -1087,6 +1069,5 @@ struct ip_conntrack_protocol ip_conntrack_protocol_tcp =
.print_conntrack = tcp_print_conntrack, .print_conntrack = tcp_print_conntrack,
.packet = tcp_packet, .packet = tcp_packet,
.new = tcp_new, .new = tcp_new,
.exp_matches_pkt = tcp_exp_matches_pkt,
.error = tcp_error, .error = tcp_error,
}; };
...@@ -364,8 +364,20 @@ static unsigned int ip_confirm(unsigned int hooknum, ...@@ -364,8 +364,20 @@ static unsigned int ip_confirm(unsigned int hooknum,
const struct net_device *out, const struct net_device *out,
int (*okfn)(struct sk_buff *)) int (*okfn)(struct sk_buff *))
{ {
struct ip_conntrack *ct;
enum ip_conntrack_info ctinfo;
/* This is where we call the helper: as the packet goes out. */
ct = ip_conntrack_get(*pskb, &ctinfo);
if (ct && ct->helper) {
unsigned int ret;
ret = ct->helper->help(pskb, ct, ctinfo);
if (ret != NF_ACCEPT)
return ret;
}
/* We've seen it coming out the other side: confirm it */ /* We've seen it coming out the other side: confirm it */
return ip_conntrack_confirm(*pskb); return ip_conntrack_confirm(pskb);
} }
static unsigned int ip_conntrack_defrag(unsigned int hooknum, static unsigned int ip_conntrack_defrag(unsigned int hooknum,
...@@ -899,7 +911,6 @@ EXPORT_SYMBOL(ip_ct_find_proto); ...@@ -899,7 +911,6 @@ EXPORT_SYMBOL(ip_ct_find_proto);
EXPORT_SYMBOL(ip_ct_find_helper); EXPORT_SYMBOL(ip_ct_find_helper);
EXPORT_SYMBOL(ip_conntrack_expect_alloc); EXPORT_SYMBOL(ip_conntrack_expect_alloc);
EXPORT_SYMBOL(ip_conntrack_expect_related); EXPORT_SYMBOL(ip_conntrack_expect_related);
EXPORT_SYMBOL(ip_conntrack_change_expect);
EXPORT_SYMBOL(ip_conntrack_unexpect_related); EXPORT_SYMBOL(ip_conntrack_unexpect_related);
EXPORT_SYMBOL_GPL(ip_conntrack_expect_find_get); EXPORT_SYMBOL_GPL(ip_conntrack_expect_find_get);
EXPORT_SYMBOL_GPL(ip_conntrack_expect_put); EXPORT_SYMBOL_GPL(ip_conntrack_expect_put);
......
...@@ -490,20 +490,6 @@ manip_pkt(u_int16_t proto, ...@@ -490,20 +490,6 @@ manip_pkt(u_int16_t proto,
return 1; return 1;
} }
static inline int exp_for_packet(struct ip_conntrack_expect *exp,
struct sk_buff *skb)
{
struct ip_conntrack_protocol *proto;
int ret = 1;
MUST_BE_READ_LOCKED(&ip_conntrack_lock);
proto = ip_ct_find_proto(skb->nh.iph->protocol);
if (proto->exp_matches_pkt)
ret = proto->exp_matches_pkt(exp, skb);
return ret;
}
/* Do packet manipulations according to binding. */ /* Do packet manipulations according to binding. */
unsigned int unsigned int
do_bindings(struct ip_conntrack *ct, do_bindings(struct ip_conntrack *ct,
...@@ -512,8 +498,7 @@ do_bindings(struct ip_conntrack *ct, ...@@ -512,8 +498,7 @@ do_bindings(struct ip_conntrack *ct,
unsigned int hooknum, unsigned int hooknum,
struct sk_buff **pskb) struct sk_buff **pskb)
{ {
unsigned int i; int i, ret = NF_ACCEPT;
struct ip_nat_helper *helper;
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
int proto = (*pskb)->nh.iph->protocol; int proto = (*pskb)->nh.iph->protocol;
...@@ -538,75 +523,32 @@ do_bindings(struct ip_conntrack *ct, ...@@ -538,75 +523,32 @@ do_bindings(struct ip_conntrack *ct,
} }
} }
} }
helper = info->helper;
READ_UNLOCK(&ip_nat_lock);
if (helper) {
struct ip_conntrack_expect *exp = NULL;
struct list_head *cur_item;
int ret = NF_ACCEPT;
int helper_called = 0;
if (info->helper) {
DEBUGP("do_bindings: helper existing for (%p)\n", ct); DEBUGP("do_bindings: helper existing for (%p)\n", ct);
/* Always defragged for helpers */ /* Always defragged for helpers */
IP_NF_ASSERT(!((*pskb)->nh.iph->frag_off IP_NF_ASSERT(!((*pskb)->nh.iph->frag_off
& htons(IP_MF|IP_OFFSET))); & htons(IP_MF|IP_OFFSET)));
/* Have to grab read lock before sibling_list traversal */ ret = info->helper->help(ct, NULL, info, ctinfo, hooknum,pskb);
READ_LOCK(&ip_conntrack_lock); }
list_for_each_prev(cur_item, &ct->sibling_list) { READ_UNLOCK(&ip_nat_lock);
exp = list_entry(cur_item, struct ip_conntrack_expect,
expected_list);
/* if this expectation is already established, skip */
if (exp->sibling)
continue;
if (exp_for_packet(exp, *pskb)) {
/* FIXME: May be true multiple times in the
* case of UDP!! */
DEBUGP("calling nat helper (exp=%p) for packet\n", exp);
ret = helper->help(ct, exp, info, ctinfo,
hooknum, pskb);
if (ret != NF_ACCEPT) {
READ_UNLOCK(&ip_conntrack_lock);
return ret;
}
helper_called = 1;
}
}
/* Helper might want to manip the packet even when there is no
* matching expectation for this packet */
if (!helper_called && helper->flags & IP_NAT_HELPER_F_ALWAYS) {
DEBUGP("calling nat helper for packet without expectation\n");
ret = helper->help(ct, NULL, info, ctinfo,
hooknum, pskb);
if (ret != NF_ACCEPT) {
READ_UNLOCK(&ip_conntrack_lock);
return ret;
}
}
READ_UNLOCK(&ip_conntrack_lock);
/* Adjust sequence number only once per packet
* (helper is called at all hooks) */
if (proto == IPPROTO_TCP
&& (hooknum == NF_IP_POST_ROUTING
|| hooknum == NF_IP_LOCAL_IN)) {
DEBUGP("ip_nat_core: adjusting sequence number\n");
/* future: put this in a l4-proto specific function,
* and call this function here. */
if (!ip_nat_seq_adjust(pskb, ct, ctinfo))
ret = NF_DROP;
}
return ret;
} else /* FIXME: NAT/conntrack helpers should set ctinfo &
return NF_ACCEPT; * CT_INFO_RESYNC on packets, so we don't have to adjust all
* connections with conntrack helpers --RR */
if (ct->helper
&& proto == IPPROTO_TCP
&& (hooknum == NF_IP_POST_ROUTING || hooknum == NF_IP_LOCAL_IN)) {
DEBUGP("ip_nat_core: adjusting sequence number\n");
/* future: put this in a l4-proto specific function,
* and call this function here. */
if (!ip_nat_seq_adjust(pskb, ct, ctinfo))
ret = NF_DROP;
}
/* not reached */ return ret;
} }
static inline int tuple_src_equal_dst(const struct ip_conntrack_tuple *t1, static inline int tuple_src_equal_dst(const struct ip_conntrack_tuple *t1,
......
...@@ -30,71 +30,8 @@ MODULE_DESCRIPTION("ftp NAT helper"); ...@@ -30,71 +30,8 @@ MODULE_DESCRIPTION("ftp NAT helper");
#define DEBUGP(format, args...) #define DEBUGP(format, args...)
#endif #endif
#define MAX_PORTS 8
static int ports[MAX_PORTS];
static int ports_c;
module_param_array(ports, int, &ports_c, 0400);
/* FIXME: Time out? --RR */ /* FIXME: Time out? --RR */
static unsigned int
ftp_nat_expected(struct sk_buff **pskb,
unsigned int hooknum,
struct ip_conntrack *ct,
struct ip_nat_info *info)
{
struct ip_nat_range range;
u_int32_t newdstip, newsrcip, newip;
struct ip_ct_ftp_expect *exp_ftp_info;
struct ip_conntrack *master = master_ct(ct);
IP_NF_ASSERT(info);
IP_NF_ASSERT(master);
IP_NF_ASSERT(!(info->initialized & (1<<HOOK2MANIP(hooknum))));
DEBUGP("nat_expected: We have a connection!\n");
exp_ftp_info = &ct->master->help.exp_ftp_info;
if (exp_ftp_info->ftptype == IP_CT_FTP_PORT
|| exp_ftp_info->ftptype == IP_CT_FTP_EPRT) {
/* PORT command: make connection go to the client. */
newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
DEBUGP("nat_expected: PORT cmd. %u.%u.%u.%u->%u.%u.%u.%u\n",
NIPQUAD(newsrcip), NIPQUAD(newdstip));
} else {
/* PASV command: make the connection go to the server */
newdstip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
DEBUGP("nat_expected: PASV cmd. %u.%u.%u.%u->%u.%u.%u.%u\n",
NIPQUAD(newsrcip), NIPQUAD(newdstip));
}
if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC)
newip = newsrcip;
else
newip = newdstip;
DEBUGP("nat_expected: IP to %u.%u.%u.%u\n", NIPQUAD(newip));
/* We don't want to manip the per-protocol, just the IPs... */
range.flags = IP_NAT_RANGE_MAP_IPS;
range.min_ip = range.max_ip = newip;
/* ... unless we're doing a MANIP_DST, in which case, make
sure we map to the correct port */
if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) {
range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
range.min = range.max
= ((union ip_conntrack_manip_proto)
{ .tcp = { htons(exp_ftp_info->port) } });
}
return ip_nat_setup_info(ct, &range, hooknum);
}
static int static int
mangle_rfc959_packet(struct sk_buff **pskb, mangle_rfc959_packet(struct sk_buff **pskb,
u_int32_t newip, u_int32_t newip,
...@@ -102,7 +39,8 @@ mangle_rfc959_packet(struct sk_buff **pskb, ...@@ -102,7 +39,8 @@ mangle_rfc959_packet(struct sk_buff **pskb,
unsigned int matchoff, unsigned int matchoff,
unsigned int matchlen, unsigned int matchlen,
struct ip_conntrack *ct, struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo) enum ip_conntrack_info ctinfo,
u32 *seq)
{ {
char buffer[sizeof("nnn,nnn,nnn,nnn,nnn,nnn")]; char buffer[sizeof("nnn,nnn,nnn,nnn,nnn,nnn")];
...@@ -111,6 +49,7 @@ mangle_rfc959_packet(struct sk_buff **pskb, ...@@ -111,6 +49,7 @@ mangle_rfc959_packet(struct sk_buff **pskb,
DEBUGP("calling ip_nat_mangle_tcp_packet\n"); DEBUGP("calling ip_nat_mangle_tcp_packet\n");
*seq += strlen(buffer) - matchlen;
return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff, return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff,
matchlen, buffer, strlen(buffer)); matchlen, buffer, strlen(buffer));
} }
...@@ -123,7 +62,8 @@ mangle_eprt_packet(struct sk_buff **pskb, ...@@ -123,7 +62,8 @@ mangle_eprt_packet(struct sk_buff **pskb,
unsigned int matchoff, unsigned int matchoff,
unsigned int matchlen, unsigned int matchlen,
struct ip_conntrack *ct, struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo) enum ip_conntrack_info ctinfo,
u32 *seq)
{ {
char buffer[sizeof("|1|255.255.255.255|65535|")]; char buffer[sizeof("|1|255.255.255.255|65535|")];
...@@ -131,6 +71,7 @@ mangle_eprt_packet(struct sk_buff **pskb, ...@@ -131,6 +71,7 @@ mangle_eprt_packet(struct sk_buff **pskb,
DEBUGP("calling ip_nat_mangle_tcp_packet\n"); DEBUGP("calling ip_nat_mangle_tcp_packet\n");
*seq += strlen(buffer) - matchlen;
return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff, return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff,
matchlen, buffer, strlen(buffer)); matchlen, buffer, strlen(buffer));
} }
...@@ -143,7 +84,8 @@ mangle_epsv_packet(struct sk_buff **pskb, ...@@ -143,7 +84,8 @@ mangle_epsv_packet(struct sk_buff **pskb,
unsigned int matchoff, unsigned int matchoff,
unsigned int matchlen, unsigned int matchlen,
struct ip_conntrack *ct, struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo) enum ip_conntrack_info ctinfo,
u32 *seq)
{ {
char buffer[sizeof("|||65535|")]; char buffer[sizeof("|||65535|")];
...@@ -151,6 +93,7 @@ mangle_epsv_packet(struct sk_buff **pskb, ...@@ -151,6 +93,7 @@ mangle_epsv_packet(struct sk_buff **pskb,
DEBUGP("calling ip_nat_mangle_tcp_packet\n"); DEBUGP("calling ip_nat_mangle_tcp_packet\n");
*seq += strlen(buffer) - matchlen;
return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff, return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff,
matchlen, buffer, strlen(buffer)); matchlen, buffer, strlen(buffer));
} }
...@@ -159,181 +102,77 @@ static int (*mangle[])(struct sk_buff **, u_int32_t, u_int16_t, ...@@ -159,181 +102,77 @@ static int (*mangle[])(struct sk_buff **, u_int32_t, u_int16_t,
unsigned int, unsigned int,
unsigned int, unsigned int,
struct ip_conntrack *, struct ip_conntrack *,
enum ip_conntrack_info) enum ip_conntrack_info,
u32 *seq)
= { [IP_CT_FTP_PORT] = mangle_rfc959_packet, = { [IP_CT_FTP_PORT] = mangle_rfc959_packet,
[IP_CT_FTP_PASV] = mangle_rfc959_packet, [IP_CT_FTP_PASV] = mangle_rfc959_packet,
[IP_CT_FTP_EPRT] = mangle_eprt_packet, [IP_CT_FTP_EPRT] = mangle_eprt_packet,
[IP_CT_FTP_EPSV] = mangle_epsv_packet [IP_CT_FTP_EPSV] = mangle_epsv_packet
}; };
static int ftp_data_fixup(const struct ip_ct_ftp_expect *exp_ftp_info, /* So, this packet has hit the connection tracking matching code.
struct ip_conntrack *ct, Mangle it, and change the expectation to match the new version. */
struct sk_buff **pskb, static unsigned int ip_nat_ftp(struct sk_buff **pskb,
u32 tcp_seq, struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo, enum ip_conntrack_info ctinfo,
struct ip_conntrack_expect *expect) enum ip_ct_ftp_type type,
unsigned int matchoff,
unsigned int matchlen,
struct ip_conntrack_expect *exp,
u32 *seq)
{ {
u_int32_t newip; u_int32_t newip;
u_int16_t port; u_int16_t port;
struct ip_conntrack_tuple newtuple; int dir = CTINFO2DIR(ctinfo);
DEBUGP("FTP_NAT: seq %u + %u in %u\n", DEBUGP("FTP_NAT: type %i, off %u len %u\n", type, matchoff, matchlen);
expect->seq, exp_ftp_info->len, tcp_seq);
/* Change address inside packet to match way we're mapping /* Connection will come from wherever this packet goes, hence !dir */
this connection. */ newip = ct->tuplehash[!dir].tuple.dst.ip;
if (exp_ftp_info->ftptype == IP_CT_FTP_PASV exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port;
|| exp_ftp_info->ftptype == IP_CT_FTP_EPSV) { exp->dir = !dir;
/* PASV/EPSV response: must be where client thinks server
is */
newip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
/* Expect something from client->server */
newtuple.src.ip =
ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
newtuple.dst.ip =
ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
} else {
/* PORT command: must be where server thinks client is */
newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
/* Expect something from server->client */
newtuple.src.ip =
ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
newtuple.dst.ip =
ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
}
newtuple.dst.protonum = IPPROTO_TCP;
newtuple.src.u.tcp.port = expect->tuple.src.u.tcp.port;
/* Try to get same port: if not, try to change it. */ /* When you see the packet, we need to NAT it the same as the
for (port = exp_ftp_info->port; port != 0; port++) { * this one. */
newtuple.dst.u.tcp.port = htons(port); exp->expectfn = ip_nat_follow_master;
if (ip_conntrack_change_expect(expect, &newtuple) == 0) /* Try to get same port: if not, try to change it. */
for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) {
int err;
exp->tuple.dst.u.tcp.port = htons(port);
atomic_inc(&exp->use);
err = ip_conntrack_expect_related(exp, ct);
/* Success, or retransmit. */
if (!err || err == -EEXIST)
break; break;
} }
if (port == 0)
return 0;
if (!mangle[exp_ftp_info->ftptype](pskb, newip, port,
expect->seq - tcp_seq,
exp_ftp_info->len, ct, ctinfo))
return 0;
return 1; if (port == 0) {
} ip_conntrack_expect_put(exp);
return NF_DROP;
static unsigned int help(struct ip_conntrack *ct,
struct ip_conntrack_expect *exp,
struct ip_nat_info *info,
enum ip_conntrack_info ctinfo,
unsigned int hooknum,
struct sk_buff **pskb)
{
struct iphdr *iph = (*pskb)->nh.iph;
struct tcphdr _tcph, *tcph;
unsigned int datalen;
int dir;
struct ip_ct_ftp_expect *exp_ftp_info;
if (!exp)
DEBUGP("ip_nat_ftp: no exp!!");
exp_ftp_info = &exp->help.exp_ftp_info;
/* Only mangle things once: original direction in POST_ROUTING
and reply direction on PRE_ROUTING. */
dir = CTINFO2DIR(ctinfo);
if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL)
|| (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) {
DEBUGP("nat_ftp: Not touching dir %s at hook %s\n",
dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
: hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
: hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???");
return NF_ACCEPT;
} }
/* We passed tcp tracking, plus ftp helper: this must succeed. */ if (!mangle[type](pskb, newip, port, matchoff, matchlen, ct, ctinfo,
tcph = skb_header_pointer(*pskb, iph->ihl * 4, sizeof(_tcph), &_tcph); seq)) {
BUG_ON(!tcph); ip_conntrack_unexpect_related(exp);
datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4;
/* If it's in the right range... */
if (between(exp->seq + exp_ftp_info->len,
ntohl(tcph->seq),
ntohl(tcph->seq) + datalen)) {
if (!ftp_data_fixup(exp_ftp_info, ct, pskb, ntohl(tcph->seq),
ctinfo, exp))
return NF_DROP;
} else {
/* Half a match? This means a partial retransmisison.
It's a cracker being funky. */
if (net_ratelimit()) {
printk("FTP_NAT: partial packet %u/%u in %u/%u\n",
exp->seq, exp_ftp_info->len,
ntohl(tcph->seq),
ntohl(tcph->seq) + datalen);
}
return NF_DROP; return NF_DROP;
} }
return NF_ACCEPT; return NF_ACCEPT;
} }
static struct ip_nat_helper ftp[MAX_PORTS]; static void __exit fini(void)
static char ftp_names[MAX_PORTS][10];
/* Not __exit: called from init() */
static void fini(void)
{ {
int i; ip_nat_ftp_hook = NULL;
/* Make sure noone calls it, meanwhile. */
for (i = 0; i < ports_c; i++) { synchronize_net();
DEBUGP("ip_nat_ftp: unregistering port %d\n", ports[i]);
ip_nat_helper_unregister(&ftp[i]);
}
} }
static int __init init(void) static int __init init(void)
{ {
int i, ret = 0; BUG_ON(ip_nat_ftp_hook);
char *tmpname; ip_nat_ftp_hook = ip_nat_ftp;
return 0;
if (ports_c == 0)
ports[ports_c++] = FTP_PORT;
for (i = 0; i < ports_c; i++) {
ftp[i].tuple.dst.protonum = IPPROTO_TCP;
ftp[i].tuple.src.u.tcp.port = htons(ports[i]);
ftp[i].mask.dst.protonum = 0xFFFF;
ftp[i].mask.src.u.tcp.port = 0xFFFF;
ftp[i].help = help;
ftp[i].me = THIS_MODULE;
ftp[i].flags = 0;
ftp[i].expect = ftp_nat_expected;
tmpname = &ftp_names[i][0];
if (ports[i] == FTP_PORT)
sprintf(tmpname, "ftp");
else
sprintf(tmpname, "ftp-%d", i);
ftp[i].name = tmpname;
DEBUGP("ip_nat_ftp: Trying to register for port %d\n",
ports[i]);
ret = ip_nat_helper_register(&ftp[i]);
if (ret) {
printk("ip_nat_ftp: error registering "
"helper for port %d\n", ports[i]);
fini();
return ret;
}
}
return ret;
} }
NEEDS_CONNTRACK(ftp);
module_init(init); module_init(init);
module_exit(fini); module_exit(fini);
...@@ -196,6 +196,8 @@ ip_nat_mangle_tcp_packet(struct sk_buff **pskb, ...@@ -196,6 +196,8 @@ ip_nat_mangle_tcp_packet(struct sk_buff **pskb,
adjust_tcp_sequence(ntohl(tcph->seq), adjust_tcp_sequence(ntohl(tcph->seq),
(int)rep_len - (int)match_len, (int)rep_len - (int)match_len,
ct, ctinfo); ct, ctinfo);
/* Tell connection tracking about seq change, to expand window */
ip_conntrack_tcp_update(*pskb, ct, CTINFO2DIR(ctinfo));
return 1; return 1;
} }
...@@ -404,6 +406,49 @@ ip_nat_seq_adjust(struct sk_buff **pskb, ...@@ -404,6 +406,49 @@ ip_nat_seq_adjust(struct sk_buff **pskb,
return 1; return 1;
} }
/* We look at the master's nat fields without ip_nat_lock. This works
because the master's NAT must be fully initialized, because we
don't match expectations set up by unconfirmed connections. We
can't grab the lock because we hold the ip_conntrack_lock, and that
would be backwards from other locking orders. */
static void ip_nat_copy_manip(struct ip_nat_info *master,
struct ip_conntrack_expect *exp,
struct ip_conntrack *ct)
{
struct ip_nat_range range;
unsigned int i;
range.flags = IP_NAT_RANGE_MAP_IPS;
/* Find what master is mapped to (if any), so we can do the same. */
for (i = 0; i < master->num_manips; i++) {
if (master->manips[i].direction != exp->dir)
continue;
range.min_ip = range.max_ip = master->manips[i].manip.ip;
/* If this is a DST manip, map port here to where it's
* expected. */
if (master->manips[i].maniptype == IP_NAT_MANIP_DST) {
range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
range.min = range.max = exp->saved_proto;
}
ip_nat_setup_info(ct, &range, master->manips[i].hooknum);
}
}
/* 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 */
void ip_nat_follow_master(struct ip_conntrack *ct)
{
struct ip_nat_info *master = &ct->master->expectant->nat.info;
/* This must be a fresh one. */
BUG_ON(ct->nat.info.initialized);
ip_nat_copy_manip(master, ct->master, ct);
}
static inline int static inline int
helper_cmp(const struct ip_nat_helper *helper, helper_cmp(const struct ip_nat_helper *helper,
const struct ip_conntrack_tuple *tuple) const struct ip_conntrack_tuple *tuple)
......
...@@ -55,15 +55,6 @@ ...@@ -55,15 +55,6 @@
: ((hooknum) == NF_IP_LOCAL_IN ? "LOCAL_IN" \ : ((hooknum) == NF_IP_LOCAL_IN ? "LOCAL_IN" \
: "*ERROR*"))) : "*ERROR*")))
static inline int call_expect(struct ip_conntrack *master,
struct sk_buff **pskb,
unsigned int hooknum,
struct ip_conntrack *ct,
struct ip_nat_info *info)
{
return master->nat.info.helper->expect(pskb, hooknum, ct, info);
}
static unsigned int static unsigned int
ip_nat_fn(unsigned int hooknum, ip_nat_fn(unsigned int hooknum,
struct sk_buff **pskb, struct sk_buff **pskb,
...@@ -131,21 +122,13 @@ ip_nat_fn(unsigned int hooknum, ...@@ -131,21 +122,13 @@ ip_nat_fn(unsigned int hooknum,
if (!(info->initialized & (1 << maniptype))) { if (!(info->initialized & (1 << maniptype))) {
unsigned int ret; unsigned int ret;
if (ct->master /* LOCAL_IN hook doesn't have a chain! */
&& master_ct(ct)->nat.info.helper if (hooknum == NF_IP_LOCAL_IN)
&& master_ct(ct)->nat.info.helper->expect) { ret = alloc_null_binding(ct, info, hooknum);
ret = call_expect(master_ct(ct), pskb, else
hooknum, ct, info); ret = ip_nat_rule_find(pskb, hooknum,
} else { in, out, ct,
/* LOCAL_IN hook doesn't have a chain! */ info);
if (hooknum == NF_IP_LOCAL_IN)
ret = alloc_null_binding(ct, info,
hooknum);
else
ret = ip_nat_rule_find(pskb, hooknum,
in, out, ct,
info);
}
if (ret != NF_ACCEPT) { if (ret != NF_ACCEPT) {
WRITE_UNLOCK(&ip_nat_lock); WRITE_UNLOCK(&ip_nat_lock);
...@@ -396,4 +379,5 @@ EXPORT_SYMBOL(ip_nat_mangle_udp_packet); ...@@ -396,4 +379,5 @@ EXPORT_SYMBOL(ip_nat_mangle_udp_packet);
EXPORT_SYMBOL(ip_nat_used_tuple); EXPORT_SYMBOL(ip_nat_used_tuple);
EXPORT_SYMBOL(ip_nat_find_helper); EXPORT_SYMBOL(ip_nat_find_helper);
EXPORT_SYMBOL(__ip_nat_find_helper); EXPORT_SYMBOL(__ip_nat_find_helper);
EXPORT_SYMBOL(ip_nat_follow_master);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
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