Skip to content

Instantly share code, notes, and snippets.

@LGA1150
Last active December 16, 2021 11:50
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LGA1150/57cb22e278838a3cf7490b244d886cb8 to your computer and use it in GitHub Desktop.
Save LGA1150/57cb22e278838a3cf7490b244d886cb8 to your computer and use it in GitHub Desktop.
#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