#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/netfilter.h>
#include <linux/netfilter_arp.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
struct nf_hook_ops hook;
unsigned int hook_func(void * priv, struct sk_buff * skb, const struct nf_hook_state * state)
{
struct iphdr * ip = ip_hdr(skb);
printk("caller: %s\n", current->comm);
printk("ip protocol: %d\n", ip->protocol);
printk("%pI4 -> %pI4\n", &ip->saddr, &ip->daddr);
return NF_ACCEPT;
}
int start(void)
{
hook.hook = hook_func;
hook.hooknum = NF_INET_PRE_ROUTING;
hook.pf = NFPROTO_IPV4;
if (!nf_register_net_hook(&init_net, &hook))
printk("netfilter register success\n");
else
printk("netfilter register fail\n");
return 0;
}
void end(void)
{
nf_unregister_net_hook(&init_net, &hook);
}
module_init(start);
module_exit(end);
MODULE_LICENSE("GPL");
module을 insmod로 적재 시 netfilter에 hooking 할 정보를 등록하게 된다.
이를 위해선 아래 구조체를 먼저 적절히 초기화 해야 한다.
typedef unsigned int nf_hookfn(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state);
struct nf_hook_ops {
/* User fills in from here down. */
nf_hookfn *hook; //hooking 시 호출 될 함수로 설정,
struct net_device *dev;
void *priv;
u_int8_t pf; //protocol family,
unsigned int hooknum;
/* Hooks are ordered in ascending priority. */
int priority;
};
1. hook: hooking 시 호출 될 함수의 포인터를 설정한다.
2. dev: 따로 초기화 하지 않아도 되는 거 같다.
3. priv: 따로 초기화 하지 않아도 되는 거 같다.
4. pf: protocol famaily, NFPROTO로 시작하는 값을 넣어 주면 된다.
5. hooknum: enum nf_inet_hooks를 참조하면 된다.
6. priority: 같은 hook 지점에 여러 callback 함수들이 등록될 시 그 우선순위를 정한다.
enum nf_ip_hook_priorities 를 따로 참조하면 되겠다.
//IPv4, IPv6를 대상으로 한다
enum nf_inet_hooks {
NF_INET_PRE_ROUTING, //모든 incoming packet들에 대한 hooking
NF_INET_LOCAL_IN, //local host 내 incoming packet들에 대한 hooking
NF_INET_FORWARD, //포워딩 되는 packet들에 대한 hooking
NF_INET_LOCAL_OUT, //local host 내 outgoing packet들에 대한 hooking
NF_INET_POST_ROUTING, //NF_INET_FORWARD hooking을 지나 포워딩 되는 패킷들에 대한 hooking
NF_INET_NUMHOOKS
};
여기서는 주로 NF_INET_PRE_ROUTING 과 NF_INET_POST_ROUTING 정도만 기억하면 될 거 같다.
NF_INET_PRE_ROUTING 은 incoming packet에 대하여,
NF_INET_POST_ROUTING 은 outgoing packet에 대하여 hooking하도록 한다.
LOCAL이 들어 있는 것은 주로 127.0.0.1 로 보낼 때 / 받을 때 에 대한 후킹을 말한다.
enum {
NFPROTO_UNSPEC = 0,
NFPROTO_INET = 1,
NFPROTO_IPV4 = 2, //IPv4를 대상으로 함. icmp, tcp, udp... 전부 확인 해볼 수 있다.
NFPROTO_ARP = 3, //ARP를 대상으로 함.
NFPROTO_NETDEV = 5,
NFPROTO_BRIDGE = 7,
NFPROTO_IPV6 = 10, //IPv6를 대상으로 함.
NFPROTO_DECNET = 12,
NFPROTO_NUMPROTO,
};
주석을 참조 하면 되겠다.
/* Responses from hook functions. */
#define NF_DROP 0
#define NF_ACCEPT 1
#define NF_STOLEN 2 //hooking한 함수에 의해 처리됨.
#define NF_QUEUE 3 //유저영역을 위해 패킷을 큐잉함.
#define NF_REPEAT 4 //hooking하는 함수가 다시 호출 되도록 함.
위 매크로는 hooking하는 함수가 반환해야만 하는 값 들을 보여주고 있다.
NF_DROP 은 말 그대로 해당 패킷을 drop, 폐기하는 것으로, 커널 레벨에서의 하나의 장점을 보여줄 수 있다.
NF_ACCEPT 역시 말 그대로 해당 패킷을 받아들인다는 의미로, 계속 커널 네트워크 스택을 거칠 수 있게 해준다.
/* ARP Hooks */
#define NF_ARP_IN 0
#define NF_ARP_OUT 1
#define NF_ARP_FORWARD 2
#define NF_ARP_NUMHOOKS 3
pf로 넘기는 값에는 arp에 대한 내용도 있다. 그 때에 hooknum은 nf_inet_hooks 와 약간 다른 점 이 있다.
IN은 상관 없지만 OUT같은 경우는 값이 차이가 나므로 위 매크로 값을 쓰면 되겠다.
끝으로 모듈을 rmmod로 해제 시에는 반드시 netfilter에 등록한 hooking을 해제해주어야 한다.
그렇지 않으면, 커널 패닉이 발생하게 된다.
이는 커널 코드 흐름 상 후킹 함수를 호출해야 하는데, 해당 함수가 위치한 영역이 모듈 제거로 인해 해제되었기 때문이다.
'리눅스 커널 > 네트워크' 카테고리의 다른 글
DHCP (0) | 2021.10.24 |
---|---|
Kernel handler packet capture (0) | 2021.10.24 |
Raw socket outgoing packet capture (0) | 2021.10.24 |
Raw socket incoming packet capture (0) | 2021.10.24 |
Ethernet & ARP Header (0) | 2021.10.24 |