LoginSignup
9
8

More than 5 years have passed since last update.

TCP パケットをランダムに drop するカーネルモジュール

Last updated at Posted at 2018-07-27

概要

パケットがちょくちょく 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   // カーネルモジュールを無効にする
9
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
8