You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
196 lines
4.7 KiB
196 lines
4.7 KiB
/* SDNS extension for IP connection tracking
|
|
* (C) 2021 by Beau Kujath <beau@breakpointingbad.com>
|
|
*/
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/moduleparam.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/in.h>
|
|
#include <linux/ip.h>
|
|
#include <linux/tcp.h>
|
|
#include <linux/udp.h>
|
|
#include <linux/netfilter.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include <net/netfilter/nf_conntrack.h>
|
|
#include <net/netfilter/nf_conntrack_expect.h>
|
|
#include <net/netfilter/nf_conntrack_helper.h>
|
|
#include <linux/netfilter/nf_conntrack_irc.h>
|
|
|
|
#define MAX_PORTS 8
|
|
static unsigned short ports[MAX_PORTS];
|
|
static unsigned int ports_c;
|
|
|
|
|
|
static char *sdns_buffer;
|
|
|
|
|
|
//static DEFINE_SPINLOCK(nf_sdns_lock);
|
|
|
|
|
|
|
|
struct dnsinfo {
|
|
__be16 txid;
|
|
};
|
|
|
|
|
|
|
|
struct nf_ct_sdns_master {
|
|
unsigned int txid;
|
|
/* Valid seq positions for cmd matching after newline */
|
|
//u_int32_t seq_aft_nl[IP_CT_DIR_MAX][NUM_SEQ_TO_REMEMBER];
|
|
/* 0 means seq_match_aft_nl not set */
|
|
//u_int16_t seq_aft_nl_num[IP_CT_DIR_MAX];
|
|
/* pickup sequence tracking, useful for conntrackd */
|
|
//u_int16_t flags[IP_CT_DIR_MAX];
|
|
};
|
|
|
|
|
|
unsigned int (*nf_nat_sdns_hook)(struct sk_buff *skb,
|
|
enum ip_conntrack_info ctinfo,
|
|
unsigned int protoff,
|
|
unsigned int matchoff,
|
|
unsigned int matchlen,
|
|
struct nf_conntrack_expect *exp) __read_mostly;
|
|
EXPORT_SYMBOL_GPL(nf_nat_sdns_hook);
|
|
|
|
#define HELPER_NAME "sdns"
|
|
|
|
|
|
|
|
MODULE_AUTHOR("Beau Kujath <beau@breakpointingbad.com>");
|
|
MODULE_DESCRIPTION("sdns connection tracking helper");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_ALIAS("ip_conntrack_sdns");
|
|
MODULE_ALIAS_NFCT_HELPER(HELPER_NAME);
|
|
|
|
module_param_array(ports, ushort, &ports_c, 0400);
|
|
MODULE_PARM_DESC(ports, "port numbers of dns servers");
|
|
|
|
|
|
|
|
|
|
|
|
static int help(struct sk_buff *skb, unsigned int protoff,
|
|
struct nf_conn *ct, enum ip_conntrack_info ctinfo)
|
|
{
|
|
|
|
|
|
struct udphdr _udph;
|
|
struct dnsinfo _dnsi;
|
|
const struct dnsinfo *dnsi;
|
|
unsigned int dataoff, datalimit;
|
|
|
|
struct nf_ct_sdns_master *ct_sdns_info = nfct_help_data(ct);
|
|
struct udphdr *uh = skb_header_pointer(skb, protoff, sizeof(_udph), &_udph);
|
|
|
|
printk(KERN_INFO "\ntriggering conntrack sdns helper function\n");
|
|
|
|
|
|
|
|
//int dport = ntohs(uh->dest);
|
|
//printk(KERN_INFO "original protoff: (%u), len: (%u)\n", protoff, sizeof(_udph));
|
|
//printk(KERN_INFO "dst port for packet: %u\n", dport);
|
|
|
|
|
|
if (uh == NULL)
|
|
return NF_ACCEPT;
|
|
|
|
|
|
dataoff = protoff + sizeof(struct udphdr);
|
|
datalimit = dataoff + sizeof(struct dnsinfo);
|
|
|
|
// could probably delete this if check
|
|
if (dataoff >= skb->len) {
|
|
pr_debug("sdns: dataoff(%u) >= skblen(%u)\n", dataoff,
|
|
skb->len);
|
|
return NF_ACCEPT;
|
|
}
|
|
|
|
|
|
if (datalimit >= skb->len) {
|
|
pr_debug("sdns: datalimit(%u) >= skblen(%u)\n", datalimit,
|
|
skb->len);
|
|
return NF_ACCEPT;
|
|
}
|
|
|
|
dnsi = skb_header_pointer(skb, dataoff,
|
|
sizeof(_dnsi), &_dnsi);
|
|
|
|
if (dnsi == NULL)
|
|
return NF_ACCEPT;
|
|
|
|
|
|
//printk(KERN_INFO "ctinfo val: %d\n", ctinfo);
|
|
|
|
if (ctinfo == IP_CT_NEW) {
|
|
// keep track of txid if it is new entry
|
|
int txid = ntohs(dnsi->txid);
|
|
ct_sdns_info->txid = txid;
|
|
printk(KERN_INFO "tracking txid for new ct entry: %d\n", ct_sdns_info->txid);
|
|
|
|
} else if (ctinfo == IP_CT_IS_REPLY) {
|
|
|
|
// check the txid of reply on reply entry
|
|
int txid = ntohs(dnsi->txid);
|
|
printk(KERN_INFO "dns txid of reply: %d, dns txid stored in ct struct: %d \n", txid, ct_sdns_info->txid);
|
|
|
|
if (txid == ct_sdns_info->txid) {
|
|
printk(KERN_INFO "yes, txid on reply matched so accept!");
|
|
return NF_ACCEPT;
|
|
} else {
|
|
printk(KERN_INFO "WARNING, txid on reply did not match so removing ct entry!");
|
|
nf_ct_delete(ct, 0, 0);
|
|
return NF_DROP;
|
|
}
|
|
}
|
|
|
|
|
|
return NF_ACCEPT;
|
|
}
|
|
|
|
static struct nf_conntrack_helper sdns[MAX_PORTS] __read_mostly;
|
|
static struct nf_conntrack_expect_policy sdns_exp_policy;
|
|
|
|
static int __init nf_conntrack_sdns_init(void)
|
|
{
|
|
int i, ret;
|
|
|
|
printk(KERN_INFO "initializing conntrack sdns helper\n");
|
|
|
|
sdns_buffer = kmalloc(65536, GFP_KERNEL);
|
|
if (!sdns_buffer)
|
|
return -ENOMEM;
|
|
|
|
/* If no port given, default to standard sdns port */
|
|
if (ports_c == 0) {
|
|
printk(KERN_INFO "no ports given so assigning default port\n");
|
|
ports[ports_c++] = IRC_PORT;
|
|
}
|
|
|
|
for (i = 0; i < ports_c; i++) {
|
|
nf_ct_helper_init(&sdns[i], AF_INET, IPPROTO_UDP, HELPER_NAME,
|
|
IRC_PORT, ports[i], i, &sdns_exp_policy,
|
|
0, help, NULL, THIS_MODULE);
|
|
}
|
|
|
|
ret = nf_conntrack_helpers_register(&sdns[0], ports_c);
|
|
if (ret) {
|
|
pr_err("failed to register helpers\n");
|
|
kfree(sdns_buffer);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __exit nf_conntrack_sdns_fini(void)
|
|
{
|
|
nf_conntrack_helpers_unregister(sdns, ports_c);
|
|
kfree(sdns_buffer);
|
|
}
|
|
|
|
module_init(nf_conntrack_sdns_init);
|
|
module_exit(nf_conntrack_sdns_fini);
|