libnftnl  1.0.5
common.c
1 /*
2  * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published
6  * by 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 <sys/socket.h>
12 #include <time.h>
13 #include <linux/netlink.h>
14 #include <linux/netfilter/nfnetlink.h>
15 #include <linux/netfilter/nf_tables.h>
16 
17 #include <libmnl/libmnl.h>
18 #include <libnftnl/common.h>
19 #include <libnftnl/set.h>
20 #include <buffer.h>
21 
22 #include <errno.h>
23 #include "internal.h"
24 
25 struct nlmsghdr *nftnl_nlmsg_build_hdr(char *buf, uint16_t cmd, uint16_t family,
26  uint16_t type, uint32_t seq)
27 {
28  struct nlmsghdr *nlh;
29  struct nfgenmsg *nfh;
30 
31  nlh = mnl_nlmsg_put_header(buf);
32  nlh->nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | cmd;
33  nlh->nlmsg_flags = NLM_F_REQUEST | type;
34  nlh->nlmsg_seq = seq;
35 
36  nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
37  nfh->nfgen_family = family;
38  nfh->version = NFNETLINK_V0;
39  nfh->res_id = 0;
40 
41  return nlh;
42 }
43 EXPORT_SYMBOL(nftnl_nlmsg_build_hdr, nft_nlmsg_build_hdr);
44 
45 struct nftnl_parse_err *nftnl_parse_err_alloc(void)
46 {
47  struct nftnl_parse_err *err;
48 
49  err = calloc(1, sizeof(struct nftnl_parse_err));
50  if (err == NULL)
51  return NULL;
52 
53  err->error = NFTNL_PARSE_EOPNOTSUPP;
54 
55  return err;
56 }
57 EXPORT_SYMBOL(nftnl_parse_err_alloc, nft_parse_err_alloc);
58 
59 void nftnl_parse_err_free(struct nftnl_parse_err *err)
60 {
61  xfree(err);
62 }
63 EXPORT_SYMBOL(nftnl_parse_err_free, nft_parse_err_free);
64 
65 int nftnl_parse_perror(const char *msg, struct nftnl_parse_err *err)
66 {
67  switch (err->error) {
68  case NFTNL_PARSE_EBADINPUT:
69  return fprintf(stderr, "%s: Bad input format in line %d column %d\n",
70  msg, err->line, err->column);
71  case NFTNL_PARSE_EMISSINGNODE:
72  return fprintf(stderr, "%s: Node \"%s\" not found\n",
73  msg, err->node_name);
74  case NFTNL_PARSE_EBADTYPE:
75  return fprintf(stderr, "%s: Invalid type in node \"%s\"\n",
76  msg, err->node_name);
77  case NFTNL_PARSE_EOPNOTSUPP:
78  return fprintf(stderr, "%s: Operation not supported\n", msg);
79  default:
80  return fprintf(stderr, "%s: Undefined error\n", msg);
81  }
82 }
83 EXPORT_SYMBOL(nftnl_parse_perror, nft_parse_perror);
84 
85 int nftnl_cmd_header_snprintf(char *buf, size_t size, uint32_t cmd, uint32_t type,
86  uint32_t flags)
87 {
88  NFTNL_BUF_INIT(b, buf, size);
89 
90  if (cmd == NFTNL_CMD_UNSPEC)
91  return 0;
92 
93  switch (type) {
94  case NFTNL_OUTPUT_XML:
95  case NFTNL_OUTPUT_JSON:
96  nftnl_buf_open_array(&b, type, nftnl_cmd2tag(cmd));
97  break;
98  default:
99  switch (cmd) {
100  case NFTNL_CMD_ADD:
101  return snprintf(buf, size, "%9s", "[ADD] ");
102  case NFTNL_CMD_DELETE:
103  return snprintf(buf, size, "%9s", "[DELETE] ");
104  default:
105  return snprintf(buf, size, "%9s", "[unknown] ");
106  }
107  break;
108  }
109  return nftnl_buf_done(&b);
110 }
111 
112 static int nftnl_cmd_header_fprintf_cb(char *buf, size_t size, void *obj,
113  uint32_t cmd, uint32_t type,
114  uint32_t flags)
115 {
116  return nftnl_cmd_header_snprintf(buf, size, cmd, type, flags);
117 }
118 
119 int nftnl_cmd_header_fprintf(FILE *fp, uint32_t cmd, uint32_t type,
120  uint32_t flags)
121 {
122  return nftnl_fprintf(fp, NULL, cmd, type, flags,
123  nftnl_cmd_header_fprintf_cb);
124 }
125 
126 int nftnl_cmd_footer_snprintf(char *buf, size_t size, uint32_t cmd, uint32_t type,
127  uint32_t flags)
128 {
129  NFTNL_BUF_INIT(b, buf, size);
130 
131  if (cmd == NFTNL_CMD_UNSPEC)
132  return 0;
133 
134  switch (type) {
135  case NFTNL_OUTPUT_XML:
136  case NFTNL_OUTPUT_JSON:
137  nftnl_buf_close_array(&b, type, nftnl_cmd2tag(cmd));
138  break;
139  default:
140  return 0;
141  }
142  return nftnl_buf_done(&b);
143 }
144 
145 static int nftnl_cmd_footer_fprintf_cb(char *buf, size_t size, void *obj,
146  uint32_t cmd, uint32_t type,
147  uint32_t flags)
148 {
149  return nftnl_cmd_footer_snprintf(buf, size, cmd, type, flags);
150 }
151 
152 int nftnl_cmd_footer_fprintf(FILE *fp, uint32_t cmd, uint32_t type,
153  uint32_t flags)
154 {
155  return nftnl_fprintf(fp, NULL, cmd, type, flags,
156  nftnl_cmd_footer_fprintf_cb);
157 }
158 
159 static void nftnl_batch_build_hdr(char *buf, uint16_t type, uint32_t seq)
160 {
161  struct nlmsghdr *nlh;
162  struct nfgenmsg *nfg;
163 
164  nlh = mnl_nlmsg_put_header(buf);
165  nlh->nlmsg_type = type;
166  nlh->nlmsg_flags = NLM_F_REQUEST;
167  nlh->nlmsg_seq = seq;
168 
169  nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
170  nfg->nfgen_family = AF_UNSPEC;
171  nfg->version = NFNETLINK_V0;
172  nfg->res_id = NFNL_SUBSYS_NFTABLES;
173 }
174 
175 void nftnl_batch_begin(char *buf, uint32_t seq)
176 {
177  nftnl_batch_build_hdr(buf, NFNL_MSG_BATCH_BEGIN, seq);
178 }
179 EXPORT_SYMBOL(nftnl_batch_begin, nft_batch_begin);
180 
181 void nftnl_batch_end(char *buf, uint32_t seq)
182 {
183  nftnl_batch_build_hdr(buf, NFNL_MSG_BATCH_END, seq);
184 }
185 EXPORT_SYMBOL(nftnl_batch_end, nft_batch_end);
186 
187 int nftnl_batch_is_supported(void)
188 {
189  struct mnl_socket *nl;
190  struct mnl_nlmsg_batch *b;
191  char buf[MNL_SOCKET_BUFFER_SIZE];
192  uint32_t seq = time(NULL), req_seq;
193  int ret;
194 
195  nl = mnl_socket_open(NETLINK_NETFILTER);
196  if (nl == NULL)
197  return -1;
198 
199  if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)
200  return -1;
201 
202  b = mnl_nlmsg_batch_start(buf, sizeof(buf));
203 
204  nftnl_batch_begin(mnl_nlmsg_batch_current(b), seq++);
205  mnl_nlmsg_batch_next(b);
206 
207  req_seq = seq;
208  nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(b),
209  NFT_MSG_NEWSET, AF_INET,
210  NLM_F_ACK, seq++);
211  mnl_nlmsg_batch_next(b);
212 
213  nftnl_batch_end(mnl_nlmsg_batch_current(b), seq++);
214  mnl_nlmsg_batch_next(b);
215 
216  ret = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(b),
217  mnl_nlmsg_batch_size(b));
218  if (ret < 0)
219  goto err;
220 
221  mnl_nlmsg_batch_stop(b);
222 
223  ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
224  while (ret > 0) {
225  ret = mnl_cb_run(buf, ret, req_seq, mnl_socket_get_portid(nl),
226  NULL, NULL);
227  if (ret <= 0)
228  break;
229 
230  ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
231  }
232  mnl_socket_close(nl);
233 
234  /* We're sending an incomplete message to see if the kernel supports
235  * set messages in batches. EINVAL means that we sent an incomplete
236  * message with missing attributes. The kernel just ignores messages
237  * that we cannot include in the batch.
238  */
239  return (ret == -1 && errno == EINVAL) ? 1 : 0;
240 err:
241  mnl_nlmsg_batch_stop(b);
242  return -1;
243 }
244 EXPORT_SYMBOL(nftnl_batch_is_supported, nft_batch_is_supported);