Skip to content

Commit

Permalink
netfilter: factor out packet duplication for IPv4/IPv6
Browse files Browse the repository at this point in the history
Extracted from the xtables TEE target. This creates two new modules for IPv4
and IPv6 that are shared between the TEE target and the new nf_tables dup
expressions.

Signed-off-by: Pablo Neira Ayuso <[email protected]>
  • Loading branch information
ummakynes committed Aug 7, 2015
1 parent 24b7811 commit bbde9fc
Show file tree
Hide file tree
Showing 10 changed files with 254 additions and 152 deletions.
7 changes: 7 additions & 0 deletions include/net/netfilter/ipv4/nf_dup_ipv4.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#ifndef _NF_DUP_IPV4_H_
#define _NF_DUP_IPV4_H_

void nf_dup_ipv4(struct sk_buff *skb, unsigned int hooknum,
const struct in_addr *gw, int oif);

#endif /* _NF_DUP_IPV4_H_ */
7 changes: 7 additions & 0 deletions include/net/netfilter/ipv6/nf_dup_ipv6.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#ifndef _NF_DUP_IPV6_H_
#define _NF_DUP_IPV6_H_

void nf_dup_ipv6(struct sk_buff *skb, unsigned int hooknum,
const struct in6_addr *gw, int oif);

#endif /* _NF_DUP_IPV6_H_ */
6 changes: 6 additions & 0 deletions net/ipv4/netfilter/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ config NF_TABLES_ARP

endif # NF_TABLES

config NF_DUP_IPV4
tristate "Netfilter IPv4 packet duplication to alternate destination"
help
This option enables the nf_dup_ipv4 core, which duplicates an IPv4
packet to be rerouted to another destination.

config NF_LOG_ARP
tristate "ARP packet logging"
default m if NETFILTER_ADVANCED=n
Expand Down
2 changes: 2 additions & 0 deletions net/ipv4/netfilter/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,5 @@ obj-$(CONFIG_IP_NF_ARP_MANGLE) += arpt_mangle.o

# just filtering instance of ARP tables for now
obj-$(CONFIG_IP_NF_ARPFILTER) += arptable_filter.o

obj-$(CONFIG_NF_DUP_IPV4) += nf_dup_ipv4.o
120 changes: 120 additions & 0 deletions net/ipv4/netfilter/nf_dup_ipv4.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* (C) 2007 by Sebastian Claßen <[email protected]>
* (C) 2007-2010 by Jan Engelhardt <[email protected]>
*
* Extracted from xt_TEE.c
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 or later, as
* published by the Free Software Foundation.
*/
#include <linux/ip.h>
#include <linux/module.h>
#include <linux/percpu.h>
#include <linux/route.h>
#include <linux/skbuff.h>
#include <net/checksum.h>
#include <net/icmp.h>
#include <net/ip.h>
#include <net/route.h>
#include <net/netfilter/ipv4/nf_dup_ipv4.h>
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
#include <net/netfilter/nf_conntrack.h>
#endif

static struct net *pick_net(struct sk_buff *skb)
{
#ifdef CONFIG_NET_NS
const struct dst_entry *dst;

if (skb->dev != NULL)
return dev_net(skb->dev);
dst = skb_dst(skb);
if (dst != NULL && dst->dev != NULL)
return dev_net(dst->dev);
#endif
return &init_net;
}

static bool nf_dup_ipv4_route(struct sk_buff *skb, const struct in_addr *gw,
int oif)
{
const struct iphdr *iph = ip_hdr(skb);
struct net *net = pick_net(skb);
struct rtable *rt;
struct flowi4 fl4;

memset(&fl4, 0, sizeof(fl4));
if (oif != -1)
fl4.flowi4_oif = oif;

fl4.daddr = gw->s_addr;
fl4.flowi4_tos = RT_TOS(iph->tos);
fl4.flowi4_scope = RT_SCOPE_UNIVERSE;
fl4.flowi4_flags = FLOWI_FLAG_KNOWN_NH;
rt = ip_route_output_key(net, &fl4);
if (IS_ERR(rt))
return false;

skb_dst_drop(skb);
skb_dst_set(skb, &rt->dst);
skb->dev = rt->dst.dev;
skb->protocol = htons(ETH_P_IP);

return true;
}

void nf_dup_ipv4(struct sk_buff *skb, unsigned int hooknum,
const struct in_addr *gw, int oif)
{
struct iphdr *iph;

if (__this_cpu_read(nf_skb_duplicated))
return;
/*
* Copy the skb, and route the copy. Will later return %XT_CONTINUE for
* the original skb, which should continue on its way as if nothing has
* happened. The copy should be independently delivered to the gateway.
*/
skb = pskb_copy(skb, GFP_ATOMIC);
if (skb == NULL)
return;

#if IS_ENABLED(CONFIG_NF_CONNTRACK)
/* Avoid counting cloned packets towards the original connection. */
nf_conntrack_put(skb->nfct);
skb->nfct = &nf_ct_untracked_get()->ct_general;
skb->nfctinfo = IP_CT_NEW;
nf_conntrack_get(skb->nfct);
#endif
/*
* If we are in PREROUTING/INPUT, the checksum must be recalculated
* since the length could have changed as a result of defragmentation.
*
* We also decrease the TTL to mitigate potential loops between two
* hosts.
*
* Set %IP_DF so that the original source is notified of a potentially
* decreased MTU on the clone route. IPv6 does this too.
*/
iph = ip_hdr(skb);
iph->frag_off |= htons(IP_DF);
if (hooknum == NF_INET_PRE_ROUTING ||
hooknum == NF_INET_LOCAL_IN)
--iph->ttl;
ip_send_check(iph);

if (nf_dup_ipv4_route(skb, gw, oif)) {
__this_cpu_write(nf_skb_duplicated, true);
ip_local_out(skb);
__this_cpu_write(nf_skb_duplicated, false);
} else {
kfree_skb(skb);
}
}
EXPORT_SYMBOL_GPL(nf_dup_ipv4);

MODULE_AUTHOR("Sebastian Claßen <[email protected]>");
MODULE_AUTHOR("Jan Engelhardt <[email protected]>");
MODULE_DESCRIPTION("nf_dup_ipv4: Duplicate IPv4 packet");
MODULE_LICENSE("GPL");
6 changes: 6 additions & 0 deletions net/ipv6/netfilter/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ config NFT_REJECT_IPV6
endif # NF_TABLES_IPV6
endif # NF_TABLES

config NF_DUP_IPV6
tristate "Netfilter IPv6 packet duplication to alternate destination"
help
This option enables the nf_dup_ipv6 core, which duplicates an IPv6
packet to be rerouted to another destination.

config NF_REJECT_IPV6
tristate "IPv6 packet rejection"
default m if NETFILTER_ADVANCED=n
Expand Down
2 changes: 2 additions & 0 deletions net/ipv6/netfilter/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ obj-$(CONFIG_NF_LOG_IPV6) += nf_log_ipv6.o
# reject
obj-$(CONFIG_NF_REJECT_IPV6) += nf_reject_ipv6.o

obj-$(CONFIG_NF_DUP_IPV6) += nf_dup_ipv6.o

# nf_tables
obj-$(CONFIG_NF_TABLES_IPV6) += nf_tables_ipv6.o
obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV6) += nft_chain_route_ipv6.o
Expand Down
96 changes: 96 additions & 0 deletions net/ipv6/netfilter/nf_dup_ipv6.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* (C) 2007 by Sebastian Claßen <[email protected]>
* (C) 2007-2010 by Jan Engelhardt <[email protected]>
*
* Extracted from xt_TEE.c
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 or later, as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/percpu.h>
#include <linux/skbuff.h>
#include <net/ipv6.h>
#include <net/ip6_route.h>
#include <net/netfilter/ipv6/nf_dup_ipv6.h>
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
#include <net/netfilter/nf_conntrack.h>
#endif

static struct net *pick_net(struct sk_buff *skb)
{
#ifdef CONFIG_NET_NS
const struct dst_entry *dst;

if (skb->dev != NULL)
return dev_net(skb->dev);
dst = skb_dst(skb);
if (dst != NULL && dst->dev != NULL)
return dev_net(dst->dev);
#endif
return &init_net;
}

static bool nf_dup_ipv6_route(struct sk_buff *skb, const struct in6_addr *gw,
int oif)
{
const struct ipv6hdr *iph = ipv6_hdr(skb);
struct net *net = pick_net(skb);
struct dst_entry *dst;
struct flowi6 fl6;

memset(&fl6, 0, sizeof(fl6));
if (oif != -1)
fl6.flowi6_oif = oif;

fl6.daddr = *gw;
fl6.flowlabel = ((iph->flow_lbl[0] & 0xF) << 16) |
(iph->flow_lbl[1] << 8) | iph->flow_lbl[2];
dst = ip6_route_output(net, NULL, &fl6);
if (dst->error) {
dst_release(dst);
return false;
}
skb_dst_drop(skb);
skb_dst_set(skb, dst);
skb->dev = dst->dev;
skb->protocol = htons(ETH_P_IPV6);

return true;
}

void nf_dup_ipv6(struct sk_buff *skb, unsigned int hooknum,
const struct in6_addr *gw, int oif)
{
if (__this_cpu_read(nf_skb_duplicated))
return;
skb = pskb_copy(skb, GFP_ATOMIC);
if (skb == NULL)
return;

#if IS_ENABLED(CONFIG_NF_CONNTRACK)
nf_conntrack_put(skb->nfct);
skb->nfct = &nf_ct_untracked_get()->ct_general;
skb->nfctinfo = IP_CT_NEW;
nf_conntrack_get(skb->nfct);
#endif
if (hooknum == NF_INET_PRE_ROUTING ||
hooknum == NF_INET_LOCAL_IN) {
struct ipv6hdr *iph = ipv6_hdr(skb);
--iph->hop_limit;
}
if (nf_dup_ipv6_route(skb, gw, oif)) {
__this_cpu_write(nf_skb_duplicated, true);
ip6_local_out(skb);
__this_cpu_write(nf_skb_duplicated, false);
} else {
kfree_skb(skb);
}
}
EXPORT_SYMBOL_GPL(nf_dup_ipv6);

MODULE_AUTHOR("Sebastian Claßen <[email protected]>");
MODULE_AUTHOR("Jan Engelhardt <[email protected]>");
MODULE_DESCRIPTION("nf_dup_ipv6: IPv6 packet duplication");
MODULE_LICENSE("GPL");
2 changes: 2 additions & 0 deletions net/netfilter/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,8 @@ config NETFILTER_XT_TARGET_TEE
depends on NETFILTER_ADVANCED
depends on IPV6 || IPV6=n
depends on !NF_CONNTRACK || NF_CONNTRACK
select NF_DUP_IPV4
select NF_DUP_IPV6 if IP6_NF_IPTABLES
---help---
This option adds a "TEE" target with which a packet can be cloned and
this clone be rerouted to another nexthop.
Expand Down
Loading

0 comments on commit bbde9fc

Please sign in to comment.