본문 바로가기

리눅스 커널/네트워크

Kernel handler packet capture

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/if_ether.h>
#include <linux/ip.h>

static struct packet_type pt;

static int proto_handler(struct sk_buff * skb, struct net_device * dev1,
                        struct packet_type * pktype, struct net_device * dev2)
{
        struct ethhdr * eth = eth_hdr(skb);
        struct iphdr * ip = ip_hdr(skb);

        printk("%pM -> %pM\n", eth->h_source, eth->h_dest);
        printk("%pI4 -> %pI4\n", &ip->saddr, &ip->daddr);

        return NF_ACCEPT;
}

int sniff_init(void)
{
        pt.type = htons(ETH_P_IP);
        pt.func = proto_handler;

        dev_add_pack(&pt);

        return 0;
}

void sniff_exit(void)
{
        dev_remove_pack(&pt);
}

module_init(sniff_init);
module_exit(sniff_exit);
MODULE_LICENSE("GPL");

커널 모듈로 패킷을 캡쳐하는 세번째 방법으로는 새로운 프로토콜 핸들러를 등록하는 방식이다.

 

dev_add_pack()이라는 함수를 이용하여 새로운 프로토콜 핸들러를 등록 할 수 있는데, 먼저 아래의 구조체를 적절히 초기화 해야 한다.

struct packet_type {
	__be16			type;	/* This is really htons(ether_type). */
	struct net_device	*dev;	/* NULL is wildcarded here	     */
	int			(*func) (struct sk_buff *,
					 struct net_device *,
					 struct packet_type *,
					 struct net_device *);
	bool			(*id_match)(struct packet_type *ptype,
					    struct sock *sk);
	void			*af_packet_priv;
	struct list_head	list;
};

type은 big endian이므로 htons()로 변환해서 초기화 해야하며,

type으로는 ETH_P_ 로 시작하는 것만 해야한다.

 

프로토콜을 처리할 함수는 func와 같은 형식으로 선언 및 정의해야 한다.

 

넷 필터에 경우 프로토콜이 NFPROTO_INET인 경우 이더넷 헤더를 볼 수 없다.

디캡슐레이션 후 넘어오기 때문이다.

 

그러나 위 방법에서는 이더넷 헤더 부터 볼 수 있다.

 

끝으로 모듈을 해제 할 때에는 dev_remove_pack()을 호출하여 등록한 핸들러를 해제 해주어야 한다.

 

'리눅스 커널 > 네트워크' 카테고리의 다른 글

promiscuous mode  (0) 2021.10.24
DHCP  (0) 2021.10.24
Kernel hooking packet capture  (0) 2021.10.24
Raw socket outgoing packet capture  (0) 2021.10.24
Raw socket incoming packet capture  (0) 2021.10.24