libmnl  1.0.4
nf-queue.c
1 /* This example is placed in the public domain. */
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <string.h>
6 #include <time.h>
7 #include <arpa/inet.h>
8 
9 #include <libmnl/libmnl.h>
10 #include <linux/netfilter.h>
11 #include <linux/netfilter/nfnetlink.h>
12 #include <linux/netfilter/nfnetlink_queue.h>
13 
14 static int parse_attr_cb(const struct nlattr *attr, void *data)
15 {
16  const struct nlattr **tb = data;
17  int type = mnl_attr_get_type(attr);
18 
19  /* skip unsupported attribute in user-space */
20  if (mnl_attr_type_valid(attr, NFQA_MAX) < 0)
21  return MNL_CB_OK;
22 
23  switch(type) {
24  case NFQA_MARK:
25  case NFQA_IFINDEX_INDEV:
26  case NFQA_IFINDEX_OUTDEV:
27  case NFQA_IFINDEX_PHYSINDEV:
28  case NFQA_IFINDEX_PHYSOUTDEV:
29  if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
30  perror("mnl_attr_validate");
31  return MNL_CB_ERROR;
32  }
33  break;
34  case NFQA_TIMESTAMP:
35  if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC,
36  sizeof(struct nfqnl_msg_packet_timestamp)) < 0) {
37  perror("mnl_attr_validate2");
38  return MNL_CB_ERROR;
39  }
40  break;
41  case NFQA_HWADDR:
42  if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC,
43  sizeof(struct nfqnl_msg_packet_hw)) < 0) {
44  perror("mnl_attr_validate2");
45  return MNL_CB_ERROR;
46  }
47  break;
48  case NFQA_PAYLOAD:
49  break;
50  }
51  tb[type] = attr;
52  return MNL_CB_OK;
53 }
54 
55 static int queue_cb(const struct nlmsghdr *nlh, void *data)
56 {
57  struct nlattr *tb[NFQA_MAX+1] = {};
58  struct nfqnl_msg_packet_hdr *ph = NULL;
59  uint32_t id = 0;
60 
61  mnl_attr_parse(nlh, sizeof(struct nfgenmsg), parse_attr_cb, tb);
62  if (tb[NFQA_PACKET_HDR]) {
63  ph = mnl_attr_get_payload(tb[NFQA_PACKET_HDR]);
64  id = ntohl(ph->packet_id);
65 
66  printf("packet received (id=%u hw=0x%04x hook=%u)\n",
67  id, ntohs(ph->hw_protocol), ph->hook);
68  }
69 
70  return MNL_CB_OK + id;
71 }
72 
73 static struct nlmsghdr *
74 nfq_build_cfg_pf_request(char *buf, uint8_t command)
75 {
76  struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
77  nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_CONFIG;
78  nlh->nlmsg_flags = NLM_F_REQUEST;
79 
80  struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
81  nfg->nfgen_family = AF_UNSPEC;
82  nfg->version = NFNETLINK_V0;
83 
84  struct nfqnl_msg_config_cmd cmd = {
85  .command = command,
86  .pf = htons(AF_INET),
87  };
88  mnl_attr_put(nlh, NFQA_CFG_CMD, sizeof(cmd), &cmd);
89 
90  return nlh;
91 }
92 
93 static struct nlmsghdr *
94 nfq_build_cfg_request(char *buf, uint8_t command, int queue_num)
95 {
96  struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
97  nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_CONFIG;
98  nlh->nlmsg_flags = NLM_F_REQUEST;
99 
100  struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
101  nfg->nfgen_family = AF_UNSPEC;
102  nfg->version = NFNETLINK_V0;
103  nfg->res_id = htons(queue_num);
104 
105  struct nfqnl_msg_config_cmd cmd = {
106  .command = command,
107  .pf = htons(AF_INET),
108  };
109  mnl_attr_put(nlh, NFQA_CFG_CMD, sizeof(cmd), &cmd);
110 
111  return nlh;
112 }
113 
114 static struct nlmsghdr *
115 nfq_build_cfg_params(char *buf, uint8_t mode, int range, int queue_num)
116 {
117  struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
118  nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_CONFIG;
119  nlh->nlmsg_flags = NLM_F_REQUEST;
120 
121  struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
122  nfg->nfgen_family = AF_UNSPEC;
123  nfg->version = NFNETLINK_V0;
124  nfg->res_id = htons(queue_num);
125 
126  struct nfqnl_msg_config_params params = {
127  .copy_range = htonl(range),
128  .copy_mode = mode,
129  };
130  mnl_attr_put(nlh, NFQA_CFG_PARAMS, sizeof(params), &params);
131 
132  return nlh;
133 }
134 
135 static struct nlmsghdr *
136 nfq_build_verdict(char *buf, int id, int queue_num, int verd)
137 {
138  struct nlmsghdr *nlh;
139  struct nfgenmsg *nfg;
140 
141  nlh = mnl_nlmsg_put_header(buf);
142  nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_VERDICT;
143  nlh->nlmsg_flags = NLM_F_REQUEST;
144  nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
145  nfg->nfgen_family = AF_UNSPEC;
146  nfg->version = NFNETLINK_V0;
147  nfg->res_id = htons(queue_num);
148 
149  struct nfqnl_msg_verdict_hdr vh = {
150  .verdict = htonl(verd),
151  .id = htonl(id),
152  };
153  mnl_attr_put(nlh, NFQA_VERDICT_HDR, sizeof(vh), &vh);
154 
155  return nlh;
156 }
157 
158 int main(int argc, char *argv[])
159 {
160  struct mnl_socket *nl;
161  char buf[MNL_SOCKET_BUFFER_SIZE];
162  struct nlmsghdr *nlh;
163  int ret;
164  unsigned int portid, queue_num;
165 
166  if (argc != 2) {
167  printf("Usage: %s [queue_num]\n", argv[0]);
168  exit(EXIT_FAILURE);
169  }
170  queue_num = atoi(argv[1]);
171 
172  nl = mnl_socket_open(NETLINK_NETFILTER);
173  if (nl == NULL) {
174  perror("mnl_socket_open");
175  exit(EXIT_FAILURE);
176  }
177 
178  if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
179  perror("mnl_socket_bind");
180  exit(EXIT_FAILURE);
181  }
182  portid = mnl_socket_get_portid(nl);
183 
184  nlh = nfq_build_cfg_pf_request(buf, NFQNL_CFG_CMD_PF_UNBIND);
185 
186  if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
187  perror("mnl_socket_sendto");
188  exit(EXIT_FAILURE);
189  }
190 
191  nlh = nfq_build_cfg_pf_request(buf, NFQNL_CFG_CMD_PF_BIND);
192 
193  if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
194  perror("mnl_socket_sendto");
195  exit(EXIT_FAILURE);
196  }
197 
198  nlh = nfq_build_cfg_request(buf, NFQNL_CFG_CMD_BIND, queue_num);
199 
200  if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
201  perror("mnl_socket_sendto");
202  exit(EXIT_FAILURE);
203  }
204 
205  nlh = nfq_build_cfg_params(buf, NFQNL_COPY_PACKET, 0xFFFF, queue_num);
206 
207  if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
208  perror("mnl_socket_sendto");
209  exit(EXIT_FAILURE);
210  }
211 
212  ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
213  if (ret == -1) {
214  perror("mnl_socket_recvfrom");
215  exit(EXIT_FAILURE);
216  }
217  while (ret > 0) {
218  uint32_t id;
219 
220  ret = mnl_cb_run(buf, ret, 0, portid, queue_cb, NULL);
221  if (ret < 0){
222  perror("mnl_cb_run");
223  exit(EXIT_FAILURE);
224  }
225 
226  id = ret - MNL_CB_OK;
227  nlh = nfq_build_verdict(buf, id, queue_num, NF_ACCEPT);
228  if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
229  perror("mnl_socket_sendto");
230  exit(EXIT_FAILURE);
231  }
232 
233  ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
234  if (ret == -1) {
235  perror("mnl_socket_recvfrom");
236  exit(EXIT_FAILURE);
237  }
238  }
239 
240  mnl_socket_close(nl);
241 
242  return 0;
243 }