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