관리 메뉴

c0smicb0y

netfilter library 본문

프로그래밍/네트워크

netfilter library

2015. 12. 8. 00:04

netfilter는 우리가 일반적으로 소켓 프로그래밍을 할 때 쓰이는 인터페이스인 Berkely socket interface의 외부에 존재하는 packet mangling에 대한 프레임워크이다.


쉽게 생각하여 일반적인 소켓 프로그래밍으로는 패킷을 모니터링하는 것은 가능하지만, 패킷을 변조, 드랍하는 것은 불가능하다. 이러한 것을 가능케 해주는 프레임워크가 netfilter이다.


netfilter는 크게 다섯 부분으로 구성되어 있으며, 각각의 프로토콜은 hook을 정의한다.



라우터로 들어오기 전인 PREROUTING

자기 자신으로 들어오는 LOCAL_IN

자기 자신에서 나가는 LOCAL_OUT

자기 자신이 라우터 역할을 하여 다른 곳으로 포워딩해주는 FORWARD

라우터를 지나 output interface로 포워드 된 패킷이 나간 후인 POSTROUTING


다섯 부분에 hook을 걸어서 패킷을 핸들링 할 수 있는데,핸들링의 종류는 다음과 같다.

NF_ACCEPT: 보통처럼 진행시킴

NF_DROP: 패킷을 버림 (소켓버퍼를 free함)

NF_STOLEN: 패킷을 훔침 (소켓버퍼를 free하지 않음)

NF_QUEUE: 패킷을 큐로 보냄 (userspace에서 처리하기 위해)

NF_REPEAT: 현재 훅을 다시 호출


netfilter 라이브러리를 사용하기 위해서는 먼저 모듈들을 다운로드 받아야 한다.


wget을 통해 다음 모듈들을 받아주자

1
wget http://netfilter.org/projects/libmnl/files/libmnl-1.0.3.tar.bz2

cs

1
wget http://netfilter.org/projects/libnfnetlink/files/libnfnetlink-1.0.1.tar.bz2
cs
1
wget http://netfilter.org/projects/libnetfilter_queue/files/libnetfilter_queue-1.0.2.tar.bz2
cs





http://netfilter.org 에서 각자 원하는 버전을 다운로드 받아주어도 된다.


우리가 원하는 기능은 netfilter_queue지만 dependency가 있어서 다른 모듈들을 먼저 설치해주어야 한다.


먼저 받은 압축파일들의 압축을 풀어준다.


1
tar -xvf libmnl-1.0.3.tar.bz2
cs
1
tar -xvf libnetfilter_queue-1.0.2.tar.bz2
cs
1
tar -xvf libnfnetlink-1.0.1.tar.bz2
cs



각 디렉토리에 들어가서 빌드 및 설치를 한다.

1
2
3
4
cd libmnl-1.0.3/
./configure
make
sudo make install
cs
1
2
3
4
cd libnfnetlink-1.0.1/
./configure
make
sudo make install
cs
1
2
3
4
cd libnetfilter_queue-1.0.2/
./configure
make
sudo make install
cs


설치는 libmnl -> libnfnetlink -> libnetfilter_queue의 순서대로 해야한다.


다음 예제 소스를 컴파일 해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <linux/types.h>
#include <linux/netfilter.h>    /* for NF_ACCEPT */
#include <errno.h>
 
#include <libnetfilter_queue/libnetfilter_queue.h>
 
/* returns packet id */
static u_int32_t print_pkt (struct nfq_data *tb)
{
  int id = 0;
  struct nfqnl_msg_packet_hdr *ph;
  struct nfqnl_msg_packet_hw *hwph;
  u_int32_t mark,ifi;
  int ret;
  unsigned char *data;
 
  ph = nfq_get_msg_packet_hdr(tb);
  if (ph) {
    id = ntohl(ph->packet_id);
    printf("hw_protocol=0x%04x hook=%u id=%u ",
      ntohs(ph->hw_protocol), ph->hook, id);
  }
 
  hwph = nfq_get_packet_hw(tb);
  if (hwph) {
    int i, hlen = ntohs(hwph->hw_addrlen);
 
    printf("hw_src_addr=");
    for (i = 0; i < hlen-1; i++)
      printf("%02x:", hwph->hw_addr[i]);
    printf("%02x ", hwph->hw_addr[hlen-1]);
  }
 
  mark = nfq_get_nfmark(tb);
  if (mark)
    printf("mark=%u ", mark);
 
  ifi = nfq_get_indev(tb);
  if (ifi)
    printf("indev=%u ", ifi);
 
  ifi = nfq_get_outdev(tb);
  if (ifi)
    printf("outdev=%u ", ifi);
  ifi = nfq_get_physindev(tb);
  if (ifi)
    printf("physindev=%u ", ifi);
 
  ifi = nfq_get_physoutdev(tb);
  if (ifi)
    printf("physoutdev=%u ", ifi);
 
  ret = nfq_get_payload(tb, &data);
  if (ret >= 0)
    printf("payload_len=%d ", ret);
 
  fputc('\n', stdout);
 
  return id;
}
 
static int cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,
        struct nfq_data *nfa, void *data)
{
  u_int32_t id = print_pkt(nfa);
  printf("entering callback\n");
  return nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL);
}
 
void dump(unsigned char*buf, size_t len) {
  size_t i;
 
  for (i = 0; i < len; i++) {
    printf("%02x "*buf++);
    if ((i + 1) % 16 == 0)
      printf("\n");
  }
  printf("\n");
  fflush(stdout);
}
 
int main(int argc, char **argv)
{
  struct nfq_handle *h;
  struct nfq_q_handle *qh;
  struct nfnl_handle *nh;
  int fd;
  int rv;
  char buf[4096] __attribute__ ((aligned));
 
  printf("opening library handle\n");
  h = nfq_open();
  if (!h) {
    fprintf(stderr, "error during nfq_open()\n");
    exit(1);
  }
 
  printf("unbinding existing nf_queue handler for AF_INET (if any)\n");
  if (nfq_unbind_pf(h, AF_INET) < 0) {
    fprintf(stderr, "error during nfq_unbind_pf()\n");
    exit(1);
  }
 
  printf("binding nfnetlink_queue as nf_queue handler for AF_INET\n");
  if (nfq_bind_pf(h, AF_INET) < 0) {
    fprintf(stderr, "error during nfq_bind_pf()\n");
    exit(1);
  }
 
  printf("binding this socket to queue '0'\n");
  qh = nfq_create_queue(h,  0, &cb, NULL);
  if (!qh) {
    fprintf(stderr, "error during nfq_create_queue()\n");
    exit(1);
  }
 
  printf("setting copy_packet mode\n");
  if (nfq_set_mode(qh, NFQNL_COPY_PACKET, 0xffff< 0) {
    fprintf(stderr, "can't set packet_copy mode\n");
    exit(1);
  }
 
  fd = nfq_fd(h);
 
  for (;;) {
    if ((rv = recv(fd, buf, sizeof(buf), 0)) >= 0) {
      printf("pkt received\n");
        dump(buf, rv);
      nfq_handle_packet(h, buf, rv);
      continue;
    }
    /* if your application is too slow to digest the packets that
     * are sent from kernel-space, the socket buffer that we use
     * to enqueue packets may fill up returning ENOBUFS. Depending
     * on your application, this error may be ignored. Please, see
     * the doxygen documentation of this library on how to improve
     * this situation.
     */
    if (rv < 0 && errno == ENOBUFS) {
      printf("losing packets!\n");
      continue;
    }
    perror("recv failed");
    break;
  }
 
  printf("unbinding from queue 0\n");
  nfq_destroy_queue(qh);
 
#ifdef INSANE
  /* normally, applications SHOULD NOT issue this command, since
   * it detaches other programs/sockets from AF_INET, too ! */
  printf("unbinding from AF_INET\n");
  nfq_unbind_pf(h, AF_INET);
#endif
 
  printf("closing library handle\n");
  nfq_close(h);
 
  exit(0);
}
cs


컴파일 할 때 링크 옵션으로 -lnetfilter_queue를 주어야 한다.


실행을 시켜보면 so 파일이 없다는 에러가 뜰 것이다.


sudo su를 통해 root 권한으로 바꾼 후에,

1
export LD_LIBRARY_PATH=/usr/local/lib; ./nfqnl_test
cs

를 입력하면 실행이 된다.


해당 프로그램은 0번 큐에 들어오는 패킷들을 덤프를 떠서 보여주는 것이다.


iptables를 통해 0번 큐로 icmp를 보내주자.

1
iptables -A OUTPUT -p icmp -j NFQUEUE --queue-num 0
cs


ping 8.8.8.8을 하면 해당 프로그램에서 포착될 것이다.





'프로그래밍 > 네트워크' 카테고리의 다른 글

무선 패킷 캡처  (0) 2015.11.13
Comments