Could netfilter support TFTP NAT?
Daniel Shane
daniel.shane@eicon.com
Wed, 9 Jan 2002 08:25:13 -0500
Thanks Magnus!
I'll look into the "bug" since I will not have to write the code =
anymore it
seems (snif...).
Regards,
Daniel shane
> -----Original Message-----
> From: Magnus Boden [mailto:mb@ozaba.mine.nu]
> Sent: Wednesday, January 09, 2002 2:37 AM
> To: Daniel Shane
> Cc: 'Sneppe Filip'; netfilter-devel@lists.samba.org
> Subject: RE: Could netfilter support TFTP NAT?
>=20
>=20
> Hello,
>=20
> I have already been hacking on a tftp helper that seems to work fine.
>=20
> There are one problem with this patch and that is that when=20
> the conntrack
> helper is run on the first packet the nat manipulations=20
> hasn't been done
> yet and so the first tftp request will fail but the second=20
> tftp request
> will work.
>=20
> I am looking into why this is so but since I am not kernel=20
> guru it will
> probably take a while.
>=20
> Read my mail on netfilter-devel "udp conntrack / nat module
> problem..." on=A0Fri, 4 Jan 2002 02:57:29 +0100(CET) for more=20
> information
> about the problem.
>=20
> Best Regards
> Magnus
>=20
> diff -Nru -X dontdiff=20
> linux-2.4.17.orig/Documentation/Configure.help=20
> linux-2.4.17/Documentation/Configure.help
> --- linux-2.4.17.orig/Documentation/Configure.help Sun Dec=20
> 23 01:14:24 2001
> +++ linux-2.4.17/Documentation/Configure.help Thu Jan 3 23:09:38 =
2002
> @@ -2390,6 +2390,16 @@
> If you want to compile it as a module, say M here and read
> <file:Documentation/modules.txt>. If unsure, say `Y'.
> =20
> +TFTP protocol support
> +CONFIG_IP_NF_TFTP
> + TFTP connection tracking helper, this is required depending
> + on how restrictive your ruleset is.
> + If you are using a tftp client behind -j SNAT or -j MASQUERADING
> + you will need this.
> +
> + If you want to compile it as a module, say M here and read
> + Documentation/modules.txt. If unsure, say `Y'.
> +
> User space queueing via NETLINK
> CONFIG_IP_NF_QUEUE
> Netfilter has the ability to queue packets to user space: the
> diff -Nru -X dontdiff=20
> linux-2.4.17.orig/net/ipv4/netfilter/Config.in=20
> linux-2.4.17/net/ipv4/netfilter/Config.in
> --- linux-2.4.17.orig/net/ipv4/netfilter/Config.in Fri Dec=20
> 21 18:42:05 2001
> +++ linux-2.4.17/net/ipv4/netfilter/Config.in Sat Dec 22 23:33:35 =
2001
> @@ -7,6 +7,7 @@
> tristate 'Connection tracking (required for masq/NAT)'=20
> CONFIG_IP_NF_CONNTRACK
> if [ "$CONFIG_IP_NF_CONNTRACK" !=3D "n" ]; then
> dep_tristate ' FTP protocol support' CONFIG_IP_NF_FTP=20
> $CONFIG_IP_NF_CONNTRACK
> + dep_tristate ' TFTP protocol support' CONFIG_IP_NF_TFTP=20
> $CONFIG_IP_NF_CONNTRACK
> dep_tristate ' IRC protocol support' CONFIG_IP_NF_IRC=20
> $CONFIG_IP_NF_CONNTRACK
> fi
> =20
> @@ -63,6 +64,13 @@
> else
> if [ "$CONFIG_IP_NF_FTP" =3D "y" ]; then
> define_tristate CONFIG_IP_NF_NAT_FTP $CONFIG_IP_NF_NAT
> + fi
> + fi
> + if [ "$CONFIG_IP_NF_TFTP" =3D "m" ]; then
> + define_tristate CONFIG_IP_NF_NAT_TFTP m
> + else
> + if [ "$CONFIG_IP_NF_TFTP" =3D "y" ]; then
> + define_tristate CONFIG_IP_NF_NAT_TFTP $CONFIG_IP_NF_NAT
> fi
> fi
> fi
> diff -Nru -X dontdiff=20
> linux-2.4.17.orig/net/ipv4/netfilter/Makefile=20
> linux-2.4.17/net/ipv4/netfilter/Makefile
> --- linux-2.4.17.orig/net/ipv4/netfilter/Makefile Wed Oct=20
> 31 00:08:12 2001
> +++ linux-2.4.17/net/ipv4/netfilter/Makefile Sat Dec 22 23:32:15 2001
> @@ -9,7 +9,7 @@
> =20
> O_TARGET :=3D netfilter.o
> =20
> -export-objs =3D ip_conntrack_standalone.o ip_conntrack_ftp.o=20
> ip_fw_compat.o ip_nat_standalone.o ip_tables.o
> +export-objs =3D ip_conntrack_standalone.o ip_conntrack_ftp.o=20
> ip_conntrack_tftp.o ip_fw_compat.o ip_nat_standalone.o ip_tables.o
> =20
> # Multipart objects.
> list-multi :=3D ip_conntrack.o iptable_nat.o=20
> ipfwadm.o ipchains.o
> @@ -37,9 +37,11 @@
> =20
> # connection tracking helpers
> obj-$(CONFIG_IP_NF_FTP) +=3D ip_conntrack_ftp.o
> +obj-$(CONFIG_IP_NF_TFTP) +=3D ip_conntrack_tftp.o
> =20
> # NAT helpers=20
> obj-$(CONFIG_IP_NF_NAT_FTP) +=3D ip_nat_ftp.o
> +obj-$(CONFIG_IP_NF_NAT_TFTP) +=3D ip_nat_tftp.o
> =20
> # generic IP tables=20
> obj-$(CONFIG_IP_NF_IPTABLES) +=3D ip_tables.o
> diff -Nru -X dontdiff=20
> linux-2.4.17.orig/net/ipv4/netfilter/ip_conntrack_tftp.c=20
> linux-2.4.17/net/ipv4/netfilter/ip_conntrack_tftp.c
> --- linux-2.4.17.orig/net/ipv4/netfilter/ip_conntrack_tftp.c=09
> Thu Jan 1 01:00:00 1970
> +++ linux-2.4.17/net/ipv4/netfilter/ip_conntrack_tftp.c=09
> Fri Jan 4 01:35:16 2002
> @@ -0,0 +1,127 @@
> +/*
> + * Licensed under GNU GPL version 2 Copyright Magnus Boden=20
> <mb@ozaba.mine.nu>
> + * Version: 0.0.6
> + */
> +#include <linux/module.h>
> +#include <linux/ip.h>
> +#include <linux/udp.h>
> +
> +#include <linux/netfilter.h>
> +#include <linux/netfilter_ipv4/ip_tables.h>
> +#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
> +
> +#define TFTP_PORT 69
> +#define OPCODE_READ 1
> +#define OPCODE_WRITE 2
> +
> +MODULE_AUTHOR("Magnus Boden <magbo@home.se>");
> +MODULE_DESCRIPTION("Netfilter connection tracking module for tftp");
> +MODULE_LICENSE("GPL");
> +
> +struct module *ip_conntrack_tftp =3D THIS_MODULE;
> +
> +#define MAX_PORTS 8
> +static int ip_ct_tftp_ports[MAX_PORTS];
> +static int ports[MAX_PORTS];
> +#ifdef MODULE_PARM
> +MODULE_PARM(ports,"1-" __MODULE_STRING(MAX_PORTS) "i");
> +MODULE_PARM_DESC(ports, "port numbers of tftp servers");
> +#endif
> +
> +#if 1
> +#define DEBUGP printk
> +#else
> +#define DEBUGP(format, args...)
> +#endif
> +
> +struct tftphdr {
> + u_int16_t opcode;
> +};
> +
> +static int tftp_help(const struct iphdr *iph, size_t len,
> + struct ip_conntrack *ct,
> + enum ip_conntrack_info ctinfo)
> +{
> + struct udphdr *udph =3D (void *)iph + iph->ihl * 4;
> + struct tftphdr *tftph =3D (void *)udph + 8;
> + struct ip_conntrack_tuple t, m;
> +
> + switch(ntohs(tftph->opcode)) {
> + /* RRQ and WRQ works the same way */
> + case OPCODE_READ:
> + case OPCODE_WRITE:
> + DEBUGP("<1> ip_ct_tftp: %u.%u.%u.%u:%u=20
> <-> %u.%u.%u.%u:%u - %u.%u.%u.%u:%u <-> %u.%u.%u.%u:%u\n",
> + =09
> NIPQUAD(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip),
> + =09
> ntohs(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port),
> + =09
> NIPQUAD(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip),
> + =09
> ntohs(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.udp.port),
> + =09
> NIPQUAD(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip),
> + =09
> ntohs(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.udp.port),
> + =09
> NIPQUAD(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip),
> + =09
> ntohs(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port));
> + memset(&t, 0, sizeof(struct=20
> ip_conntrack_tuple));
> + memset(&m, 0, sizeof(struct=20
> ip_conntrack_tuple));
> + t.dst.protonum =3D IPPROTO_UDP;
> + t.src.ip =3D=20
> ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
> + t.dst.ip =3D=20
> ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
> + t.dst.u.udp.port =3D=20
> ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port;
> + m.dst.protonum =3D 0xFFFF;
> + m.src.ip =3D 0xFFFF;
> + m.dst.ip =3D 0xFFFF;
> + m.dst.u.udp.port =3D 0xFFFF;
> + ip_conntrack_expect_related(ct, &t, &m, NULL);
> + break;
> + default:
> + DEBUGP("<1>Unknown opcode\n");
> + }
> + return(NF_ACCEPT);
> +}
> +
> +static struct ip_conntrack_helper tftp[MAX_PORTS];
> +
> +static void fini(void)
> +{
> + int i;
> +
> + for(i =3D 0 ; (i < MAX_PORTS) && ports[i] ; i++) {
> + DEBUGP("<1> Unregistering ip_conntrack_tftp for=20
> port %d\n",
> + ports[i]);
> + ip_conntrack_helper_unregister(&tftp[i]);
> + }=20
> +}
> +
> +static int __init init(void)
> +{
> + int i, ret;
> +
> + if(!ports[0])
> + ports[0]=3DTFTP_PORT;
> +
> + for(i =3D 0 ; (i < MAX_PORTS) && ports[i] ; i++) {
> + /* Copy ports for ip_nat_tftp.o */
> + ip_ct_tftp_ports[i]=3Dports[i];
> +
> + /* Create helper structure */
> + memset(&tftp[i], 0, sizeof(struct ip_conntrack_helper));
> + tftp[i].tuple.dst.protonum =3D IPPROTO_UDP;
> + tftp[i].tuple.src.u.udp.port =3D htons(ports[i]);
> + tftp[i].mask.dst.protonum =3D 0xFFFF;
> + tftp[i].mask.src.u.udp.port =3D 0xFFFF;
> + tftp[i].help =3D tftp_help;
> + DEBUGP("<1> Registering ip_conntrack_tftp.o for=20
> port %d\n",
> + ports[i]);
> + ret=3Dip_conntrack_helper_register(&tftp[i]);
> + if(ret) {
> + fini();
> + return(ret);
> + }
> + }
> +
> + return(0);
> +}
> +
> +EXPORT_SYMBOL(ip_conntrack_tftp);
> +EXPORT_SYMBOL(ip_ct_tftp_ports);
> +
> +module_init(init);
> +module_exit(fini);
> diff -Nru -X dontdiff=20
> linux-2.4.17.orig/net/ipv4/netfilter/ip_nat_tftp.c=20
> linux-2.4.17/net/ipv4/netfilter/ip_nat_tftp.c
> --- linux-2.4.17.orig/net/ipv4/netfilter/ip_nat_tftp.c=09
> Thu Jan 1 01:00:00 1970
> +++ linux-2.4.17/net/ipv4/netfilter/ip_nat_tftp.c Fri Jan=20
> 4 02:21:31 2002
> @@ -0,0 +1,109 @@
> +/*
> + * Licensed under GNU GPL version 2 Copyright Magnus Boden=20
> <mb@ozaba.mine.nu>
> + * Version: 0.0.6
> + * This module currently supports DNAT:
> + * iptables -t nat -A PREROUTING -d x.x.x.x -j DNAT --to-dest =
x.x.x.y
> + *
> + * and SNAT:
> + * iptables -t nat -A POSTROUTING { -j MASQUERADE , -j SNAT=20
> --to-source x.x.x.x }
> + *
> + * It has not been tested with
> + * -j SNAT --to-source x.x.x.x-x.x.x.y since I only have one=20
> external ip
> + * If you do test this please let me know if it works or not.
> + */
> +#include <linux/module.h>
> +#include <linux/netfilter_ipv4.h>
> +#include <linux/ip.h>
> +#include <linux/udp.h>
> +
> +#include <linux/netfilter.h>
> +#include <linux/netfilter_ipv4/ip_tables.h>
> +#include <linux/netfilter_ipv4/ip_nat.h>
> +#include <linux/netfilter_ipv4/ip_nat_helper.h>
> +#include <linux/netfilter_ipv4/ip_nat_rule.h>
> +#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
> +
> +#define MAX_PORTS 8
> +
> +MODULE_AUTHOR("Magnus Boden <magbo@home.se>");
> +MODULE_DESCRIPTION("Netfilter NAT helper for tftp");
> +MODULE_LICENSE("GPL");
> +
> +#if 1
> +#define DEBUGP printk
> +#else
> +#define DEBUGP(format, args...)
> +#endif
> +
> +extern int ip_ct_tftp_ports[MAX_PORTS];
> +
> +static int tftp_nat_expected(struct sk_buff **pskb,
> + unsigned int hooknum,
> + struct ip_conntrack *ct,
> + struct ip_nat_info *info,
> + struct ip_conntrack *master,
> + struct ip_nat_info *masterinfo,
> + unsigned int *verdict)
> +{
> + struct ip_nat_multi_range mr;
> + int i;
> + u_int8_t is_tftp=3D0;
> + struct iphdr *iph =3D (*pskb)->nh.iph;
> + struct udphdr *udph =3D (void *)iph + iph->ihl*4;
> +
> + /* Check if master is tftp, The check is only
> + if the helper is registered for that port */
> + for(i =3D 0; i<MAX_PORTS && ip_ct_tftp_ports[i]; i++) {
> + =09
> if(master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.udp.port=20
> =3D=3D htons(ip_ct_tftp_ports[i]))
> + is_tftp=3D1;
> + }
> + if(!is_tftp)
> + return(0);
> +
> + mr.rangesize =3D 1;
> + mr.range[0].flags =3D IP_NAT_RANGE_MAP_IPS;
> +
> + if(HOOK2MANIP(hooknum) =3D=3D IP_NAT_MANIP_SRC){
> + mr.range[0].min_ip =3D mr.range[0].max_ip =3D=20
> master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
> +
> + DEBUGP("<1> ip_ct_tftp - orig: %u.%u.%u.%u:%u=20
> <-> %u.%u.%u.%u:%u newsrc: %u.%u.%u.%u\n",
> + NIPQUAD((*pskb)->nh.iph->saddr),
> + ntohs(udph->source),
> + NIPQUAD((*pskb)->nh.iph->daddr),
> + ntohs(udph->dest),
> + =20
> NIPQUAD(master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip));
> + }else{
> + mr.range[0].min_ip =3D mr.range[0].max_ip =3D=20
> master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
> + mr.range[0].min.udp.port =3D=20
> mr.range[0].max.udp.port =3D=20
> master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port;
> + mr.range[0].flags |=3D IP_NAT_RANGE_PROTO_SPECIFIED;
> +
> + DEBUGP("<1> ip_ct_tftp - orig: %u.%u.%u.%u:%u
> <-> %u.%u.%u.%u:%u newdst: %u.%u.%u.%u:%u\n",
> + NIPQUAD((*pskb)->nh.iph->saddr),
> + ntohs(udph->source),
> + NIPQUAD((*pskb)->nh.iph->daddr),
> + ntohs(udph->dest),
> + =20
> NIPQUAD(master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip),
> + =09
> ntohs(master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port));
> + }
> +
> + *verdict =3D ip_nat_setup_info(ct,&mr,hooknum);
> +
> + return(NF_ACCEPT);
> +}
> +
> +static struct ip_nat_expect tftp_expect =3D {
> + { NULL, NULL },
> + tftp_nat_expected };
> +
> +static void fini(void)
> +{
> + ip_nat_expect_unregister(&tftp_expect);
> +}
> +
> +static int __init init(void)
> +{
> + return(ip_nat_expect_register(&tftp_expect));
> +}
> +
> +module_init(init);
> +module_exit(fini);
>=20
> On Tue, 8 Jan 2002, Daniel Shane wrote:
>=20
> > Thanks!
> > =A0
> > I read the code, but didnt think about that. I'll try to=20
> make a dummy module
> > and see if I can intercept the initial packet.
> > =A0
> > Daniel Shane.
> >=20
> > -----Original Message-----
> > From: Sneppe Filip [mailto:Filip.Sneppe@cronos.be]
> > Sent: Tuesday, January 08, 2002 6:29 PM
> > To: netfilter-devel@lists.samba.org
> > Subject: RE: Could netfilter support TFTP NAT?
> >=20
> >=20
> >=20
> > Hi Daniel,
> >=20
> > >
> >=20
> >=20
> > >In the HOWTO, it is stated that the helper module will
> >=20
> > >only kick in once the
> > protocol is in a ESTABLISHED state.
> >=20
> >=20
> >=20
> > Well you could more or less get this impression
> > from
> >=20
> > reading the howto, but I don't know if this is really
> >=20
> > required.
> >=20
> >=20
> >=20
> > If you look at the ftp conntrack code (and irc conntrack
> >=20
> > code too), in net/ipv4/netfilter/ip_conntrack_ftp.c
> >=20
> > on line 250, is says:
> >=20
> >=20
> >=20
> > /* Until there's been traffic both ways, don't look in packets. */
> >=20
> > if (ctinfo !=3D IP_CT_ESTABLISHED
> >=20
> > =A0=A0=A0 && ctinfo !=3D IP_CT_ESTABLISHED + IP_CT_IS_REPLY) {
> >=20
> > =A0=A0=A0=A0=A0 DEBUGP("Conntrackinfo =3D %u\n", ctinfo);
> >=20
> > =A0=A0=A0=A0=A0 return NF_ACCEPT;
> >=20
> > =A0}
> >=20
> >=20
> >=20
> > Some thing on line 128 of the irc conntracker.
> >=20
> >=20
> >=20
> > So I think you should be able to get enough info
> >=20
> > from just one UDP packet to port 68 to build your expected
> >=20
> > connection by *not* using the extra test in all normal
> >=20
> > contrackers.
> >=20
> >=20
> >=20
> > I'm no netfilter guru, so don't take my word for it. It's
> >=20
> > just what I understood from the code when I made an attempt
> >=20
> > at writing a quake3 conntracker :-)
> >=20
> >=20
> >=20
> > Regards,
> >=20
> > Filip
> >=20
> >=20
> >=20
> >=20
> >=20
> >=20
> >=20
> >=20
> >=20
>=20