#define DEBUGP(format, args...)
#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 */
-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_multi_range mr;
- 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));
-
- mr.rangesize = 1;
- /* We don't want to manip the per-protocol, just the IPs... */
- mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
- mr.range[0].min_ip = mr.range[0].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) {
- mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
- mr.range[0].min = mr.range[0].max
- = ((union ip_conntrack_manip_proto)
- { .tcp = { htons(exp_ftp_info->port) } });
- }
- return ip_nat_setup_info(ct, &mr, hooknum);
-}
-
static int
mangle_rfc959_packet(struct sk_buff **pskb,
- u_int32_t newip,
+ __be32 newip,
u_int16_t port,
unsigned int matchoff,
unsigned int matchlen,
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")];
DEBUGP("calling ip_nat_mangle_tcp_packet\n");
+ *seq += strlen(buffer) - matchlen;
return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff,
matchlen, buffer, strlen(buffer));
}
/* |1|132.235.1.2|6275| */
static int
mangle_eprt_packet(struct sk_buff **pskb,
- u_int32_t newip,
+ __be32 newip,
u_int16_t port,
unsigned int matchoff,
unsigned int matchlen,
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|")];
DEBUGP("calling ip_nat_mangle_tcp_packet\n");
+ *seq += strlen(buffer) - matchlen;
return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff,
matchlen, buffer, strlen(buffer));
}
/* |1|132.235.1.2|6275| */
static int
mangle_epsv_packet(struct sk_buff **pskb,
- u_int32_t newip,
+ __be32 newip,
u_int16_t port,
unsigned int matchoff,
unsigned int matchlen,
struct ip_conntrack *ct,
- enum ip_conntrack_info ctinfo)
+ enum ip_conntrack_info ctinfo,
+ u32 *seq)
{
char buffer[sizeof("|||65535|")];
DEBUGP("calling ip_nat_mangle_tcp_packet\n");
+ *seq += strlen(buffer) - matchlen;
return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff,
matchlen, buffer, strlen(buffer));
}
-static int (*mangle[])(struct sk_buff **, u_int32_t, u_int16_t,
+static int (*mangle[])(struct sk_buff **, __be32, u_int16_t,
unsigned int,
unsigned int,
struct ip_conntrack *,
- enum ip_conntrack_info)
+ enum ip_conntrack_info,
+ u32 *seq)
= { [IP_CT_FTP_PORT] = mangle_rfc959_packet,
[IP_CT_FTP_PASV] = mangle_rfc959_packet,
[IP_CT_FTP_EPRT] = mangle_eprt_packet,
[IP_CT_FTP_EPSV] = mangle_epsv_packet
};
-static int ftp_data_fixup(const struct ip_ct_ftp_expect *exp_ftp_info,
- struct ip_conntrack *ct,
- struct sk_buff **pskb,
- enum ip_conntrack_info ctinfo,
- struct ip_conntrack_expect *expect)
+/* So, this packet has hit the connection tracking matching code.
+ Mangle it, and change the expectation to match the new version. */
+static unsigned int ip_nat_ftp(struct sk_buff **pskb,
+ enum ip_conntrack_info ctinfo,
+ enum ip_ct_ftp_type type,
+ unsigned int matchoff,
+ unsigned int matchlen,
+ struct ip_conntrack_expect *exp,
+ u32 *seq)
{
- u_int32_t newip;
- struct iphdr *iph = (*pskb)->nh.iph;
- struct tcphdr *tcph = (void *)iph + iph->ihl*4;
+ __be32 newip;
u_int16_t port;
- struct ip_conntrack_tuple newtuple;
+ int dir = CTINFO2DIR(ctinfo);
+ struct ip_conntrack *ct = exp->master;
- DEBUGP("FTP_NAT: seq %u + %u in %u\n",
- expect->seq, exp_ftp_info->len,
- ntohl(tcph->seq));
+ DEBUGP("FTP_NAT: type %i, off %u len %u\n", type, matchoff, matchlen);
- /* Change address inside packet to match way we're mapping
- this connection. */
- if (exp_ftp_info->ftptype == IP_CT_FTP_PASV
- || exp_ftp_info->ftptype == IP_CT_FTP_EPSV) {
- /* 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;
+ /* Connection will come from wherever this packet goes, hence !dir */
+ newip = ct->tuplehash[!dir].tuple.dst.ip;
+ exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port;
+ exp->dir = !dir;
- /* Try to get same port: if not, try to change it. */
- for (port = exp_ftp_info->port; port != 0; port++) {
- newtuple.dst.u.tcp.port = htons(port);
+ /* When you see the packet, we need to NAT it the same as the
+ * this one. */
+ 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++) {
+ exp->tuple.dst.u.tcp.port = htons(port);
+ if (ip_conntrack_expect_related(exp) == 0)
break;
}
- if (port == 0)
- return 0;
- if (!mangle[exp_ftp_info->ftptype](pskb, newip, port,
- expect->seq - ntohl(tcph->seq),
- exp_ftp_info->len, ct, ctinfo))
- return 0;
-
- return 1;
-}
-
-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 = (void *)iph + iph->ihl*4;
- 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;
- }
+ if (port == 0)
+ return NF_DROP;
- 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, 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);
- }
+ if (!mangle[type](pskb, newip, port, matchoff, matchlen, ct, ctinfo,
+ seq)) {
+ ip_conntrack_unexpect_related(exp);
return NF_DROP;
}
return NF_ACCEPT;
}
-static struct ip_nat_helper ftp[MAX_PORTS];
-static char ftp_names[MAX_PORTS][10];
-
-/* Not __exit: called from init() */
-static void fini(void)
+static void __exit ip_nat_ftp_fini(void)
{
- int i;
-
- for (i = 0; i < ports_c; i++) {
- DEBUGP("ip_nat_ftp: unregistering port %d\n", ports[i]);
- ip_nat_helper_unregister(&ftp[i]);
- }
+ rcu_assign_pointer(ip_nat_ftp_hook, NULL);
+ synchronize_rcu();
}
-static int __init init(void)
+static int __init ip_nat_ftp_init(void)
{
- int i, ret = 0;
- char *tmpname;
-
- 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;
+ BUG_ON(rcu_dereference(ip_nat_ftp_hook));
+ rcu_assign_pointer(ip_nat_ftp_hook, ip_nat_ftp);
+ return 0;
}
-NEEDS_CONNTRACK(ftp);
+/* Prior to 2.6.11, we had a ports param. No longer, but don't break users. */
+static int warn_set(const char *val, struct kernel_param *kp)
+{
+ printk(KERN_INFO KBUILD_MODNAME
+ ": kernel >= 2.6.10 only uses 'ports' for conntrack modules\n");
+ return 0;
+}
+module_param_call(ports, warn_set, NULL, NULL, 0);
-module_init(init);
-module_exit(fini);
+module_init(ip_nat_ftp_init);
+module_exit(ip_nat_ftp_fini);