Merge branch 'master' of git://1984.lsi.us.es/nf-next
This commit is contained in:
@@ -523,6 +523,10 @@ found:
|
||||
if (offset == 0)
|
||||
qp->q.last_in |= INET_FRAG_FIRST_IN;
|
||||
|
||||
if (ip_hdr(skb)->frag_off & htons(IP_DF) &&
|
||||
skb->len + ihl > qp->q.max_size)
|
||||
qp->q.max_size = skb->len + ihl;
|
||||
|
||||
if (qp->q.last_in == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
|
||||
qp->q.meat == qp->q.len)
|
||||
return ip_frag_reasm(qp, prev, dev);
|
||||
@@ -646,9 +650,11 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
|
||||
head->next = NULL;
|
||||
head->dev = dev;
|
||||
head->tstamp = qp->q.stamp;
|
||||
IPCB(head)->frag_max_size = qp->q.max_size;
|
||||
|
||||
iph = ip_hdr(head);
|
||||
iph->frag_off = 0;
|
||||
/* max_size != 0 implies at least one fragment had IP_DF set */
|
||||
iph->frag_off = qp->q.max_size ? htons(IP_DF) : 0;
|
||||
iph->tot_len = htons(len);
|
||||
iph->tos |= ecn;
|
||||
IP_INC_STATS_BH(net, IPSTATS_MIB_REASMOKS);
|
||||
|
@@ -467,7 +467,9 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
|
||||
|
||||
iph = ip_hdr(skb);
|
||||
|
||||
if (unlikely((iph->frag_off & htons(IP_DF)) && !skb->local_df)) {
|
||||
if (unlikely(((iph->frag_off & htons(IP_DF)) && !skb->local_df) ||
|
||||
(IPCB(skb)->frag_max_size &&
|
||||
IPCB(skb)->frag_max_size > dst_mtu(&rt->dst)))) {
|
||||
IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
|
||||
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
|
||||
htonl(ip_skb_dst_mtu(skb)));
|
||||
|
@@ -72,43 +72,6 @@ int ip_route_me_harder(struct sk_buff *skb, unsigned int addr_type)
|
||||
}
|
||||
EXPORT_SYMBOL(ip_route_me_harder);
|
||||
|
||||
#ifdef CONFIG_XFRM
|
||||
int ip_xfrm_me_harder(struct sk_buff *skb)
|
||||
{
|
||||
struct flowi fl;
|
||||
unsigned int hh_len;
|
||||
struct dst_entry *dst;
|
||||
|
||||
if (IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED)
|
||||
return 0;
|
||||
if (xfrm_decode_session(skb, &fl, AF_INET) < 0)
|
||||
return -1;
|
||||
|
||||
dst = skb_dst(skb);
|
||||
if (dst->xfrm)
|
||||
dst = ((struct xfrm_dst *)dst)->route;
|
||||
dst_hold(dst);
|
||||
|
||||
dst = xfrm_lookup(dev_net(dst->dev), dst, &fl, skb->sk, 0);
|
||||
if (IS_ERR(dst))
|
||||
return -1;
|
||||
|
||||
skb_dst_drop(skb);
|
||||
skb_dst_set(skb, dst);
|
||||
|
||||
/* Change in oif may mean change in hh_len. */
|
||||
hh_len = skb_dst(skb)->dev->hard_header_len;
|
||||
if (skb_headroom(skb) < hh_len &&
|
||||
pskb_expand_head(skb, hh_len - skb_headroom(skb), 0, GFP_ATOMIC))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ip_xfrm_me_harder);
|
||||
#endif
|
||||
|
||||
void (*ip_nat_decode_session)(struct sk_buff *, struct flowi *);
|
||||
EXPORT_SYMBOL(ip_nat_decode_session);
|
||||
|
||||
/*
|
||||
* Extra routing may needed on local out, as the QUEUE target never
|
||||
* returns control to the table.
|
||||
@@ -225,12 +188,12 @@ static const struct nf_afinfo nf_ip_afinfo = {
|
||||
.route_key_size = sizeof(struct ip_rt_info),
|
||||
};
|
||||
|
||||
static int ipv4_netfilter_init(void)
|
||||
static int __init ipv4_netfilter_init(void)
|
||||
{
|
||||
return nf_register_afinfo(&nf_ip_afinfo);
|
||||
}
|
||||
|
||||
static void ipv4_netfilter_fini(void)
|
||||
static void __exit ipv4_netfilter_fini(void)
|
||||
{
|
||||
nf_unregister_afinfo(&nf_ip_afinfo);
|
||||
}
|
||||
|
@@ -143,25 +143,22 @@ config IP_NF_TARGET_ULOG
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
# NAT + specific targets: nf_conntrack
|
||||
config NF_NAT
|
||||
tristate "Full NAT"
|
||||
config NF_NAT_IPV4
|
||||
tristate "IPv4 NAT"
|
||||
depends on NF_CONNTRACK_IPV4
|
||||
default m if NETFILTER_ADVANCED=n
|
||||
select NF_NAT
|
||||
help
|
||||
The Full NAT option allows masquerading, port forwarding and other
|
||||
The IPv4 NAT option allows masquerading, port forwarding and other
|
||||
forms of full Network Address Port Translation. It is controlled by
|
||||
the `nat' table in iptables: see the man page for iptables(8).
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NF_NAT_NEEDED
|
||||
bool
|
||||
depends on NF_NAT
|
||||
default y
|
||||
if NF_NAT_IPV4
|
||||
|
||||
config IP_NF_TARGET_MASQUERADE
|
||||
tristate "MASQUERADE target support"
|
||||
depends on NF_NAT
|
||||
default m if NETFILTER_ADVANCED=n
|
||||
help
|
||||
Masquerading is a special case of NAT: all outgoing connections are
|
||||
@@ -174,7 +171,6 @@ config IP_NF_TARGET_MASQUERADE
|
||||
|
||||
config IP_NF_TARGET_NETMAP
|
||||
tristate "NETMAP target support"
|
||||
depends on NF_NAT
|
||||
depends on NETFILTER_ADVANCED
|
||||
help
|
||||
NETMAP is an implementation of static 1:1 NAT mapping of network
|
||||
@@ -185,7 +181,6 @@ config IP_NF_TARGET_NETMAP
|
||||
|
||||
config IP_NF_TARGET_REDIRECT
|
||||
tristate "REDIRECT target support"
|
||||
depends on NF_NAT
|
||||
depends on NETFILTER_ADVANCED
|
||||
help
|
||||
REDIRECT is a special case of NAT: all incoming connections are
|
||||
@@ -195,9 +190,11 @@ config IP_NF_TARGET_REDIRECT
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
endif
|
||||
|
||||
config NF_NAT_SNMP_BASIC
|
||||
tristate "Basic SNMP-ALG support"
|
||||
depends on NF_CONNTRACK_SNMP && NF_NAT
|
||||
depends on NF_CONNTRACK_SNMP && NF_NAT_IPV4
|
||||
depends on NETFILTER_ADVANCED
|
||||
default NF_NAT && NF_CONNTRACK_SNMP
|
||||
---help---
|
||||
@@ -219,61 +216,21 @@ config NF_NAT_SNMP_BASIC
|
||||
# <expr> '&&' <expr> (6)
|
||||
#
|
||||
# (6) Returns the result of min(/expr/, /expr/).
|
||||
config NF_NAT_PROTO_DCCP
|
||||
tristate
|
||||
depends on NF_NAT && NF_CT_PROTO_DCCP
|
||||
default NF_NAT && NF_CT_PROTO_DCCP
|
||||
|
||||
config NF_NAT_PROTO_GRE
|
||||
tristate
|
||||
depends on NF_NAT && NF_CT_PROTO_GRE
|
||||
|
||||
config NF_NAT_PROTO_UDPLITE
|
||||
tristate
|
||||
depends on NF_NAT && NF_CT_PROTO_UDPLITE
|
||||
default NF_NAT && NF_CT_PROTO_UDPLITE
|
||||
|
||||
config NF_NAT_PROTO_SCTP
|
||||
tristate
|
||||
default NF_NAT && NF_CT_PROTO_SCTP
|
||||
depends on NF_NAT && NF_CT_PROTO_SCTP
|
||||
select LIBCRC32C
|
||||
|
||||
config NF_NAT_FTP
|
||||
tristate
|
||||
depends on NF_CONNTRACK && NF_NAT
|
||||
default NF_NAT && NF_CONNTRACK_FTP
|
||||
|
||||
config NF_NAT_IRC
|
||||
tristate
|
||||
depends on NF_CONNTRACK && NF_NAT
|
||||
default NF_NAT && NF_CONNTRACK_IRC
|
||||
|
||||
config NF_NAT_TFTP
|
||||
tristate
|
||||
depends on NF_CONNTRACK && NF_NAT
|
||||
default NF_NAT && NF_CONNTRACK_TFTP
|
||||
|
||||
config NF_NAT_AMANDA
|
||||
tristate
|
||||
depends on NF_CONNTRACK && NF_NAT
|
||||
default NF_NAT && NF_CONNTRACK_AMANDA
|
||||
depends on NF_NAT_IPV4 && NF_CT_PROTO_GRE
|
||||
|
||||
config NF_NAT_PPTP
|
||||
tristate
|
||||
depends on NF_CONNTRACK && NF_NAT
|
||||
default NF_NAT && NF_CONNTRACK_PPTP
|
||||
depends on NF_CONNTRACK && NF_NAT_IPV4
|
||||
default NF_NAT_IPV4 && NF_CONNTRACK_PPTP
|
||||
select NF_NAT_PROTO_GRE
|
||||
|
||||
config NF_NAT_H323
|
||||
tristate
|
||||
depends on NF_CONNTRACK && NF_NAT
|
||||
default NF_NAT && NF_CONNTRACK_H323
|
||||
|
||||
config NF_NAT_SIP
|
||||
tristate
|
||||
depends on NF_CONNTRACK && NF_NAT
|
||||
default NF_NAT && NF_CONNTRACK_SIP
|
||||
depends on NF_CONNTRACK && NF_NAT_IPV4
|
||||
default NF_NAT_IPV4 && NF_CONNTRACK_H323
|
||||
|
||||
# mangle + specific targets
|
||||
config IP_NF_MANGLE
|
||||
|
@@ -10,32 +10,22 @@ nf_conntrack_ipv4-objs += nf_conntrack_l3proto_ipv4_compat.o
|
||||
endif
|
||||
endif
|
||||
|
||||
nf_nat-y := nf_nat_core.o nf_nat_helper.o nf_nat_proto_unknown.o nf_nat_proto_common.o nf_nat_proto_tcp.o nf_nat_proto_udp.o nf_nat_proto_icmp.o
|
||||
iptable_nat-y := nf_nat_rule.o nf_nat_standalone.o
|
||||
|
||||
# connection tracking
|
||||
obj-$(CONFIG_NF_CONNTRACK_IPV4) += nf_conntrack_ipv4.o
|
||||
|
||||
obj-$(CONFIG_NF_NAT) += nf_nat.o
|
||||
nf_nat_ipv4-y := nf_nat_l3proto_ipv4.o nf_nat_proto_icmp.o
|
||||
obj-$(CONFIG_NF_NAT_IPV4) += nf_nat_ipv4.o
|
||||
|
||||
# defrag
|
||||
obj-$(CONFIG_NF_DEFRAG_IPV4) += nf_defrag_ipv4.o
|
||||
|
||||
# NAT helpers (nf_conntrack)
|
||||
obj-$(CONFIG_NF_NAT_AMANDA) += nf_nat_amanda.o
|
||||
obj-$(CONFIG_NF_NAT_FTP) += nf_nat_ftp.o
|
||||
obj-$(CONFIG_NF_NAT_H323) += nf_nat_h323.o
|
||||
obj-$(CONFIG_NF_NAT_IRC) += nf_nat_irc.o
|
||||
obj-$(CONFIG_NF_NAT_PPTP) += nf_nat_pptp.o
|
||||
obj-$(CONFIG_NF_NAT_SIP) += nf_nat_sip.o
|
||||
obj-$(CONFIG_NF_NAT_SNMP_BASIC) += nf_nat_snmp_basic.o
|
||||
obj-$(CONFIG_NF_NAT_TFTP) += nf_nat_tftp.o
|
||||
|
||||
# NAT protocols (nf_nat)
|
||||
obj-$(CONFIG_NF_NAT_PROTO_DCCP) += nf_nat_proto_dccp.o
|
||||
obj-$(CONFIG_NF_NAT_PROTO_GRE) += nf_nat_proto_gre.o
|
||||
obj-$(CONFIG_NF_NAT_PROTO_UDPLITE) += nf_nat_proto_udplite.o
|
||||
obj-$(CONFIG_NF_NAT_PROTO_SCTP) += nf_nat_proto_sctp.o
|
||||
|
||||
# generic IP tables
|
||||
obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o
|
||||
@@ -43,7 +33,7 @@ obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o
|
||||
# the three instances of ip_tables
|
||||
obj-$(CONFIG_IP_NF_FILTER) += iptable_filter.o
|
||||
obj-$(CONFIG_IP_NF_MANGLE) += iptable_mangle.o
|
||||
obj-$(CONFIG_NF_NAT) += iptable_nat.o
|
||||
obj-$(CONFIG_NF_NAT_IPV4) += iptable_nat.o
|
||||
obj-$(CONFIG_IP_NF_RAW) += iptable_raw.o
|
||||
obj-$(CONFIG_IP_NF_SECURITY) += iptable_security.o
|
||||
|
||||
|
@@ -19,9 +19,9 @@
|
||||
#include <net/ip.h>
|
||||
#include <net/checksum.h>
|
||||
#include <net/route.h>
|
||||
#include <net/netfilter/nf_nat_rule.h>
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <net/netfilter/nf_nat.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
|
||||
@@ -49,7 +49,7 @@ masquerade_tg(struct sk_buff *skb, const struct xt_action_param *par)
|
||||
struct nf_conn *ct;
|
||||
struct nf_conn_nat *nat;
|
||||
enum ip_conntrack_info ctinfo;
|
||||
struct nf_nat_ipv4_range newrange;
|
||||
struct nf_nat_range newrange;
|
||||
const struct nf_nat_ipv4_multi_range_compat *mr;
|
||||
const struct rtable *rt;
|
||||
__be32 newsrc, nh;
|
||||
@@ -80,10 +80,13 @@ masquerade_tg(struct sk_buff *skb, const struct xt_action_param *par)
|
||||
nat->masq_index = par->out->ifindex;
|
||||
|
||||
/* Transfer from original range. */
|
||||
newrange = ((struct nf_nat_ipv4_range)
|
||||
{ mr->range[0].flags | NF_NAT_RANGE_MAP_IPS,
|
||||
newsrc, newsrc,
|
||||
mr->range[0].min, mr->range[0].max });
|
||||
memset(&newrange.min_addr, 0, sizeof(newrange.min_addr));
|
||||
memset(&newrange.max_addr, 0, sizeof(newrange.max_addr));
|
||||
newrange.flags = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS;
|
||||
newrange.min_addr.ip = newsrc;
|
||||
newrange.max_addr.ip = newsrc;
|
||||
newrange.min_proto = mr->range[0].min;
|
||||
newrange.max_proto = mr->range[0].max;
|
||||
|
||||
/* Hand modified range to generic setup. */
|
||||
return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_SRC);
|
||||
@@ -96,7 +99,8 @@ device_cmp(struct nf_conn *i, void *ifindex)
|
||||
|
||||
if (!nat)
|
||||
return 0;
|
||||
|
||||
if (nf_ct_l3num(i) != NFPROTO_IPV4)
|
||||
return 0;
|
||||
return nat->masq_index == (int)(long)ifindex;
|
||||
}
|
||||
|
||||
|
@@ -16,7 +16,7 @@
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <net/netfilter/nf_nat_rule.h>
|
||||
#include <net/netfilter/nf_nat.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Svenning Soerensen <svenning@post5.tele.dk>");
|
||||
@@ -44,7 +44,7 @@ netmap_tg(struct sk_buff *skb, const struct xt_action_param *par)
|
||||
enum ip_conntrack_info ctinfo;
|
||||
__be32 new_ip, netmask;
|
||||
const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo;
|
||||
struct nf_nat_ipv4_range newrange;
|
||||
struct nf_nat_range newrange;
|
||||
|
||||
NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING ||
|
||||
par->hooknum == NF_INET_POST_ROUTING ||
|
||||
@@ -61,10 +61,13 @@ netmap_tg(struct sk_buff *skb, const struct xt_action_param *par)
|
||||
new_ip = ip_hdr(skb)->saddr & ~netmask;
|
||||
new_ip |= mr->range[0].min_ip & netmask;
|
||||
|
||||
newrange = ((struct nf_nat_ipv4_range)
|
||||
{ mr->range[0].flags | NF_NAT_RANGE_MAP_IPS,
|
||||
new_ip, new_ip,
|
||||
mr->range[0].min, mr->range[0].max });
|
||||
memset(&newrange.min_addr, 0, sizeof(newrange.min_addr));
|
||||
memset(&newrange.max_addr, 0, sizeof(newrange.max_addr));
|
||||
newrange.flags = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS;
|
||||
newrange.min_addr.ip = new_ip;
|
||||
newrange.max_addr.ip = new_ip;
|
||||
newrange.min_proto = mr->range[0].min;
|
||||
newrange.max_proto = mr->range[0].max;
|
||||
|
||||
/* Hand modified range to generic setup. */
|
||||
return nf_nat_setup_info(ct, &newrange, HOOK2MANIP(par->hooknum));
|
||||
|
@@ -19,7 +19,7 @@
|
||||
#include <net/checksum.h>
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <net/netfilter/nf_nat_rule.h>
|
||||
#include <net/netfilter/nf_nat.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
|
||||
@@ -48,7 +48,7 @@ redirect_tg(struct sk_buff *skb, const struct xt_action_param *par)
|
||||
enum ip_conntrack_info ctinfo;
|
||||
__be32 newdst;
|
||||
const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo;
|
||||
struct nf_nat_ipv4_range newrange;
|
||||
struct nf_nat_range newrange;
|
||||
|
||||
NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING ||
|
||||
par->hooknum == NF_INET_LOCAL_OUT);
|
||||
@@ -76,10 +76,13 @@ redirect_tg(struct sk_buff *skb, const struct xt_action_param *par)
|
||||
}
|
||||
|
||||
/* Transfer from original range. */
|
||||
newrange = ((struct nf_nat_ipv4_range)
|
||||
{ mr->range[0].flags | NF_NAT_RANGE_MAP_IPS,
|
||||
newdst, newdst,
|
||||
mr->range[0].min, mr->range[0].max });
|
||||
memset(&newrange.min_addr, 0, sizeof(newrange.min_addr));
|
||||
memset(&newrange.max_addr, 0, sizeof(newrange.max_addr));
|
||||
newrange.flags = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS;
|
||||
newrange.min_addr.ip = newdst;
|
||||
newrange.max_addr.ip = newdst;
|
||||
newrange.min_proto = mr->range[0].min;
|
||||
newrange.max_proto = mr->range[0].max;
|
||||
|
||||
/* Hand modified range to generic setup. */
|
||||
return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST);
|
||||
|
@@ -1,84 +1,71 @@
|
||||
/* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
|
||||
* (C) 2011 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/types.h>
|
||||
#include <linux/icmp.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/ip.h>
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <net/ip.h>
|
||||
#include <net/checksum.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_core.h>
|
||||
#include <net/netfilter/nf_conntrack_extend.h>
|
||||
#include <net/netfilter/nf_nat.h>
|
||||
#include <net/netfilter/nf_nat_rule.h>
|
||||
#include <net/netfilter/nf_nat_protocol.h>
|
||||
#include <net/netfilter/nf_nat_core.h>
|
||||
#include <net/netfilter/nf_nat_helper.h>
|
||||
#include <linux/netfilter_ipv4/ip_tables.h>
|
||||
#include <linux/ip.h>
|
||||
#include <net/ip.h>
|
||||
|
||||
#ifdef CONFIG_XFRM
|
||||
static void nat_decode_session(struct sk_buff *skb, struct flowi *fl)
|
||||
#include <net/netfilter/nf_nat.h>
|
||||
#include <net/netfilter/nf_nat_core.h>
|
||||
#include <net/netfilter/nf_nat_l3proto.h>
|
||||
|
||||
static const struct xt_table nf_nat_ipv4_table = {
|
||||
.name = "nat",
|
||||
.valid_hooks = (1 << NF_INET_PRE_ROUTING) |
|
||||
(1 << NF_INET_POST_ROUTING) |
|
||||
(1 << NF_INET_LOCAL_OUT) |
|
||||
(1 << NF_INET_LOCAL_IN),
|
||||
.me = THIS_MODULE,
|
||||
.af = NFPROTO_IPV4,
|
||||
};
|
||||
|
||||
static unsigned int alloc_null_binding(struct nf_conn *ct, unsigned int hooknum)
|
||||
{
|
||||
struct flowi4 *fl4 = &fl->u.ip4;
|
||||
const struct nf_conn *ct;
|
||||
const struct nf_conntrack_tuple *t;
|
||||
enum ip_conntrack_info ctinfo;
|
||||
enum ip_conntrack_dir dir;
|
||||
unsigned long statusbit;
|
||||
/* Force range to this IP; let proto decide mapping for
|
||||
* per-proto parts (hence not IP_NAT_RANGE_PROTO_SPECIFIED).
|
||||
*/
|
||||
struct nf_nat_range range;
|
||||
|
||||
ct = nf_ct_get(skb, &ctinfo);
|
||||
if (ct == NULL)
|
||||
return;
|
||||
dir = CTINFO2DIR(ctinfo);
|
||||
t = &ct->tuplehash[dir].tuple;
|
||||
range.flags = 0;
|
||||
pr_debug("Allocating NULL binding for %p (%pI4)\n", ct,
|
||||
HOOK2MANIP(hooknum) == NF_NAT_MANIP_SRC ?
|
||||
&ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip :
|
||||
&ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip);
|
||||
|
||||
if (dir == IP_CT_DIR_ORIGINAL)
|
||||
statusbit = IPS_DST_NAT;
|
||||
else
|
||||
statusbit = IPS_SRC_NAT;
|
||||
|
||||
if (ct->status & statusbit) {
|
||||
fl4->daddr = t->dst.u3.ip;
|
||||
if (t->dst.protonum == IPPROTO_TCP ||
|
||||
t->dst.protonum == IPPROTO_UDP ||
|
||||
t->dst.protonum == IPPROTO_UDPLITE ||
|
||||
t->dst.protonum == IPPROTO_DCCP ||
|
||||
t->dst.protonum == IPPROTO_SCTP)
|
||||
fl4->fl4_dport = t->dst.u.tcp.port;
|
||||
}
|
||||
|
||||
statusbit ^= IPS_NAT_MASK;
|
||||
|
||||
if (ct->status & statusbit) {
|
||||
fl4->saddr = t->src.u3.ip;
|
||||
if (t->dst.protonum == IPPROTO_TCP ||
|
||||
t->dst.protonum == IPPROTO_UDP ||
|
||||
t->dst.protonum == IPPROTO_UDPLITE ||
|
||||
t->dst.protonum == IPPROTO_DCCP ||
|
||||
t->dst.protonum == IPPROTO_SCTP)
|
||||
fl4->fl4_sport = t->src.u.tcp.port;
|
||||
}
|
||||
return nf_nat_setup_info(ct, &range, HOOK2MANIP(hooknum));
|
||||
}
|
||||
|
||||
static unsigned int nf_nat_rule_find(struct sk_buff *skb, unsigned int hooknum,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
struct nf_conn *ct)
|
||||
{
|
||||
struct net *net = nf_ct_net(ct);
|
||||
unsigned int ret;
|
||||
|
||||
ret = ipt_do_table(skb, hooknum, in, out, net->ipv4.nat_table);
|
||||
if (ret == NF_ACCEPT) {
|
||||
if (!nf_nat_initialized(ct, HOOK2MANIP(hooknum)))
|
||||
ret = alloc_null_binding(ct, hooknum);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static unsigned int
|
||||
nf_nat_fn(unsigned int hooknum,
|
||||
struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
int (*okfn)(struct sk_buff *))
|
||||
nf_nat_ipv4_fn(unsigned int hooknum,
|
||||
struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
int (*okfn)(struct sk_buff *))
|
||||
{
|
||||
struct nf_conn *ct;
|
||||
enum ip_conntrack_info ctinfo;
|
||||
@@ -87,14 +74,16 @@ nf_nat_fn(unsigned int hooknum,
|
||||
enum nf_nat_manip_type maniptype = HOOK2MANIP(hooknum);
|
||||
|
||||
/* We never see fragments: conntrack defrags on pre-routing
|
||||
and local-out, and nf_nat_out protects post-routing. */
|
||||
* and local-out, and nf_nat_out protects post-routing.
|
||||
*/
|
||||
NF_CT_ASSERT(!ip_is_fragment(ip_hdr(skb)));
|
||||
|
||||
ct = nf_ct_get(skb, &ctinfo);
|
||||
/* Can't track? It's not due to stress, or conntrack would
|
||||
have dropped it. Hence it's the user's responsibilty to
|
||||
packet filter it out, or implement conntrack/NAT for that
|
||||
protocol. 8) --RR */
|
||||
* have dropped it. Hence it's the user's responsibilty to
|
||||
* packet filter it out, or implement conntrack/NAT for that
|
||||
* protocol. 8) --RR
|
||||
*/
|
||||
if (!ct)
|
||||
return NF_ACCEPT;
|
||||
|
||||
@@ -118,17 +107,17 @@ nf_nat_fn(unsigned int hooknum,
|
||||
case IP_CT_RELATED:
|
||||
case IP_CT_RELATED_REPLY:
|
||||
if (ip_hdr(skb)->protocol == IPPROTO_ICMP) {
|
||||
if (!nf_nat_icmp_reply_translation(ct, ctinfo,
|
||||
hooknum, skb))
|
||||
if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo,
|
||||
hooknum))
|
||||
return NF_DROP;
|
||||
else
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
/* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */
|
||||
case IP_CT_NEW:
|
||||
|
||||
/* Seen it before? This can happen for loopback, retrans,
|
||||
or local packets.. */
|
||||
* or local packets.
|
||||
*/
|
||||
if (!nf_nat_initialized(ct, maniptype)) {
|
||||
unsigned int ret;
|
||||
|
||||
@@ -151,16 +140,16 @@ nf_nat_fn(unsigned int hooknum,
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
nf_nat_in(unsigned int hooknum,
|
||||
struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
int (*okfn)(struct sk_buff *))
|
||||
nf_nat_ipv4_in(unsigned int hooknum,
|
||||
struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
int (*okfn)(struct sk_buff *))
|
||||
{
|
||||
unsigned int ret;
|
||||
__be32 daddr = ip_hdr(skb)->daddr;
|
||||
|
||||
ret = nf_nat_fn(hooknum, skb, in, out, okfn);
|
||||
ret = nf_nat_ipv4_fn(hooknum, skb, in, out, okfn);
|
||||
if (ret != NF_DROP && ret != NF_STOLEN &&
|
||||
daddr != ip_hdr(skb)->daddr)
|
||||
skb_dst_drop(skb);
|
||||
@@ -169,11 +158,11 @@ nf_nat_in(unsigned int hooknum,
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
nf_nat_out(unsigned int hooknum,
|
||||
struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
int (*okfn)(struct sk_buff *))
|
||||
nf_nat_ipv4_out(unsigned int hooknum,
|
||||
struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
int (*okfn)(struct sk_buff *))
|
||||
{
|
||||
#ifdef CONFIG_XFRM
|
||||
const struct nf_conn *ct;
|
||||
@@ -186,29 +175,30 @@ nf_nat_out(unsigned int hooknum,
|
||||
ip_hdrlen(skb) < sizeof(struct iphdr))
|
||||
return NF_ACCEPT;
|
||||
|
||||
ret = nf_nat_fn(hooknum, skb, in, out, okfn);
|
||||
ret = nf_nat_ipv4_fn(hooknum, skb, in, out, okfn);
|
||||
#ifdef CONFIG_XFRM
|
||||
if (ret != NF_DROP && ret != NF_STOLEN &&
|
||||
!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&
|
||||
(ct = nf_ct_get(skb, &ctinfo)) != NULL) {
|
||||
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
|
||||
|
||||
if ((ct->tuplehash[dir].tuple.src.u3.ip !=
|
||||
ct->tuplehash[!dir].tuple.dst.u3.ip) ||
|
||||
(ct->tuplehash[dir].tuple.src.u.all !=
|
||||
ct->tuplehash[!dir].tuple.dst.u.all)
|
||||
)
|
||||
return ip_xfrm_me_harder(skb) == 0 ? ret : NF_DROP;
|
||||
ct->tuplehash[!dir].tuple.dst.u.all))
|
||||
if (nf_xfrm_me_harder(skb, AF_INET) < 0)
|
||||
ret = NF_DROP;
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
nf_nat_local_fn(unsigned int hooknum,
|
||||
struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
int (*okfn)(struct sk_buff *))
|
||||
nf_nat_ipv4_local_fn(unsigned int hooknum,
|
||||
struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
int (*okfn)(struct sk_buff *))
|
||||
{
|
||||
const struct nf_conn *ct;
|
||||
enum ip_conntrack_info ctinfo;
|
||||
@@ -219,7 +209,7 @@ nf_nat_local_fn(unsigned int hooknum,
|
||||
ip_hdrlen(skb) < sizeof(struct iphdr))
|
||||
return NF_ACCEPT;
|
||||
|
||||
ret = nf_nat_fn(hooknum, skb, in, out, okfn);
|
||||
ret = nf_nat_ipv4_fn(hooknum, skb, in, out, okfn);
|
||||
if (ret != NF_DROP && ret != NF_STOLEN &&
|
||||
(ct = nf_ct_get(skb, &ctinfo)) != NULL) {
|
||||
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
|
||||
@@ -230,21 +220,20 @@ nf_nat_local_fn(unsigned int hooknum,
|
||||
ret = NF_DROP;
|
||||
}
|
||||
#ifdef CONFIG_XFRM
|
||||
else if (ct->tuplehash[dir].tuple.dst.u.all !=
|
||||
else if (!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&
|
||||
ct->tuplehash[dir].tuple.dst.u.all !=
|
||||
ct->tuplehash[!dir].tuple.src.u.all)
|
||||
if (ip_xfrm_me_harder(skb))
|
||||
if (nf_xfrm_me_harder(skb, AF_INET) < 0)
|
||||
ret = NF_DROP;
|
||||
#endif
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* We must be after connection tracking and before packet filtering. */
|
||||
|
||||
static struct nf_hook_ops nf_nat_ops[] __read_mostly = {
|
||||
static struct nf_hook_ops nf_nat_ipv4_ops[] __read_mostly = {
|
||||
/* Before packet filtering, change destination */
|
||||
{
|
||||
.hook = nf_nat_in,
|
||||
.hook = nf_nat_ipv4_in,
|
||||
.owner = THIS_MODULE,
|
||||
.pf = NFPROTO_IPV4,
|
||||
.hooknum = NF_INET_PRE_ROUTING,
|
||||
@@ -252,7 +241,7 @@ static struct nf_hook_ops nf_nat_ops[] __read_mostly = {
|
||||
},
|
||||
/* After packet filtering, change source */
|
||||
{
|
||||
.hook = nf_nat_out,
|
||||
.hook = nf_nat_ipv4_out,
|
||||
.owner = THIS_MODULE,
|
||||
.pf = NFPROTO_IPV4,
|
||||
.hooknum = NF_INET_POST_ROUTING,
|
||||
@@ -260,7 +249,7 @@ static struct nf_hook_ops nf_nat_ops[] __read_mostly = {
|
||||
},
|
||||
/* Before packet filtering, change destination */
|
||||
{
|
||||
.hook = nf_nat_local_fn,
|
||||
.hook = nf_nat_ipv4_local_fn,
|
||||
.owner = THIS_MODULE,
|
||||
.pf = NFPROTO_IPV4,
|
||||
.hooknum = NF_INET_LOCAL_OUT,
|
||||
@@ -268,7 +257,7 @@ static struct nf_hook_ops nf_nat_ops[] __read_mostly = {
|
||||
},
|
||||
/* After packet filtering, change source */
|
||||
{
|
||||
.hook = nf_nat_fn,
|
||||
.hook = nf_nat_ipv4_fn,
|
||||
.owner = THIS_MODULE,
|
||||
.pf = NFPROTO_IPV4,
|
||||
.hooknum = NF_INET_LOCAL_IN,
|
||||
@@ -276,51 +265,56 @@ static struct nf_hook_ops nf_nat_ops[] __read_mostly = {
|
||||
},
|
||||
};
|
||||
|
||||
static int __init nf_nat_standalone_init(void)
|
||||
static int __net_init iptable_nat_net_init(struct net *net)
|
||||
{
|
||||
int ret = 0;
|
||||
struct ipt_replace *repl;
|
||||
|
||||
need_ipv4_conntrack();
|
||||
|
||||
#ifdef CONFIG_XFRM
|
||||
BUG_ON(ip_nat_decode_session != NULL);
|
||||
RCU_INIT_POINTER(ip_nat_decode_session, nat_decode_session);
|
||||
#endif
|
||||
ret = nf_nat_rule_init();
|
||||
if (ret < 0) {
|
||||
pr_err("nf_nat_init: can't setup rules.\n");
|
||||
goto cleanup_decode_session;
|
||||
}
|
||||
ret = nf_register_hooks(nf_nat_ops, ARRAY_SIZE(nf_nat_ops));
|
||||
if (ret < 0) {
|
||||
pr_err("nf_nat_init: can't register hooks.\n");
|
||||
goto cleanup_rule_init;
|
||||
}
|
||||
return ret;
|
||||
|
||||
cleanup_rule_init:
|
||||
nf_nat_rule_cleanup();
|
||||
cleanup_decode_session:
|
||||
#ifdef CONFIG_XFRM
|
||||
RCU_INIT_POINTER(ip_nat_decode_session, NULL);
|
||||
synchronize_net();
|
||||
#endif
|
||||
return ret;
|
||||
repl = ipt_alloc_initial_table(&nf_nat_ipv4_table);
|
||||
if (repl == NULL)
|
||||
return -ENOMEM;
|
||||
net->ipv4.nat_table = ipt_register_table(net, &nf_nat_ipv4_table, repl);
|
||||
kfree(repl);
|
||||
if (IS_ERR(net->ipv4.nat_table))
|
||||
return PTR_ERR(net->ipv4.nat_table);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit nf_nat_standalone_fini(void)
|
||||
static void __net_exit iptable_nat_net_exit(struct net *net)
|
||||
{
|
||||
nf_unregister_hooks(nf_nat_ops, ARRAY_SIZE(nf_nat_ops));
|
||||
nf_nat_rule_cleanup();
|
||||
#ifdef CONFIG_XFRM
|
||||
RCU_INIT_POINTER(ip_nat_decode_session, NULL);
|
||||
synchronize_net();
|
||||
#endif
|
||||
/* Conntrack caches are unregistered in nf_conntrack_cleanup */
|
||||
ipt_unregister_table(net, net->ipv4.nat_table);
|
||||
}
|
||||
|
||||
module_init(nf_nat_standalone_init);
|
||||
module_exit(nf_nat_standalone_fini);
|
||||
static struct pernet_operations iptable_nat_net_ops = {
|
||||
.init = iptable_nat_net_init,
|
||||
.exit = iptable_nat_net_exit,
|
||||
};
|
||||
|
||||
static int __init iptable_nat_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = register_pernet_subsys(&iptable_nat_net_ops);
|
||||
if (err < 0)
|
||||
goto err1;
|
||||
|
||||
err = nf_register_hooks(nf_nat_ipv4_ops, ARRAY_SIZE(nf_nat_ipv4_ops));
|
||||
if (err < 0)
|
||||
goto err2;
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
unregister_pernet_subsys(&iptable_nat_net_ops);
|
||||
err1:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit iptable_nat_exit(void)
|
||||
{
|
||||
nf_unregister_hooks(nf_nat_ipv4_ops, ARRAY_SIZE(nf_nat_ipv4_ops));
|
||||
unregister_pernet_subsys(&iptable_nat_net_ops);
|
||||
}
|
||||
|
||||
module_init(iptable_nat_init);
|
||||
module_exit(iptable_nat_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("ip_nat");
|
@@ -29,11 +29,6 @@
|
||||
#include <net/netfilter/ipv4/nf_defrag_ipv4.h>
|
||||
#include <net/netfilter/nf_log.h>
|
||||
|
||||
int (*nf_nat_seq_adjust_hook)(struct sk_buff *skb,
|
||||
struct nf_conn *ct,
|
||||
enum ip_conntrack_info ctinfo);
|
||||
EXPORT_SYMBOL_GPL(nf_nat_seq_adjust_hook);
|
||||
|
||||
static bool ipv4_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff,
|
||||
struct nf_conntrack_tuple *tuple)
|
||||
{
|
||||
@@ -149,7 +144,8 @@ static unsigned int ipv4_confirm(unsigned int hooknum,
|
||||
typeof(nf_nat_seq_adjust_hook) seq_adjust;
|
||||
|
||||
seq_adjust = rcu_dereference(nf_nat_seq_adjust_hook);
|
||||
if (!seq_adjust || !seq_adjust(skb, ct, ctinfo)) {
|
||||
if (!seq_adjust ||
|
||||
!seq_adjust(skb, ct, ctinfo, ip_hdrlen(skb))) {
|
||||
NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop);
|
||||
return NF_DROP;
|
||||
}
|
||||
|
@@ -1,85 +0,0 @@
|
||||
/* Amanda extension for TCP NAT alteration.
|
||||
* (C) 2002 by Brian J. Murrell <netfilter@interlinx.bc.ca>
|
||||
* based on a copy of HW's ip_nat_irc.c as well as other modules
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/udp.h>
|
||||
|
||||
#include <net/netfilter/nf_conntrack_helper.h>
|
||||
#include <net/netfilter/nf_conntrack_expect.h>
|
||||
#include <net/netfilter/nf_nat_helper.h>
|
||||
#include <net/netfilter/nf_nat_rule.h>
|
||||
#include <linux/netfilter/nf_conntrack_amanda.h>
|
||||
|
||||
MODULE_AUTHOR("Brian J. Murrell <netfilter@interlinx.bc.ca>");
|
||||
MODULE_DESCRIPTION("Amanda NAT helper");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("ip_nat_amanda");
|
||||
|
||||
static unsigned int help(struct sk_buff *skb,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
unsigned int matchoff,
|
||||
unsigned int matchlen,
|
||||
struct nf_conntrack_expect *exp)
|
||||
{
|
||||
char buffer[sizeof("65535")];
|
||||
u_int16_t port;
|
||||
unsigned int ret;
|
||||
|
||||
/* Connection comes from client. */
|
||||
exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port;
|
||||
exp->dir = IP_CT_DIR_ORIGINAL;
|
||||
|
||||
/* When you see the packet, we need to NAT it the same as the
|
||||
* this one (ie. same IP: it will be TCP and master is UDP). */
|
||||
exp->expectfn = nf_nat_follow_master;
|
||||
|
||||
/* Try to get same port: if not, try to change it. */
|
||||
for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) {
|
||||
int res;
|
||||
|
||||
exp->tuple.dst.u.tcp.port = htons(port);
|
||||
res = nf_ct_expect_related(exp);
|
||||
if (res == 0)
|
||||
break;
|
||||
else if (res != -EBUSY) {
|
||||
port = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (port == 0)
|
||||
return NF_DROP;
|
||||
|
||||
sprintf(buffer, "%u", port);
|
||||
ret = nf_nat_mangle_udp_packet(skb, exp->master, ctinfo,
|
||||
matchoff, matchlen,
|
||||
buffer, strlen(buffer));
|
||||
if (ret != NF_ACCEPT)
|
||||
nf_ct_unexpect_related(exp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit nf_nat_amanda_fini(void)
|
||||
{
|
||||
RCU_INIT_POINTER(nf_nat_amanda_hook, NULL);
|
||||
synchronize_rcu();
|
||||
}
|
||||
|
||||
static int __init nf_nat_amanda_init(void)
|
||||
{
|
||||
BUG_ON(nf_nat_amanda_hook != NULL);
|
||||
RCU_INIT_POINTER(nf_nat_amanda_hook, help);
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(nf_nat_amanda_init);
|
||||
module_exit(nf_nat_amanda_fini);
|
@@ -1,763 +0,0 @@
|
||||
/* NAT for netfilter; shared with compatibility layer. */
|
||||
|
||||
/* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <net/checksum.h>
|
||||
#include <net/icmp.h>
|
||||
#include <net/ip.h>
|
||||
#include <net/tcp.h> /* For tcp_prot in getorigdst */
|
||||
#include <linux/icmp.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/jhash.h>
|
||||
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_core.h>
|
||||
#include <net/netfilter/nf_nat.h>
|
||||
#include <net/netfilter/nf_nat_protocol.h>
|
||||
#include <net/netfilter/nf_nat_core.h>
|
||||
#include <net/netfilter/nf_nat_helper.h>
|
||||
#include <net/netfilter/nf_conntrack_helper.h>
|
||||
#include <net/netfilter/nf_conntrack_l3proto.h>
|
||||
#include <net/netfilter/nf_conntrack_zones.h>
|
||||
|
||||
static DEFINE_SPINLOCK(nf_nat_lock);
|
||||
|
||||
static struct nf_conntrack_l3proto *l3proto __read_mostly;
|
||||
|
||||
#define MAX_IP_NAT_PROTO 256
|
||||
static const struct nf_nat_protocol __rcu *nf_nat_protos[MAX_IP_NAT_PROTO]
|
||||
__read_mostly;
|
||||
|
||||
static inline const struct nf_nat_protocol *
|
||||
__nf_nat_proto_find(u_int8_t protonum)
|
||||
{
|
||||
return rcu_dereference(nf_nat_protos[protonum]);
|
||||
}
|
||||
|
||||
/* We keep an extra hash for each conntrack, for fast searching. */
|
||||
static inline unsigned int
|
||||
hash_by_src(const struct net *net, u16 zone,
|
||||
const struct nf_conntrack_tuple *tuple)
|
||||
{
|
||||
unsigned int hash;
|
||||
|
||||
/* Original src, to ensure we map it consistently if poss. */
|
||||
hash = jhash_3words((__force u32)tuple->src.u3.ip,
|
||||
(__force u32)tuple->src.u.all ^ zone,
|
||||
tuple->dst.protonum, nf_conntrack_hash_rnd);
|
||||
return ((u64)hash * net->ipv4.nat_htable_size) >> 32;
|
||||
}
|
||||
|
||||
/* Is this tuple already taken? (not by us) */
|
||||
int
|
||||
nf_nat_used_tuple(const struct nf_conntrack_tuple *tuple,
|
||||
const struct nf_conn *ignored_conntrack)
|
||||
{
|
||||
/* Conntrack tracking doesn't keep track of outgoing tuples; only
|
||||
incoming ones. NAT means they don't have a fixed mapping,
|
||||
so we invert the tuple and look for the incoming reply.
|
||||
|
||||
We could keep a separate hash if this proves too slow. */
|
||||
struct nf_conntrack_tuple reply;
|
||||
|
||||
nf_ct_invert_tuplepr(&reply, tuple);
|
||||
return nf_conntrack_tuple_taken(&reply, ignored_conntrack);
|
||||
}
|
||||
EXPORT_SYMBOL(nf_nat_used_tuple);
|
||||
|
||||
/* If we source map this tuple so reply looks like reply_tuple, will
|
||||
* that meet the constraints of range. */
|
||||
static int
|
||||
in_range(const struct nf_conntrack_tuple *tuple,
|
||||
const struct nf_nat_ipv4_range *range)
|
||||
{
|
||||
const struct nf_nat_protocol *proto;
|
||||
int ret = 0;
|
||||
|
||||
/* If we are supposed to map IPs, then we must be in the
|
||||
range specified, otherwise let this drag us onto a new src IP. */
|
||||
if (range->flags & NF_NAT_RANGE_MAP_IPS) {
|
||||
if (ntohl(tuple->src.u3.ip) < ntohl(range->min_ip) ||
|
||||
ntohl(tuple->src.u3.ip) > ntohl(range->max_ip))
|
||||
return 0;
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
proto = __nf_nat_proto_find(tuple->dst.protonum);
|
||||
if (!(range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) ||
|
||||
proto->in_range(tuple, NF_NAT_MANIP_SRC,
|
||||
&range->min, &range->max))
|
||||
ret = 1;
|
||||
rcu_read_unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
same_src(const struct nf_conn *ct,
|
||||
const struct nf_conntrack_tuple *tuple)
|
||||
{
|
||||
const struct nf_conntrack_tuple *t;
|
||||
|
||||
t = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
|
||||
return (t->dst.protonum == tuple->dst.protonum &&
|
||||
t->src.u3.ip == tuple->src.u3.ip &&
|
||||
t->src.u.all == tuple->src.u.all);
|
||||
}
|
||||
|
||||
/* Only called for SRC manip */
|
||||
static int
|
||||
find_appropriate_src(struct net *net, u16 zone,
|
||||
const struct nf_conntrack_tuple *tuple,
|
||||
struct nf_conntrack_tuple *result,
|
||||
const struct nf_nat_ipv4_range *range)
|
||||
{
|
||||
unsigned int h = hash_by_src(net, zone, tuple);
|
||||
const struct nf_conn_nat *nat;
|
||||
const struct nf_conn *ct;
|
||||
const struct hlist_node *n;
|
||||
|
||||
rcu_read_lock();
|
||||
hlist_for_each_entry_rcu(nat, n, &net->ipv4.nat_bysource[h], bysource) {
|
||||
ct = nat->ct;
|
||||
if (same_src(ct, tuple) && nf_ct_zone(ct) == zone) {
|
||||
/* Copy source part from reply tuple. */
|
||||
nf_ct_invert_tuplepr(result,
|
||||
&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
|
||||
result->dst = tuple->dst;
|
||||
|
||||
if (in_range(result, range)) {
|
||||
rcu_read_unlock();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* For [FUTURE] fragmentation handling, we want the least-used
|
||||
src-ip/dst-ip/proto triple. Fairness doesn't come into it. Thus
|
||||
if the range specifies 1.2.3.4 ports 10000-10005 and 1.2.3.5 ports
|
||||
1-65535, we don't do pro-rata allocation based on ports; we choose
|
||||
the ip with the lowest src-ip/dst-ip/proto usage.
|
||||
*/
|
||||
static void
|
||||
find_best_ips_proto(u16 zone, struct nf_conntrack_tuple *tuple,
|
||||
const struct nf_nat_ipv4_range *range,
|
||||
const struct nf_conn *ct,
|
||||
enum nf_nat_manip_type maniptype)
|
||||
{
|
||||
__be32 *var_ipp;
|
||||
/* Host order */
|
||||
u_int32_t minip, maxip, j;
|
||||
|
||||
/* No IP mapping? Do nothing. */
|
||||
if (!(range->flags & NF_NAT_RANGE_MAP_IPS))
|
||||
return;
|
||||
|
||||
if (maniptype == NF_NAT_MANIP_SRC)
|
||||
var_ipp = &tuple->src.u3.ip;
|
||||
else
|
||||
var_ipp = &tuple->dst.u3.ip;
|
||||
|
||||
/* Fast path: only one choice. */
|
||||
if (range->min_ip == range->max_ip) {
|
||||
*var_ipp = range->min_ip;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Hashing source and destination IPs gives a fairly even
|
||||
* spread in practice (if there are a small number of IPs
|
||||
* involved, there usually aren't that many connections
|
||||
* anyway). The consistency means that servers see the same
|
||||
* client coming from the same IP (some Internet Banking sites
|
||||
* like this), even across reboots. */
|
||||
minip = ntohl(range->min_ip);
|
||||
maxip = ntohl(range->max_ip);
|
||||
j = jhash_2words((__force u32)tuple->src.u3.ip,
|
||||
range->flags & NF_NAT_RANGE_PERSISTENT ?
|
||||
0 : (__force u32)tuple->dst.u3.ip ^ zone, 0);
|
||||
j = ((u64)j * (maxip - minip + 1)) >> 32;
|
||||
*var_ipp = htonl(minip + j);
|
||||
}
|
||||
|
||||
/* Manipulate the tuple into the range given. For NF_INET_POST_ROUTING,
|
||||
* we change the source to map into the range. For NF_INET_PRE_ROUTING
|
||||
* and NF_INET_LOCAL_OUT, we change the destination to map into the
|
||||
* range. It might not be possible to get a unique tuple, but we try.
|
||||
* At worst (or if we race), we will end up with a final duplicate in
|
||||
* __ip_conntrack_confirm and drop the packet. */
|
||||
static void
|
||||
get_unique_tuple(struct nf_conntrack_tuple *tuple,
|
||||
const struct nf_conntrack_tuple *orig_tuple,
|
||||
const struct nf_nat_ipv4_range *range,
|
||||
struct nf_conn *ct,
|
||||
enum nf_nat_manip_type maniptype)
|
||||
{
|
||||
struct net *net = nf_ct_net(ct);
|
||||
const struct nf_nat_protocol *proto;
|
||||
u16 zone = nf_ct_zone(ct);
|
||||
|
||||
/* 1) If this srcip/proto/src-proto-part is currently mapped,
|
||||
and that same mapping gives a unique tuple within the given
|
||||
range, use that.
|
||||
|
||||
This is only required for source (ie. NAT/masq) mappings.
|
||||
So far, we don't do local source mappings, so multiple
|
||||
manips not an issue. */
|
||||
if (maniptype == NF_NAT_MANIP_SRC &&
|
||||
!(range->flags & NF_NAT_RANGE_PROTO_RANDOM)) {
|
||||
/* try the original tuple first */
|
||||
if (in_range(orig_tuple, range)) {
|
||||
if (!nf_nat_used_tuple(orig_tuple, ct)) {
|
||||
*tuple = *orig_tuple;
|
||||
return;
|
||||
}
|
||||
} else if (find_appropriate_src(net, zone, orig_tuple, tuple,
|
||||
range)) {
|
||||
pr_debug("get_unique_tuple: Found current src map\n");
|
||||
if (!nf_nat_used_tuple(tuple, ct))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* 2) Select the least-used IP/proto combination in the given
|
||||
range. */
|
||||
*tuple = *orig_tuple;
|
||||
find_best_ips_proto(zone, tuple, range, ct, maniptype);
|
||||
|
||||
/* 3) The per-protocol part of the manip is made to map into
|
||||
the range to make a unique tuple. */
|
||||
|
||||
rcu_read_lock();
|
||||
proto = __nf_nat_proto_find(orig_tuple->dst.protonum);
|
||||
|
||||
/* Only bother mapping if it's not already in range and unique */
|
||||
if (!(range->flags & NF_NAT_RANGE_PROTO_RANDOM)) {
|
||||
if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
|
||||
if (proto->in_range(tuple, maniptype, &range->min,
|
||||
&range->max) &&
|
||||
(range->min.all == range->max.all ||
|
||||
!nf_nat_used_tuple(tuple, ct)))
|
||||
goto out;
|
||||
} else if (!nf_nat_used_tuple(tuple, ct)) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Last change: get protocol to try to obtain unique tuple. */
|
||||
proto->unique_tuple(tuple, range, maniptype, ct);
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
unsigned int
|
||||
nf_nat_setup_info(struct nf_conn *ct,
|
||||
const struct nf_nat_ipv4_range *range,
|
||||
enum nf_nat_manip_type maniptype)
|
||||
{
|
||||
struct net *net = nf_ct_net(ct);
|
||||
struct nf_conntrack_tuple curr_tuple, new_tuple;
|
||||
struct nf_conn_nat *nat;
|
||||
|
||||
/* nat helper or nfctnetlink also setup binding */
|
||||
nat = nfct_nat(ct);
|
||||
if (!nat) {
|
||||
nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC);
|
||||
if (nat == NULL) {
|
||||
pr_debug("failed to add NAT extension\n");
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
}
|
||||
|
||||
NF_CT_ASSERT(maniptype == NF_NAT_MANIP_SRC ||
|
||||
maniptype == NF_NAT_MANIP_DST);
|
||||
BUG_ON(nf_nat_initialized(ct, maniptype));
|
||||
|
||||
/* What we've got will look like inverse of reply. Normally
|
||||
this is what is in the conntrack, except for prior
|
||||
manipulations (future optimization: if num_manips == 0,
|
||||
orig_tp =
|
||||
conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple) */
|
||||
nf_ct_invert_tuplepr(&curr_tuple,
|
||||
&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
|
||||
|
||||
get_unique_tuple(&new_tuple, &curr_tuple, range, ct, maniptype);
|
||||
|
||||
if (!nf_ct_tuple_equal(&new_tuple, &curr_tuple)) {
|
||||
struct nf_conntrack_tuple reply;
|
||||
|
||||
/* Alter conntrack table so will recognize replies. */
|
||||
nf_ct_invert_tuplepr(&reply, &new_tuple);
|
||||
nf_conntrack_alter_reply(ct, &reply);
|
||||
|
||||
/* Non-atomic: we own this at the moment. */
|
||||
if (maniptype == NF_NAT_MANIP_SRC)
|
||||
ct->status |= IPS_SRC_NAT;
|
||||
else
|
||||
ct->status |= IPS_DST_NAT;
|
||||
}
|
||||
|
||||
if (maniptype == NF_NAT_MANIP_SRC) {
|
||||
unsigned int srchash;
|
||||
|
||||
srchash = hash_by_src(net, nf_ct_zone(ct),
|
||||
&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
|
||||
spin_lock_bh(&nf_nat_lock);
|
||||
/* nf_conntrack_alter_reply might re-allocate extension area */
|
||||
nat = nfct_nat(ct);
|
||||
nat->ct = ct;
|
||||
hlist_add_head_rcu(&nat->bysource,
|
||||
&net->ipv4.nat_bysource[srchash]);
|
||||
spin_unlock_bh(&nf_nat_lock);
|
||||
}
|
||||
|
||||
/* It's done. */
|
||||
if (maniptype == NF_NAT_MANIP_DST)
|
||||
ct->status |= IPS_DST_NAT_DONE;
|
||||
else
|
||||
ct->status |= IPS_SRC_NAT_DONE;
|
||||
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
EXPORT_SYMBOL(nf_nat_setup_info);
|
||||
|
||||
/* Returns true if succeeded. */
|
||||
static bool
|
||||
manip_pkt(u_int16_t proto,
|
||||
struct sk_buff *skb,
|
||||
unsigned int iphdroff,
|
||||
const struct nf_conntrack_tuple *target,
|
||||
enum nf_nat_manip_type maniptype)
|
||||
{
|
||||
struct iphdr *iph;
|
||||
const struct nf_nat_protocol *p;
|
||||
|
||||
if (!skb_make_writable(skb, iphdroff + sizeof(*iph)))
|
||||
return false;
|
||||
|
||||
iph = (void *)skb->data + iphdroff;
|
||||
|
||||
/* Manipulate protcol part. */
|
||||
|
||||
/* rcu_read_lock()ed by nf_hook_slow */
|
||||
p = __nf_nat_proto_find(proto);
|
||||
if (!p->manip_pkt(skb, iphdroff, target, maniptype))
|
||||
return false;
|
||||
|
||||
iph = (void *)skb->data + iphdroff;
|
||||
|
||||
if (maniptype == NF_NAT_MANIP_SRC) {
|
||||
csum_replace4(&iph->check, iph->saddr, target->src.u3.ip);
|
||||
iph->saddr = target->src.u3.ip;
|
||||
} else {
|
||||
csum_replace4(&iph->check, iph->daddr, target->dst.u3.ip);
|
||||
iph->daddr = target->dst.u3.ip;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Do packet manipulations according to nf_nat_setup_info. */
|
||||
unsigned int nf_nat_packet(struct nf_conn *ct,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
unsigned int hooknum,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
|
||||
unsigned long statusbit;
|
||||
enum nf_nat_manip_type mtype = HOOK2MANIP(hooknum);
|
||||
|
||||
if (mtype == NF_NAT_MANIP_SRC)
|
||||
statusbit = IPS_SRC_NAT;
|
||||
else
|
||||
statusbit = IPS_DST_NAT;
|
||||
|
||||
/* Invert if this is reply dir. */
|
||||
if (dir == IP_CT_DIR_REPLY)
|
||||
statusbit ^= IPS_NAT_MASK;
|
||||
|
||||
/* Non-atomic: these bits don't change. */
|
||||
if (ct->status & statusbit) {
|
||||
struct nf_conntrack_tuple target;
|
||||
|
||||
/* We are aiming to look like inverse of other direction. */
|
||||
nf_ct_invert_tuplepr(&target, &ct->tuplehash[!dir].tuple);
|
||||
|
||||
if (!manip_pkt(target.dst.protonum, skb, 0, &target, mtype))
|
||||
return NF_DROP;
|
||||
}
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_nat_packet);
|
||||
|
||||
/* Dir is direction ICMP is coming from (opposite to packet it contains) */
|
||||
int nf_nat_icmp_reply_translation(struct nf_conn *ct,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
unsigned int hooknum,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct {
|
||||
struct icmphdr icmp;
|
||||
struct iphdr ip;
|
||||
} *inside;
|
||||
struct nf_conntrack_tuple target;
|
||||
int hdrlen = ip_hdrlen(skb);
|
||||
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
|
||||
unsigned long statusbit;
|
||||
enum nf_nat_manip_type manip = HOOK2MANIP(hooknum);
|
||||
|
||||
if (!skb_make_writable(skb, hdrlen + sizeof(*inside)))
|
||||
return 0;
|
||||
|
||||
inside = (void *)skb->data + hdrlen;
|
||||
|
||||
/* We're actually going to mangle it beyond trivial checksum
|
||||
adjustment, so make sure the current checksum is correct. */
|
||||
if (nf_ip_checksum(skb, hooknum, hdrlen, 0))
|
||||
return 0;
|
||||
|
||||
/* Must be RELATED */
|
||||
NF_CT_ASSERT(skb->nfctinfo == IP_CT_RELATED ||
|
||||
skb->nfctinfo == IP_CT_RELATED_REPLY);
|
||||
|
||||
/* Redirects on non-null nats must be dropped, else they'll
|
||||
start talking to each other without our translation, and be
|
||||
confused... --RR */
|
||||
if (inside->icmp.type == ICMP_REDIRECT) {
|
||||
/* If NAT isn't finished, assume it and drop. */
|
||||
if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK)
|
||||
return 0;
|
||||
|
||||
if (ct->status & IPS_NAT_MASK)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (manip == NF_NAT_MANIP_SRC)
|
||||
statusbit = IPS_SRC_NAT;
|
||||
else
|
||||
statusbit = IPS_DST_NAT;
|
||||
|
||||
/* Invert if this is reply dir. */
|
||||
if (dir == IP_CT_DIR_REPLY)
|
||||
statusbit ^= IPS_NAT_MASK;
|
||||
|
||||
if (!(ct->status & statusbit))
|
||||
return 1;
|
||||
|
||||
pr_debug("icmp_reply_translation: translating error %p manip %u "
|
||||
"dir %s\n", skb, manip,
|
||||
dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY");
|
||||
|
||||
/* Change inner back to look like incoming packet. We do the
|
||||
opposite manip on this hook to normal, because it might not
|
||||
pass all hooks (locally-generated ICMP). Consider incoming
|
||||
packet: PREROUTING (DST manip), routing produces ICMP, goes
|
||||
through POSTROUTING (which must correct the DST manip). */
|
||||
if (!manip_pkt(inside->ip.protocol, skb, hdrlen + sizeof(inside->icmp),
|
||||
&ct->tuplehash[!dir].tuple, !manip))
|
||||
return 0;
|
||||
|
||||
if (skb->ip_summed != CHECKSUM_PARTIAL) {
|
||||
/* Reloading "inside" here since manip_pkt inner. */
|
||||
inside = (void *)skb->data + hdrlen;
|
||||
inside->icmp.checksum = 0;
|
||||
inside->icmp.checksum =
|
||||
csum_fold(skb_checksum(skb, hdrlen,
|
||||
skb->len - hdrlen, 0));
|
||||
}
|
||||
|
||||
/* Change outer to look the reply to an incoming packet
|
||||
* (proto 0 means don't invert per-proto part). */
|
||||
nf_ct_invert_tuplepr(&target, &ct->tuplehash[!dir].tuple);
|
||||
if (!manip_pkt(0, skb, 0, &target, manip))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_nat_icmp_reply_translation);
|
||||
|
||||
/* Protocol registration. */
|
||||
int nf_nat_protocol_register(const struct nf_nat_protocol *proto)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_bh(&nf_nat_lock);
|
||||
if (rcu_dereference_protected(
|
||||
nf_nat_protos[proto->protonum],
|
||||
lockdep_is_held(&nf_nat_lock)
|
||||
) != &nf_nat_unknown_protocol) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
RCU_INIT_POINTER(nf_nat_protos[proto->protonum], proto);
|
||||
out:
|
||||
spin_unlock_bh(&nf_nat_lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(nf_nat_protocol_register);
|
||||
|
||||
/* No one stores the protocol anywhere; simply delete it. */
|
||||
void nf_nat_protocol_unregister(const struct nf_nat_protocol *proto)
|
||||
{
|
||||
spin_lock_bh(&nf_nat_lock);
|
||||
RCU_INIT_POINTER(nf_nat_protos[proto->protonum],
|
||||
&nf_nat_unknown_protocol);
|
||||
spin_unlock_bh(&nf_nat_lock);
|
||||
synchronize_rcu();
|
||||
}
|
||||
EXPORT_SYMBOL(nf_nat_protocol_unregister);
|
||||
|
||||
/* No one using conntrack by the time this called. */
|
||||
static void nf_nat_cleanup_conntrack(struct nf_conn *ct)
|
||||
{
|
||||
struct nf_conn_nat *nat = nf_ct_ext_find(ct, NF_CT_EXT_NAT);
|
||||
|
||||
if (nat == NULL || nat->ct == NULL)
|
||||
return;
|
||||
|
||||
NF_CT_ASSERT(nat->ct->status & IPS_SRC_NAT_DONE);
|
||||
|
||||
spin_lock_bh(&nf_nat_lock);
|
||||
hlist_del_rcu(&nat->bysource);
|
||||
spin_unlock_bh(&nf_nat_lock);
|
||||
}
|
||||
|
||||
static void nf_nat_move_storage(void *new, void *old)
|
||||
{
|
||||
struct nf_conn_nat *new_nat = new;
|
||||
struct nf_conn_nat *old_nat = old;
|
||||
struct nf_conn *ct = old_nat->ct;
|
||||
|
||||
if (!ct || !(ct->status & IPS_SRC_NAT_DONE))
|
||||
return;
|
||||
|
||||
spin_lock_bh(&nf_nat_lock);
|
||||
hlist_replace_rcu(&old_nat->bysource, &new_nat->bysource);
|
||||
spin_unlock_bh(&nf_nat_lock);
|
||||
}
|
||||
|
||||
static struct nf_ct_ext_type nat_extend __read_mostly = {
|
||||
.len = sizeof(struct nf_conn_nat),
|
||||
.align = __alignof__(struct nf_conn_nat),
|
||||
.destroy = nf_nat_cleanup_conntrack,
|
||||
.move = nf_nat_move_storage,
|
||||
.id = NF_CT_EXT_NAT,
|
||||
.flags = NF_CT_EXT_F_PREALLOC,
|
||||
};
|
||||
|
||||
#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
|
||||
|
||||
#include <linux/netfilter/nfnetlink.h>
|
||||
#include <linux/netfilter/nfnetlink_conntrack.h>
|
||||
|
||||
static const struct nla_policy protonat_nla_policy[CTA_PROTONAT_MAX+1] = {
|
||||
[CTA_PROTONAT_PORT_MIN] = { .type = NLA_U16 },
|
||||
[CTA_PROTONAT_PORT_MAX] = { .type = NLA_U16 },
|
||||
};
|
||||
|
||||
static int nfnetlink_parse_nat_proto(struct nlattr *attr,
|
||||
const struct nf_conn *ct,
|
||||
struct nf_nat_ipv4_range *range)
|
||||
{
|
||||
struct nlattr *tb[CTA_PROTONAT_MAX+1];
|
||||
const struct nf_nat_protocol *npt;
|
||||
int err;
|
||||
|
||||
err = nla_parse_nested(tb, CTA_PROTONAT_MAX, attr, protonat_nla_policy);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
rcu_read_lock();
|
||||
npt = __nf_nat_proto_find(nf_ct_protonum(ct));
|
||||
if (npt->nlattr_to_range)
|
||||
err = npt->nlattr_to_range(tb, range);
|
||||
rcu_read_unlock();
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct nla_policy nat_nla_policy[CTA_NAT_MAX+1] = {
|
||||
[CTA_NAT_MINIP] = { .type = NLA_U32 },
|
||||
[CTA_NAT_MAXIP] = { .type = NLA_U32 },
|
||||
[CTA_NAT_PROTO] = { .type = NLA_NESTED },
|
||||
};
|
||||
|
||||
static int
|
||||
nfnetlink_parse_nat(const struct nlattr *nat,
|
||||
const struct nf_conn *ct, struct nf_nat_ipv4_range *range)
|
||||
{
|
||||
struct nlattr *tb[CTA_NAT_MAX+1];
|
||||
int err;
|
||||
|
||||
memset(range, 0, sizeof(*range));
|
||||
|
||||
err = nla_parse_nested(tb, CTA_NAT_MAX, nat, nat_nla_policy);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (tb[CTA_NAT_MINIP])
|
||||
range->min_ip = nla_get_be32(tb[CTA_NAT_MINIP]);
|
||||
|
||||
if (!tb[CTA_NAT_MAXIP])
|
||||
range->max_ip = range->min_ip;
|
||||
else
|
||||
range->max_ip = nla_get_be32(tb[CTA_NAT_MAXIP]);
|
||||
|
||||
if (range->min_ip)
|
||||
range->flags |= NF_NAT_RANGE_MAP_IPS;
|
||||
|
||||
if (!tb[CTA_NAT_PROTO])
|
||||
return 0;
|
||||
|
||||
err = nfnetlink_parse_nat_proto(tb[CTA_NAT_PROTO], ct, range);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nfnetlink_parse_nat_setup(struct nf_conn *ct,
|
||||
enum nf_nat_manip_type manip,
|
||||
const struct nlattr *attr)
|
||||
{
|
||||
struct nf_nat_ipv4_range range;
|
||||
|
||||
if (nfnetlink_parse_nat(attr, ct, &range) < 0)
|
||||
return -EINVAL;
|
||||
if (nf_nat_initialized(ct, manip))
|
||||
return -EEXIST;
|
||||
|
||||
return nf_nat_setup_info(ct, &range, manip);
|
||||
}
|
||||
#else
|
||||
static int
|
||||
nfnetlink_parse_nat_setup(struct nf_conn *ct,
|
||||
enum nf_nat_manip_type manip,
|
||||
const struct nlattr *attr)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __net_init nf_nat_net_init(struct net *net)
|
||||
{
|
||||
/* Leave them the same for the moment. */
|
||||
net->ipv4.nat_htable_size = net->ct.htable_size;
|
||||
net->ipv4.nat_bysource = nf_ct_alloc_hashtable(&net->ipv4.nat_htable_size, 0);
|
||||
if (!net->ipv4.nat_bysource)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Clear NAT section of all conntracks, in case we're loaded again. */
|
||||
static int clean_nat(struct nf_conn *i, void *data)
|
||||
{
|
||||
struct nf_conn_nat *nat = nfct_nat(i);
|
||||
|
||||
if (!nat)
|
||||
return 0;
|
||||
memset(nat, 0, sizeof(*nat));
|
||||
i->status &= ~(IPS_NAT_MASK | IPS_NAT_DONE_MASK | IPS_SEQ_ADJUST);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __net_exit nf_nat_net_exit(struct net *net)
|
||||
{
|
||||
nf_ct_iterate_cleanup(net, &clean_nat, NULL);
|
||||
synchronize_rcu();
|
||||
nf_ct_free_hashtable(net->ipv4.nat_bysource, net->ipv4.nat_htable_size);
|
||||
}
|
||||
|
||||
static struct pernet_operations nf_nat_net_ops = {
|
||||
.init = nf_nat_net_init,
|
||||
.exit = nf_nat_net_exit,
|
||||
};
|
||||
|
||||
static struct nf_ct_helper_expectfn follow_master_nat = {
|
||||
.name = "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)
|
||||
{
|
||||
size_t i;
|
||||
int ret;
|
||||
|
||||
need_ipv4_conntrack();
|
||||
|
||||
ret = nf_ct_extend_register(&nat_extend);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "nf_nat_core: Unable to register extension\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = register_pernet_subsys(&nf_nat_net_ops);
|
||||
if (ret < 0)
|
||||
goto cleanup_extend;
|
||||
|
||||
/* Sew in builtin protocols. */
|
||||
spin_lock_bh(&nf_nat_lock);
|
||||
for (i = 0; i < MAX_IP_NAT_PROTO; i++)
|
||||
RCU_INIT_POINTER(nf_nat_protos[i], &nf_nat_unknown_protocol);
|
||||
RCU_INIT_POINTER(nf_nat_protos[IPPROTO_TCP], &nf_nat_protocol_tcp);
|
||||
RCU_INIT_POINTER(nf_nat_protos[IPPROTO_UDP], &nf_nat_protocol_udp);
|
||||
RCU_INIT_POINTER(nf_nat_protos[IPPROTO_ICMP], &nf_nat_protocol_icmp);
|
||||
spin_unlock_bh(&nf_nat_lock);
|
||||
|
||||
/* Initialize fake conntrack so that NAT will skip it */
|
||||
nf_ct_untracked_status_or(IPS_NAT_DONE_MASK);
|
||||
|
||||
l3proto = nf_ct_l3proto_find_get((u_int16_t)AF_INET);
|
||||
|
||||
nf_ct_helper_expectfn_register(&follow_master_nat);
|
||||
|
||||
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);
|
||||
RCU_INIT_POINTER(nfnetlink_parse_nat_setup_hook,
|
||||
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);
|
||||
return 0;
|
||||
|
||||
cleanup_extend:
|
||||
nf_ct_extend_unregister(&nat_extend);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit nf_nat_cleanup(void)
|
||||
{
|
||||
unregister_pernet_subsys(&nf_nat_net_ops);
|
||||
nf_ct_l3proto_put(l3proto);
|
||||
nf_ct_extend_unregister(&nat_extend);
|
||||
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(nf_ct_nat_offset, NULL);
|
||||
RCU_INIT_POINTER(nfq_ct_nat_hook, NULL);
|
||||
synchronize_net();
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("nf-nat-ipv4");
|
||||
|
||||
module_init(nf_nat_init);
|
||||
module_exit(nf_nat_cleanup);
|
@@ -1,137 +0,0 @@
|
||||
/* FTP extension for TCP NAT alteration. */
|
||||
|
||||
/* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#include <net/netfilter/nf_nat.h>
|
||||
#include <net/netfilter/nf_nat_helper.h>
|
||||
#include <net/netfilter/nf_nat_rule.h>
|
||||
#include <net/netfilter/nf_conntrack_helper.h>
|
||||
#include <net/netfilter/nf_conntrack_expect.h>
|
||||
#include <linux/netfilter/nf_conntrack_ftp.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Rusty Russell <rusty@rustcorp.com.au>");
|
||||
MODULE_DESCRIPTION("ftp NAT helper");
|
||||
MODULE_ALIAS("ip_nat_ftp");
|
||||
|
||||
/* FIXME: Time out? --RR */
|
||||
|
||||
static int nf_nat_ftp_fmt_cmd(enum nf_ct_ftp_type type,
|
||||
char *buffer, size_t buflen,
|
||||
__be32 addr, u16 port)
|
||||
{
|
||||
switch (type) {
|
||||
case NF_CT_FTP_PORT:
|
||||
case NF_CT_FTP_PASV:
|
||||
return snprintf(buffer, buflen, "%u,%u,%u,%u,%u,%u",
|
||||
((unsigned char *)&addr)[0],
|
||||
((unsigned char *)&addr)[1],
|
||||
((unsigned char *)&addr)[2],
|
||||
((unsigned char *)&addr)[3],
|
||||
port >> 8,
|
||||
port & 0xFF);
|
||||
case NF_CT_FTP_EPRT:
|
||||
return snprintf(buffer, buflen, "|1|%pI4|%u|", &addr, port);
|
||||
case NF_CT_FTP_EPSV:
|
||||
return snprintf(buffer, buflen, "|||%u|", port);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* So, this packet has hit the connection tracking matching code.
|
||||
Mangle it, and change the expectation to match the new version. */
|
||||
static unsigned int nf_nat_ftp(struct sk_buff *skb,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
enum nf_ct_ftp_type type,
|
||||
unsigned int matchoff,
|
||||
unsigned int matchlen,
|
||||
struct nf_conntrack_expect *exp)
|
||||
{
|
||||
__be32 newip;
|
||||
u_int16_t port;
|
||||
int dir = CTINFO2DIR(ctinfo);
|
||||
struct nf_conn *ct = exp->master;
|
||||
char buffer[sizeof("|1|255.255.255.255|65535|")];
|
||||
unsigned int buflen;
|
||||
|
||||
pr_debug("FTP_NAT: type %i, off %u len %u\n", type, matchoff, matchlen);
|
||||
|
||||
/* Connection will come from wherever this packet goes, hence !dir */
|
||||
newip = ct->tuplehash[!dir].tuple.dst.u3.ip;
|
||||
exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port;
|
||||
exp->dir = !dir;
|
||||
|
||||
/* When you see the packet, we need to NAT it the same as the
|
||||
* this one. */
|
||||
exp->expectfn = nf_nat_follow_master;
|
||||
|
||||
/* Try to get same port: if not, try to change it. */
|
||||
for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) {
|
||||
int ret;
|
||||
|
||||
exp->tuple.dst.u.tcp.port = htons(port);
|
||||
ret = nf_ct_expect_related(exp);
|
||||
if (ret == 0)
|
||||
break;
|
||||
else if (ret != -EBUSY) {
|
||||
port = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (port == 0)
|
||||
return NF_DROP;
|
||||
|
||||
buflen = nf_nat_ftp_fmt_cmd(type, buffer, sizeof(buffer), newip, port);
|
||||
if (!buflen)
|
||||
goto out;
|
||||
|
||||
pr_debug("calling nf_nat_mangle_tcp_packet\n");
|
||||
|
||||
if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, matchoff,
|
||||
matchlen, buffer, buflen))
|
||||
goto out;
|
||||
|
||||
return NF_ACCEPT;
|
||||
|
||||
out:
|
||||
nf_ct_unexpect_related(exp);
|
||||
return NF_DROP;
|
||||
}
|
||||
|
||||
static void __exit nf_nat_ftp_fini(void)
|
||||
{
|
||||
RCU_INIT_POINTER(nf_nat_ftp_hook, NULL);
|
||||
synchronize_rcu();
|
||||
}
|
||||
|
||||
static int __init nf_nat_ftp_init(void)
|
||||
{
|
||||
BUG_ON(nf_nat_ftp_hook != NULL);
|
||||
RCU_INIT_POINTER(nf_nat_ftp_hook, nf_nat_ftp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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(nf_nat_ftp_init);
|
||||
module_exit(nf_nat_ftp_fini);
|
@@ -15,13 +15,12 @@
|
||||
|
||||
#include <net/netfilter/nf_nat.h>
|
||||
#include <net/netfilter/nf_nat_helper.h>
|
||||
#include <net/netfilter/nf_nat_rule.h>
|
||||
#include <net/netfilter/nf_conntrack_helper.h>
|
||||
#include <net/netfilter/nf_conntrack_expect.h>
|
||||
#include <linux/netfilter/nf_conntrack_h323.h>
|
||||
|
||||
/****************************************************************************/
|
||||
static int set_addr(struct sk_buff *skb,
|
||||
static int set_addr(struct sk_buff *skb, unsigned int protoff,
|
||||
unsigned char **data, int dataoff,
|
||||
unsigned int addroff, __be32 ip, __be16 port)
|
||||
{
|
||||
@@ -40,7 +39,7 @@ static int set_addr(struct sk_buff *skb,
|
||||
|
||||
if (ip_hdr(skb)->protocol == IPPROTO_TCP) {
|
||||
if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo,
|
||||
addroff, sizeof(buf),
|
||||
protoff, addroff, sizeof(buf),
|
||||
(char *) &buf, sizeof(buf))) {
|
||||
net_notice_ratelimited("nf_nat_h323: nf_nat_mangle_tcp_packet error\n");
|
||||
return -1;
|
||||
@@ -54,7 +53,7 @@ static int set_addr(struct sk_buff *skb,
|
||||
*data = skb->data + ip_hdrlen(skb) + th->doff * 4 + dataoff;
|
||||
} else {
|
||||
if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo,
|
||||
addroff, sizeof(buf),
|
||||
protoff, addroff, sizeof(buf),
|
||||
(char *) &buf, sizeof(buf))) {
|
||||
net_notice_ratelimited("nf_nat_h323: nf_nat_mangle_udp_packet error\n");
|
||||
return -1;
|
||||
@@ -69,22 +68,22 @@ static int set_addr(struct sk_buff *skb,
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
static int set_h225_addr(struct sk_buff *skb,
|
||||
static int set_h225_addr(struct sk_buff *skb, unsigned int protoff,
|
||||
unsigned char **data, int dataoff,
|
||||
TransportAddress *taddr,
|
||||
union nf_inet_addr *addr, __be16 port)
|
||||
{
|
||||
return set_addr(skb, data, dataoff, taddr->ipAddress.ip,
|
||||
return set_addr(skb, protoff, data, dataoff, taddr->ipAddress.ip,
|
||||
addr->ip, port);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
static int set_h245_addr(struct sk_buff *skb,
|
||||
static int set_h245_addr(struct sk_buff *skb, unsigned protoff,
|
||||
unsigned char **data, int dataoff,
|
||||
H245_TransportAddress *taddr,
|
||||
union nf_inet_addr *addr, __be16 port)
|
||||
{
|
||||
return set_addr(skb, data, dataoff,
|
||||
return set_addr(skb, protoff, data, dataoff,
|
||||
taddr->unicastAddress.iPAddress.network,
|
||||
addr->ip, port);
|
||||
}
|
||||
@@ -92,7 +91,7 @@ static int set_h245_addr(struct sk_buff *skb,
|
||||
/****************************************************************************/
|
||||
static int set_sig_addr(struct sk_buff *skb, struct nf_conn *ct,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
unsigned char **data,
|
||||
unsigned int protoff, unsigned char **data,
|
||||
TransportAddress *taddr, int count)
|
||||
{
|
||||
const struct nf_ct_h323_master *info = nfct_help_data(ct);
|
||||
@@ -118,7 +117,8 @@ static int set_sig_addr(struct sk_buff *skb, struct nf_conn *ct,
|
||||
&addr.ip, port,
|
||||
&ct->tuplehash[!dir].tuple.dst.u3.ip,
|
||||
info->sig_port[!dir]);
|
||||
return set_h225_addr(skb, data, 0, &taddr[i],
|
||||
return set_h225_addr(skb, protoff, data, 0,
|
||||
&taddr[i],
|
||||
&ct->tuplehash[!dir].
|
||||
tuple.dst.u3,
|
||||
info->sig_port[!dir]);
|
||||
@@ -129,7 +129,8 @@ static int set_sig_addr(struct sk_buff *skb, struct nf_conn *ct,
|
||||
&addr.ip, port,
|
||||
&ct->tuplehash[!dir].tuple.src.u3.ip,
|
||||
info->sig_port[!dir]);
|
||||
return set_h225_addr(skb, data, 0, &taddr[i],
|
||||
return set_h225_addr(skb, protoff, data, 0,
|
||||
&taddr[i],
|
||||
&ct->tuplehash[!dir].
|
||||
tuple.src.u3,
|
||||
info->sig_port[!dir]);
|
||||
@@ -143,7 +144,7 @@ static int set_sig_addr(struct sk_buff *skb, struct nf_conn *ct,
|
||||
/****************************************************************************/
|
||||
static int set_ras_addr(struct sk_buff *skb, struct nf_conn *ct,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
unsigned char **data,
|
||||
unsigned int protoff, unsigned char **data,
|
||||
TransportAddress *taddr, int count)
|
||||
{
|
||||
int dir = CTINFO2DIR(ctinfo);
|
||||
@@ -159,7 +160,7 @@ static int set_ras_addr(struct sk_buff *skb, struct nf_conn *ct,
|
||||
&addr.ip, ntohs(port),
|
||||
&ct->tuplehash[!dir].tuple.dst.u3.ip,
|
||||
ntohs(ct->tuplehash[!dir].tuple.dst.u.udp.port));
|
||||
return set_h225_addr(skb, data, 0, &taddr[i],
|
||||
return set_h225_addr(skb, protoff, data, 0, &taddr[i],
|
||||
&ct->tuplehash[!dir].tuple.dst.u3,
|
||||
ct->tuplehash[!dir].tuple.
|
||||
dst.u.udp.port);
|
||||
@@ -172,7 +173,7 @@ static int set_ras_addr(struct sk_buff *skb, struct nf_conn *ct,
|
||||
/****************************************************************************/
|
||||
static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
unsigned char **data, int dataoff,
|
||||
unsigned int protoff, unsigned char **data, int dataoff,
|
||||
H245_TransportAddress *taddr,
|
||||
__be16 port, __be16 rtp_port,
|
||||
struct nf_conntrack_expect *rtp_exp,
|
||||
@@ -244,7 +245,7 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct,
|
||||
}
|
||||
|
||||
/* Modify signal */
|
||||
if (set_h245_addr(skb, data, dataoff, taddr,
|
||||
if (set_h245_addr(skb, protoff, data, dataoff, taddr,
|
||||
&ct->tuplehash[!dir].tuple.dst.u3,
|
||||
htons((port & htons(1)) ? nated_port + 1 :
|
||||
nated_port)) == 0) {
|
||||
@@ -275,7 +276,7 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct,
|
||||
/****************************************************************************/
|
||||
static int nat_t120(struct sk_buff *skb, struct nf_conn *ct,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
unsigned char **data, int dataoff,
|
||||
unsigned int protoff, unsigned char **data, int dataoff,
|
||||
H245_TransportAddress *taddr, __be16 port,
|
||||
struct nf_conntrack_expect *exp)
|
||||
{
|
||||
@@ -307,7 +308,7 @@ static int nat_t120(struct sk_buff *skb, struct nf_conn *ct,
|
||||
}
|
||||
|
||||
/* Modify signal */
|
||||
if (set_h245_addr(skb, data, dataoff, taddr,
|
||||
if (set_h245_addr(skb, protoff, data, dataoff, taddr,
|
||||
&ct->tuplehash[!dir].tuple.dst.u3,
|
||||
htons(nated_port)) < 0) {
|
||||
nf_ct_unexpect_related(exp);
|
||||
@@ -326,7 +327,7 @@ static int nat_t120(struct sk_buff *skb, struct nf_conn *ct,
|
||||
/****************************************************************************/
|
||||
static int nat_h245(struct sk_buff *skb, struct nf_conn *ct,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
unsigned char **data, int dataoff,
|
||||
unsigned int protoff, unsigned char **data, int dataoff,
|
||||
TransportAddress *taddr, __be16 port,
|
||||
struct nf_conntrack_expect *exp)
|
||||
{
|
||||
@@ -363,7 +364,7 @@ static int nat_h245(struct sk_buff *skb, struct nf_conn *ct,
|
||||
}
|
||||
|
||||
/* Modify signal */
|
||||
if (set_h225_addr(skb, data, dataoff, taddr,
|
||||
if (set_h225_addr(skb, protoff, data, dataoff, taddr,
|
||||
&ct->tuplehash[!dir].tuple.dst.u3,
|
||||
htons(nated_port)) == 0) {
|
||||
/* Save ports */
|
||||
@@ -390,7 +391,7 @@ static int nat_h245(struct sk_buff *skb, struct nf_conn *ct,
|
||||
static void ip_nat_q931_expect(struct nf_conn *new,
|
||||
struct nf_conntrack_expect *this)
|
||||
{
|
||||
struct nf_nat_ipv4_range range;
|
||||
struct nf_nat_range range;
|
||||
|
||||
if (this->tuple.src.u3.ip != 0) { /* Only accept calls from GK */
|
||||
nf_nat_follow_master(new, this);
|
||||
@@ -402,21 +403,23 @@ static void ip_nat_q931_expect(struct nf_conn *new,
|
||||
|
||||
/* Change src to where master sends to */
|
||||
range.flags = NF_NAT_RANGE_MAP_IPS;
|
||||
range.min_ip = range.max_ip = new->tuplehash[!this->dir].tuple.src.u3.ip;
|
||||
range.min_addr = range.max_addr =
|
||||
new->tuplehash[!this->dir].tuple.src.u3;
|
||||
nf_nat_setup_info(new, &range, NF_NAT_MANIP_SRC);
|
||||
|
||||
/* For DST manip, map port here to where it's expected. */
|
||||
range.flags = (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED);
|
||||
range.min = range.max = this->saved_proto;
|
||||
range.min_ip = range.max_ip =
|
||||
new->master->tuplehash[!this->dir].tuple.src.u3.ip;
|
||||
range.min_proto = range.max_proto = this->saved_proto;
|
||||
range.min_addr = range.max_addr =
|
||||
new->master->tuplehash[!this->dir].tuple.src.u3;
|
||||
nf_nat_setup_info(new, &range, NF_NAT_MANIP_DST);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
static int nat_q931(struct sk_buff *skb, struct nf_conn *ct,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
unsigned char **data, TransportAddress *taddr, int idx,
|
||||
unsigned int protoff, unsigned char **data,
|
||||
TransportAddress *taddr, int idx,
|
||||
__be16 port, struct nf_conntrack_expect *exp)
|
||||
{
|
||||
struct nf_ct_h323_master *info = nfct_help_data(ct);
|
||||
@@ -453,7 +456,7 @@ static int nat_q931(struct sk_buff *skb, struct nf_conn *ct,
|
||||
}
|
||||
|
||||
/* Modify signal */
|
||||
if (set_h225_addr(skb, data, 0, &taddr[idx],
|
||||
if (set_h225_addr(skb, protoff, data, 0, &taddr[idx],
|
||||
&ct->tuplehash[!dir].tuple.dst.u3,
|
||||
htons(nated_port)) == 0) {
|
||||
/* Save ports */
|
||||
@@ -464,7 +467,7 @@ static int nat_q931(struct sk_buff *skb, struct nf_conn *ct,
|
||||
if (idx > 0 &&
|
||||
get_h225_addr(ct, *data, &taddr[0], &addr, &port) &&
|
||||
(ntohl(addr.ip) & 0xff000000) == 0x7f000000) {
|
||||
set_h225_addr(skb, data, 0, &taddr[0],
|
||||
set_h225_addr(skb, protoff, data, 0, &taddr[0],
|
||||
&ct->tuplehash[!dir].tuple.dst.u3,
|
||||
info->sig_port[!dir]);
|
||||
}
|
||||
@@ -487,26 +490,28 @@ static int nat_q931(struct sk_buff *skb, struct nf_conn *ct,
|
||||
static void ip_nat_callforwarding_expect(struct nf_conn *new,
|
||||
struct nf_conntrack_expect *this)
|
||||
{
|
||||
struct nf_nat_ipv4_range range;
|
||||
struct nf_nat_range range;
|
||||
|
||||
/* This must be a fresh one. */
|
||||
BUG_ON(new->status & IPS_NAT_DONE_MASK);
|
||||
|
||||
/* Change src to where master sends to */
|
||||
range.flags = NF_NAT_RANGE_MAP_IPS;
|
||||
range.min_ip = range.max_ip = new->tuplehash[!this->dir].tuple.src.u3.ip;
|
||||
range.min_addr = range.max_addr =
|
||||
new->tuplehash[!this->dir].tuple.src.u3;
|
||||
nf_nat_setup_info(new, &range, NF_NAT_MANIP_SRC);
|
||||
|
||||
/* For DST manip, map port here to where it's expected. */
|
||||
range.flags = (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED);
|
||||
range.min = range.max = this->saved_proto;
|
||||
range.min_ip = range.max_ip = this->saved_ip;
|
||||
range.min_proto = range.max_proto = this->saved_proto;
|
||||
range.min_addr = range.max_addr = this->saved_addr;
|
||||
nf_nat_setup_info(new, &range, NF_NAT_MANIP_DST);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
static int nat_callforwarding(struct sk_buff *skb, struct nf_conn *ct,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
unsigned int protoff,
|
||||
unsigned char **data, int dataoff,
|
||||
TransportAddress *taddr, __be16 port,
|
||||
struct nf_conntrack_expect *exp)
|
||||
@@ -515,7 +520,7 @@ static int nat_callforwarding(struct sk_buff *skb, struct nf_conn *ct,
|
||||
u_int16_t nated_port;
|
||||
|
||||
/* Set expectations for NAT */
|
||||
exp->saved_ip = exp->tuple.dst.u3.ip;
|
||||
exp->saved_addr = exp->tuple.dst.u3;
|
||||
exp->tuple.dst.u3.ip = ct->tuplehash[!dir].tuple.dst.u3.ip;
|
||||
exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port;
|
||||
exp->expectfn = ip_nat_callforwarding_expect;
|
||||
@@ -541,7 +546,7 @@ static int nat_callforwarding(struct sk_buff *skb, struct nf_conn *ct,
|
||||
}
|
||||
|
||||
/* Modify signal */
|
||||
if (!set_h225_addr(skb, data, dataoff, taddr,
|
||||
if (!set_h225_addr(skb, protoff, data, dataoff, taddr,
|
||||
&ct->tuplehash[!dir].tuple.dst.u3,
|
||||
htons(nated_port)) == 0) {
|
||||
nf_ct_unexpect_related(exp);
|
||||
|
@@ -1,458 +0,0 @@
|
||||
/* ip_nat_helper.c - generic support functions for NAT helpers
|
||||
*
|
||||
* (C) 2000-2002 Harald Welte <laforge@netfilter.org>
|
||||
* (C) 2003-2006 Netfilter Core Team <coreteam@netfilter.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/kmod.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/udp.h>
|
||||
#include <net/checksum.h>
|
||||
#include <net/tcp.h>
|
||||
#include <net/route.h>
|
||||
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_helper.h>
|
||||
#include <net/netfilter/nf_conntrack_ecache.h>
|
||||
#include <net/netfilter/nf_conntrack_expect.h>
|
||||
#include <net/netfilter/nf_nat.h>
|
||||
#include <net/netfilter/nf_nat_protocol.h>
|
||||
#include <net/netfilter/nf_nat_core.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);
|
||||
|
||||
static DEFINE_SPINLOCK(nf_nat_seqofs_lock);
|
||||
|
||||
/* 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(&nf_nat_seqofs_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(&nf_nat_seqofs_lock);
|
||||
|
||||
pr_debug("adjust_tcp_sequence: Seq_offset after: ");
|
||||
DUMP_OFFSET(this_way);
|
||||
}
|
||||
|
||||
/* Get the offset value, for conntrack */
|
||||
s16 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;
|
||||
s16 offset;
|
||||
|
||||
if (!nat)
|
||||
return 0;
|
||||
|
||||
this_way = &nat->seq[dir];
|
||||
spin_lock_bh(&nf_nat_seqofs_lock);
|
||||
offset = after(seq, this_way->correction_pos)
|
||||
? this_way->offset_after : this_way->offset_before;
|
||||
spin_unlock_bh(&nf_nat_seqofs_lock);
|
||||
|
||||
return offset;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_nat_get_offset);
|
||||
|
||||
/* Frobs data inside this packet, which is linear. */
|
||||
static void mangle_contents(struct sk_buff *skb,
|
||||
unsigned int dataoff,
|
||||
unsigned int match_offset,
|
||||
unsigned int match_len,
|
||||
const char *rep_buffer,
|
||||
unsigned int rep_len)
|
||||
{
|
||||
unsigned char *data;
|
||||
|
||||
BUG_ON(skb_is_nonlinear(skb));
|
||||
data = skb_network_header(skb) + dataoff;
|
||||
|
||||
/* move post-replacement */
|
||||
memmove(data + match_offset + rep_len,
|
||||
data + match_offset + match_len,
|
||||
skb->tail - (skb->network_header + dataoff +
|
||||
match_offset + match_len));
|
||||
|
||||
/* insert data from buffer */
|
||||
memcpy(data + match_offset, rep_buffer, rep_len);
|
||||
|
||||
/* update skb info */
|
||||
if (rep_len > match_len) {
|
||||
pr_debug("nf_nat_mangle_packet: Extending packet by "
|
||||
"%u from %u bytes\n", rep_len - match_len, skb->len);
|
||||
skb_put(skb, rep_len - match_len);
|
||||
} else {
|
||||
pr_debug("nf_nat_mangle_packet: Shrinking packet from "
|
||||
"%u from %u bytes\n", match_len - rep_len, skb->len);
|
||||
__skb_trim(skb, skb->len + rep_len - match_len);
|
||||
}
|
||||
|
||||
/* fix IP hdr checksum information */
|
||||
ip_hdr(skb)->tot_len = htons(skb->len);
|
||||
ip_send_check(ip_hdr(skb));
|
||||
}
|
||||
|
||||
/* Unusual, but possible case. */
|
||||
static int enlarge_skb(struct sk_buff *skb, unsigned int extra)
|
||||
{
|
||||
if (skb->len + extra > 65535)
|
||||
return 0;
|
||||
|
||||
if (pskb_expand_head(skb, 0, extra - skb_tailroom(skb), GFP_ATOMIC))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void nf_nat_set_seq_adjust(struct nf_conn *ct, enum ip_conntrack_info ctinfo,
|
||||
__be32 seq, s16 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);
|
||||
|
||||
static void nf_nat_csum(struct sk_buff *skb, const struct iphdr *iph, void *data,
|
||||
int datalen, __sum16 *check, int oldlen)
|
||||
{
|
||||
struct rtable *rt = skb_rtable(skb);
|
||||
|
||||
if (skb->ip_summed != CHECKSUM_PARTIAL) {
|
||||
if (!(rt->rt_flags & RTCF_LOCAL) &&
|
||||
(!skb->dev || skb->dev->features & NETIF_F_V4_CSUM)) {
|
||||
skb->ip_summed = CHECKSUM_PARTIAL;
|
||||
skb->csum_start = skb_headroom(skb) +
|
||||
skb_network_offset(skb) +
|
||||
iph->ihl * 4;
|
||||
skb->csum_offset = (void *)check - data;
|
||||
*check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
|
||||
datalen, iph->protocol, 0);
|
||||
} else {
|
||||
*check = 0;
|
||||
*check = csum_tcpudp_magic(iph->saddr, iph->daddr,
|
||||
datalen, iph->protocol,
|
||||
csum_partial(data, datalen,
|
||||
0));
|
||||
if (iph->protocol == IPPROTO_UDP && !*check)
|
||||
*check = CSUM_MANGLED_0;
|
||||
}
|
||||
} else
|
||||
inet_proto_csum_replace2(check, skb,
|
||||
htons(oldlen), htons(datalen), 1);
|
||||
}
|
||||
|
||||
/* Generic function for mangling variable-length address changes inside
|
||||
* NATed TCP connections (like the PORT XXX,XXX,XXX,XXX,XXX,XXX
|
||||
* command in FTP).
|
||||
*
|
||||
* Takes care about all the nasty sequence number changes, checksumming,
|
||||
* skb enlargement, ...
|
||||
*
|
||||
* */
|
||||
int __nf_nat_mangle_tcp_packet(struct sk_buff *skb,
|
||||
struct nf_conn *ct,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
unsigned int match_offset,
|
||||
unsigned int match_len,
|
||||
const char *rep_buffer,
|
||||
unsigned int rep_len, bool adjust)
|
||||
{
|
||||
struct iphdr *iph;
|
||||
struct tcphdr *tcph;
|
||||
int oldlen, datalen;
|
||||
|
||||
if (!skb_make_writable(skb, skb->len))
|
||||
return 0;
|
||||
|
||||
if (rep_len > match_len &&
|
||||
rep_len - match_len > skb_tailroom(skb) &&
|
||||
!enlarge_skb(skb, rep_len - match_len))
|
||||
return 0;
|
||||
|
||||
SKB_LINEAR_ASSERT(skb);
|
||||
|
||||
iph = ip_hdr(skb);
|
||||
tcph = (void *)iph + iph->ihl*4;
|
||||
|
||||
oldlen = skb->len - iph->ihl*4;
|
||||
mangle_contents(skb, iph->ihl*4 + tcph->doff*4,
|
||||
match_offset, match_len, rep_buffer, rep_len);
|
||||
|
||||
datalen = skb->len - iph->ihl*4;
|
||||
nf_nat_csum(skb, iph, tcph, datalen, &tcph->check, oldlen);
|
||||
|
||||
if (adjust && rep_len != match_len)
|
||||
nf_nat_set_seq_adjust(ct, ctinfo, tcph->seq,
|
||||
(int)rep_len - (int)match_len);
|
||||
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL(__nf_nat_mangle_tcp_packet);
|
||||
|
||||
/* Generic function for mangling variable-length address changes inside
|
||||
* NATed UDP connections (like the CONNECT DATA XXXXX MESG XXXXX INDEX XXXXX
|
||||
* command in the Amanda protocol)
|
||||
*
|
||||
* Takes care about all the nasty sequence number changes, checksumming,
|
||||
* skb enlargement, ...
|
||||
*
|
||||
* XXX - This function could be merged with nf_nat_mangle_tcp_packet which
|
||||
* should be fairly easy to do.
|
||||
*/
|
||||
int
|
||||
nf_nat_mangle_udp_packet(struct sk_buff *skb,
|
||||
struct nf_conn *ct,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
unsigned int match_offset,
|
||||
unsigned int match_len,
|
||||
const char *rep_buffer,
|
||||
unsigned int rep_len)
|
||||
{
|
||||
struct iphdr *iph;
|
||||
struct udphdr *udph;
|
||||
int datalen, oldlen;
|
||||
|
||||
if (!skb_make_writable(skb, skb->len))
|
||||
return 0;
|
||||
|
||||
if (rep_len > match_len &&
|
||||
rep_len - match_len > skb_tailroom(skb) &&
|
||||
!enlarge_skb(skb, rep_len - match_len))
|
||||
return 0;
|
||||
|
||||
iph = ip_hdr(skb);
|
||||
udph = (void *)iph + iph->ihl*4;
|
||||
|
||||
oldlen = skb->len - iph->ihl*4;
|
||||
mangle_contents(skb, iph->ihl*4 + sizeof(*udph),
|
||||
match_offset, match_len, rep_buffer, rep_len);
|
||||
|
||||
/* update the length of the UDP packet */
|
||||
datalen = skb->len - iph->ihl*4;
|
||||
udph->len = htons(datalen);
|
||||
|
||||
/* fix udp checksum if udp checksum was previously calculated */
|
||||
if (!udph->check && skb->ip_summed != CHECKSUM_PARTIAL)
|
||||
return 1;
|
||||
|
||||
nf_nat_csum(skb, iph, udph, datalen, &udph->check, oldlen);
|
||||
|
||||
return 1;
|
||||
}
|
||||
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,
|
||||
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 = ip_hdrlen(skb) + sizeof(struct tcphdr);
|
||||
optend = ip_hdrlen(skb) + 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)
|
||||
{
|
||||
struct tcphdr *tcph;
|
||||
int dir;
|
||||
__be32 newseq, newack;
|
||||
s16 seqoff, ackoff;
|
||||
struct nf_conn_nat *nat = nfct_nat(ct);
|
||||
struct nf_nat_seq *this_way, *other_way;
|
||||
|
||||
dir = CTINFO2DIR(ctinfo);
|
||||
|
||||
this_way = &nat->seq[dir];
|
||||
other_way = &nat->seq[!dir];
|
||||
|
||||
if (!skb_make_writable(skb, ip_hdrlen(skb) + sizeof(*tcph)))
|
||||
return 0;
|
||||
|
||||
tcph = (void *)skb->data + ip_hdrlen(skb);
|
||||
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;
|
||||
|
||||
return nf_nat_sack_adjust(skb, tcph, ct, ctinfo);
|
||||
}
|
||||
|
||||
/* 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 nf_nat_follow_master(struct nf_conn *ct,
|
||||
struct nf_conntrack_expect *exp)
|
||||
{
|
||||
struct nf_nat_ipv4_range range;
|
||||
|
||||
/* This must be a fresh one. */
|
||||
BUG_ON(ct->status & IPS_NAT_DONE_MASK);
|
||||
|
||||
/* Change src to where master sends to */
|
||||
range.flags = NF_NAT_RANGE_MAP_IPS;
|
||||
range.min_ip = range.max_ip
|
||||
= ct->master->tuplehash[!exp->dir].tuple.dst.u3.ip;
|
||||
nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC);
|
||||
|
||||
/* For DST manip, map port here to where it's expected. */
|
||||
range.flags = (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED);
|
||||
range.min = range.max = exp->saved_proto;
|
||||
range.min_ip = range.max_ip
|
||||
= ct->master->tuplehash[!exp->dir].tuple.src.u3.ip;
|
||||
nf_nat_setup_info(ct, &range, NF_NAT_MANIP_DST);
|
||||
}
|
||||
EXPORT_SYMBOL(nf_nat_follow_master);
|
@@ -1,99 +0,0 @@
|
||||
/* IRC extension for TCP NAT alteration.
|
||||
*
|
||||
* (C) 2000-2001 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2004 Rusty Russell <rusty@rustcorp.com.au> IBM Corporation
|
||||
* based on a copy of RR's ip_nat_ftp.c
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include <net/netfilter/nf_nat.h>
|
||||
#include <net/netfilter/nf_nat_helper.h>
|
||||
#include <net/netfilter/nf_nat_rule.h>
|
||||
#include <net/netfilter/nf_conntrack_helper.h>
|
||||
#include <net/netfilter/nf_conntrack_expect.h>
|
||||
#include <linux/netfilter/nf_conntrack_irc.h>
|
||||
|
||||
MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
|
||||
MODULE_DESCRIPTION("IRC (DCC) NAT helper");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("ip_nat_irc");
|
||||
|
||||
static unsigned int help(struct sk_buff *skb,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
unsigned int matchoff,
|
||||
unsigned int matchlen,
|
||||
struct nf_conntrack_expect *exp)
|
||||
{
|
||||
char buffer[sizeof("4294967296 65635")];
|
||||
u_int32_t ip;
|
||||
u_int16_t port;
|
||||
unsigned int ret;
|
||||
|
||||
/* Reply comes from server. */
|
||||
exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port;
|
||||
exp->dir = IP_CT_DIR_REPLY;
|
||||
exp->expectfn = nf_nat_follow_master;
|
||||
|
||||
/* Try to get same port: if not, try to change it. */
|
||||
for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) {
|
||||
int ret;
|
||||
|
||||
exp->tuple.dst.u.tcp.port = htons(port);
|
||||
ret = nf_ct_expect_related(exp);
|
||||
if (ret == 0)
|
||||
break;
|
||||
else if (ret != -EBUSY) {
|
||||
port = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (port == 0)
|
||||
return NF_DROP;
|
||||
|
||||
ip = ntohl(exp->master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip);
|
||||
sprintf(buffer, "%u %u", ip, port);
|
||||
pr_debug("nf_nat_irc: inserting '%s' == %pI4, port %u\n",
|
||||
buffer, &ip, port);
|
||||
|
||||
ret = nf_nat_mangle_tcp_packet(skb, exp->master, ctinfo,
|
||||
matchoff, matchlen, buffer,
|
||||
strlen(buffer));
|
||||
if (ret != NF_ACCEPT)
|
||||
nf_ct_unexpect_related(exp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit nf_nat_irc_fini(void)
|
||||
{
|
||||
RCU_INIT_POINTER(nf_nat_irc_hook, NULL);
|
||||
synchronize_rcu();
|
||||
}
|
||||
|
||||
static int __init nf_nat_irc_init(void)
|
||||
{
|
||||
BUG_ON(nf_nat_irc_hook != NULL);
|
||||
RCU_INIT_POINTER(nf_nat_irc_hook, help);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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(nf_nat_irc_init);
|
||||
module_exit(nf_nat_irc_fini);
|
281
net/ipv4/netfilter/nf_nat_l3proto_ipv4.c
Normal file
281
net/ipv4/netfilter/nf_nat_l3proto_ipv4.c
Normal file
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
|
||||
* (C) 2011 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/icmp.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#include <net/secure_seq.h>
|
||||
#include <net/checksum.h>
|
||||
#include <net/route.h>
|
||||
#include <net/ip.h>
|
||||
|
||||
#include <net/netfilter/nf_conntrack_core.h>
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_nat_core.h>
|
||||
#include <net/netfilter/nf_nat_l3proto.h>
|
||||
#include <net/netfilter/nf_nat_l4proto.h>
|
||||
|
||||
static const struct nf_nat_l3proto nf_nat_l3proto_ipv4;
|
||||
|
||||
#ifdef CONFIG_XFRM
|
||||
static void nf_nat_ipv4_decode_session(struct sk_buff *skb,
|
||||
const struct nf_conn *ct,
|
||||
enum ip_conntrack_dir dir,
|
||||
unsigned long statusbit,
|
||||
struct flowi *fl)
|
||||
{
|
||||
const struct nf_conntrack_tuple *t = &ct->tuplehash[dir].tuple;
|
||||
struct flowi4 *fl4 = &fl->u.ip4;
|
||||
|
||||
if (ct->status & statusbit) {
|
||||
fl4->daddr = t->dst.u3.ip;
|
||||
if (t->dst.protonum == IPPROTO_TCP ||
|
||||
t->dst.protonum == IPPROTO_UDP ||
|
||||
t->dst.protonum == IPPROTO_UDPLITE ||
|
||||
t->dst.protonum == IPPROTO_DCCP ||
|
||||
t->dst.protonum == IPPROTO_SCTP)
|
||||
fl4->fl4_dport = t->dst.u.all;
|
||||
}
|
||||
|
||||
statusbit ^= IPS_NAT_MASK;
|
||||
|
||||
if (ct->status & statusbit) {
|
||||
fl4->saddr = t->src.u3.ip;
|
||||
if (t->dst.protonum == IPPROTO_TCP ||
|
||||
t->dst.protonum == IPPROTO_UDP ||
|
||||
t->dst.protonum == IPPROTO_UDPLITE ||
|
||||
t->dst.protonum == IPPROTO_DCCP ||
|
||||
t->dst.protonum == IPPROTO_SCTP)
|
||||
fl4->fl4_sport = t->src.u.all;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_XFRM */
|
||||
|
||||
static bool nf_nat_ipv4_in_range(const struct nf_conntrack_tuple *t,
|
||||
const struct nf_nat_range *range)
|
||||
{
|
||||
return ntohl(t->src.u3.ip) >= ntohl(range->min_addr.ip) &&
|
||||
ntohl(t->src.u3.ip) <= ntohl(range->max_addr.ip);
|
||||
}
|
||||
|
||||
static u32 nf_nat_ipv4_secure_port(const struct nf_conntrack_tuple *t,
|
||||
__be16 dport)
|
||||
{
|
||||
return secure_ipv4_port_ephemeral(t->src.u3.ip, t->dst.u3.ip, dport);
|
||||
}
|
||||
|
||||
static bool nf_nat_ipv4_manip_pkt(struct sk_buff *skb,
|
||||
unsigned int iphdroff,
|
||||
const struct nf_nat_l4proto *l4proto,
|
||||
const struct nf_conntrack_tuple *target,
|
||||
enum nf_nat_manip_type maniptype)
|
||||
{
|
||||
struct iphdr *iph;
|
||||
unsigned int hdroff;
|
||||
|
||||
if (!skb_make_writable(skb, iphdroff + sizeof(*iph)))
|
||||
return false;
|
||||
|
||||
iph = (void *)skb->data + iphdroff;
|
||||
hdroff = iphdroff + iph->ihl * 4;
|
||||
|
||||
if (!l4proto->manip_pkt(skb, &nf_nat_l3proto_ipv4, iphdroff, hdroff,
|
||||
target, maniptype))
|
||||
return false;
|
||||
iph = (void *)skb->data + iphdroff;
|
||||
|
||||
if (maniptype == NF_NAT_MANIP_SRC) {
|
||||
csum_replace4(&iph->check, iph->saddr, target->src.u3.ip);
|
||||
iph->saddr = target->src.u3.ip;
|
||||
} else {
|
||||
csum_replace4(&iph->check, iph->daddr, target->dst.u3.ip);
|
||||
iph->daddr = target->dst.u3.ip;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void nf_nat_ipv4_csum_update(struct sk_buff *skb,
|
||||
unsigned int iphdroff, __sum16 *check,
|
||||
const struct nf_conntrack_tuple *t,
|
||||
enum nf_nat_manip_type maniptype)
|
||||
{
|
||||
struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff);
|
||||
__be32 oldip, newip;
|
||||
|
||||
if (maniptype == NF_NAT_MANIP_SRC) {
|
||||
oldip = iph->saddr;
|
||||
newip = t->src.u3.ip;
|
||||
} else {
|
||||
oldip = iph->daddr;
|
||||
newip = t->dst.u3.ip;
|
||||
}
|
||||
inet_proto_csum_replace4(check, skb, oldip, newip, 1);
|
||||
}
|
||||
|
||||
static void nf_nat_ipv4_csum_recalc(struct sk_buff *skb,
|
||||
u8 proto, void *data, __sum16 *check,
|
||||
int datalen, int oldlen)
|
||||
{
|
||||
const struct iphdr *iph = ip_hdr(skb);
|
||||
struct rtable *rt = skb_rtable(skb);
|
||||
|
||||
if (skb->ip_summed != CHECKSUM_PARTIAL) {
|
||||
if (!(rt->rt_flags & RTCF_LOCAL) &&
|
||||
(!skb->dev || skb->dev->features & NETIF_F_V4_CSUM)) {
|
||||
skb->ip_summed = CHECKSUM_PARTIAL;
|
||||
skb->csum_start = skb_headroom(skb) +
|
||||
skb_network_offset(skb) +
|
||||
ip_hdrlen(skb);
|
||||
skb->csum_offset = (void *)check - data;
|
||||
*check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
|
||||
datalen, proto, 0);
|
||||
} else {
|
||||
*check = 0;
|
||||
*check = csum_tcpudp_magic(iph->saddr, iph->daddr,
|
||||
datalen, proto,
|
||||
csum_partial(data, datalen,
|
||||
0));
|
||||
if (proto == IPPROTO_UDP && !*check)
|
||||
*check = CSUM_MANGLED_0;
|
||||
}
|
||||
} else
|
||||
inet_proto_csum_replace2(check, skb,
|
||||
htons(oldlen), htons(datalen), 1);
|
||||
}
|
||||
|
||||
static int nf_nat_ipv4_nlattr_to_range(struct nlattr *tb[],
|
||||
struct nf_nat_range *range)
|
||||
{
|
||||
if (tb[CTA_NAT_V4_MINIP]) {
|
||||
range->min_addr.ip = nla_get_be32(tb[CTA_NAT_V4_MINIP]);
|
||||
range->flags |= NF_NAT_RANGE_MAP_IPS;
|
||||
}
|
||||
|
||||
if (tb[CTA_NAT_V4_MAXIP])
|
||||
range->max_addr.ip = nla_get_be32(tb[CTA_NAT_V4_MAXIP]);
|
||||
else
|
||||
range->max_addr.ip = range->min_addr.ip;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nf_nat_l3proto nf_nat_l3proto_ipv4 = {
|
||||
.l3proto = NFPROTO_IPV4,
|
||||
.in_range = nf_nat_ipv4_in_range,
|
||||
.secure_port = nf_nat_ipv4_secure_port,
|
||||
.manip_pkt = nf_nat_ipv4_manip_pkt,
|
||||
.csum_update = nf_nat_ipv4_csum_update,
|
||||
.csum_recalc = nf_nat_ipv4_csum_recalc,
|
||||
.nlattr_to_range = nf_nat_ipv4_nlattr_to_range,
|
||||
#ifdef CONFIG_XFRM
|
||||
.decode_session = nf_nat_ipv4_decode_session,
|
||||
#endif
|
||||
};
|
||||
|
||||
int nf_nat_icmp_reply_translation(struct sk_buff *skb,
|
||||
struct nf_conn *ct,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
unsigned int hooknum)
|
||||
{
|
||||
struct {
|
||||
struct icmphdr icmp;
|
||||
struct iphdr ip;
|
||||
} *inside;
|
||||
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
|
||||
enum nf_nat_manip_type manip = HOOK2MANIP(hooknum);
|
||||
unsigned int hdrlen = ip_hdrlen(skb);
|
||||
const struct nf_nat_l4proto *l4proto;
|
||||
struct nf_conntrack_tuple target;
|
||||
unsigned long statusbit;
|
||||
|
||||
NF_CT_ASSERT(ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED_REPLY);
|
||||
|
||||
if (!skb_make_writable(skb, hdrlen + sizeof(*inside)))
|
||||
return 0;
|
||||
if (nf_ip_checksum(skb, hooknum, hdrlen, 0))
|
||||
return 0;
|
||||
|
||||
inside = (void *)skb->data + hdrlen;
|
||||
if (inside->icmp.type == ICMP_REDIRECT) {
|
||||
if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK)
|
||||
return 0;
|
||||
if (ct->status & IPS_NAT_MASK)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (manip == NF_NAT_MANIP_SRC)
|
||||
statusbit = IPS_SRC_NAT;
|
||||
else
|
||||
statusbit = IPS_DST_NAT;
|
||||
|
||||
/* Invert if this is reply direction */
|
||||
if (dir == IP_CT_DIR_REPLY)
|
||||
statusbit ^= IPS_NAT_MASK;
|
||||
|
||||
if (!(ct->status & statusbit))
|
||||
return 1;
|
||||
|
||||
l4proto = __nf_nat_l4proto_find(NFPROTO_IPV4, inside->ip.protocol);
|
||||
if (!nf_nat_ipv4_manip_pkt(skb, hdrlen + sizeof(inside->icmp),
|
||||
l4proto, &ct->tuplehash[!dir].tuple, !manip))
|
||||
return 0;
|
||||
|
||||
if (skb->ip_summed != CHECKSUM_PARTIAL) {
|
||||
/* Reloading "inside" here since manip_pkt may reallocate */
|
||||
inside = (void *)skb->data + hdrlen;
|
||||
inside->icmp.checksum = 0;
|
||||
inside->icmp.checksum =
|
||||
csum_fold(skb_checksum(skb, hdrlen,
|
||||
skb->len - hdrlen, 0));
|
||||
}
|
||||
|
||||
/* Change outer to look like the reply to an incoming packet */
|
||||
nf_ct_invert_tuplepr(&target, &ct->tuplehash[!dir].tuple);
|
||||
l4proto = __nf_nat_l4proto_find(NFPROTO_IPV4, 0);
|
||||
if (!nf_nat_ipv4_manip_pkt(skb, 0, l4proto, &target, manip))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_nat_icmp_reply_translation);
|
||||
|
||||
static int __init nf_nat_l3proto_ipv4_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = nf_nat_l4proto_register(NFPROTO_IPV4, &nf_nat_l4proto_icmp);
|
||||
if (err < 0)
|
||||
goto err1;
|
||||
err = nf_nat_l3proto_register(&nf_nat_l3proto_ipv4);
|
||||
if (err < 0)
|
||||
goto err2;
|
||||
return err;
|
||||
|
||||
err2:
|
||||
nf_nat_l4proto_unregister(NFPROTO_IPV4, &nf_nat_l4proto_icmp);
|
||||
err1:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit nf_nat_l3proto_ipv4_exit(void)
|
||||
{
|
||||
nf_nat_l3proto_unregister(&nf_nat_l3proto_ipv4);
|
||||
nf_nat_l4proto_unregister(NFPROTO_IPV4, &nf_nat_l4proto_icmp);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("nf-nat-" __stringify(AF_INET));
|
||||
|
||||
module_init(nf_nat_l3proto_ipv4_init);
|
||||
module_exit(nf_nat_l3proto_ipv4_exit);
|
@@ -22,7 +22,6 @@
|
||||
|
||||
#include <net/netfilter/nf_nat.h>
|
||||
#include <net/netfilter/nf_nat_helper.h>
|
||||
#include <net/netfilter/nf_nat_rule.h>
|
||||
#include <net/netfilter/nf_conntrack_helper.h>
|
||||
#include <net/netfilter/nf_conntrack_expect.h>
|
||||
#include <net/netfilter/nf_conntrack_zones.h>
|
||||
@@ -47,7 +46,7 @@ static void pptp_nat_expected(struct nf_conn *ct,
|
||||
struct nf_conntrack_tuple t;
|
||||
const struct nf_ct_pptp_master *ct_pptp_info;
|
||||
const struct nf_nat_pptp *nat_pptp_info;
|
||||
struct nf_nat_ipv4_range range;
|
||||
struct nf_nat_range range;
|
||||
|
||||
ct_pptp_info = nfct_help_data(master);
|
||||
nat_pptp_info = &nfct_nat(master)->help.nat_pptp_info;
|
||||
@@ -89,21 +88,21 @@ static void pptp_nat_expected(struct nf_conn *ct,
|
||||
|
||||
/* Change src to where master sends to */
|
||||
range.flags = NF_NAT_RANGE_MAP_IPS;
|
||||
range.min_ip = range.max_ip
|
||||
= ct->master->tuplehash[!exp->dir].tuple.dst.u3.ip;
|
||||
range.min_addr = range.max_addr
|
||||
= ct->master->tuplehash[!exp->dir].tuple.dst.u3;
|
||||
if (exp->dir == IP_CT_DIR_ORIGINAL) {
|
||||
range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
|
||||
range.min = range.max = exp->saved_proto;
|
||||
range.min_proto = range.max_proto = exp->saved_proto;
|
||||
}
|
||||
nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC);
|
||||
|
||||
/* For DST manip, map port here to where it's expected. */
|
||||
range.flags = NF_NAT_RANGE_MAP_IPS;
|
||||
range.min_ip = range.max_ip
|
||||
= ct->master->tuplehash[!exp->dir].tuple.src.u3.ip;
|
||||
range.min_addr = range.max_addr
|
||||
= ct->master->tuplehash[!exp->dir].tuple.src.u3;
|
||||
if (exp->dir == IP_CT_DIR_REPLY) {
|
||||
range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
|
||||
range.min = range.max = exp->saved_proto;
|
||||
range.min_proto = range.max_proto = exp->saved_proto;
|
||||
}
|
||||
nf_nat_setup_info(ct, &range, NF_NAT_MANIP_DST);
|
||||
}
|
||||
@@ -113,6 +112,7 @@ static int
|
||||
pptp_outbound_pkt(struct sk_buff *skb,
|
||||
struct nf_conn *ct,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
unsigned int protoff,
|
||||
struct PptpControlHeader *ctlh,
|
||||
union pptp_ctrl_union *pptpReq)
|
||||
|
||||
@@ -175,7 +175,7 @@ pptp_outbound_pkt(struct sk_buff *skb,
|
||||
ntohs(REQ_CID(pptpReq, cid_off)), ntohs(new_callid));
|
||||
|
||||
/* mangle packet */
|
||||
if (nf_nat_mangle_tcp_packet(skb, ct, ctinfo,
|
||||
if (nf_nat_mangle_tcp_packet(skb, ct, ctinfo, protoff,
|
||||
cid_off + sizeof(struct pptp_pkt_hdr) +
|
||||
sizeof(struct PptpControlHeader),
|
||||
sizeof(new_callid), (char *)&new_callid,
|
||||
@@ -216,6 +216,7 @@ static int
|
||||
pptp_inbound_pkt(struct sk_buff *skb,
|
||||
struct nf_conn *ct,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
unsigned int protoff,
|
||||
struct PptpControlHeader *ctlh,
|
||||
union pptp_ctrl_union *pptpReq)
|
||||
{
|
||||
@@ -268,7 +269,7 @@ pptp_inbound_pkt(struct sk_buff *skb,
|
||||
pr_debug("altering peer call id from 0x%04x to 0x%04x\n",
|
||||
ntohs(REQ_CID(pptpReq, pcid_off)), ntohs(new_pcid));
|
||||
|
||||
if (nf_nat_mangle_tcp_packet(skb, ct, ctinfo,
|
||||
if (nf_nat_mangle_tcp_packet(skb, ct, ctinfo, protoff,
|
||||
pcid_off + sizeof(struct pptp_pkt_hdr) +
|
||||
sizeof(struct PptpControlHeader),
|
||||
sizeof(new_pcid), (char *)&new_pcid,
|
||||
|
@@ -1,114 +0,0 @@
|
||||
/* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
|
||||
* (C) 2008 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/ip.h>
|
||||
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/export.h>
|
||||
#include <net/secure_seq.h>
|
||||
#include <net/netfilter/nf_nat.h>
|
||||
#include <net/netfilter/nf_nat_core.h>
|
||||
#include <net/netfilter/nf_nat_rule.h>
|
||||
#include <net/netfilter/nf_nat_protocol.h>
|
||||
|
||||
bool nf_nat_proto_in_range(const struct nf_conntrack_tuple *tuple,
|
||||
enum nf_nat_manip_type maniptype,
|
||||
const union nf_conntrack_man_proto *min,
|
||||
const union nf_conntrack_man_proto *max)
|
||||
{
|
||||
__be16 port;
|
||||
|
||||
if (maniptype == NF_NAT_MANIP_SRC)
|
||||
port = tuple->src.u.all;
|
||||
else
|
||||
port = tuple->dst.u.all;
|
||||
|
||||
return ntohs(port) >= ntohs(min->all) &&
|
||||
ntohs(port) <= ntohs(max->all);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_nat_proto_in_range);
|
||||
|
||||
void nf_nat_proto_unique_tuple(struct nf_conntrack_tuple *tuple,
|
||||
const struct nf_nat_ipv4_range *range,
|
||||
enum nf_nat_manip_type maniptype,
|
||||
const struct nf_conn *ct,
|
||||
u_int16_t *rover)
|
||||
{
|
||||
unsigned int range_size, min, i;
|
||||
__be16 *portptr;
|
||||
u_int16_t off;
|
||||
|
||||
if (maniptype == NF_NAT_MANIP_SRC)
|
||||
portptr = &tuple->src.u.all;
|
||||
else
|
||||
portptr = &tuple->dst.u.all;
|
||||
|
||||
/* If no range specified... */
|
||||
if (!(range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)) {
|
||||
/* If it's dst rewrite, can't change port */
|
||||
if (maniptype == NF_NAT_MANIP_DST)
|
||||
return;
|
||||
|
||||
if (ntohs(*portptr) < 1024) {
|
||||
/* Loose convention: >> 512 is credential passing */
|
||||
if (ntohs(*portptr) < 512) {
|
||||
min = 1;
|
||||
range_size = 511 - min + 1;
|
||||
} else {
|
||||
min = 600;
|
||||
range_size = 1023 - min + 1;
|
||||
}
|
||||
} else {
|
||||
min = 1024;
|
||||
range_size = 65535 - 1024 + 1;
|
||||
}
|
||||
} else {
|
||||
min = ntohs(range->min.all);
|
||||
range_size = ntohs(range->max.all) - min + 1;
|
||||
}
|
||||
|
||||
if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
|
||||
off = secure_ipv4_port_ephemeral(tuple->src.u3.ip, tuple->dst.u3.ip,
|
||||
maniptype == NF_NAT_MANIP_SRC
|
||||
? tuple->dst.u.all
|
||||
: tuple->src.u.all);
|
||||
else
|
||||
off = *rover;
|
||||
|
||||
for (i = 0; ; ++off) {
|
||||
*portptr = htons(min + off % range_size);
|
||||
if (++i != range_size && nf_nat_used_tuple(tuple, ct))
|
||||
continue;
|
||||
if (!(range->flags & NF_NAT_RANGE_PROTO_RANDOM))
|
||||
*rover = off;
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_nat_proto_unique_tuple);
|
||||
|
||||
#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
|
||||
int nf_nat_proto_nlattr_to_range(struct nlattr *tb[],
|
||||
struct nf_nat_ipv4_range *range)
|
||||
{
|
||||
if (tb[CTA_PROTONAT_PORT_MIN]) {
|
||||
range->min.all = nla_get_be16(tb[CTA_PROTONAT_PORT_MIN]);
|
||||
range->max.all = range->min.tcp.port;
|
||||
range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
|
||||
}
|
||||
if (tb[CTA_PROTONAT_PORT_MAX]) {
|
||||
range->max.all = nla_get_be16(tb[CTA_PROTONAT_PORT_MAX]);
|
||||
range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_nat_proto_nlattr_to_range);
|
||||
#endif
|
@@ -1,106 +0,0 @@
|
||||
/*
|
||||
* DCCP NAT protocol helper
|
||||
*
|
||||
* Copyright (c) 2005, 2006. 2008 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/dccp.h>
|
||||
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_nat.h>
|
||||
#include <net/netfilter/nf_nat_protocol.h>
|
||||
|
||||
static u_int16_t dccp_port_rover;
|
||||
|
||||
static void
|
||||
dccp_unique_tuple(struct nf_conntrack_tuple *tuple,
|
||||
const struct nf_nat_ipv4_range *range,
|
||||
enum nf_nat_manip_type maniptype,
|
||||
const struct nf_conn *ct)
|
||||
{
|
||||
nf_nat_proto_unique_tuple(tuple, range, maniptype, ct,
|
||||
&dccp_port_rover);
|
||||
}
|
||||
|
||||
static bool
|
||||
dccp_manip_pkt(struct sk_buff *skb,
|
||||
unsigned int iphdroff,
|
||||
const struct nf_conntrack_tuple *tuple,
|
||||
enum nf_nat_manip_type maniptype)
|
||||
{
|
||||
const struct iphdr *iph = (const void *)(skb->data + iphdroff);
|
||||
struct dccp_hdr *hdr;
|
||||
unsigned int hdroff = iphdroff + iph->ihl * 4;
|
||||
__be32 oldip, newip;
|
||||
__be16 *portptr, oldport, newport;
|
||||
int hdrsize = 8; /* DCCP connection tracking guarantees this much */
|
||||
|
||||
if (skb->len >= hdroff + sizeof(struct dccp_hdr))
|
||||
hdrsize = sizeof(struct dccp_hdr);
|
||||
|
||||
if (!skb_make_writable(skb, hdroff + hdrsize))
|
||||
return false;
|
||||
|
||||
iph = (struct iphdr *)(skb->data + iphdroff);
|
||||
hdr = (struct dccp_hdr *)(skb->data + hdroff);
|
||||
|
||||
if (maniptype == NF_NAT_MANIP_SRC) {
|
||||
oldip = iph->saddr;
|
||||
newip = tuple->src.u3.ip;
|
||||
newport = tuple->src.u.dccp.port;
|
||||
portptr = &hdr->dccph_sport;
|
||||
} else {
|
||||
oldip = iph->daddr;
|
||||
newip = tuple->dst.u3.ip;
|
||||
newport = tuple->dst.u.dccp.port;
|
||||
portptr = &hdr->dccph_dport;
|
||||
}
|
||||
|
||||
oldport = *portptr;
|
||||
*portptr = newport;
|
||||
|
||||
if (hdrsize < sizeof(*hdr))
|
||||
return true;
|
||||
|
||||
inet_proto_csum_replace4(&hdr->dccph_checksum, skb, oldip, newip, 1);
|
||||
inet_proto_csum_replace2(&hdr->dccph_checksum, skb, oldport, newport,
|
||||
0);
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct nf_nat_protocol nf_nat_protocol_dccp = {
|
||||
.protonum = IPPROTO_DCCP,
|
||||
.manip_pkt = dccp_manip_pkt,
|
||||
.in_range = nf_nat_proto_in_range,
|
||||
.unique_tuple = dccp_unique_tuple,
|
||||
#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
|
||||
.nlattr_to_range = nf_nat_proto_nlattr_to_range,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int __init nf_nat_proto_dccp_init(void)
|
||||
{
|
||||
return nf_nat_protocol_register(&nf_nat_protocol_dccp);
|
||||
}
|
||||
|
||||
static void __exit nf_nat_proto_dccp_fini(void)
|
||||
{
|
||||
nf_nat_protocol_unregister(&nf_nat_protocol_dccp);
|
||||
}
|
||||
|
||||
module_init(nf_nat_proto_dccp_init);
|
||||
module_exit(nf_nat_proto_dccp_fini);
|
||||
|
||||
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
|
||||
MODULE_DESCRIPTION("DCCP NAT protocol helper");
|
||||
MODULE_LICENSE("GPL");
|
@@ -28,8 +28,7 @@
|
||||
#include <linux/ip.h>
|
||||
|
||||
#include <net/netfilter/nf_nat.h>
|
||||
#include <net/netfilter/nf_nat_rule.h>
|
||||
#include <net/netfilter/nf_nat_protocol.h>
|
||||
#include <net/netfilter/nf_nat_l4proto.h>
|
||||
#include <linux/netfilter/nf_conntrack_proto_gre.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -38,8 +37,9 @@ MODULE_DESCRIPTION("Netfilter NAT protocol helper module for GRE");
|
||||
|
||||
/* generate unique tuple ... */
|
||||
static void
|
||||
gre_unique_tuple(struct nf_conntrack_tuple *tuple,
|
||||
const struct nf_nat_ipv4_range *range,
|
||||
gre_unique_tuple(const struct nf_nat_l3proto *l3proto,
|
||||
struct nf_conntrack_tuple *tuple,
|
||||
const struct nf_nat_range *range,
|
||||
enum nf_nat_manip_type maniptype,
|
||||
const struct nf_conn *ct)
|
||||
{
|
||||
@@ -62,8 +62,8 @@ gre_unique_tuple(struct nf_conntrack_tuple *tuple,
|
||||
min = 1;
|
||||
range_size = 0xffff;
|
||||
} else {
|
||||
min = ntohs(range->min.gre.key);
|
||||
range_size = ntohs(range->max.gre.key) - min + 1;
|
||||
min = ntohs(range->min_proto.gre.key);
|
||||
range_size = ntohs(range->max_proto.gre.key) - min + 1;
|
||||
}
|
||||
|
||||
pr_debug("min = %u, range_size = %u\n", min, range_size);
|
||||
@@ -80,14 +80,14 @@ gre_unique_tuple(struct nf_conntrack_tuple *tuple,
|
||||
|
||||
/* manipulate a GRE packet according to maniptype */
|
||||
static bool
|
||||
gre_manip_pkt(struct sk_buff *skb, unsigned int iphdroff,
|
||||
gre_manip_pkt(struct sk_buff *skb,
|
||||
const struct nf_nat_l3proto *l3proto,
|
||||
unsigned int iphdroff, unsigned int hdroff,
|
||||
const struct nf_conntrack_tuple *tuple,
|
||||
enum nf_nat_manip_type maniptype)
|
||||
{
|
||||
const struct gre_hdr *greh;
|
||||
struct gre_hdr_pptp *pgreh;
|
||||
const struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff);
|
||||
unsigned int hdroff = iphdroff + iph->ihl * 4;
|
||||
|
||||
/* pgreh includes two optional 32bit fields which are not required
|
||||
* to be there. That's where the magic '8' comes from */
|
||||
@@ -117,24 +117,24 @@ gre_manip_pkt(struct sk_buff *skb, unsigned int iphdroff,
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct nf_nat_protocol gre = {
|
||||
.protonum = IPPROTO_GRE,
|
||||
static const struct nf_nat_l4proto gre = {
|
||||
.l4proto = IPPROTO_GRE,
|
||||
.manip_pkt = gre_manip_pkt,
|
||||
.in_range = nf_nat_proto_in_range,
|
||||
.in_range = nf_nat_l4proto_in_range,
|
||||
.unique_tuple = gre_unique_tuple,
|
||||
#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
|
||||
.nlattr_to_range = nf_nat_proto_nlattr_to_range,
|
||||
.nlattr_to_range = nf_nat_l4proto_nlattr_to_range,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int __init nf_nat_proto_gre_init(void)
|
||||
{
|
||||
return nf_nat_protocol_register(&gre);
|
||||
return nf_nat_l4proto_register(NFPROTO_IPV4, &gre);
|
||||
}
|
||||
|
||||
static void __exit nf_nat_proto_gre_fini(void)
|
||||
{
|
||||
nf_nat_protocol_unregister(&gre);
|
||||
nf_nat_l4proto_unregister(NFPROTO_IPV4, &gre);
|
||||
}
|
||||
|
||||
module_init(nf_nat_proto_gre_init);
|
||||
|
@@ -15,8 +15,7 @@
|
||||
#include <linux/netfilter.h>
|
||||
#include <net/netfilter/nf_nat.h>
|
||||
#include <net/netfilter/nf_nat_core.h>
|
||||
#include <net/netfilter/nf_nat_rule.h>
|
||||
#include <net/netfilter/nf_nat_protocol.h>
|
||||
#include <net/netfilter/nf_nat_l4proto.h>
|
||||
|
||||
static bool
|
||||
icmp_in_range(const struct nf_conntrack_tuple *tuple,
|
||||
@@ -29,8 +28,9 @@ icmp_in_range(const struct nf_conntrack_tuple *tuple,
|
||||
}
|
||||
|
||||
static void
|
||||
icmp_unique_tuple(struct nf_conntrack_tuple *tuple,
|
||||
const struct nf_nat_ipv4_range *range,
|
||||
icmp_unique_tuple(const struct nf_nat_l3proto *l3proto,
|
||||
struct nf_conntrack_tuple *tuple,
|
||||
const struct nf_nat_range *range,
|
||||
enum nf_nat_manip_type maniptype,
|
||||
const struct nf_conn *ct)
|
||||
{
|
||||
@@ -38,13 +38,14 @@ icmp_unique_tuple(struct nf_conntrack_tuple *tuple,
|
||||
unsigned int range_size;
|
||||
unsigned int i;
|
||||
|
||||
range_size = ntohs(range->max.icmp.id) - ntohs(range->min.icmp.id) + 1;
|
||||
range_size = ntohs(range->max_proto.icmp.id) -
|
||||
ntohs(range->min_proto.icmp.id) + 1;
|
||||
/* If no range specified... */
|
||||
if (!(range->flags & NF_NAT_RANGE_PROTO_SPECIFIED))
|
||||
range_size = 0xFFFF;
|
||||
|
||||
for (i = 0; ; ++id) {
|
||||
tuple->src.u.icmp.id = htons(ntohs(range->min.icmp.id) +
|
||||
tuple->src.u.icmp.id = htons(ntohs(range->min_proto.icmp.id) +
|
||||
(id % range_size));
|
||||
if (++i == range_size || !nf_nat_used_tuple(tuple, ct))
|
||||
return;
|
||||
@@ -54,13 +55,12 @@ icmp_unique_tuple(struct nf_conntrack_tuple *tuple,
|
||||
|
||||
static bool
|
||||
icmp_manip_pkt(struct sk_buff *skb,
|
||||
unsigned int iphdroff,
|
||||
const struct nf_nat_l3proto *l3proto,
|
||||
unsigned int iphdroff, unsigned int hdroff,
|
||||
const struct nf_conntrack_tuple *tuple,
|
||||
enum nf_nat_manip_type maniptype)
|
||||
{
|
||||
const struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff);
|
||||
struct icmphdr *hdr;
|
||||
unsigned int hdroff = iphdroff + iph->ihl*4;
|
||||
|
||||
if (!skb_make_writable(skb, hdroff + sizeof(*hdr)))
|
||||
return false;
|
||||
@@ -72,12 +72,12 @@ icmp_manip_pkt(struct sk_buff *skb,
|
||||
return true;
|
||||
}
|
||||
|
||||
const struct nf_nat_protocol nf_nat_protocol_icmp = {
|
||||
.protonum = IPPROTO_ICMP,
|
||||
const struct nf_nat_l4proto nf_nat_l4proto_icmp = {
|
||||
.l4proto = IPPROTO_ICMP,
|
||||
.manip_pkt = icmp_manip_pkt,
|
||||
.in_range = icmp_in_range,
|
||||
.unique_tuple = icmp_unique_tuple,
|
||||
#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
|
||||
.nlattr_to_range = nf_nat_proto_nlattr_to_range,
|
||||
.nlattr_to_range = nf_nat_l4proto_nlattr_to_range,
|
||||
#endif
|
||||
};
|
||||
|
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/sctp.h>
|
||||
#include <linux/module.h>
|
||||
#include <net/sctp/checksum.h>
|
||||
|
||||
#include <net/netfilter/nf_nat_protocol.h>
|
||||
|
||||
static u_int16_t nf_sctp_port_rover;
|
||||
|
||||
static void
|
||||
sctp_unique_tuple(struct nf_conntrack_tuple *tuple,
|
||||
const struct nf_nat_ipv4_range *range,
|
||||
enum nf_nat_manip_type maniptype,
|
||||
const struct nf_conn *ct)
|
||||
{
|
||||
nf_nat_proto_unique_tuple(tuple, range, maniptype, ct,
|
||||
&nf_sctp_port_rover);
|
||||
}
|
||||
|
||||
static bool
|
||||
sctp_manip_pkt(struct sk_buff *skb,
|
||||
unsigned int iphdroff,
|
||||
const struct nf_conntrack_tuple *tuple,
|
||||
enum nf_nat_manip_type maniptype)
|
||||
{
|
||||
const struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff);
|
||||
struct sk_buff *frag;
|
||||
sctp_sctphdr_t *hdr;
|
||||
unsigned int hdroff = iphdroff + iph->ihl*4;
|
||||
__be32 oldip, newip;
|
||||
__be32 crc32;
|
||||
|
||||
if (!skb_make_writable(skb, hdroff + sizeof(*hdr)))
|
||||
return false;
|
||||
|
||||
iph = (struct iphdr *)(skb->data + iphdroff);
|
||||
hdr = (struct sctphdr *)(skb->data + hdroff);
|
||||
|
||||
if (maniptype == NF_NAT_MANIP_SRC) {
|
||||
/* Get rid of src ip and src pt */
|
||||
oldip = iph->saddr;
|
||||
newip = tuple->src.u3.ip;
|
||||
hdr->source = tuple->src.u.sctp.port;
|
||||
} else {
|
||||
/* Get rid of dst ip and dst pt */
|
||||
oldip = iph->daddr;
|
||||
newip = tuple->dst.u3.ip;
|
||||
hdr->dest = tuple->dst.u.sctp.port;
|
||||
}
|
||||
|
||||
crc32 = sctp_start_cksum((u8 *)hdr, skb_headlen(skb) - hdroff);
|
||||
skb_walk_frags(skb, frag)
|
||||
crc32 = sctp_update_cksum((u8 *)frag->data, skb_headlen(frag),
|
||||
crc32);
|
||||
crc32 = sctp_end_cksum(crc32);
|
||||
hdr->checksum = crc32;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct nf_nat_protocol nf_nat_protocol_sctp = {
|
||||
.protonum = IPPROTO_SCTP,
|
||||
.manip_pkt = sctp_manip_pkt,
|
||||
.in_range = nf_nat_proto_in_range,
|
||||
.unique_tuple = sctp_unique_tuple,
|
||||
#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
|
||||
.nlattr_to_range = nf_nat_proto_nlattr_to_range,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int __init nf_nat_proto_sctp_init(void)
|
||||
{
|
||||
return nf_nat_protocol_register(&nf_nat_protocol_sctp);
|
||||
}
|
||||
|
||||
static void __exit nf_nat_proto_sctp_exit(void)
|
||||
{
|
||||
nf_nat_protocol_unregister(&nf_nat_protocol_sctp);
|
||||
}
|
||||
|
||||
module_init(nf_nat_proto_sctp_init);
|
||||
module_exit(nf_nat_proto_sctp_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("SCTP NAT protocol helper");
|
||||
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
|
@@ -1,91 +0,0 @@
|
||||
/* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/tcp.h>
|
||||
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/netfilter/nfnetlink_conntrack.h>
|
||||
#include <net/netfilter/nf_nat.h>
|
||||
#include <net/netfilter/nf_nat_rule.h>
|
||||
#include <net/netfilter/nf_nat_protocol.h>
|
||||
#include <net/netfilter/nf_nat_core.h>
|
||||
|
||||
static u_int16_t tcp_port_rover;
|
||||
|
||||
static void
|
||||
tcp_unique_tuple(struct nf_conntrack_tuple *tuple,
|
||||
const struct nf_nat_ipv4_range *range,
|
||||
enum nf_nat_manip_type maniptype,
|
||||
const struct nf_conn *ct)
|
||||
{
|
||||
nf_nat_proto_unique_tuple(tuple, range, maniptype, ct, &tcp_port_rover);
|
||||
}
|
||||
|
||||
static bool
|
||||
tcp_manip_pkt(struct sk_buff *skb,
|
||||
unsigned int iphdroff,
|
||||
const struct nf_conntrack_tuple *tuple,
|
||||
enum nf_nat_manip_type maniptype)
|
||||
{
|
||||
const struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff);
|
||||
struct tcphdr *hdr;
|
||||
unsigned int hdroff = iphdroff + iph->ihl*4;
|
||||
__be32 oldip, newip;
|
||||
__be16 *portptr, newport, oldport;
|
||||
int hdrsize = 8; /* TCP connection tracking guarantees this much */
|
||||
|
||||
/* this could be a inner header returned in icmp packet; in such
|
||||
cases we cannot update the checksum field since it is outside of
|
||||
the 8 bytes of transport layer headers we are guaranteed */
|
||||
if (skb->len >= hdroff + sizeof(struct tcphdr))
|
||||
hdrsize = sizeof(struct tcphdr);
|
||||
|
||||
if (!skb_make_writable(skb, hdroff + hdrsize))
|
||||
return false;
|
||||
|
||||
iph = (struct iphdr *)(skb->data + iphdroff);
|
||||
hdr = (struct tcphdr *)(skb->data + hdroff);
|
||||
|
||||
if (maniptype == NF_NAT_MANIP_SRC) {
|
||||
/* Get rid of src ip and src pt */
|
||||
oldip = iph->saddr;
|
||||
newip = tuple->src.u3.ip;
|
||||
newport = tuple->src.u.tcp.port;
|
||||
portptr = &hdr->source;
|
||||
} else {
|
||||
/* Get rid of dst ip and dst pt */
|
||||
oldip = iph->daddr;
|
||||
newip = tuple->dst.u3.ip;
|
||||
newport = tuple->dst.u.tcp.port;
|
||||
portptr = &hdr->dest;
|
||||
}
|
||||
|
||||
oldport = *portptr;
|
||||
*portptr = newport;
|
||||
|
||||
if (hdrsize < sizeof(*hdr))
|
||||
return true;
|
||||
|
||||
inet_proto_csum_replace4(&hdr->check, skb, oldip, newip, 1);
|
||||
inet_proto_csum_replace2(&hdr->check, skb, oldport, newport, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
const struct nf_nat_protocol nf_nat_protocol_tcp = {
|
||||
.protonum = IPPROTO_TCP,
|
||||
.manip_pkt = tcp_manip_pkt,
|
||||
.in_range = nf_nat_proto_in_range,
|
||||
.unique_tuple = tcp_unique_tuple,
|
||||
#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
|
||||
.nlattr_to_range = nf_nat_proto_nlattr_to_range,
|
||||
#endif
|
||||
};
|
@@ -1,82 +0,0 @@
|
||||
/* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/udp.h>
|
||||
|
||||
#include <linux/netfilter.h>
|
||||
#include <net/netfilter/nf_nat.h>
|
||||
#include <net/netfilter/nf_nat_core.h>
|
||||
#include <net/netfilter/nf_nat_rule.h>
|
||||
#include <net/netfilter/nf_nat_protocol.h>
|
||||
|
||||
static u_int16_t udp_port_rover;
|
||||
|
||||
static void
|
||||
udp_unique_tuple(struct nf_conntrack_tuple *tuple,
|
||||
const struct nf_nat_ipv4_range *range,
|
||||
enum nf_nat_manip_type maniptype,
|
||||
const struct nf_conn *ct)
|
||||
{
|
||||
nf_nat_proto_unique_tuple(tuple, range, maniptype, ct, &udp_port_rover);
|
||||
}
|
||||
|
||||
static bool
|
||||
udp_manip_pkt(struct sk_buff *skb,
|
||||
unsigned int iphdroff,
|
||||
const struct nf_conntrack_tuple *tuple,
|
||||
enum nf_nat_manip_type maniptype)
|
||||
{
|
||||
const struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff);
|
||||
struct udphdr *hdr;
|
||||
unsigned int hdroff = iphdroff + iph->ihl*4;
|
||||
__be32 oldip, newip;
|
||||
__be16 *portptr, newport;
|
||||
|
||||
if (!skb_make_writable(skb, hdroff + sizeof(*hdr)))
|
||||
return false;
|
||||
|
||||
iph = (struct iphdr *)(skb->data + iphdroff);
|
||||
hdr = (struct udphdr *)(skb->data + hdroff);
|
||||
|
||||
if (maniptype == NF_NAT_MANIP_SRC) {
|
||||
/* Get rid of src ip and src pt */
|
||||
oldip = iph->saddr;
|
||||
newip = tuple->src.u3.ip;
|
||||
newport = tuple->src.u.udp.port;
|
||||
portptr = &hdr->source;
|
||||
} else {
|
||||
/* Get rid of dst ip and dst pt */
|
||||
oldip = iph->daddr;
|
||||
newip = tuple->dst.u3.ip;
|
||||
newport = tuple->dst.u.udp.port;
|
||||
portptr = &hdr->dest;
|
||||
}
|
||||
if (hdr->check || skb->ip_summed == CHECKSUM_PARTIAL) {
|
||||
inet_proto_csum_replace4(&hdr->check, skb, oldip, newip, 1);
|
||||
inet_proto_csum_replace2(&hdr->check, skb, *portptr, newport,
|
||||
0);
|
||||
if (!hdr->check)
|
||||
hdr->check = CSUM_MANGLED_0;
|
||||
}
|
||||
*portptr = newport;
|
||||
return true;
|
||||
}
|
||||
|
||||
const struct nf_nat_protocol nf_nat_protocol_udp = {
|
||||
.protonum = IPPROTO_UDP,
|
||||
.manip_pkt = udp_manip_pkt,
|
||||
.in_range = nf_nat_proto_in_range,
|
||||
.unique_tuple = udp_unique_tuple,
|
||||
#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
|
||||
.nlattr_to_range = nf_nat_proto_nlattr_to_range,
|
||||
#endif
|
||||
};
|
@@ -1,98 +0,0 @@
|
||||
/* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
|
||||
* (C) 2008 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/udp.h>
|
||||
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/module.h>
|
||||
#include <net/netfilter/nf_nat.h>
|
||||
#include <net/netfilter/nf_nat_protocol.h>
|
||||
|
||||
static u_int16_t udplite_port_rover;
|
||||
|
||||
static void
|
||||
udplite_unique_tuple(struct nf_conntrack_tuple *tuple,
|
||||
const struct nf_nat_ipv4_range *range,
|
||||
enum nf_nat_manip_type maniptype,
|
||||
const struct nf_conn *ct)
|
||||
{
|
||||
nf_nat_proto_unique_tuple(tuple, range, maniptype, ct,
|
||||
&udplite_port_rover);
|
||||
}
|
||||
|
||||
static bool
|
||||
udplite_manip_pkt(struct sk_buff *skb,
|
||||
unsigned int iphdroff,
|
||||
const struct nf_conntrack_tuple *tuple,
|
||||
enum nf_nat_manip_type maniptype)
|
||||
{
|
||||
const struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff);
|
||||
struct udphdr *hdr;
|
||||
unsigned int hdroff = iphdroff + iph->ihl*4;
|
||||
__be32 oldip, newip;
|
||||
__be16 *portptr, newport;
|
||||
|
||||
if (!skb_make_writable(skb, hdroff + sizeof(*hdr)))
|
||||
return false;
|
||||
|
||||
iph = (struct iphdr *)(skb->data + iphdroff);
|
||||
hdr = (struct udphdr *)(skb->data + hdroff);
|
||||
|
||||
if (maniptype == NF_NAT_MANIP_SRC) {
|
||||
/* Get rid of src ip and src pt */
|
||||
oldip = iph->saddr;
|
||||
newip = tuple->src.u3.ip;
|
||||
newport = tuple->src.u.udp.port;
|
||||
portptr = &hdr->source;
|
||||
} else {
|
||||
/* Get rid of dst ip and dst pt */
|
||||
oldip = iph->daddr;
|
||||
newip = tuple->dst.u3.ip;
|
||||
newport = tuple->dst.u.udp.port;
|
||||
portptr = &hdr->dest;
|
||||
}
|
||||
|
||||
inet_proto_csum_replace4(&hdr->check, skb, oldip, newip, 1);
|
||||
inet_proto_csum_replace2(&hdr->check, skb, *portptr, newport, 0);
|
||||
if (!hdr->check)
|
||||
hdr->check = CSUM_MANGLED_0;
|
||||
|
||||
*portptr = newport;
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct nf_nat_protocol nf_nat_protocol_udplite = {
|
||||
.protonum = IPPROTO_UDPLITE,
|
||||
.manip_pkt = udplite_manip_pkt,
|
||||
.in_range = nf_nat_proto_in_range,
|
||||
.unique_tuple = udplite_unique_tuple,
|
||||
#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
|
||||
.nlattr_to_range = nf_nat_proto_nlattr_to_range,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int __init nf_nat_proto_udplite_init(void)
|
||||
{
|
||||
return nf_nat_protocol_register(&nf_nat_protocol_udplite);
|
||||
}
|
||||
|
||||
static void __exit nf_nat_proto_udplite_fini(void)
|
||||
{
|
||||
nf_nat_protocol_unregister(&nf_nat_protocol_udplite);
|
||||
}
|
||||
|
||||
module_init(nf_nat_proto_udplite_init);
|
||||
module_exit(nf_nat_proto_udplite_fini);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("UDP-Lite NAT protocol helper");
|
||||
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
|
@@ -1,52 +0,0 @@
|
||||
/* The "unknown" protocol. This is what is used for protocols we
|
||||
* don't understand. It's returned by ip_ct_find_proto().
|
||||
*/
|
||||
|
||||
/* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <linux/netfilter.h>
|
||||
#include <net/netfilter/nf_nat.h>
|
||||
#include <net/netfilter/nf_nat_rule.h>
|
||||
#include <net/netfilter/nf_nat_protocol.h>
|
||||
|
||||
static bool unknown_in_range(const struct nf_conntrack_tuple *tuple,
|
||||
enum nf_nat_manip_type manip_type,
|
||||
const union nf_conntrack_man_proto *min,
|
||||
const union nf_conntrack_man_proto *max)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void unknown_unique_tuple(struct nf_conntrack_tuple *tuple,
|
||||
const struct nf_nat_ipv4_range *range,
|
||||
enum nf_nat_manip_type maniptype,
|
||||
const struct nf_conn *ct)
|
||||
{
|
||||
/* Sorry: we can't help you; if it's not unique, we can't frob
|
||||
anything. */
|
||||
return;
|
||||
}
|
||||
|
||||
static bool
|
||||
unknown_manip_pkt(struct sk_buff *skb,
|
||||
unsigned int iphdroff,
|
||||
const struct nf_conntrack_tuple *tuple,
|
||||
enum nf_nat_manip_type maniptype)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const struct nf_nat_protocol nf_nat_unknown_protocol = {
|
||||
.manip_pkt = unknown_manip_pkt,
|
||||
.in_range = unknown_in_range,
|
||||
.unique_tuple = unknown_unique_tuple,
|
||||
};
|
@@ -1,214 +0,0 @@
|
||||
/* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/* Everything about the rules for NAT. */
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
#include <linux/types.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kmod.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/checksum.h>
|
||||
#include <net/route.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <linux/netfilter_ipv4/ip_tables.h>
|
||||
#include <net/netfilter/nf_nat.h>
|
||||
#include <net/netfilter/nf_nat_core.h>
|
||||
#include <net/netfilter/nf_nat_rule.h>
|
||||
|
||||
#define NAT_VALID_HOOKS ((1 << NF_INET_PRE_ROUTING) | \
|
||||
(1 << NF_INET_POST_ROUTING) | \
|
||||
(1 << NF_INET_LOCAL_OUT) | \
|
||||
(1 << NF_INET_LOCAL_IN))
|
||||
|
||||
static const struct xt_table nat_table = {
|
||||
.name = "nat",
|
||||
.valid_hooks = NAT_VALID_HOOKS,
|
||||
.me = THIS_MODULE,
|
||||
.af = NFPROTO_IPV4,
|
||||
};
|
||||
|
||||
/* Source NAT */
|
||||
static unsigned int
|
||||
ipt_snat_target(struct sk_buff *skb, const struct xt_action_param *par)
|
||||
{
|
||||
struct nf_conn *ct;
|
||||
enum ip_conntrack_info ctinfo;
|
||||
const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo;
|
||||
|
||||
NF_CT_ASSERT(par->hooknum == NF_INET_POST_ROUTING ||
|
||||
par->hooknum == NF_INET_LOCAL_IN);
|
||||
|
||||
ct = nf_ct_get(skb, &ctinfo);
|
||||
|
||||
/* Connection must be valid and new. */
|
||||
NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED ||
|
||||
ctinfo == IP_CT_RELATED_REPLY));
|
||||
NF_CT_ASSERT(par->out != NULL);
|
||||
|
||||
return nf_nat_setup_info(ct, &mr->range[0], NF_NAT_MANIP_SRC);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
ipt_dnat_target(struct sk_buff *skb, const struct xt_action_param *par)
|
||||
{
|
||||
struct nf_conn *ct;
|
||||
enum ip_conntrack_info ctinfo;
|
||||
const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo;
|
||||
|
||||
NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING ||
|
||||
par->hooknum == NF_INET_LOCAL_OUT);
|
||||
|
||||
ct = nf_ct_get(skb, &ctinfo);
|
||||
|
||||
/* Connection must be valid and new. */
|
||||
NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED));
|
||||
|
||||
return nf_nat_setup_info(ct, &mr->range[0], NF_NAT_MANIP_DST);
|
||||
}
|
||||
|
||||
static int ipt_snat_checkentry(const struct xt_tgchk_param *par)
|
||||
{
|
||||
const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo;
|
||||
|
||||
/* Must be a valid range */
|
||||
if (mr->rangesize != 1) {
|
||||
pr_info("SNAT: multiple ranges no longer supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipt_dnat_checkentry(const struct xt_tgchk_param *par)
|
||||
{
|
||||
const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo;
|
||||
|
||||
/* Must be a valid range */
|
||||
if (mr->rangesize != 1) {
|
||||
pr_info("DNAT: multiple ranges no longer supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
alloc_null_binding(struct nf_conn *ct, unsigned int hooknum)
|
||||
{
|
||||
/* Force range to this IP; let proto decide mapping for
|
||||
per-proto parts (hence not NF_NAT_RANGE_PROTO_SPECIFIED).
|
||||
*/
|
||||
struct nf_nat_ipv4_range range;
|
||||
|
||||
range.flags = 0;
|
||||
pr_debug("Allocating NULL binding for %p (%pI4)\n", ct,
|
||||
HOOK2MANIP(hooknum) == NF_NAT_MANIP_SRC ?
|
||||
&ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip :
|
||||
&ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip);
|
||||
|
||||
return nf_nat_setup_info(ct, &range, HOOK2MANIP(hooknum));
|
||||
}
|
||||
|
||||
int nf_nat_rule_find(struct sk_buff *skb,
|
||||
unsigned int hooknum,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
struct nf_conn *ct)
|
||||
{
|
||||
struct net *net = nf_ct_net(ct);
|
||||
int ret;
|
||||
|
||||
ret = ipt_do_table(skb, hooknum, in, out, net->ipv4.nat_table);
|
||||
|
||||
if (ret == NF_ACCEPT) {
|
||||
if (!nf_nat_initialized(ct, HOOK2MANIP(hooknum)))
|
||||
/* NUL mapping */
|
||||
ret = alloc_null_binding(ct, hooknum);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct xt_target ipt_snat_reg __read_mostly = {
|
||||
.name = "SNAT",
|
||||
.target = ipt_snat_target,
|
||||
.targetsize = sizeof(struct nf_nat_ipv4_multi_range_compat),
|
||||
.table = "nat",
|
||||
.hooks = (1 << NF_INET_POST_ROUTING) | (1 << NF_INET_LOCAL_IN),
|
||||
.checkentry = ipt_snat_checkentry,
|
||||
.family = AF_INET,
|
||||
};
|
||||
|
||||
static struct xt_target ipt_dnat_reg __read_mostly = {
|
||||
.name = "DNAT",
|
||||
.target = ipt_dnat_target,
|
||||
.targetsize = sizeof(struct nf_nat_ipv4_multi_range_compat),
|
||||
.table = "nat",
|
||||
.hooks = (1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_OUT),
|
||||
.checkentry = ipt_dnat_checkentry,
|
||||
.family = AF_INET,
|
||||
};
|
||||
|
||||
static int __net_init nf_nat_rule_net_init(struct net *net)
|
||||
{
|
||||
struct ipt_replace *repl;
|
||||
|
||||
repl = ipt_alloc_initial_table(&nat_table);
|
||||
if (repl == NULL)
|
||||
return -ENOMEM;
|
||||
net->ipv4.nat_table = ipt_register_table(net, &nat_table, repl);
|
||||
kfree(repl);
|
||||
if (IS_ERR(net->ipv4.nat_table))
|
||||
return PTR_ERR(net->ipv4.nat_table);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __net_exit nf_nat_rule_net_exit(struct net *net)
|
||||
{
|
||||
ipt_unregister_table(net, net->ipv4.nat_table);
|
||||
}
|
||||
|
||||
static struct pernet_operations nf_nat_rule_net_ops = {
|
||||
.init = nf_nat_rule_net_init,
|
||||
.exit = nf_nat_rule_net_exit,
|
||||
};
|
||||
|
||||
int __init nf_nat_rule_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = register_pernet_subsys(&nf_nat_rule_net_ops);
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
ret = xt_register_target(&ipt_snat_reg);
|
||||
if (ret != 0)
|
||||
goto unregister_table;
|
||||
|
||||
ret = xt_register_target(&ipt_dnat_reg);
|
||||
if (ret != 0)
|
||||
goto unregister_snat;
|
||||
|
||||
return ret;
|
||||
|
||||
unregister_snat:
|
||||
xt_unregister_target(&ipt_snat_reg);
|
||||
unregister_table:
|
||||
unregister_pernet_subsys(&nf_nat_rule_net_ops);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void nf_nat_rule_cleanup(void)
|
||||
{
|
||||
xt_unregister_target(&ipt_dnat_reg);
|
||||
xt_unregister_target(&ipt_snat_reg);
|
||||
unregister_pernet_subsys(&nf_nat_rule_net_ops);
|
||||
}
|
@@ -1,572 +0,0 @@
|
||||
/* SIP extension for NAT alteration.
|
||||
*
|
||||
* (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar>
|
||||
* based on RR's ip_nat_ftp.c and other modules.
|
||||
* (C) 2007 United Security Providers
|
||||
* (C) 2007, 2008 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/ip.h>
|
||||
#include <net/ip.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/tcp.h>
|
||||
|
||||
#include <net/netfilter/nf_nat.h>
|
||||
#include <net/netfilter/nf_nat_helper.h>
|
||||
#include <net/netfilter/nf_nat_rule.h>
|
||||
#include <net/netfilter/nf_conntrack_helper.h>
|
||||
#include <net/netfilter/nf_conntrack_expect.h>
|
||||
#include <linux/netfilter/nf_conntrack_sip.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>");
|
||||
MODULE_DESCRIPTION("SIP NAT helper");
|
||||
MODULE_ALIAS("ip_nat_sip");
|
||||
|
||||
|
||||
static unsigned int mangle_packet(struct sk_buff *skb, unsigned int dataoff,
|
||||
const char **dptr, unsigned int *datalen,
|
||||
unsigned int matchoff, unsigned int matchlen,
|
||||
const char *buffer, unsigned int buflen)
|
||||
{
|
||||
enum ip_conntrack_info ctinfo;
|
||||
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
|
||||
struct tcphdr *th;
|
||||
unsigned int baseoff;
|
||||
|
||||
if (nf_ct_protonum(ct) == IPPROTO_TCP) {
|
||||
th = (struct tcphdr *)(skb->data + ip_hdrlen(skb));
|
||||
baseoff = ip_hdrlen(skb) + th->doff * 4;
|
||||
matchoff += dataoff - baseoff;
|
||||
|
||||
if (!__nf_nat_mangle_tcp_packet(skb, ct, ctinfo,
|
||||
matchoff, matchlen,
|
||||
buffer, buflen, false))
|
||||
return 0;
|
||||
} else {
|
||||
baseoff = ip_hdrlen(skb) + sizeof(struct udphdr);
|
||||
matchoff += dataoff - baseoff;
|
||||
|
||||
if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo,
|
||||
matchoff, matchlen,
|
||||
buffer, buflen))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reload data pointer and adjust datalen value */
|
||||
*dptr = skb->data + dataoff;
|
||||
*datalen += buflen - matchlen;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int map_addr(struct sk_buff *skb, unsigned int dataoff,
|
||||
const char **dptr, unsigned int *datalen,
|
||||
unsigned int matchoff, unsigned int matchlen,
|
||||
union nf_inet_addr *addr, __be16 port)
|
||||
{
|
||||
enum ip_conntrack_info ctinfo;
|
||||
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
|
||||
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
|
||||
char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
|
||||
unsigned int buflen;
|
||||
__be32 newaddr;
|
||||
__be16 newport;
|
||||
|
||||
if (ct->tuplehash[dir].tuple.src.u3.ip == addr->ip &&
|
||||
ct->tuplehash[dir].tuple.src.u.udp.port == port) {
|
||||
newaddr = ct->tuplehash[!dir].tuple.dst.u3.ip;
|
||||
newport = ct->tuplehash[!dir].tuple.dst.u.udp.port;
|
||||
} else if (ct->tuplehash[dir].tuple.dst.u3.ip == addr->ip &&
|
||||
ct->tuplehash[dir].tuple.dst.u.udp.port == port) {
|
||||
newaddr = ct->tuplehash[!dir].tuple.src.u3.ip;
|
||||
newport = ct->tuplehash[!dir].tuple.src.u.udp.port;
|
||||
} else
|
||||
return 1;
|
||||
|
||||
if (newaddr == addr->ip && newport == port)
|
||||
return 1;
|
||||
|
||||
buflen = sprintf(buffer, "%pI4:%u", &newaddr, ntohs(newport));
|
||||
|
||||
return mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen,
|
||||
buffer, buflen);
|
||||
}
|
||||
|
||||
static int map_sip_addr(struct sk_buff *skb, unsigned int dataoff,
|
||||
const char **dptr, unsigned int *datalen,
|
||||
enum sip_header_types type)
|
||||
{
|
||||
enum ip_conntrack_info ctinfo;
|
||||
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
|
||||
unsigned int matchlen, matchoff;
|
||||
union nf_inet_addr addr;
|
||||
__be16 port;
|
||||
|
||||
if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, type, NULL,
|
||||
&matchoff, &matchlen, &addr, &port) <= 0)
|
||||
return 1;
|
||||
return map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen,
|
||||
&addr, port);
|
||||
}
|
||||
|
||||
static unsigned int ip_nat_sip(struct sk_buff *skb, unsigned int dataoff,
|
||||
const char **dptr, unsigned int *datalen)
|
||||
{
|
||||
enum ip_conntrack_info ctinfo;
|
||||
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
|
||||
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
|
||||
unsigned int coff, matchoff, matchlen;
|
||||
enum sip_header_types hdr;
|
||||
union nf_inet_addr addr;
|
||||
__be16 port;
|
||||
int request, in_header;
|
||||
|
||||
/* Basic rules: requests and responses. */
|
||||
if (strnicmp(*dptr, "SIP/2.0", strlen("SIP/2.0")) != 0) {
|
||||
if (ct_sip_parse_request(ct, *dptr, *datalen,
|
||||
&matchoff, &matchlen,
|
||||
&addr, &port) > 0 &&
|
||||
!map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen,
|
||||
&addr, port))
|
||||
return NF_DROP;
|
||||
request = 1;
|
||||
} else
|
||||
request = 0;
|
||||
|
||||
if (nf_ct_protonum(ct) == IPPROTO_TCP)
|
||||
hdr = SIP_HDR_VIA_TCP;
|
||||
else
|
||||
hdr = SIP_HDR_VIA_UDP;
|
||||
|
||||
/* Translate topmost Via header and parameters */
|
||||
if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen,
|
||||
hdr, NULL, &matchoff, &matchlen,
|
||||
&addr, &port) > 0) {
|
||||
unsigned int olen, matchend, poff, plen, buflen, n;
|
||||
char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
|
||||
|
||||
/* We're only interested in headers related to this
|
||||
* connection */
|
||||
if (request) {
|
||||
if (addr.ip != ct->tuplehash[dir].tuple.src.u3.ip ||
|
||||
port != ct->tuplehash[dir].tuple.src.u.udp.port)
|
||||
goto next;
|
||||
} else {
|
||||
if (addr.ip != ct->tuplehash[dir].tuple.dst.u3.ip ||
|
||||
port != ct->tuplehash[dir].tuple.dst.u.udp.port)
|
||||
goto next;
|
||||
}
|
||||
|
||||
olen = *datalen;
|
||||
if (!map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen,
|
||||
&addr, port))
|
||||
return NF_DROP;
|
||||
|
||||
matchend = matchoff + matchlen + *datalen - olen;
|
||||
|
||||
/* The maddr= parameter (RFC 2361) specifies where to send
|
||||
* the reply. */
|
||||
if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen,
|
||||
"maddr=", &poff, &plen,
|
||||
&addr, true) > 0 &&
|
||||
addr.ip == ct->tuplehash[dir].tuple.src.u3.ip &&
|
||||
addr.ip != ct->tuplehash[!dir].tuple.dst.u3.ip) {
|
||||
buflen = sprintf(buffer, "%pI4",
|
||||
&ct->tuplehash[!dir].tuple.dst.u3.ip);
|
||||
if (!mangle_packet(skb, dataoff, dptr, datalen,
|
||||
poff, plen, buffer, buflen))
|
||||
return NF_DROP;
|
||||
}
|
||||
|
||||
/* The received= parameter (RFC 2361) contains the address
|
||||
* from which the server received the request. */
|
||||
if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen,
|
||||
"received=", &poff, &plen,
|
||||
&addr, false) > 0 &&
|
||||
addr.ip == ct->tuplehash[dir].tuple.dst.u3.ip &&
|
||||
addr.ip != ct->tuplehash[!dir].tuple.src.u3.ip) {
|
||||
buflen = sprintf(buffer, "%pI4",
|
||||
&ct->tuplehash[!dir].tuple.src.u3.ip);
|
||||
if (!mangle_packet(skb, dataoff, dptr, datalen,
|
||||
poff, plen, buffer, buflen))
|
||||
return NF_DROP;
|
||||
}
|
||||
|
||||
/* The rport= parameter (RFC 3581) contains the port number
|
||||
* from which the server received the request. */
|
||||
if (ct_sip_parse_numerical_param(ct, *dptr, matchend, *datalen,
|
||||
"rport=", &poff, &plen,
|
||||
&n) > 0 &&
|
||||
htons(n) == ct->tuplehash[dir].tuple.dst.u.udp.port &&
|
||||
htons(n) != ct->tuplehash[!dir].tuple.src.u.udp.port) {
|
||||
__be16 p = ct->tuplehash[!dir].tuple.src.u.udp.port;
|
||||
buflen = sprintf(buffer, "%u", ntohs(p));
|
||||
if (!mangle_packet(skb, dataoff, dptr, datalen,
|
||||
poff, plen, buffer, buflen))
|
||||
return NF_DROP;
|
||||
}
|
||||
}
|
||||
|
||||
next:
|
||||
/* Translate Contact headers */
|
||||
coff = 0;
|
||||
in_header = 0;
|
||||
while (ct_sip_parse_header_uri(ct, *dptr, &coff, *datalen,
|
||||
SIP_HDR_CONTACT, &in_header,
|
||||
&matchoff, &matchlen,
|
||||
&addr, &port) > 0) {
|
||||
if (!map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen,
|
||||
&addr, port))
|
||||
return NF_DROP;
|
||||
}
|
||||
|
||||
if (!map_sip_addr(skb, dataoff, dptr, datalen, SIP_HDR_FROM) ||
|
||||
!map_sip_addr(skb, dataoff, dptr, datalen, SIP_HDR_TO))
|
||||
return NF_DROP;
|
||||
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
static void ip_nat_sip_seq_adjust(struct sk_buff *skb, s16 off)
|
||||
{
|
||||
enum ip_conntrack_info ctinfo;
|
||||
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
|
||||
const struct tcphdr *th;
|
||||
|
||||
if (nf_ct_protonum(ct) != IPPROTO_TCP || off == 0)
|
||||
return;
|
||||
|
||||
th = (struct tcphdr *)(skb->data + ip_hdrlen(skb));
|
||||
nf_nat_set_seq_adjust(ct, ctinfo, th->seq, off);
|
||||
}
|
||||
|
||||
/* Handles expected signalling connections and media streams */
|
||||
static void ip_nat_sip_expected(struct nf_conn *ct,
|
||||
struct nf_conntrack_expect *exp)
|
||||
{
|
||||
struct nf_nat_ipv4_range range;
|
||||
|
||||
/* This must be a fresh one. */
|
||||
BUG_ON(ct->status & IPS_NAT_DONE_MASK);
|
||||
|
||||
/* For DST manip, map port here to where it's expected. */
|
||||
range.flags = (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED);
|
||||
range.min = range.max = exp->saved_proto;
|
||||
range.min_ip = range.max_ip = exp->saved_ip;
|
||||
nf_nat_setup_info(ct, &range, NF_NAT_MANIP_DST);
|
||||
|
||||
/* Change src to where master sends to, but only if the connection
|
||||
* actually came from the same source. */
|
||||
if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip ==
|
||||
ct->master->tuplehash[exp->dir].tuple.src.u3.ip) {
|
||||
range.flags = NF_NAT_RANGE_MAP_IPS;
|
||||
range.min_ip = range.max_ip
|
||||
= ct->master->tuplehash[!exp->dir].tuple.dst.u3.ip;
|
||||
nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int ip_nat_sip_expect(struct sk_buff *skb, unsigned int dataoff,
|
||||
const char **dptr, unsigned int *datalen,
|
||||
struct nf_conntrack_expect *exp,
|
||||
unsigned int matchoff,
|
||||
unsigned int matchlen)
|
||||
{
|
||||
enum ip_conntrack_info ctinfo;
|
||||
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
|
||||
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
|
||||
__be32 newip;
|
||||
u_int16_t port;
|
||||
char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
|
||||
unsigned int buflen;
|
||||
|
||||
/* Connection will come from reply */
|
||||
if (ct->tuplehash[dir].tuple.src.u3.ip == ct->tuplehash[!dir].tuple.dst.u3.ip)
|
||||
newip = exp->tuple.dst.u3.ip;
|
||||
else
|
||||
newip = ct->tuplehash[!dir].tuple.dst.u3.ip;
|
||||
|
||||
/* If the signalling port matches the connection's source port in the
|
||||
* original direction, try to use the destination port in the opposite
|
||||
* direction. */
|
||||
if (exp->tuple.dst.u.udp.port ==
|
||||
ct->tuplehash[dir].tuple.src.u.udp.port)
|
||||
port = ntohs(ct->tuplehash[!dir].tuple.dst.u.udp.port);
|
||||
else
|
||||
port = ntohs(exp->tuple.dst.u.udp.port);
|
||||
|
||||
exp->saved_ip = exp->tuple.dst.u3.ip;
|
||||
exp->tuple.dst.u3.ip = newip;
|
||||
exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port;
|
||||
exp->dir = !dir;
|
||||
exp->expectfn = ip_nat_sip_expected;
|
||||
|
||||
for (; port != 0; port++) {
|
||||
int ret;
|
||||
|
||||
exp->tuple.dst.u.udp.port = htons(port);
|
||||
ret = nf_ct_expect_related(exp);
|
||||
if (ret == 0)
|
||||
break;
|
||||
else if (ret != -EBUSY) {
|
||||
port = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (port == 0)
|
||||
return NF_DROP;
|
||||
|
||||
if (exp->tuple.dst.u3.ip != exp->saved_ip ||
|
||||
exp->tuple.dst.u.udp.port != exp->saved_proto.udp.port) {
|
||||
buflen = sprintf(buffer, "%pI4:%u", &newip, port);
|
||||
if (!mangle_packet(skb, dataoff, dptr, datalen,
|
||||
matchoff, matchlen, buffer, buflen))
|
||||
goto err;
|
||||
}
|
||||
return NF_ACCEPT;
|
||||
|
||||
err:
|
||||
nf_ct_unexpect_related(exp);
|
||||
return NF_DROP;
|
||||
}
|
||||
|
||||
static int mangle_content_len(struct sk_buff *skb, unsigned int dataoff,
|
||||
const char **dptr, unsigned int *datalen)
|
||||
{
|
||||
enum ip_conntrack_info ctinfo;
|
||||
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
|
||||
unsigned int matchoff, matchlen;
|
||||
char buffer[sizeof("65536")];
|
||||
int buflen, c_len;
|
||||
|
||||
/* Get actual SDP length */
|
||||
if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen,
|
||||
SDP_HDR_VERSION, SDP_HDR_UNSPEC,
|
||||
&matchoff, &matchlen) <= 0)
|
||||
return 0;
|
||||
c_len = *datalen - matchoff + strlen("v=");
|
||||
|
||||
/* Now, update SDP length */
|
||||
if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CONTENT_LENGTH,
|
||||
&matchoff, &matchlen) <= 0)
|
||||
return 0;
|
||||
|
||||
buflen = sprintf(buffer, "%u", c_len);
|
||||
return mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen,
|
||||
buffer, buflen);
|
||||
}
|
||||
|
||||
static int mangle_sdp_packet(struct sk_buff *skb, unsigned int dataoff,
|
||||
const char **dptr, unsigned int *datalen,
|
||||
unsigned int sdpoff,
|
||||
enum sdp_header_types type,
|
||||
enum sdp_header_types term,
|
||||
char *buffer, int buflen)
|
||||
{
|
||||
enum ip_conntrack_info ctinfo;
|
||||
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
|
||||
unsigned int matchlen, matchoff;
|
||||
|
||||
if (ct_sip_get_sdp_header(ct, *dptr, sdpoff, *datalen, type, term,
|
||||
&matchoff, &matchlen) <= 0)
|
||||
return -ENOENT;
|
||||
return mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen,
|
||||
buffer, buflen) ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
static unsigned int ip_nat_sdp_addr(struct sk_buff *skb, unsigned int dataoff,
|
||||
const char **dptr, unsigned int *datalen,
|
||||
unsigned int sdpoff,
|
||||
enum sdp_header_types type,
|
||||
enum sdp_header_types term,
|
||||
const union nf_inet_addr *addr)
|
||||
{
|
||||
char buffer[sizeof("nnn.nnn.nnn.nnn")];
|
||||
unsigned int buflen;
|
||||
|
||||
buflen = sprintf(buffer, "%pI4", &addr->ip);
|
||||
if (mangle_sdp_packet(skb, dataoff, dptr, datalen, sdpoff, type, term,
|
||||
buffer, buflen))
|
||||
return 0;
|
||||
|
||||
return mangle_content_len(skb, dataoff, dptr, datalen);
|
||||
}
|
||||
|
||||
static unsigned int ip_nat_sdp_port(struct sk_buff *skb, unsigned int dataoff,
|
||||
const char **dptr, unsigned int *datalen,
|
||||
unsigned int matchoff,
|
||||
unsigned int matchlen,
|
||||
u_int16_t port)
|
||||
{
|
||||
char buffer[sizeof("nnnnn")];
|
||||
unsigned int buflen;
|
||||
|
||||
buflen = sprintf(buffer, "%u", port);
|
||||
if (!mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen,
|
||||
buffer, buflen))
|
||||
return 0;
|
||||
|
||||
return mangle_content_len(skb, dataoff, dptr, datalen);
|
||||
}
|
||||
|
||||
static unsigned int ip_nat_sdp_session(struct sk_buff *skb, unsigned int dataoff,
|
||||
const char **dptr, unsigned int *datalen,
|
||||
unsigned int sdpoff,
|
||||
const union nf_inet_addr *addr)
|
||||
{
|
||||
char buffer[sizeof("nnn.nnn.nnn.nnn")];
|
||||
unsigned int buflen;
|
||||
|
||||
/* Mangle session description owner and contact addresses */
|
||||
buflen = sprintf(buffer, "%pI4", &addr->ip);
|
||||
if (mangle_sdp_packet(skb, dataoff, dptr, datalen, sdpoff,
|
||||
SDP_HDR_OWNER_IP4, SDP_HDR_MEDIA,
|
||||
buffer, buflen))
|
||||
return 0;
|
||||
|
||||
switch (mangle_sdp_packet(skb, dataoff, dptr, datalen, sdpoff,
|
||||
SDP_HDR_CONNECTION_IP4, SDP_HDR_MEDIA,
|
||||
buffer, buflen)) {
|
||||
case 0:
|
||||
/*
|
||||
* RFC 2327:
|
||||
*
|
||||
* Session description
|
||||
*
|
||||
* c=* (connection information - not required if included in all media)
|
||||
*/
|
||||
case -ENOENT:
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return mangle_content_len(skb, dataoff, dptr, datalen);
|
||||
}
|
||||
|
||||
/* 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_sdp_media(struct sk_buff *skb, unsigned int dataoff,
|
||||
const char **dptr, unsigned int *datalen,
|
||||
struct nf_conntrack_expect *rtp_exp,
|
||||
struct nf_conntrack_expect *rtcp_exp,
|
||||
unsigned int mediaoff,
|
||||
unsigned int medialen,
|
||||
union nf_inet_addr *rtp_addr)
|
||||
{
|
||||
enum ip_conntrack_info ctinfo;
|
||||
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
|
||||
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
|
||||
u_int16_t port;
|
||||
|
||||
/* Connection will come from reply */
|
||||
if (ct->tuplehash[dir].tuple.src.u3.ip ==
|
||||
ct->tuplehash[!dir].tuple.dst.u3.ip)
|
||||
rtp_addr->ip = rtp_exp->tuple.dst.u3.ip;
|
||||
else
|
||||
rtp_addr->ip = ct->tuplehash[!dir].tuple.dst.u3.ip;
|
||||
|
||||
rtp_exp->saved_ip = rtp_exp->tuple.dst.u3.ip;
|
||||
rtp_exp->tuple.dst.u3.ip = rtp_addr->ip;
|
||||
rtp_exp->saved_proto.udp.port = rtp_exp->tuple.dst.u.udp.port;
|
||||
rtp_exp->dir = !dir;
|
||||
rtp_exp->expectfn = ip_nat_sip_expected;
|
||||
|
||||
rtcp_exp->saved_ip = rtcp_exp->tuple.dst.u3.ip;
|
||||
rtcp_exp->tuple.dst.u3.ip = rtp_addr->ip;
|
||||
rtcp_exp->saved_proto.udp.port = rtcp_exp->tuple.dst.u.udp.port;
|
||||
rtcp_exp->dir = !dir;
|
||||
rtcp_exp->expectfn = ip_nat_sip_expected;
|
||||
|
||||
/* Try to get same pair of ports: if not, try to change them. */
|
||||
for (port = ntohs(rtp_exp->tuple.dst.u.udp.port);
|
||||
port != 0; port += 2) {
|
||||
int ret;
|
||||
|
||||
rtp_exp->tuple.dst.u.udp.port = htons(port);
|
||||
ret = nf_ct_expect_related(rtp_exp);
|
||||
if (ret == -EBUSY)
|
||||
continue;
|
||||
else if (ret < 0) {
|
||||
port = 0;
|
||||
break;
|
||||
}
|
||||
rtcp_exp->tuple.dst.u.udp.port = htons(port + 1);
|
||||
ret = nf_ct_expect_related(rtcp_exp);
|
||||
if (ret == 0)
|
||||
break;
|
||||
else if (ret == -EBUSY) {
|
||||
nf_ct_unexpect_related(rtp_exp);
|
||||
continue;
|
||||
} else if (ret < 0) {
|
||||
nf_ct_unexpect_related(rtp_exp);
|
||||
port = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (port == 0)
|
||||
goto err1;
|
||||
|
||||
/* Update media port. */
|
||||
if (rtp_exp->tuple.dst.u.udp.port != rtp_exp->saved_proto.udp.port &&
|
||||
!ip_nat_sdp_port(skb, dataoff, dptr, datalen,
|
||||
mediaoff, medialen, port))
|
||||
goto err2;
|
||||
|
||||
return NF_ACCEPT;
|
||||
|
||||
err2:
|
||||
nf_ct_unexpect_related(rtp_exp);
|
||||
nf_ct_unexpect_related(rtcp_exp);
|
||||
err1:
|
||||
return NF_DROP;
|
||||
}
|
||||
|
||||
static struct nf_ct_helper_expectfn sip_nat = {
|
||||
.name = "sip",
|
||||
.expectfn = ip_nat_sip_expected,
|
||||
};
|
||||
|
||||
static void __exit nf_nat_sip_fini(void)
|
||||
{
|
||||
RCU_INIT_POINTER(nf_nat_sip_hook, NULL);
|
||||
RCU_INIT_POINTER(nf_nat_sip_seq_adjust_hook, NULL);
|
||||
RCU_INIT_POINTER(nf_nat_sip_expect_hook, NULL);
|
||||
RCU_INIT_POINTER(nf_nat_sdp_addr_hook, NULL);
|
||||
RCU_INIT_POINTER(nf_nat_sdp_port_hook, NULL);
|
||||
RCU_INIT_POINTER(nf_nat_sdp_session_hook, NULL);
|
||||
RCU_INIT_POINTER(nf_nat_sdp_media_hook, NULL);
|
||||
nf_ct_helper_expectfn_unregister(&sip_nat);
|
||||
synchronize_rcu();
|
||||
}
|
||||
|
||||
static int __init nf_nat_sip_init(void)
|
||||
{
|
||||
BUG_ON(nf_nat_sip_hook != NULL);
|
||||
BUG_ON(nf_nat_sip_seq_adjust_hook != NULL);
|
||||
BUG_ON(nf_nat_sip_expect_hook != NULL);
|
||||
BUG_ON(nf_nat_sdp_addr_hook != NULL);
|
||||
BUG_ON(nf_nat_sdp_port_hook != NULL);
|
||||
BUG_ON(nf_nat_sdp_session_hook != NULL);
|
||||
BUG_ON(nf_nat_sdp_media_hook != NULL);
|
||||
RCU_INIT_POINTER(nf_nat_sip_hook, ip_nat_sip);
|
||||
RCU_INIT_POINTER(nf_nat_sip_seq_adjust_hook, ip_nat_sip_seq_adjust);
|
||||
RCU_INIT_POINTER(nf_nat_sip_expect_hook, ip_nat_sip_expect);
|
||||
RCU_INIT_POINTER(nf_nat_sdp_addr_hook, ip_nat_sdp_addr);
|
||||
RCU_INIT_POINTER(nf_nat_sdp_port_hook, ip_nat_sdp_port);
|
||||
RCU_INIT_POINTER(nf_nat_sdp_session_hook, ip_nat_sdp_session);
|
||||
RCU_INIT_POINTER(nf_nat_sdp_media_hook, ip_nat_sdp_media);
|
||||
nf_ct_helper_expectfn_register(&sip_nat);
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(nf_nat_sip_init);
|
||||
module_exit(nf_nat_sip_fini);
|
@@ -1,51 +0,0 @@
|
||||
/* (C) 2001-2002 Magnus Boden <mb@ozaba.mine.nu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/udp.h>
|
||||
|
||||
#include <net/netfilter/nf_conntrack_helper.h>
|
||||
#include <net/netfilter/nf_conntrack_expect.h>
|
||||
#include <net/netfilter/nf_nat_helper.h>
|
||||
#include <net/netfilter/nf_nat_rule.h>
|
||||
#include <linux/netfilter/nf_conntrack_tftp.h>
|
||||
|
||||
MODULE_AUTHOR("Magnus Boden <mb@ozaba.mine.nu>");
|
||||
MODULE_DESCRIPTION("TFTP NAT helper");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("ip_nat_tftp");
|
||||
|
||||
static unsigned int help(struct sk_buff *skb,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
struct nf_conntrack_expect *exp)
|
||||
{
|
||||
const struct nf_conn *ct = exp->master;
|
||||
|
||||
exp->saved_proto.udp.port
|
||||
= ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port;
|
||||
exp->dir = IP_CT_DIR_REPLY;
|
||||
exp->expectfn = nf_nat_follow_master;
|
||||
if (nf_ct_expect_related(exp) != 0)
|
||||
return NF_DROP;
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
static void __exit nf_nat_tftp_fini(void)
|
||||
{
|
||||
RCU_INIT_POINTER(nf_nat_tftp_hook, NULL);
|
||||
synchronize_rcu();
|
||||
}
|
||||
|
||||
static int __init nf_nat_tftp_init(void)
|
||||
{
|
||||
BUG_ON(nf_nat_tftp_hook != NULL);
|
||||
RCU_INIT_POINTER(nf_nat_tftp_hook, help);
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(nf_nat_tftp_init);
|
||||
module_exit(nf_nat_tftp_fini);
|
Reference in New Issue
Block a user