mirror of
https://dev.iopsys.eu/feed/iopsys.git
synced 2025-12-10 07:44:50 +01:00
ipt-trigger: setup package repo
This commit is contained in:
parent
e661570fbd
commit
c165587b54
6 changed files with 24 additions and 872 deletions
|
|
@ -6,15 +6,24 @@ include $(TOPDIR)/rules.mk
|
|||
include $(INCLUDE_DIR)/kernel.mk
|
||||
|
||||
PKG_NAME:=ipt-trigger
|
||||
PKG_VERSION:=1.0.0
|
||||
PKG_VERSION:=1.0.1
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
|
||||
LOCAL_DEV:=0
|
||||
ifneq ($(LOCAL_DEV),1)
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_VERSION:=8d4b4520a2935a5717a27f486a3fc78357b2a0cd
|
||||
PKG_SOURCE_URL:=https://dev.iopsys.eu/network/ipt-trigger.git
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz
|
||||
PKG_MIRROR_HASH:=skip
|
||||
endif
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define KernelPackage/ipt-trigger
|
||||
SUBMENU:=Other modules
|
||||
TITLE:=Kernel module for iptables port trigger
|
||||
FILES:=$(PKG_BUILD_DIR)/ipv4/ipt_TRIGGER.ko
|
||||
FILES:=$(PKG_BUILD_DIR)/src/ipv4/ipt_TRIGGER.ko
|
||||
DEPENDS+=+kmod-nf-nat +xtables-legacy
|
||||
AUTOLOAD:=$(call AutoLoad,30,ipt_TRIGGER,1)
|
||||
KCONFIG:=
|
||||
|
|
@ -24,7 +33,7 @@ define KernelPackage/ip6t-trigger
|
|||
SUBMENU:=Other modules
|
||||
TITLE:=Kernel module for ip6tables port trigger
|
||||
DEPENDS+=+kmod-nf-nat +xtables-legacy
|
||||
FILES:=$(PKG_BUILD_DIR)/ipv6/ip6t_TRIGGER.ko
|
||||
FILES:=$(PKG_BUILD_DIR)/src/ipv6/ip6t_TRIGGER.ko
|
||||
AUTOLOAD:=$(call AutoLoad,30,ip6t_TRIGGER,1)
|
||||
KCONFIG:=
|
||||
endef
|
||||
|
|
@ -41,21 +50,28 @@ ifeq ($(CONFIG_TARGET_brcmbca),y)
|
|||
include ../../broadcom/bcmkernel/bcm-kernel-toolchain.mk
|
||||
endif
|
||||
|
||||
ifeq ($(LOCAL_DEV),1)
|
||||
define Build/Prepare
|
||||
$(CP) -rf ./src/* $(PKG_BUILD_DIR)/
|
||||
$(CP) $(PKG_BUILD_DIR)/ipt_TRIGGER.h $(LINUX_DIR)/include/linux/netfilter_ipv4/
|
||||
$(CP) -rf ./ipt-trigger/* $(PKG_BUILD_DIR)/
|
||||
$(CP) ./ipt-trigger/include/ipt_TRIGGER.h $(LINUX_DIR)/include/linux/netfilter_ipv4/
|
||||
endef
|
||||
else
|
||||
define Build/Prepare
|
||||
$(Build/Prepare/Default)
|
||||
$(CP) $(PKG_BUILD_DIR)/include/ipt_TRIGGER.h $(LINUX_DIR)/include/linux/netfilter_ipv4/
|
||||
endef
|
||||
endif
|
||||
|
||||
define Build/InstallDev
|
||||
$(INSTALL_DIR) $(1)/include/linux/netfilter_ipv4
|
||||
$(CP) $(PKG_BUILD_DIR)/ipt_TRIGGER.h $(1)/include/linux/netfilter_ipv4/
|
||||
$(CP) $(PKG_BUILD_DIR)/include/ipt_TRIGGER.h $(1)/include/linux/netfilter_ipv4/
|
||||
endef
|
||||
|
||||
KERNEL_MAKE_FLAGS += -I$(LINUX_DIR)/include
|
||||
|
||||
define Build/Compile
|
||||
$(KERNEL_MAKE) M="$(PKG_BUILD_DIR)/ipv4/" modules
|
||||
$(KERNEL_MAKE) M="$(PKG_BUILD_DIR)/ipv6/" modules
|
||||
$(KERNEL_MAKE) M="$(PKG_BUILD_DIR)/src/ipv4/" modules
|
||||
$(KERNEL_MAKE) M="$(PKG_BUILD_DIR)/src/ipv6/" modules
|
||||
endef
|
||||
|
||||
$(eval $(call KernelPackage,ipt-trigger))
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
#ifndef _IPT_TRIGGER_H_target
|
||||
#define _IPT_TRIGGER_H_target
|
||||
|
||||
#define TRIGGER_TIMEOUT 600 /* 600 secs */
|
||||
|
||||
enum ipt_trigger_type
|
||||
{
|
||||
IPT_TRIGGER_DNAT = 1,
|
||||
IPT_TRIGGER_IN = 2,
|
||||
IPT_TRIGGER_OUT = 3,
|
||||
IPT_TRIGGER_REFRESH = 4
|
||||
};
|
||||
|
||||
struct ipt_trigger_ports {
|
||||
u_int16_t mport[2]; /* Related destination port range */
|
||||
u_int16_t rport[2]; /* Port range to map related destination port range to */
|
||||
};
|
||||
|
||||
struct ipt_trigger_info {
|
||||
enum ipt_trigger_type type;
|
||||
u_int16_t proto; /* Related protocol */
|
||||
u_int16_t trigger_timeout; /* Auto disable duration */
|
||||
struct ipt_trigger_ports ports;
|
||||
};
|
||||
|
||||
#endif /*_IPT_TRIGGER_H_target*/
|
||||
|
|
@ -1 +0,0 @@
|
|||
obj-m +=ipt_TRIGGER.o
|
||||
|
|
@ -1,407 +0,0 @@
|
|||
/* Kernel module to match the port-ranges, trigger related port-ranges,
|
||||
* and alters the destination to a local IP address.
|
||||
*
|
||||
* Copyright (C) 2003, CyberTAN Corporation
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Description:
|
||||
* This is kernel module for port-triggering.
|
||||
*
|
||||
* The module follows the Netfilter framework, called extended packet
|
||||
* matching modules.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/inetdevice.h>
|
||||
#include <linux/list.h>
|
||||
#include <net/protocol.h>
|
||||
#include <net/checksum.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#include <linux/netfilter_ipv4/ip_tables.h>
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_core.h>
|
||||
#include <net/netfilter/nf_conntrack_tuple.h>
|
||||
#include <net/netfilter/nf_nat.h>
|
||||
#include <linux/netfilter_ipv4/ipt_TRIGGER.h>
|
||||
|
||||
/* This rwlock protects the main hash table, protocol/helper/expected
|
||||
* registrations, conntrack timers*/
|
||||
|
||||
|
||||
static DEFINE_SPINLOCK(nf_trigger_lock);
|
||||
|
||||
|
||||
|
||||
#define NF_IP_PRE_ROUTING 0
|
||||
#define NF_IP_FORWARD 2
|
||||
#define IPT_CONTINUE XT_CONTINUE
|
||||
|
||||
|
||||
|
||||
/***********************lock help**********************/
|
||||
#define MUST_BE_READ_LOCKED(l)
|
||||
#define MUST_BE_WRITE_LOCKED(l)
|
||||
|
||||
|
||||
#define LOCK_BH(l) spin_lock_bh(l)
|
||||
#define UNLOCK_BH(l) spin_unlock_bh(l)
|
||||
|
||||
#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&nf_trigger_lock)
|
||||
#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&nf_trigger_lock)
|
||||
|
||||
|
||||
|
||||
|
||||
/***********************list help**********************/
|
||||
#define LIST_FIND(head, cmpfn, type, args...) \
|
||||
({ \
|
||||
const struct list_head *__i, *__j = NULL; \
|
||||
\
|
||||
ASSERT_READ_LOCK(head); \
|
||||
list_for_each(__i, (head)) \
|
||||
if (cmpfn((const type)__i , ## args)) { \
|
||||
__j = __i; \
|
||||
break; \
|
||||
} \
|
||||
(type)__j; \
|
||||
})
|
||||
|
||||
static inline int
|
||||
__list_cmp_same(const void *p1, const void *p2) { return p1 == p2; }
|
||||
|
||||
static inline void
|
||||
list_prepend(struct list_head *head, void *new)
|
||||
{
|
||||
ASSERT_WRITE_LOCK(head);
|
||||
list_add(new, head);
|
||||
}
|
||||
|
||||
#define list_named_find(head, name) \
|
||||
LIST_FIND(head, __list_cmp_name, void *, name)
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
|
||||
MODULE_DESCRIPTION("iptables trigger target module");
|
||||
|
||||
#if 0
|
||||
#define DEBUGP printk
|
||||
#else
|
||||
#define DEBUGP(format, args...)
|
||||
#endif
|
||||
|
||||
struct ipt_trigger {
|
||||
struct list_head list; /* Trigger list */
|
||||
struct timer_list timeout; /* Timer for list destroying */
|
||||
u_int32_t srcip; /* Outgoing source address */
|
||||
u_int32_t dstip; /* Outgoing destination address */
|
||||
u_int16_t mproto; /* Trigger protocol */
|
||||
u_int16_t rproto; /* Related protocol */
|
||||
u_int16_t trigger_timeout; /* Auto disable duration */
|
||||
struct ipt_trigger_ports ports; /* Trigger and related ports */
|
||||
u_int8_t reply; /* Confirm a reply connection */
|
||||
};
|
||||
|
||||
LIST_HEAD(ipt_trigger_list);
|
||||
|
||||
static void trigger_refresh(struct ipt_trigger *trig, unsigned long extra_jiffies)
|
||||
{
|
||||
DEBUGP("%s: \n", __FUNCTION__);
|
||||
LOCK_BH(&nf_trigger_lock);
|
||||
/* Need del_timer for race avoidance (may already be dying). */
|
||||
if (del_timer(&trig->timeout)) {
|
||||
trig->timeout.expires = jiffies + extra_jiffies;
|
||||
add_timer(&trig->timeout);
|
||||
}
|
||||
|
||||
UNLOCK_BH(&nf_trigger_lock);
|
||||
}
|
||||
|
||||
static void __del_trigger(struct ipt_trigger *trig)
|
||||
{
|
||||
DEBUGP("%s: \n", __FUNCTION__);
|
||||
MUST_BE_WRITE_LOCKED(&nf_trigger_lock);
|
||||
|
||||
/* delete from 'ipt_trigger_list' */
|
||||
list_del(&trig->list);
|
||||
kfree(trig);
|
||||
}
|
||||
|
||||
static void trigger_timeout(struct timer_list *t)
|
||||
{
|
||||
struct ipt_trigger *trig = from_timer(trig, t, timeout);
|
||||
|
||||
DEBUGP("trigger list %p timed out\n", trig);
|
||||
LOCK_BH(&nf_trigger_lock);
|
||||
__del_trigger(trig);
|
||||
UNLOCK_BH(&nf_trigger_lock);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
add_new_trigger(struct ipt_trigger *trig)
|
||||
{
|
||||
struct ipt_trigger *new = NULL;
|
||||
|
||||
DEBUGP("!!!!!!!!!!!! %s !!!!!!!!!!!\n", __FUNCTION__);
|
||||
|
||||
LOCK_BH(&nf_trigger_lock);
|
||||
new = (struct ipt_trigger *)
|
||||
kmalloc(sizeof(struct ipt_trigger), GFP_ATOMIC);
|
||||
|
||||
if (!new) {
|
||||
UNLOCK_BH(&nf_trigger_lock);
|
||||
DEBUGP("%s: OOM allocating trigger list\n", __FUNCTION__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memset(new, 0, sizeof(*trig));
|
||||
INIT_LIST_HEAD(&new->list);
|
||||
memcpy(new, trig, sizeof(*trig));
|
||||
|
||||
/* add to global table of trigger */
|
||||
list_prepend(&ipt_trigger_list, &new->list);
|
||||
|
||||
/* add and start timer if required */
|
||||
timer_setup(&new->timeout, trigger_timeout, 0);
|
||||
mod_timer(&new->timeout, jiffies + (trig->trigger_timeout * HZ));
|
||||
|
||||
UNLOCK_BH(&nf_trigger_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Service-Name OutBound InBound
|
||||
* 1. TMD UDP:1000 TCP/UDP:2000..2010
|
||||
* 2. WOKAO UDP:1000 TCP/UDP:3000..3010
|
||||
* 3. net2phone-1 UDP:6801 TCP:30000..30000
|
||||
* 4. net2phone-2 UDP:6801 UDP:30000..30000
|
||||
*
|
||||
* For supporting to use the same outgoing port to trigger different port rules,
|
||||
* it should check the inbound protocol and port range value. If all conditions
|
||||
* are matched, it is a same trigger item, else it needs to create a new one.
|
||||
*/
|
||||
static inline int trigger_out_matched(const struct ipt_trigger *i,
|
||||
const u_int16_t proto, const u_int16_t dport, const struct ipt_trigger_info *info)
|
||||
{
|
||||
DEBUGP("%s: i=%p, proto= %d, dport=%d.\n", __FUNCTION__, i, proto, dport);
|
||||
DEBUGP("%s: Got one, mproto= %d, mport[0..1]=%d, %d, ", __FUNCTION__,
|
||||
i->mproto, i->ports.mport[0], i->ports.mport[1]);
|
||||
DEBUGP("rproto= %d, rport[0..1]=%d, %d.\n",
|
||||
i->rproto, i->ports.rport[0], i->ports.rport[1]);
|
||||
|
||||
return ((i->mproto == proto) &&
|
||||
(i->ports.mport[0] <= dport) &&
|
||||
(i->ports.mport[1] >= dport) &&
|
||||
(i->rproto == info->proto) &&
|
||||
(i->ports.rport[0] == info->ports.rport[0]) &&
|
||||
(i->ports.rport[1] == info->ports.rport[1]));
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
trigger_out(struct sk_buff *skb,
|
||||
unsigned int hooknum,
|
||||
const void *targinfo)
|
||||
{
|
||||
const struct ipt_trigger_info *info = targinfo;
|
||||
struct ipt_trigger trig, *found;
|
||||
const struct iphdr *iph = ip_hdr(skb);
|
||||
struct tcphdr *tcph = (void *)iph + iph->ihl*4; /* Might be TCP, UDP */
|
||||
|
||||
DEBUGP("############# %s ############\n", __FUNCTION__);
|
||||
/* Check if the trigger range has already existed in 'ipt_trigger_list'. */
|
||||
found = LIST_FIND(&ipt_trigger_list, trigger_out_matched,
|
||||
struct ipt_trigger *, iph->protocol, ntohs(tcph->dest), info);
|
||||
|
||||
|
||||
if (found) {
|
||||
/* Yeah, it exists. We need to update(delay) the destroying timer. */
|
||||
trigger_refresh(found, info->trigger_timeout * HZ);
|
||||
/* In order to allow multiple hosts use the same port range, we update
|
||||
the 'saddr' after previous trigger has a reply connection. */
|
||||
if (found->reply)
|
||||
found->srcip = iph->saddr;
|
||||
}
|
||||
else {
|
||||
/* Create new trigger */
|
||||
memset(&trig, 0, sizeof(trig));
|
||||
trig.srcip = iph->saddr;
|
||||
trig.mproto = iph->protocol;
|
||||
trig.rproto = info->proto;
|
||||
trig.trigger_timeout = info->trigger_timeout;
|
||||
memcpy(&trig.ports, &info->ports, sizeof(struct ipt_trigger_ports));
|
||||
add_new_trigger(&trig); /* Add the new 'trig' to list 'ipt_trigger_list'. */
|
||||
}
|
||||
|
||||
return IPT_CONTINUE; /* We don't block any packet. */
|
||||
}
|
||||
|
||||
static inline int trigger_in_matched(const struct ipt_trigger *i,
|
||||
const u_int16_t proto, const u_int16_t dport)
|
||||
{
|
||||
u_int16_t rproto = i->rproto;
|
||||
|
||||
DEBUGP("%s: i=%p, proto= %d, dport=%d.\n", __FUNCTION__, i, proto, dport);
|
||||
DEBUGP("%s: Got one, rproto= %d, rport[0..1]=%d, %d.\n", __FUNCTION__,
|
||||
i->rproto, i->ports.rport[0], i->ports.rport[1]);
|
||||
|
||||
if (!rproto)
|
||||
rproto = proto;
|
||||
|
||||
return ((rproto == proto) && (i->ports.rport[0] <= dport)
|
||||
&& (i->ports.rport[1] >= dport));
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
trigger_in(struct sk_buff *skb,
|
||||
unsigned int hooknum,
|
||||
const void *targinfo)
|
||||
{
|
||||
const struct ipt_trigger_info *info = targinfo;
|
||||
struct ipt_trigger *found;
|
||||
const struct iphdr *iph = ip_hdr(skb);
|
||||
struct tcphdr *tcph = (void *)iph + iph->ihl*4; /* Might be TCP, UDP */
|
||||
/* Check if the trigger-ed range has already existed in 'ipt_trigger_list'. */
|
||||
found = LIST_FIND(&ipt_trigger_list, trigger_in_matched,
|
||||
struct ipt_trigger *, iph->protocol, ntohs(tcph->dest));
|
||||
if (found) {
|
||||
DEBUGP("############# %s ############\n", __FUNCTION__);
|
||||
/* Yeah, it exists. We need to update(delay) the destroying timer. */
|
||||
trigger_refresh(found, info->trigger_timeout * HZ);
|
||||
return NF_ACCEPT; /* Accept it, or the imcoming packet could be
|
||||
dropped in the FORWARD chain */
|
||||
}
|
||||
|
||||
return IPT_CONTINUE; /* Our job is the interception. */
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
trigger_dnat(struct sk_buff *skb,
|
||||
unsigned int hooknum,
|
||||
const void *targinfo)
|
||||
{
|
||||
struct ipt_trigger *found = NULL;
|
||||
const struct iphdr *iph = ip_hdr(skb);
|
||||
struct tcphdr *tcph = (void *)iph + iph->ihl*4; /* Might be TCP, UDP */
|
||||
struct nf_conn *ct = NULL;
|
||||
enum ip_conntrack_info ctinfo;
|
||||
struct nf_nat_range2 newrange;
|
||||
|
||||
DEBUGP("############# %s ############%d\n", __FUNCTION__, __LINE__);
|
||||
/* Check if the trigger-ed range has already existed in 'ipt_trigger_list'. */
|
||||
found = LIST_FIND(&ipt_trigger_list, trigger_in_matched,
|
||||
struct ipt_trigger *, iph->protocol, ntohs(tcph->dest));
|
||||
if (found) {
|
||||
DEBUGP("############# %s ############%d srcip:%d\n", __FUNCTION__, __LINE__, found->srcip);
|
||||
}
|
||||
|
||||
if (!found || !found->srcip)
|
||||
return IPT_CONTINUE; /* We don't block any packet. */
|
||||
|
||||
DEBUGP("############# %s ############\n", __FUNCTION__);
|
||||
found->reply = 1; /* Confirm there has been a reply connection. */
|
||||
ct = nf_ct_get(skb, &ctinfo);
|
||||
|
||||
DEBUGP("%s: got ", __FUNCTION__);
|
||||
|
||||
|
||||
/* Alter the destination of imcoming packet. */
|
||||
/* Transfer from original range. */
|
||||
memset(&newrange.min_addr, 0, sizeof(newrange.min_addr));
|
||||
memset(&newrange.max_addr, 0, sizeof(newrange.max_addr));
|
||||
memset(&newrange.min_proto, 0, sizeof(newrange.min_proto));
|
||||
memset(&newrange.max_proto, 0, sizeof(newrange.max_proto));
|
||||
newrange.flags = NF_NAT_RANGE_MAP_IPS;
|
||||
newrange.min_addr.ip = found->srcip;
|
||||
newrange.max_addr.ip = found->srcip;
|
||||
DEBUGP("%s: found->srcip = %x\n", __FUNCTION__, found->srcip);
|
||||
|
||||
/* Hand modified range to generic setup. */
|
||||
return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
trigger_target(struct sk_buff *skb,
|
||||
const struct xt_action_param *par)
|
||||
{
|
||||
const struct ipt_trigger_info *info = par->targinfo;
|
||||
const struct iphdr *iph = ip_hdr(skb);
|
||||
unsigned int hooknum = xt_hooknum(par);
|
||||
|
||||
DEBUGP("%s: type = %s\n", __FUNCTION__,
|
||||
(info->type == IPT_TRIGGER_DNAT) ? "dnat" :
|
||||
(info->type == IPT_TRIGGER_IN) ? "in" : "out");
|
||||
|
||||
/* The Port-trigger only supports TCP and UDP. */
|
||||
if ((iph->protocol != IPPROTO_TCP) && (iph->protocol != IPPROTO_UDP))
|
||||
return IPT_CONTINUE;
|
||||
|
||||
if (info->type == IPT_TRIGGER_OUT)
|
||||
return trigger_out(skb, hooknum, info);
|
||||
else if (info->type == IPT_TRIGGER_IN)
|
||||
return trigger_in(skb, hooknum, info);
|
||||
else if (info->type == IPT_TRIGGER_DNAT)
|
||||
return trigger_dnat(skb, hooknum, info);
|
||||
|
||||
return IPT_CONTINUE;
|
||||
}
|
||||
static int
|
||||
trigger_check(const struct xt_tgchk_param *par)
|
||||
{
|
||||
const struct ipt_trigger_info *info = par->targinfo;
|
||||
|
||||
if ((strcmp(par->table, "mangle") == 0)) {
|
||||
DEBUGP("trigger_check: bad table `%s'.\n", par->table);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (par->hook_mask & ~((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_FORWARD))) {
|
||||
DEBUGP("trigger_check: bad hooks %x.\n", par->hook_mask);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (info->proto) {
|
||||
if (info->proto != IPPROTO_TCP && info->proto != IPPROTO_UDP) {
|
||||
DEBUGP("trigger_check: bad proto %d.\n", info->proto);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
if (info->type == IPT_TRIGGER_OUT) {
|
||||
if (!info->ports.mport[0] || !info->ports.rport[0]) {
|
||||
DEBUGP("trigger_check: Try 'iptbles -j TRIGGER -h' for help.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct xt_target redirect_reg = {
|
||||
.name = "TRIGGER",
|
||||
.family = NFPROTO_IPV4,
|
||||
.target = trigger_target,
|
||||
.targetsize = sizeof(struct ipt_trigger_info),
|
||||
.checkentry = trigger_check,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init init(void)
|
||||
{
|
||||
return xt_register_target(&redirect_reg);
|
||||
}
|
||||
|
||||
static void __exit fini(void)
|
||||
{
|
||||
xt_unregister_target(&redirect_reg);
|
||||
}
|
||||
|
||||
module_init(init);
|
||||
module_exit(fini);
|
||||
|
|
@ -1 +0,0 @@
|
|||
obj-m +=ip6t_TRIGGER.o
|
||||
|
|
@ -1,429 +0,0 @@
|
|||
/* Kernel module to match the port-ranges, trigger related port-ranges,
|
||||
* and alters the destination to a local IPv6 address.
|
||||
*
|
||||
* Copyright (C) 2024, IOPSYS
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Description:
|
||||
* This is kernel module for port-triggering.
|
||||
*
|
||||
* The module follows the Netfilter framework, called extended packet
|
||||
* matching modules.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/inetdevice.h>
|
||||
#include <linux/list.h>
|
||||
#include <net/protocol.h>
|
||||
#include <net/checksum.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <linux/netfilter_ipv6.h>
|
||||
#include <linux/netfilter_ipv6/ip6_tables.h>
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_core.h>
|
||||
#include <net/netfilter/nf_conntrack_tuple.h>
|
||||
#include <net/netfilter/nf_nat.h>
|
||||
#include <linux/netfilter_ipv4/ipt_TRIGGER.h>
|
||||
|
||||
/* This rwlock protects the main hash table, protocol/helper/expected
|
||||
* registrations, conntrack timers*/
|
||||
|
||||
|
||||
static DEFINE_SPINLOCK(nf_trigger_lock);
|
||||
|
||||
|
||||
|
||||
#define NF_IP_PRE_ROUTING 0
|
||||
#define NF_IP_FORWARD 2
|
||||
#define IPT_CONTINUE XT_CONTINUE
|
||||
|
||||
|
||||
|
||||
/***********************lock help**********************/
|
||||
#define MUST_BE_READ_LOCKED(l)
|
||||
#define MUST_BE_WRITE_LOCKED(l)
|
||||
|
||||
|
||||
#define LOCK_BH(l) spin_lock_bh(l)
|
||||
#define UNLOCK_BH(l) spin_unlock_bh(l)
|
||||
|
||||
#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&nf_trigger_lock)
|
||||
#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&nf_trigger_lock)
|
||||
|
||||
|
||||
|
||||
|
||||
/***********************list help**********************/
|
||||
#define LIST_FIND(head, cmpfn, type, args...) \
|
||||
({ \
|
||||
const struct list_head *__i, *__j = NULL; \
|
||||
\
|
||||
ASSERT_READ_LOCK(head); \
|
||||
list_for_each(__i, (head)) \
|
||||
if (cmpfn((const type)__i , ## args)) { \
|
||||
__j = __i; \
|
||||
break; \
|
||||
} \
|
||||
(type)__j; \
|
||||
})
|
||||
|
||||
static inline int
|
||||
__list_cmp_same(const void *p1, const void *p2) { return p1 == p2; }
|
||||
|
||||
static inline void
|
||||
list_prepend(struct list_head *head, void *new)
|
||||
{
|
||||
ASSERT_WRITE_LOCK(head);
|
||||
list_add(new, head);
|
||||
}
|
||||
|
||||
#define list_named_find(head, name) \
|
||||
LIST_FIND(head, __list_cmp_name, void *, name)
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("IOPSYS Network Team");
|
||||
MODULE_DESCRIPTION("iptables trigger target module");
|
||||
|
||||
#if 0
|
||||
#define DEBUGP printk
|
||||
#else
|
||||
#define DEBUGP(format, args...)
|
||||
#endif
|
||||
|
||||
struct ipt_trigger {
|
||||
struct list_head list; /* Trigger list */
|
||||
struct timer_list timeout; /* Timer for list destroying */
|
||||
struct in6_addr srcip; /* Outgoing source address */
|
||||
struct in6_addr dstip; /* Outgoing destination address */
|
||||
u_int16_t mproto; /* Trigger protocol */
|
||||
u_int16_t rproto; /* Related protocol */
|
||||
u_int16_t trigger_timeout; /* Auto disable duration */
|
||||
struct ipt_trigger_ports ports; /* Trigger and related ports */
|
||||
u_int8_t reply; /* Confirm a reply connection */
|
||||
};
|
||||
|
||||
LIST_HEAD(ipt_trigger_list);
|
||||
|
||||
static unsigned char *ipv6_header_get_L4_header_offset(const struct ipv6hdr *ip6h_p)
|
||||
{
|
||||
unsigned int ext_head_count = 8;
|
||||
const struct ipv6_opt_hdr *ip_ext_p;
|
||||
unsigned int payload_offset = 0;
|
||||
char *tcpudp_hdr = NULL;
|
||||
uint8_t nextHdr_p;
|
||||
|
||||
nextHdr_p = ip6h_p->nexthdr;
|
||||
ip_ext_p = (const struct ipv6_opt_hdr *)(ip6h_p + 1);
|
||||
payload_offset = sizeof(struct ipv6hdr);
|
||||
|
||||
do {
|
||||
if ((nextHdr_p == IPPROTO_TCP) || (nextHdr_p == IPPROTO_UDP)) {
|
||||
tcpudp_hdr = (unsigned char *)ip6h_p + payload_offset;
|
||||
break;
|
||||
}
|
||||
|
||||
payload_offset += (ip_ext_p->hdrlen + 1U) << 3U;
|
||||
nextHdr_p = ip_ext_p->nexthdr;
|
||||
ip_ext_p = (struct ipv6_opt_hdr *)((uint8_t *)ip6h_p + payload_offset);
|
||||
ext_head_count--; /* at most 8 extension headers */
|
||||
} while (ext_head_count);
|
||||
|
||||
return tcpudp_hdr;
|
||||
}
|
||||
|
||||
static void trigger_refresh(struct ipt_trigger *trig, unsigned long extra_jiffies)
|
||||
{
|
||||
DEBUGP("%s: \n", __FUNCTION__);
|
||||
LOCK_BH(&nf_trigger_lock);
|
||||
/* Need del_timer for race avoidance (may already be dying). */
|
||||
if (del_timer(&trig->timeout)) {
|
||||
trig->timeout.expires = jiffies + extra_jiffies;
|
||||
add_timer(&trig->timeout);
|
||||
}
|
||||
|
||||
UNLOCK_BH(&nf_trigger_lock);
|
||||
}
|
||||
|
||||
static void __del_trigger(struct ipt_trigger *trig)
|
||||
{
|
||||
DEBUGP("%s: \n", __FUNCTION__);
|
||||
MUST_BE_WRITE_LOCKED(&nf_trigger_lock);
|
||||
|
||||
/* delete from 'ipt_trigger_list' */
|
||||
list_del(&trig->list);
|
||||
kfree(trig);
|
||||
}
|
||||
|
||||
static void trigger_timeout(struct timer_list *t)
|
||||
{
|
||||
struct ipt_trigger *trig = from_timer(trig, t, timeout);
|
||||
|
||||
DEBUGP("trigger list %p timed out\n", trig);
|
||||
LOCK_BH(&nf_trigger_lock);
|
||||
__del_trigger(trig);
|
||||
UNLOCK_BH(&nf_trigger_lock);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
add_new_trigger(struct ipt_trigger *trig)
|
||||
{
|
||||
struct ipt_trigger *new = NULL;
|
||||
|
||||
DEBUGP("!!!!!!!!!!!! %s !!!!!!!!!!!\n", __FUNCTION__);
|
||||
|
||||
LOCK_BH(&nf_trigger_lock);
|
||||
new = (struct ipt_trigger *)
|
||||
kmalloc(sizeof(struct ipt_trigger), GFP_ATOMIC);
|
||||
|
||||
if (!new) {
|
||||
UNLOCK_BH(&nf_trigger_lock);
|
||||
DEBUGP("%s: OOM allocating trigger list\n", __FUNCTION__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memset(new, 0, sizeof(*trig));
|
||||
INIT_LIST_HEAD(&new->list);
|
||||
memcpy(new, trig, sizeof(*trig));
|
||||
|
||||
/* add to global table of trigger */
|
||||
list_prepend(&ipt_trigger_list, &new->list);
|
||||
|
||||
/* add and start timer if required */
|
||||
timer_setup(&new->timeout, trigger_timeout, 0);
|
||||
mod_timer(&new->timeout, jiffies + (trig->trigger_timeout * HZ));
|
||||
|
||||
UNLOCK_BH(&nf_trigger_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Service-Name OutBound InBound
|
||||
* 1. TMD UDP:1000 TCP/UDP:2000..2010
|
||||
* 2. WOKAO UDP:1000 TCP/UDP:3000..3010
|
||||
* 3. net2phone-1 UDP:6801 TCP:30000..30000
|
||||
* 4. net2phone-2 UDP:6801 UDP:30000..30000
|
||||
*
|
||||
* For supporting to use the same outgoing port to trigger different port rules,
|
||||
* it should check the inbound protocol and port range value. If all conditions
|
||||
* are matched, it is a same trigger item, else it needs to create a new one.
|
||||
*/
|
||||
static inline int trigger_out_matched(const struct ipt_trigger *i,
|
||||
const u_int16_t proto, const u_int16_t dport, const struct ipt_trigger_info *info)
|
||||
{
|
||||
DEBUGP("%s: i=%p, proto= %d, dport=%d.\n", __FUNCTION__, i, proto, dport);
|
||||
DEBUGP("%s: Got one, mproto= %d, mport[0..1]=%d, %d, ", __FUNCTION__,
|
||||
i->mproto, i->ports.mport[0], i->ports.mport[1]);
|
||||
DEBUGP("rproto= %d, rport[0..1]=%d, %d.\n",
|
||||
i->rproto, i->ports.rport[0], i->ports.rport[1]);
|
||||
|
||||
return ((i->mproto == proto) &&
|
||||
(i->ports.mport[0] <= dport) &&
|
||||
(i->ports.mport[1] >= dport) &&
|
||||
(i->rproto == info->proto) &&
|
||||
(i->ports.rport[0] == info->ports.rport[0]) &&
|
||||
(i->ports.rport[1] == info->ports.rport[1]));
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
trigger_out(struct sk_buff *skb,
|
||||
unsigned int hooknum,
|
||||
const void *targinfo)
|
||||
{
|
||||
const struct ipt_trigger_info *info = targinfo;
|
||||
struct ipt_trigger trig, *found;
|
||||
const struct ipv6hdr *ip6h = ipv6_hdr(skb);
|
||||
struct tcphdr *tcph = (struct tcphdr*)ipv6_header_get_L4_header_offset(ip6h); /* Might be TCP, UDP */
|
||||
|
||||
DEBUGP("############# %s ############\n", __FUNCTION__);
|
||||
/* Check if the trigger range has already existed in 'ipt_trigger_list'. */
|
||||
found = LIST_FIND(&ipt_trigger_list, trigger_out_matched,
|
||||
struct ipt_trigger *, ip6h->nexthdr, ntohs(tcph->dest), info);
|
||||
|
||||
|
||||
if (found) {
|
||||
/* Yeah, it exists. We need to update(delay) the destroying timer. */
|
||||
trigger_refresh(found, info->trigger_timeout * HZ);
|
||||
/* In order to allow multiple hosts use the same port range, we update
|
||||
the 'saddr' after previous trigger has a reply connection. */
|
||||
if (found->reply)
|
||||
found->srcip = ip6h->saddr;
|
||||
}
|
||||
else {
|
||||
/* Create new trigger */
|
||||
memset(&trig, 0, sizeof(trig));
|
||||
memcpy(&trig.srcip, &ip6h->saddr, sizeof(trig.srcip));
|
||||
trig.mproto = ip6h->nexthdr;
|
||||
trig.rproto = info->proto;
|
||||
trig.trigger_timeout = info->trigger_timeout;
|
||||
memcpy(&trig.ports, &info->ports, sizeof(struct ipt_trigger_ports));
|
||||
add_new_trigger(&trig); /* Add the new 'trig' to list 'ipt_trigger_list'. */
|
||||
}
|
||||
|
||||
return IPT_CONTINUE; /* We don't block any packet. */
|
||||
}
|
||||
|
||||
static inline int trigger_in_matched(const struct ipt_trigger *i,
|
||||
const u_int16_t proto, const u_int16_t dport)
|
||||
{
|
||||
u_int16_t rproto = i->rproto;
|
||||
|
||||
DEBUGP("%s: i=%p, proto= %d, dport=%d.\n", __FUNCTION__, i, proto, dport);
|
||||
DEBUGP("%s: Got one, rproto= %d, rport[0..1]=%d, %d.\n", __FUNCTION__,
|
||||
i->rproto, i->ports.rport[0], i->ports.rport[1]);
|
||||
|
||||
if (!rproto)
|
||||
rproto = proto;
|
||||
|
||||
return ((rproto == proto) && (i->ports.rport[0] <= dport)
|
||||
&& (i->ports.rport[1] >= dport));
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
trigger_in(struct sk_buff *skb,
|
||||
unsigned int hooknum,
|
||||
const void *targinfo)
|
||||
{
|
||||
const struct ipt_trigger_info *info = targinfo;
|
||||
struct ipt_trigger *found;
|
||||
const struct ipv6hdr *ip6h = ipv6_hdr(skb);
|
||||
struct tcphdr *tcph =(struct tcphdr*)ipv6_header_get_L4_header_offset(ip6h); /* Might be TCP, UDP */
|
||||
/* Check if the trigger-ed range has already existed in 'ipt_trigger_list'. */
|
||||
found = LIST_FIND(&ipt_trigger_list, trigger_in_matched,
|
||||
struct ipt_trigger *, ip6h->nexthdr, ntohs(tcph->dest));
|
||||
if (found) {
|
||||
DEBUGP("############# %s ############\n", __FUNCTION__);
|
||||
/* Yeah, it exists. We need to update(delay) the destroying timer. */
|
||||
trigger_refresh(found, info->trigger_timeout * HZ);
|
||||
return NF_ACCEPT; /* Accept it, or the imcoming packet could be
|
||||
dropped in the FORWARD chain */
|
||||
}
|
||||
|
||||
return IPT_CONTINUE; /* Our job is the interception. */
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
trigger_dnat(struct sk_buff *skb,
|
||||
unsigned int hooknum,
|
||||
const void *targinfo)
|
||||
{
|
||||
struct ipt_trigger *found = NULL;
|
||||
const struct ipv6hdr *ip6h = ipv6_hdr(skb);
|
||||
struct tcphdr *tcph =(struct tcphdr*)ipv6_header_get_L4_header_offset(ip6h); /* Might be TCP, UDP */
|
||||
struct nf_conn *ct = NULL;
|
||||
enum ip_conntrack_info ctinfo;
|
||||
struct nf_nat_range2 newrange;
|
||||
|
||||
/* Check if the trigger-ed range has already existed in 'ipt_trigger_list'. */
|
||||
found = LIST_FIND(&ipt_trigger_list, trigger_in_matched,
|
||||
struct ipt_trigger *, ip6h->nexthdr, ntohs(tcph->dest));
|
||||
|
||||
if (!found)
|
||||
return IPT_CONTINUE; /* We don't block any packet. */
|
||||
|
||||
DEBUGP("############# %s ############\n", __FUNCTION__);
|
||||
found->reply = 1; /* Confirm there has been a reply connection. */
|
||||
ct = nf_ct_get(skb, &ctinfo);
|
||||
|
||||
DEBUGP("%s: got ", __FUNCTION__);
|
||||
|
||||
|
||||
/* Alter the destination of imcoming packet. */
|
||||
/* Transfer from original range. */
|
||||
memset(&newrange.min_addr, 0, sizeof(newrange.min_addr));
|
||||
memset(&newrange.max_addr, 0, sizeof(newrange.max_addr));
|
||||
memset(&newrange.min_proto, 0, sizeof(newrange.min_proto));
|
||||
memset(&newrange.max_proto, 0, sizeof(newrange.max_proto));
|
||||
newrange.flags = NF_NAT_RANGE_MAP_IPS;
|
||||
memcpy(&newrange.min_addr.ip, &found->srcip, sizeof(newrange.min_addr.ip));
|
||||
memcpy(&newrange.max_addr.ip, &found->srcip, sizeof(newrange.max_addr.ip));
|
||||
DEBUGP("%s: found->srcip = %x\n", __FUNCTION__, found->srcip);
|
||||
|
||||
/* Hand modified range to generic setup. */
|
||||
return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
trigger_target(struct sk_buff *skb,
|
||||
const struct xt_action_param *par)
|
||||
{
|
||||
const struct ipt_trigger_info *info = par->targinfo;
|
||||
const struct ipv6hdr *ip6h = ipv6_hdr(skb);
|
||||
unsigned int hooknum = xt_hooknum(par);
|
||||
|
||||
DEBUGP("%s: type = %s\n", __FUNCTION__,
|
||||
(info->type == IPT_TRIGGER_DNAT) ? "dnat" :
|
||||
(info->type == IPT_TRIGGER_IN) ? "in" : "out");
|
||||
|
||||
/* The Port-trigger only supports TCP and UDP. */
|
||||
if ((ip6h->nexthdr != IPPROTO_TCP) && (ip6h->nexthdr != IPPROTO_UDP))
|
||||
return IPT_CONTINUE;
|
||||
|
||||
if (info->type == IPT_TRIGGER_OUT)
|
||||
return trigger_out(skb, hooknum, info);
|
||||
else if (info->type == IPT_TRIGGER_IN)
|
||||
return trigger_in(skb, hooknum, info);
|
||||
else if (info->type == IPT_TRIGGER_DNAT)
|
||||
return trigger_dnat(skb, hooknum, info);
|
||||
|
||||
return IPT_CONTINUE;
|
||||
}
|
||||
static int
|
||||
trigger_check(const struct xt_tgchk_param *par)
|
||||
{
|
||||
const struct ipt_trigger_info *info = par->targinfo;
|
||||
|
||||
if ((strcmp(par->table, "mangle") == 0)) {
|
||||
DEBUGP("trigger_check: bad table `%s'.\n", par->table);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (par->hook_mask & ~((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_FORWARD))) {
|
||||
DEBUGP("trigger_check: bad hooks %x.\n", par->hook_mask);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (info->proto) {
|
||||
if (info->proto != IPPROTO_TCP && info->proto != IPPROTO_UDP) {
|
||||
DEBUGP("trigger_check: bad proto %d.\n", info->proto);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
if (info->type == IPT_TRIGGER_OUT) {
|
||||
if (!info->ports.mport[0] || !info->ports.rport[0]) {
|
||||
DEBUGP("trigger_check: Try 'iptbles -j TRIGGER -h' for help.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct xt_target redirect_reg = {
|
||||
.name = "TRIGGER",
|
||||
.family = NFPROTO_IPV6,
|
||||
.target = trigger_target,
|
||||
.targetsize = sizeof(struct ipt_trigger_info),
|
||||
.checkentry = trigger_check,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init init(void)
|
||||
{
|
||||
return xt_register_target(&redirect_reg);
|
||||
}
|
||||
|
||||
static void __exit fini(void)
|
||||
{
|
||||
xt_unregister_target(&redirect_reg);
|
||||
}
|
||||
|
||||
module_init(init);
|
||||
module_exit(fini);
|
||||
Loading…
Add table
Reference in a new issue