libnftnl 1.2.9
common.c
1/* SPDX-License-Identifier: GPL-2.0-or-later */
2/*
3 * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
4 */
5
6#include <stdlib.h>
7#include <sys/socket.h>
8#include <time.h>
9#include <arpa/inet.h>
10#include <linux/netlink.h>
11#include <linux/netfilter/nfnetlink.h>
12#include <linux/netfilter/nf_tables.h>
13
14#include <libmnl/libmnl.h>
15#include <libnftnl/common.h>
16#include <libnftnl/set.h>
17
18#include <errno.h>
19#include "internal.h"
20
21static struct nlmsghdr *__nftnl_nlmsg_build_hdr(char *buf, uint16_t type,
22 uint16_t family,
23 uint16_t flags, uint32_t seq,
24 uint16_t res_id)
25{
26 struct nlmsghdr *nlh;
27 struct nfgenmsg *nfh;
28
29 nlh = mnl_nlmsg_put_header(buf);
30 nlh->nlmsg_type = type;
31 nlh->nlmsg_flags = NLM_F_REQUEST | flags;
32 nlh->nlmsg_seq = seq;
33
34 nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
35 nfh->nfgen_family = family;
36 nfh->version = NFNETLINK_V0;
37 nfh->res_id = htons(res_id);
38
39 return nlh;
40}
41
42EXPORT_SYMBOL(nftnl_nlmsg_build_hdr);
43struct nlmsghdr *nftnl_nlmsg_build_hdr(char *buf, uint16_t type, uint16_t family,
44 uint16_t flags, uint32_t seq)
45{
46 return __nftnl_nlmsg_build_hdr(buf, (NFNL_SUBSYS_NFTABLES << 8) | type,
47 family, flags, seq, 0);
48}
49
50EXPORT_SYMBOL(nftnl_parse_err_alloc);
51struct nftnl_parse_err *nftnl_parse_err_alloc(void)
52{
53 struct nftnl_parse_err *err;
54
55 err = calloc(1, sizeof(struct nftnl_parse_err));
56 if (err == NULL)
57 return NULL;
58
59 err->error = NFTNL_PARSE_EOPNOTSUPP;
60
61 return err;
62}
63
64EXPORT_SYMBOL(nftnl_parse_err_free);
65void nftnl_parse_err_free(struct nftnl_parse_err *err)
66{
67 xfree(err);
68}
69
70EXPORT_SYMBOL(nftnl_parse_perror);
71int nftnl_parse_perror(const char *msg, struct nftnl_parse_err *err)
72{
73 switch (err->error) {
74 case NFTNL_PARSE_EBADINPUT:
75 return fprintf(stderr, "%s: Bad input format in line %d column %d\n",
76 msg, err->line, err->column);
77 case NFTNL_PARSE_EMISSINGNODE:
78 return fprintf(stderr, "%s: Node \"%s\" not found\n",
79 msg, err->node_name);
80 case NFTNL_PARSE_EBADTYPE:
81 return fprintf(stderr, "%s: Invalid type in node \"%s\"\n",
82 msg, err->node_name);
83 case NFTNL_PARSE_EOPNOTSUPP:
84 return fprintf(stderr, "%s: Operation not supported\n", msg);
85 default:
86 return fprintf(stderr, "%s: Undefined error\n", msg);
87 }
88}
89
90EXPORT_SYMBOL(nftnl_batch_begin);
91struct nlmsghdr *nftnl_batch_begin(char *buf, uint32_t seq)
92{
93 return __nftnl_nlmsg_build_hdr(buf, NFNL_MSG_BATCH_BEGIN, AF_UNSPEC,
94 0, seq, NFNL_SUBSYS_NFTABLES);
95}
96
97EXPORT_SYMBOL(nftnl_batch_end);
98struct nlmsghdr *nftnl_batch_end(char *buf, uint32_t seq)
99{
100 return __nftnl_nlmsg_build_hdr(buf, NFNL_MSG_BATCH_END, AF_UNSPEC,
101 0, seq, NFNL_SUBSYS_NFTABLES);
102}
103
104EXPORT_SYMBOL(nftnl_batch_is_supported);
105int nftnl_batch_is_supported(void)
106{
107 struct mnl_socket *nl;
108 struct mnl_nlmsg_batch *b;
109 char buf[MNL_SOCKET_BUFFER_SIZE];
110 uint32_t seq = time(NULL), req_seq;
111 int ret;
112
113 nl = mnl_socket_open(NETLINK_NETFILTER);
114 if (nl == NULL)
115 return -1;
116
117 if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)
118 return -1;
119
120 b = mnl_nlmsg_batch_start(buf, sizeof(buf));
121
122 nftnl_batch_begin(mnl_nlmsg_batch_current(b), seq++);
123 mnl_nlmsg_batch_next(b);
124
125 req_seq = seq;
126 nftnl_nlmsg_build_hdr(mnl_nlmsg_batch_current(b), NFT_MSG_NEWSET,
127 AF_INET, NLM_F_ACK, seq++);
128 mnl_nlmsg_batch_next(b);
129
130 nftnl_batch_end(mnl_nlmsg_batch_current(b), seq++);
131 mnl_nlmsg_batch_next(b);
132
133 ret = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(b),
134 mnl_nlmsg_batch_size(b));
135 if (ret < 0)
136 goto err;
137
138 mnl_nlmsg_batch_stop(b);
139
140 ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
141 while (ret > 0) {
142 ret = mnl_cb_run(buf, ret, req_seq, mnl_socket_get_portid(nl),
143 NULL, NULL);
144 if (ret <= 0)
145 break;
146
147 ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
148 }
149 mnl_socket_close(nl);
150
151 /* We're sending an incomplete message to see if the kernel supports
152 * set messages in batches. EINVAL means that we sent an incomplete
153 * message with missing attributes. The kernel just ignores messages
154 * that we cannot include in the batch.
155 */
156 return (ret == -1 && errno == EINVAL) ? 1 : 0;
157err:
158 mnl_nlmsg_batch_stop(b);
159 return -1;
160}