libnftnl  1.0.6
nft-ruleset-parse-file.c
1 /*
2  * (C) 2014 by Alvaro Neira Ayuso <alvaroneay@gmail.com>
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  */
9 
10 #include <stdlib.h>
11 #include <time.h>
12 #include <string.h>
13 #include <stddef.h> /* for offsetof */
14 #include <netinet/in.h>
15 #include <netinet/ip.h>
16 #include <netinet/tcp.h>
17 #include <arpa/inet.h>
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <errno.h>
21 
22 #include <linux/netfilter.h>
23 #include <linux/netfilter/nfnetlink.h>
24 #include <linux/netfilter/nf_tables.h>
25 
26 #include <libmnl/libmnl.h>
27 #include <libnftnl/ruleset.h>
28 #include <libnftnl/table.h>
29 #include <libnftnl/chain.h>
30 #include <libnftnl/rule.h>
31 #include <libnftnl/set.h>
32 
33 struct mnl_nlmsg_batch *batch;
34 uint32_t seq;
35 
36 static int nftnl_ruleset_set_elems(const struct nftnl_parse_ctx *ctx)
37 {
38  struct nftnl_set_elems_iter *iter_elems;
39  uint16_t nl_type, nl_flags;
40  uint32_t cmd;
41  struct nlmsghdr *nlh;
42  struct nftnl_set *set;
43 
44  cmd = nftnl_ruleset_ctx_get_u32(ctx, NFTNL_RULESET_CTX_CMD);
45 
46  set = nftnl_ruleset_ctx_get(ctx, NFTNL_RULESET_CTX_SET);
47  if (set == NULL)
48  return -1;
49 
50  switch (cmd) {
51  case NFTNL_CMD_ADD:
52  nl_type = NFT_MSG_NEWSETELEM;
53  nl_flags = NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK;
54  break;
55  case NFTNL_CMD_DELETE:
56  nl_type = NFT_MSG_DELSETELEM;
57  /* This will generate an ACK message for each request. When
58  * removing NLM_F_ACK, the kernel will only report when things
59  * go wrong
60  */
61  nl_flags = NLM_F_ACK;
62  break;
63  default:
64  goto err;
65  }
66 
67  iter_elems = nftnl_set_elems_iter_create(set);
68  if (iter_elems == NULL)
69  goto err;
70 
71  nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), nl_type,
72  nftnl_set_get_u32(set,
73  NFTNL_SET_FAMILY),
74  nl_flags, seq++);
75 
76  nftnl_set_elems_nlmsg_build_payload_iter(nlh, iter_elems);
77  mnl_nlmsg_batch_next(batch);
78 
79  nftnl_set_elems_iter_destroy(iter_elems);
80  return 0;
81 err:
82  return -1;
83 }
84 
85 static int nftnl_ruleset_parse_set(const struct nftnl_parse_ctx *ctx)
86 {
87 
88  struct nlmsghdr *nlh;
89  uint16_t nl_type, nl_flags;
90  struct nftnl_set *set;
91  uint32_t cmd;
92  int ret;
93 
94  cmd = nftnl_ruleset_ctx_get_u32(ctx, NFTNL_RULESET_CTX_CMD);
95 
96  set = nftnl_ruleset_ctx_get(ctx, NFTNL_RULESET_CTX_SET);
97  if (set == NULL)
98  return -1;
99 
100  switch (cmd) {
101  case NFTNL_CMD_ADD:
102  nl_type = NFT_MSG_NEWSET;
103  nl_flags = NLM_F_CREATE|NLM_F_ACK;
104  break;
105  case NFTNL_CMD_DELETE:
106  nl_type = NFT_MSG_DELSET;
107  nl_flags = NLM_F_ACK;
108  break;
109  default:
110  goto err;
111  }
112 
113  nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
114  nl_type,
115  nftnl_set_get_u32(set,
116  NFTNL_SET_FAMILY),
117  nl_flags,
118  seq++);
119 
120  nftnl_set_nlmsg_build_payload(nlh, set);
121  mnl_nlmsg_batch_next(batch);
122 
123  ret = nftnl_ruleset_set_elems(ctx);
124  return ret;
125 err:
126  return -1;
127 }
128 
129 static int nftnl_ruleset_rule_build_msg(const struct nftnl_parse_ctx *ctx,
130  uint32_t cmd, struct nftnl_rule *rule)
131 {
132  struct nlmsghdr *nlh;
133  uint16_t nl_type, nl_flags;
134 
135  switch (cmd) {
136  case NFTNL_CMD_ADD:
137  nl_type = NFT_MSG_NEWRULE;
138  nl_flags = NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK;
139  nftnl_rule_unset(rule, NFTNL_RULE_HANDLE);
140  break;
141  case NFTNL_CMD_DELETE:
142  nl_type = NFT_MSG_DELRULE;
143  nl_flags = NLM_F_ACK;
144  break;
145  case NFTNL_CMD_REPLACE:
146  nl_type = NFT_MSG_NEWRULE;
147  nl_flags = NLM_F_REPLACE|NLM_F_ACK;
148  break;
149  case NFTNL_CMD_INSERT:
150  nl_type = NFT_MSG_NEWRULE;
151  nl_flags = NLM_F_CREATE|NLM_F_ACK;
152  nftnl_rule_unset(rule, NFTNL_RULE_HANDLE);
153  break;
154  default:
155  return -1;
156  }
157 
158  nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
159  nl_type,
160  nftnl_rule_get_u32(rule,
161  NFTNL_RULE_FAMILY),
162  nl_flags,
163  seq++);
164 
165  nftnl_rule_nlmsg_build_payload(nlh, rule);
166  mnl_nlmsg_batch_next(batch);
167 
168  return 0;
169 }
170 
171 static int nftnl_ruleset_rule(const struct nftnl_parse_ctx *ctx)
172 {
173  struct nftnl_rule *rule;
174  int ret;
175  uint32_t cmd;
176 
177  cmd = nftnl_ruleset_ctx_get_u32(ctx, NFTNL_RULESET_CTX_CMD);
178 
179  rule = nftnl_ruleset_ctx_get(ctx, NFTNL_RULESET_CTX_RULE);
180  if (rule == NULL)
181  return -1;
182 
183  ret = nftnl_ruleset_rule_build_msg(ctx, cmd, rule);
184 
185  return ret;
186 }
187 
188 static int nftnl_ruleset_flush_rules(const struct nftnl_parse_ctx *ctx)
189 {
190  struct nftnl_rule *nlr;
191  struct nftnl_table *nlt;
192  struct nftnl_chain *nlc;
193  uint32_t type;
194  int ret;
195 
196  nlr = nftnl_rule_alloc();
197  if (nlr == NULL)
198  return -1;
199 
200  type = nftnl_ruleset_ctx_get_u32(ctx, NFTNL_RULESET_CTX_TYPE);
201  switch (type) {
202  case NFTNL_RULESET_TABLE:
203  nlt = nftnl_ruleset_ctx_get(ctx, NFTNL_RULESET_CTX_TABLE);
204  nftnl_rule_set(nlr, NFTNL_RULE_TABLE,
205  nftnl_table_get(nlt, NFTNL_TABLE_NAME));
206  nftnl_rule_set(nlr, NFTNL_RULE_FAMILY,
207  nftnl_table_get(nlt, NFTNL_TABLE_FAMILY));
208  break;
209  case NFTNL_RULESET_CHAIN:
210  nlc = nftnl_ruleset_ctx_get(ctx, NFTNL_RULESET_CTX_CHAIN);
211  nftnl_rule_set(nlr, NFTNL_RULE_TABLE,
212  nftnl_chain_get(nlc,
213  NFTNL_CHAIN_TABLE));
214  nftnl_rule_set(nlr, NFTNL_RULE_CHAIN,
215  nftnl_chain_get(nlc,
216  NFTNL_CHAIN_NAME));
217  nftnl_rule_set(nlr, NFTNL_RULE_FAMILY,
218  nftnl_chain_get(nlc, NFTNL_TABLE_FAMILY));
219  break;
220  default:
221  goto err;
222  }
223 
224  ret = nftnl_ruleset_rule_build_msg(ctx, NFTNL_CMD_DELETE, nlr);
225  nftnl_rule_free(nlr);
226 
227  return ret;
228 err:
229  nftnl_rule_free(nlr);
230  return -1;
231 }
232 
233 static int nftnl_ruleset_chain(const struct nftnl_parse_ctx *ctx)
234 {
235  struct nlmsghdr *nlh;
236  uint16_t nl_type, nl_flags;
237  uint32_t cmd;
238  struct nftnl_chain *chain;
239 
240  cmd = nftnl_ruleset_ctx_get_u32(ctx, NFTNL_RULESET_CTX_CMD);
241 
242  chain = nftnl_ruleset_ctx_get(ctx, NFTNL_RULESET_CTX_CHAIN);
243  if (chain == NULL)
244  return -1;
245 
246  switch (cmd) {
247  case NFTNL_CMD_ADD:
248  nl_type = NFT_MSG_NEWCHAIN;
249  nl_flags = NLM_F_CREATE|NLM_F_ACK;
250  break;
251  case NFTNL_CMD_DELETE:
252  nl_type = NFT_MSG_DELCHAIN;
253  nl_flags = NLM_F_ACK;
254  break;
255  case NFTNL_CMD_FLUSH:
256  return nftnl_ruleset_flush_rules(ctx);
257  default:
258  goto err;
259  }
260 
261  nftnl_chain_unset(chain, NFTNL_CHAIN_HANDLE);
262  nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
263  nl_type,
264  nftnl_chain_get_u32(chain,
265  NFTNL_CHAIN_FAMILY),
266  nl_flags,
267  seq++);
268 
269  nftnl_chain_nlmsg_build_payload(nlh, chain);
270  mnl_nlmsg_batch_next(batch);
271 
272  return 0;
273 err:
274  return -1;
275 }
276 
277 static int nftnl_ruleset_table_build_msg(const struct nftnl_parse_ctx *ctx,
278  uint32_t cmd, struct nftnl_table *table)
279 {
280  struct nlmsghdr *nlh;
281  uint16_t nl_type, nl_flags;
282 
283  switch (cmd) {
284  case NFTNL_CMD_ADD:
285  nl_type = NFT_MSG_NEWTABLE;
286  nl_flags = NLM_F_CREATE|NLM_F_ACK;
287  break;
288  case NFTNL_CMD_DELETE:
289  nl_type = NFT_MSG_DELTABLE;
290  nl_flags = NLM_F_ACK;
291  break;
292  case NFTNL_CMD_FLUSH:
293  return nftnl_ruleset_flush_rules(ctx);
294  default:
295  return -1;
296  }
297 
298  nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
299  nl_type,
300  nftnl_table_get_u32(table,
301  NFTNL_TABLE_FAMILY),
302  nl_flags,
303  seq++);
304 
305  nftnl_table_nlmsg_build_payload(nlh, table);
306  mnl_nlmsg_batch_next(batch);
307 
308  return 0;
309 }
310 
311 static int nftnl_ruleset_table(const struct nftnl_parse_ctx *ctx)
312 {
313  struct nftnl_table *table;
314  uint32_t cmd;
315  int ret;
316 
317  cmd = nftnl_ruleset_ctx_get_u32(ctx, NFTNL_RULESET_CTX_CMD);
318 
319  table = nftnl_ruleset_ctx_get(ctx, NFTNL_RULESET_CTX_TABLE);
320  if (table == NULL)
321  return -1;
322 
323  ret = nftnl_ruleset_table_build_msg(ctx, cmd, table);
324 
325  return ret;
326 }
327 
328 static int nftnl_ruleset_flush_ruleset(const struct nftnl_parse_ctx *ctx)
329 {
330  struct nftnl_table *table;
331  int ret;
332 
333  table = nftnl_table_alloc();
334  if (table == NULL)
335  return -1;
336 
337  ret = nftnl_ruleset_table_build_msg(ctx, NFTNL_CMD_DELETE, table);
338  nftnl_table_free(table);
339 
340  return ret;
341 }
342 
343 static int ruleset_elems_cb(const struct nftnl_parse_ctx *ctx)
344 {
345  uint32_t type;
346  int ret;
347 
348  type = nftnl_ruleset_ctx_get_u32(ctx, NFTNL_RULESET_CTX_TYPE);
349 
350  switch (type) {
351  case NFTNL_RULESET_TABLE:
352  ret = nftnl_ruleset_table(ctx);
353  break;
354  case NFTNL_RULESET_CHAIN:
355  ret = nftnl_ruleset_chain(ctx);
356  break;
357  case NFTNL_RULESET_RULE:
358  ret = nftnl_ruleset_rule(ctx);
359  break;
360  case NFTNL_RULESET_SET:
361  ret = nftnl_ruleset_parse_set(ctx);
362  break;
363  case NFTNL_RULESET_SET_ELEMS:
364  ret = nftnl_ruleset_set_elems(ctx);
365  break;
366  case NFTNL_RULESET_RULESET:
367  ret = nftnl_ruleset_flush_ruleset(ctx);
368  break;
369  default:
370  return -1;
371  }
372 
373  nftnl_ruleset_ctx_free(ctx);
374  return ret;
375 }
376 
377 int main(int argc, char *argv[])
378 {
379  struct nftnl_parse_err *err;
380  const char *filename;
381  FILE *fp;
382  int ret = -1, len, batching, portid;
383  uint32_t ruleset_seq;
384  char buf[MNL_SOCKET_BUFFER_SIZE];
385  struct mnl_socket *nl;
386 
387  if (argc < 2) {
388  printf("Usage: %s <file>\n", argv[0]);
389  exit(EXIT_FAILURE);
390  }
391 
392  fp = fopen(argv[1], "r");
393  if (fp == NULL) {
394  printf("unable to open file %s: %s\n", argv[1],
395  strerror(errno));
396  exit(EXIT_FAILURE);
397  }
398 
399  err = nftnl_parse_err_alloc();
400  if (err == NULL) {
401  perror("error");
402  exit(EXIT_FAILURE);
403  }
404 
405  batching = nftnl_batch_is_supported();
406  if (batching < 0) {
407  perror("Cannot talk to nfnetlink");
408  exit(EXIT_FAILURE);
409  }
410 
411  seq = time(NULL);
412  batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
413 
414  if (batching) {
415  nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
416  mnl_nlmsg_batch_next(batch);
417  }
418  ruleset_seq = seq;
419 
420  filename = argv[1];
421  len = strlen(filename);
422  if (len >= 5 && strcmp(&filename[len - 5], ".json") == 0)
423  ret = nftnl_ruleset_parse_file_cb(NFTNL_PARSE_JSON, fp, err, NULL,
424  &ruleset_elems_cb);
425  else if (len >= 4 && strcmp(&filename[len - 4], ".xml") == 0)
426  ret = nftnl_ruleset_parse_file_cb(NFTNL_PARSE_XML, fp, err, NULL,
427  &ruleset_elems_cb);
428  else {
429  printf("the filename %s must to end in .xml or .json\n",
430  filename);
431  exit(EXIT_FAILURE);
432  }
433 
434  if (ret < 0) {
435  nftnl_parse_perror("fail", err);
436  exit(EXIT_FAILURE);
437  }
438 
439  fclose(fp);
440 
441  if (batching) {
442  nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
443  mnl_nlmsg_batch_next(batch);
444  }
445 
446  nl = mnl_socket_open(NETLINK_NETFILTER);
447  if (nl == NULL) {
448  perror("mnl_socket_open");
449  exit(EXIT_FAILURE);
450  }
451 
452  if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
453  perror("mnl_socket_bind");
454  exit(EXIT_FAILURE);
455  }
456  portid = mnl_socket_get_portid(nl);
457 
458  if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
459  mnl_nlmsg_batch_size(batch)) < 0) {
460  perror("mnl_socket_send");
461  exit(EXIT_FAILURE);
462  }
463 
464  mnl_nlmsg_batch_stop(batch);
465 
466  ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
467  while (ret > 0) {
468  ret = mnl_cb_run(buf, ret, ruleset_seq, portid, NULL, NULL);
469  if (ret <= 0)
470  break;
471  ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
472  }
473  if (ret == -1) {
474  perror("error");
475  exit(EXIT_FAILURE);
476  }
477 
478  mnl_socket_close(nl);
479  return EXIT_SUCCESS;
480 }