mirror of
https://dev.iopsys.eu/feed/iopsys.git
synced 2025-12-10 07:44:50 +01:00
ipt-trigger: package for kernel module to support port trigger
This commit is contained in:
parent
8101ef0c5a
commit
7536b725b4
10 changed files with 1158 additions and 0 deletions
61
ipt-trigger/Makefile
Normal file
61
ipt-trigger/Makefile
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
#
|
||||||
|
# Copyright (C) 2024 IOPSYS Software Solutions AB
|
||||||
|
#
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
include $(INCLUDE_DIR)/kernel.mk
|
||||||
|
|
||||||
|
PKG_NAME:=ipt-trigger
|
||||||
|
PKG_VERSION:=1.0.0
|
||||||
|
PKG_LICENSE:=GPL-2.0
|
||||||
|
|
||||||
|
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
|
||||||
|
AUTOLOAD:=$(call AutoLoad,30,ipt_TRIGGER,1)
|
||||||
|
KCONFIG:=
|
||||||
|
endef
|
||||||
|
|
||||||
|
define KernelPackage/ip6t-trigger
|
||||||
|
SUBMENU:=Other modules
|
||||||
|
TITLE:=Kernel module for ip6tables port trigger
|
||||||
|
DEPENDS+=+(TARGET_brcmbca):kmod-nf-nat
|
||||||
|
FILES:=$(PKG_BUILD_DIR)/ipv6/ip6t_TRIGGER.ko
|
||||||
|
AUTOLOAD:=$(call AutoLoad,30,ip6t_TRIGGER,1)
|
||||||
|
KCONFIG:=
|
||||||
|
endef
|
||||||
|
|
||||||
|
define KernelPackage/ipt-trigger/description
|
||||||
|
Kernel module to enable port trigger for iptables
|
||||||
|
endef
|
||||||
|
|
||||||
|
define KernelPackage/ip6t-trigger/description
|
||||||
|
Kernel module to enable port trigger for ip6tables
|
||||||
|
endef
|
||||||
|
|
||||||
|
ifeq ($(CONFIG_TARGET_brcmbca),y)
|
||||||
|
include ../../broadcom/bcmkernel/bcm-kernel-toolchain.mk
|
||||||
|
endif
|
||||||
|
|
||||||
|
define Build/Prepare
|
||||||
|
$(CP) -rf ./src/* $(PKG_BUILD_DIR)/
|
||||||
|
$(CP) $(PKG_BUILD_DIR)/ipt_TRIGGER.h $(LINUX_DIR)/include/linux/netfilter_ipv4/
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Build/InstallDev
|
||||||
|
$(INSTALL_DIR) $(1)/include/linux/netfilter_ipv4
|
||||||
|
$(CP) $(PKG_BUILD_DIR)/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
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call KernelPackage,ipt-trigger))
|
||||||
|
$(eval $(call KernelPackage,ip6t-trigger))
|
||||||
26
ipt-trigger/src/ipt_TRIGGER.h
Normal file
26
ipt-trigger/src/ipt_TRIGGER.h
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
#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
ipt-trigger/src/ipv4/Makefile
Normal file
1
ipt-trigger/src/ipv4/Makefile
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
obj-m +=ipt_TRIGGER.o
|
||||||
407
ipt-trigger/src/ipv4/ipt_TRIGGER.c
Normal file
407
ipt-trigger/src/ipv4/ipt_TRIGGER.c
Normal file
|
|
@ -0,0 +1,407 @@
|
||||||
|
/* 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
ipt-trigger/src/ipv6/Makefile
Normal file
1
ipt-trigger/src/ipv6/Makefile
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
obj-m +=ip6t_TRIGGER.o
|
||||||
429
ipt-trigger/src/ipv6/ip6t_TRIGGER.c
Normal file
429
ipt-trigger/src/ipv6/ip6t_TRIGGER.c
Normal file
|
|
@ -0,0 +1,429 @@
|
||||||
|
/* 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);
|
||||||
54
port-trigger/Makefile
Normal file
54
port-trigger/Makefile
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
#
|
||||||
|
# Copyright (C) 2024 IOPSYS Software Solutions AB
|
||||||
|
#
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=port-trigger
|
||||||
|
PKG_VERSION:=1.0.0
|
||||||
|
|
||||||
|
LOCAL_DEV:=0
|
||||||
|
ifneq ($(LOCAL_DEV),1)
|
||||||
|
PKG_SOURCE_PROTO:=git
|
||||||
|
PKG_SOURCE_URL:=https://dev.iopsys.eu/network/port-trigger.git
|
||||||
|
PKG_SOURCE_VERSION:=715fa689e5c22721d8ccd9d4e1cbe290caca3662
|
||||||
|
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz
|
||||||
|
PKG_MIRROR_HASH:=skip
|
||||||
|
endif
|
||||||
|
|
||||||
|
PKG_LICENSE:=BSD-3-Clause
|
||||||
|
PKG_LICENSE_FILES:=LICENSE
|
||||||
|
|
||||||
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
include ../bbfdm/bbfdm.mk
|
||||||
|
|
||||||
|
define Package/port-trigger
|
||||||
|
SECTION:=utils
|
||||||
|
CATEGORY:=Utilities
|
||||||
|
TITLE:=Port Trigger Daemon
|
||||||
|
DEPENDS:=+libuci +libubox +libubus +libblobmsg-json +libjson-c +libbbfdm-api +kmod-ipt-trigger +kmod-ip6t-trigger +iptables-mod-nfqueue
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/port-trigger/description
|
||||||
|
Manage port trigger
|
||||||
|
endef
|
||||||
|
|
||||||
|
ifeq ($(LOCAL_DEV),1)
|
||||||
|
define Build/Prepare
|
||||||
|
$(CP) -rf ./port-trigger/* $(PKG_BUILD_DIR)/
|
||||||
|
endef
|
||||||
|
endif
|
||||||
|
|
||||||
|
define Package/port-trigger/install
|
||||||
|
$(INSTALL_DIR) $(1)/etc/config
|
||||||
|
$(INSTALL_DIR) $(1)/etc/init.d
|
||||||
|
$(INSTALL_DIR) $(1)/lib/port-trigger
|
||||||
|
$(CP) ./files/* $(1)/
|
||||||
|
|
||||||
|
$(INSTALL_BIN) ./files/etc/init.d/port-trigger $(1)/etc/init.d/
|
||||||
|
$(INSTALL_DATA) ./files/etc/config/port-trigger $(1)/etc/config/
|
||||||
|
$(INSTALL_DATA) ./files/lib/port-trigger/port_trigger.sh $(1)/lib/port-trigger/
|
||||||
|
$(call BbfdmInstallPlugin,$(1),$(PKG_BUILD_DIR)/bbf_plugin/libporttrigger.so)
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackage,port-trigger))
|
||||||
1
port-trigger/files/etc/config/port-trigger
Normal file
1
port-trigger/files/etc/config/port-trigger
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
#port trigger uci file
|
||||||
21
port-trigger/files/etc/init.d/port-trigger
Normal file
21
port-trigger/files/etc/init.d/port-trigger
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#!/bin/sh /etc/rc.common
|
||||||
|
|
||||||
|
START=65
|
||||||
|
STOP=20
|
||||||
|
USE_PROCD=1
|
||||||
|
|
||||||
|
. /lib/port-trigger/port_trigger.sh
|
||||||
|
|
||||||
|
start_service() {
|
||||||
|
port_trigger_handling
|
||||||
|
}
|
||||||
|
|
||||||
|
service_triggers()
|
||||||
|
{
|
||||||
|
procd_add_reload_trigger firewall
|
||||||
|
procd_add_reload_trigger port-trigger
|
||||||
|
}
|
||||||
|
|
||||||
|
reload_service() {
|
||||||
|
start
|
||||||
|
}
|
||||||
157
port-trigger/files/lib/port-trigger/port_trigger.sh
Executable file
157
port-trigger/files/lib/port-trigger/port_trigger.sh
Executable file
|
|
@ -0,0 +1,157 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
. /lib/functions.sh
|
||||||
|
|
||||||
|
process_port_trigger() {
|
||||||
|
local rule_id="$1"
|
||||||
|
local is_enabled=""
|
||||||
|
local duration=""
|
||||||
|
local trigger_dport=""
|
||||||
|
local trigger_dport_end=""
|
||||||
|
local protocol=""
|
||||||
|
local interface=""
|
||||||
|
local open_dport=""
|
||||||
|
local open_dport_end=""
|
||||||
|
local open_protocol=""
|
||||||
|
local ptg_id=""
|
||||||
|
local IP_RULE=""
|
||||||
|
local IP6_RULE=""
|
||||||
|
local IP_RULE_FWD=""
|
||||||
|
|
||||||
|
get_port_trigger() {
|
||||||
|
local ptg_name
|
||||||
|
config_get ptg_name "$1" "name"
|
||||||
|
if [ "$ptg_name" == "$2" ]; then
|
||||||
|
ptg_id="$1"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
ptg_id=""
|
||||||
|
config_get name "$rule_id" "port_trigger"
|
||||||
|
config_foreach get_port_trigger "port_trigger" "$name"
|
||||||
|
[ -z "$ptg_id" ] && return
|
||||||
|
|
||||||
|
is_enabled=$(uci -q get port-trigger."$ptg_id".enable)
|
||||||
|
|
||||||
|
if [ -z "$is_enabled" ] || [ "$is_enabled" = "0" ]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
protocol=$(uci -q get port-trigger."$ptg_id".protocol)
|
||||||
|
[ -z "$protocol" ] && return
|
||||||
|
|
||||||
|
if [ "$protocol" = "UDP" ] || [ "$protocol" = "udp" ]; then
|
||||||
|
IP_RULE="$IP_RULE -p udp"
|
||||||
|
IP6_RULE="$IP6_RULE -p udp"
|
||||||
|
IP_RULE_FWD="$IP_RULE_FWD -p udp"
|
||||||
|
elif [ "$protocol" = "TCP" ] || [ "$protocol" = "tcp" ]; then
|
||||||
|
IP_RULE="$IP_RULE -p tcp"
|
||||||
|
IP6_RULE="$IP6_RULE -p tcp"
|
||||||
|
IP_RULE_FWD="$IP_RULE_FWD -p tcp"
|
||||||
|
else
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
trigger_dport=$(uci -q get port-trigger."$ptg_id".port)
|
||||||
|
[ -z "$trigger_dport" ] && return
|
||||||
|
IP_RULE="$IP_RULE --dport $trigger_dport"
|
||||||
|
IP6_RULE="$IP6_RULE --dport $trigger_dport"
|
||||||
|
|
||||||
|
trigger_dport_end=$(uci -q get port-trigger."$ptg_id".end_port_range)
|
||||||
|
if [ -n "$trigger_dport_end" ]; then
|
||||||
|
IP_RULE="$IP_RULE:$trigger_dport"
|
||||||
|
IP6_RULE="$IP6_RULE:$trigger_dport"
|
||||||
|
fi
|
||||||
|
|
||||||
|
config_get open_protocol "$rule_id" "protocol"
|
||||||
|
if [ "$open_protocol" = "UDP" ] || [ "$open_protocol" = "udp" ]; then
|
||||||
|
IP_RULE="$IP_RULE -j TRIGGER --trigger-type out --trigger-proto udp"
|
||||||
|
IP6_RULE="$IP6_RULE -j TRIGGER --trigger-type out --trigger-proto udp"
|
||||||
|
elif [ "$open_protocol" = "TCP" ] || [ "$open_protocol" = "tcp" ]; then
|
||||||
|
IP_RULE="$IP_RULE -j TRIGGER --trigger-type out --trigger-proto tcp"
|
||||||
|
IP6_RULE="$IP6_RULE -j TRIGGER --trigger-type out --trigger-proto tcp"
|
||||||
|
else
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
config_get open_dport "$rule_id" "port"
|
||||||
|
[ -z "$open_dport" ] && return
|
||||||
|
IP_RULE="$IP_RULE --trigger-match $open_dport"
|
||||||
|
IP6_RULE="$IP6_RULE --trigger-match $open_dport"
|
||||||
|
IP_RULE_FWD="$IP_RULE_FWD --dport $open_dport"
|
||||||
|
|
||||||
|
config_get open_dport_end "$rule_id" "end_port_range"
|
||||||
|
if [ -z "$open_dport_end" ]; then
|
||||||
|
IP_RULE="$IP_RULE --trigger-relate $open_dport"
|
||||||
|
IP6_RULE="$IP6_RULE --trigger-relate $open_dport"
|
||||||
|
else
|
||||||
|
IP_RULE="$IP_RULE-$open_dport_end --trigger-relate $open_dport-$open_dport_end"
|
||||||
|
IP6_RULE="$IP6_RULE-$open_dport_end --trigger-relate $open_dport-$open_dport_end"
|
||||||
|
IP_RULE_FWD="$IP_RULE_FWD:$open_dport_end"
|
||||||
|
fi
|
||||||
|
|
||||||
|
duration=$(uci -q get port-trigger."$ptg_id".auto_disable_duration)
|
||||||
|
if [ -n "$duration" ]; then
|
||||||
|
IP_RULE="$IP_RULE --trigger-timeout $duration"
|
||||||
|
IP6_RULE="$IP6_RULE --trigger-timeout $duration"
|
||||||
|
fi
|
||||||
|
|
||||||
|
interface=$(uci -q get port-trigger."$ptg_id".src)
|
||||||
|
[ -z "$interface" ] && return
|
||||||
|
device=$(uci -q get network.$interface.device)
|
||||||
|
IP_RULE_1="iptables -w -t nat -A prerouting_porttrigger -i $device $IP_RULE"
|
||||||
|
echo "$IP_RULE_1">>/tmp/port_trigger_iptables
|
||||||
|
|
||||||
|
IP_RULE_1="ip6tables -w -t nat -A prerouting_porttrigger -i $device $IP6_RULE"
|
||||||
|
echo "$IP_RULE_1">>/tmp/port_trigger_ip6tables
|
||||||
|
|
||||||
|
if [ -n "$duration" ]; then
|
||||||
|
echo "iptables -w -t filter -A forwarding_wan_porttrigger $IP_RULE_FWD -j TRIGGER --trigger-type in --trigger-timeout $duration">>/tmp/port_trigger_iptables
|
||||||
|
echo "ip6tables -w -t filter -A forwarding_wan_porttrigger $IP_RULE_FWD -j TRIGGER --trigger-type in --trigger-timeout $duration">>/tmp/port_trigger_ip6tables
|
||||||
|
|
||||||
|
echo "iptables -w -t nat -A prerouting_wan_porttrigger $IP_RULE_FWD -j TRIGGER --trigger-type dnat --trigger-timeout $duration">>/tmp/port_trigger_iptables
|
||||||
|
else
|
||||||
|
echo "iptables -w -t filter -A forwarding_wan_porttrigger $IP_RULE_FWD -j TRIGGER --trigger-type in">>/tmp/port_trigger_iptables
|
||||||
|
echo "ip6tables -w -t filter -A forwarding_wan_porttrigger $IP_RULE_FWD -j TRIGGER --trigger-type in">>/tmp/port_trigger_ip6tables
|
||||||
|
|
||||||
|
echo "iptables -w -t nat -A prerouting_wan_porttrigger $IP_RULE_FWD -j TRIGGER --trigger-type dnat">>/tmp/port_trigger_iptables
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
port_trigger_handling() {
|
||||||
|
rm /tmp/port_trigger_iptables 2> /dev/null
|
||||||
|
rm /tmp/port_trigger_ip6tables 2> /dev/null
|
||||||
|
touch /tmp/port_trigger_iptables
|
||||||
|
touch /tmp/port_trigger_ip6tables
|
||||||
|
|
||||||
|
echo "iptables -w -t nat -F prerouting_porttrigger 2> /dev/null">>/tmp/port_trigger_iptables
|
||||||
|
echo "iptables -w -t filter -F forwarding_wan_porttrigger 2> /dev/null">>/tmp/port_trigger_iptables
|
||||||
|
echo "iptables -w -t nat -F prerouting_wan_porttrigger 2> /dev/null">>/tmp/port_trigger_iptables
|
||||||
|
echo "ip6tables -w -t nat -F prerouting_porttrigger 2> /dev/null">>/tmp/port_trigger_ip6tables
|
||||||
|
echo "ip6tables -w -t filter -F forwarding_wan_porttrigger 2> /dev/null">>/tmp/port_trigger_ip6tables
|
||||||
|
|
||||||
|
echo "iptables -w -t nat -N prerouting_porttrigger 2> /dev/null">>/tmp/port_trigger_iptables
|
||||||
|
ret=$?
|
||||||
|
[ $ret -eq 0 ] && echo "iptables -w -t nat -I PREROUTING -j prerouting_porttrigger 2> /dev/null">>/tmp/port_trigger_iptables
|
||||||
|
echo "iptables -w -t filter -N forwarding_wan_porttrigger 2> /dev/null">>/tmp/port_trigger_iptables
|
||||||
|
ret=$?
|
||||||
|
[ $ret -eq 0 ] && echo "iptables -w -t filter -I forwarding_wan_rule -j forwarding_wan_porttrigger 2> /dev/null">>/tmp/port_trigger_iptables
|
||||||
|
echo "iptables -w -t nat -N prerouting_wan_porttrigger 2> /dev/null">>/tmp/port_trigger_iptables
|
||||||
|
ret=$?
|
||||||
|
[ $ret -eq 0 ] && echo "iptables -w -t nat -I prerouting_wan_rule -j prerouting_wan_porttrigger 2> /dev/null">>/tmp/port_trigger_iptables
|
||||||
|
|
||||||
|
echo "ip6tables -w -t nat -N prerouting_porttrigger 2> /dev/null">>/tmp/port_trigger_ip6tables
|
||||||
|
ret=$?
|
||||||
|
[ $ret -eq 0 ] && echo "ip6tables -w -t nat -I PREROUTING -j prerouting_porttrigger 2> /dev/null">>/tmp/port_trigger_ip6tables
|
||||||
|
echo "ip6tables -w -t filter -N forwarding_wan_porttrigger 2> /dev/null">>/tmp/port_trigger_ip6tables
|
||||||
|
ret=$?
|
||||||
|
[ $ret -eq 0 ] && echo "ip6tables -w -t filter -I forwarding_wan_rule -j forwarding_wan_porttrigger 2> /dev/null">>/tmp/port_trigger_ip6tables
|
||||||
|
|
||||||
|
# Load /etc/config/port-trigger UCI file
|
||||||
|
config_load port-trigger
|
||||||
|
config_foreach process_port_trigger rule
|
||||||
|
|
||||||
|
sh /tmp/port_trigger_iptables
|
||||||
|
sh /tmp/port_trigger_ip6tables
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue