-
-
Save LGA1150/57cb22e278838a3cf7490b244d886cb8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <linux/module.h> | |
#include <linux/netfilter_ipv4.h> | |
#include <linux/netfilter_ipv6.h> | |
#include <net/udp.h> | |
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
static char devname[IFNAMSIZ]; | |
module_param_string(dev, devname, IFNAMSIZ, 0644); | |
static u8 ttl_thresh; | |
module_param(ttl_thresh, byte, 0644); | |
static bool drop_aa; | |
module_param(drop_aa, bool, 0644); | |
static bool drop_df; | |
module_param(drop_df, bool, 0644); | |
static bool drop_null_id; | |
module_param(drop_null_id, bool, 0644); | |
static bool drop_no_opt; | |
module_param(drop_no_opt, bool, 0644); | |
struct dnshdr { | |
struct udphdr uh; | |
__be16 id; | |
__be16 flags; | |
__be16 questions; | |
__be16 answer_rrs; | |
__be16 authority_rrs; | |
__be16 additional_rrs; | |
}; | |
#define DNS_AA BIT(10) | |
static unsigned int | |
dns4_filter_hook(void *priv, struct sk_buff *skb, | |
const struct nf_hook_state *state) | |
{ | |
const struct net_device *in = state->in; | |
const struct iphdr *iph = ip_hdr(skb); | |
const struct dnshdr *dh; | |
if (!in || (devname[0] && strncmp(devname, in->name, IFNAMSIZ))) | |
goto out; | |
if (likely(iph->protocol != IPPROTO_UDP)) | |
goto out; | |
if (iph->frag_off & htons(IP_OFFSET)) | |
goto out; | |
if (!pskb_may_pull(skb, iph->ihl * 4 + sizeof(*dh))) | |
goto out; | |
dh = (struct dnshdr *)skb_transport_header(skb); | |
if (likely(dh->uh.source != htons(53))) | |
goto out; | |
if (iph->ttl < ttl_thresh) { | |
net_dbg_ratelimited("received a packet from %pI4 with TTL less than %u, dropping\n", &iph->saddr, ttl_thresh); | |
return NF_DROP; | |
} | |
if (drop_null_id && !iph->id) { | |
net_dbg_ratelimited("received a packet from %pI4 with id of 0, dropping\n", &iph->saddr); | |
return NF_DROP; | |
} | |
if (drop_df && (iph->frag_off & htons(IP_DF))) { | |
net_dbg_ratelimited("received a packet from %pI4 with DF set, dropping\n", &iph->saddr); | |
return NF_DROP; | |
} | |
if (drop_aa && (dh->flags & htons(DNS_AA))) { | |
net_dbg_ratelimited("received a packet from %pI4 with DNS AA set, dropping\n", &iph->saddr); | |
return NF_DROP; | |
} | |
if (drop_no_opt && !dh->additional_rrs) { | |
net_dbg_ratelimited("received a packet from %pI4 without additional RRs, dropping\n", &iph->saddr); | |
return NF_DROP; | |
} | |
out: | |
return NF_ACCEPT; | |
} | |
static unsigned int | |
dns6_filter_hook(void *priv, struct sk_buff *skb, | |
const struct nf_hook_state *state) | |
{ | |
const struct ipv6hdr *ip6h = ipv6_hdr(skb); | |
const struct net_device *in = state->in; | |
const struct dnshdr *dh; | |
if (!in || (devname[0] && strncmp(devname, in->name, IFNAMSIZ))) | |
goto out; | |
if (likely(ip6h->nexthdr != IPPROTO_UDP)) | |
goto out; | |
if (!pskb_may_pull(skb, sizeof(*ip6h) + sizeof(*dh))) | |
goto out; | |
dh = (struct dnshdr *)skb_transport_header(skb); | |
if (likely(dh->uh.source != htons(53))) | |
goto out; | |
if (ip6h->hop_limit < ttl_thresh) { | |
net_dbg_ratelimited("received a packet from %pI6c with hop limit less than %u, dropping\n", &ip6h->saddr, ttl_thresh); | |
return NF_DROP; | |
} | |
if (drop_null_id && !ip6h->flow_lbl[0] && !ip6h->flow_lbl[1] && !ip6h->flow_lbl[2]) { | |
net_dbg_ratelimited("received a packet from %pI6c with flow label of 0, dropping\n", &ip6h->saddr); | |
return NF_DROP; | |
} | |
if (drop_aa && (dh->flags & htons(DNS_AA))) { | |
net_dbg_ratelimited("received a packet from %pI6c with DNS AA set, dropping\n", &ip6h->saddr); | |
return NF_DROP; | |
} | |
if (drop_no_opt && !dh->additional_rrs) { | |
net_dbg_ratelimited("received a packet from %pI6c without additional RRs, dropping\n", &ip6h->saddr); | |
return NF_DROP; | |
} | |
out: | |
return NF_ACCEPT; | |
} | |
static const struct nf_hook_ops dns_filter_ops[] = { | |
{ | |
.hook = dns4_filter_hook, | |
.pf = NFPROTO_IPV4, | |
.hooknum = NF_INET_PRE_ROUTING, | |
.priority = -350, /* between defrag and raw */ | |
}, | |
{ | |
.hook = dns6_filter_hook, | |
.pf = NFPROTO_IPV6, | |
.hooknum = NF_INET_PRE_ROUTING, | |
.priority = -350, /* between defrag and raw */ | |
}, | |
}; | |
static int __init dns_filter_init(void) | |
{ | |
if (!ttl_thresh && !drop_aa && !drop_df && !drop_null_id && !drop_no_opt) { | |
pr_err("please enable at least one of the drop_*/ttl_thresh parameters\n"); | |
return -EINVAL; | |
} | |
if (!devname[0]) | |
pr_warn("dev parameter is not specified, matching all interfaces\n"); | |
return nf_register_net_hooks(&init_net, dns_filter_ops, ARRAY_SIZE(dns_filter_ops)); | |
} | |
static void __exit dns_filter_exit(void) | |
{ | |
nf_unregister_net_hooks(&init_net, dns_filter_ops, ARRAY_SIZE(dns_filter_ops)); | |
} | |
module_init(dns_filter_init); | |
module_exit(dns_filter_exit); | |
MODULE_LICENSE("GPL"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment