概要
パケットがちょくちょく lost する高負荷環境で、アプリのエラー処理のバグを踏んでデバッグする必要が出たので、ランダムで SYN パケットを drop するカーネルモジュールを作ってみました。CentOS7 で動作確認してます。
random_packet_drop.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/netfilter_ipv4.h>
#include <linux/skbuff.h>
#include <linux/tcp.h>
#include <linux/random.h>
#include <net/ip.h>
#include <net/tcp.h>
MODULE_LICENSE("GPL");
static unsigned int handle_hook(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff*))
{
struct ethhdr *eth = eth_hdr(skb);
struct iphdr *iph = ip_hdr(skb);
if (!iph) {
return NF_ACCEPT;
}
// IPv4 だけ対象にする
if (iph->version != 4) {
return NF_ACCEPT;
}
// TCP 以外は通す
if (iph->protocol != IPPROTO_TCP) {
return NF_ACCEPT;
}
// 192.168.1.1 (0xc0a80101) 以外からのパケットは通す
if ((be32_to_cpu(iph->saddr) & 0xffffffff) != 0xc0a80101) {
return NF_ACCEPT;
}
struct tcphdr *tcph = tcp_hdr(skb);
// ポート 80 への通信以外は通す
if (be16_to_cpu(tcph->dest) != 80) {
return NF_ACCEPT;
}
// SYN フラグが立っていないパケットは通す
if (!tcph->syn) {
return NF_ACCEPT;
}
// ランダムで通す
char rnd_byte;
get_random_bytes(&rnd_byte, sizeof(rnd_byte));
if (rnd_byte & 1) {
return NF_ACCEPT;
}
printk(KERN_INFO "remote:%d -> local:%d DROP\n", be16_to_cpu(tcph->source), be16_to_cpu(tcph->dest));
return NF_DROP;
}
static struct nf_hook_ops hook_ops = {
.hook = handle_hook,
.pf = PF_INET,
//.hooknum = NF_INET_PRE_ROUTING,
.hooknum = NF_INET_LOCAL_IN, // 入ってくるパケットを対象にする
//.hooknum = NF_INET_FORWARD,
//.hooknum = NF_INET_LOCAL_OUT,
//.hooknum = NF_INET_POST_ROUTING,
//.priority = NF_IP_PRI_FILTER,
.priority = NF_IP_PRI_FIRST,
};
static int __init random_packet_drop_init() {
int err;
err = nf_register_hook(&hook_ops);
if (err < 0) {
return err;
}
return 0;
}
static void __exit random_packet_drop_cleanup() {
nf_unregister_hook(&hook_ops);
}
module_init(random_packet_drop_init);
module_exit(random_packet_drop_cleanup);
Makefile
obj-m := random_packet_drop.o
KERN_DIR=/usr/src/kernels/`uname -r`
all:
make -C ${KERN_DIR} M=`pwd` modules
clean:
rm -f *.ko.unsigned *.mod.o modules.order Module.symvers *.ko *.mod.c *.o
実行
root# make
root# insmod random_packet_drop.ko // カーネルモジュールを有効にする
root# tail -f /var/log/messages
Jul 24 14:25:28 myhost kernel: remote:47632 -> local:80 DROP
...
root# rmmod random_packet_drop // カーネルモジュールを無効にする