1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <netinet/in.h>
4 #include <linux/if_addrlabel.h>
5 #include <linux/netfilter/nfnetlink.h>
6 #include <linux/netfilter/nf_tables.h>
7 #include <linux/nexthop.h>
8 #include <stdbool.h>
9 #include <unistd.h>
10
11 #include "sd-netlink.h"
12
13 #include "format-util.h"
14 #include "netlink-internal.h"
15 #include "netlink-types.h"
16 #include "socket-util.h"
17
nft_message_new(sd_netlink * nfnl,sd_netlink_message ** ret,int family,uint16_t msg_type,uint16_t flags)18 static int nft_message_new(sd_netlink *nfnl, sd_netlink_message **ret, int family, uint16_t msg_type, uint16_t flags) {
19 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
20 int r;
21
22 assert_return(nfnl, -EINVAL);
23 assert_return(ret, -EINVAL);
24
25 r = message_new(nfnl, &m, NFNL_SUBSYS_NFTABLES << 8 | msg_type);
26 if (r < 0)
27 return r;
28
29 m->hdr->nlmsg_flags |= flags;
30
31 *(struct nfgenmsg*) NLMSG_DATA(m->hdr) = (struct nfgenmsg) {
32 .nfgen_family = family,
33 .version = NFNETLINK_V0,
34 .res_id = nfnl->serial,
35 };
36
37 *ret = TAKE_PTR(m);
38 return 0;
39 }
40
nfnl_message_batch(sd_netlink * nfnl,sd_netlink_message ** ret,uint16_t msg_type)41 static int nfnl_message_batch(sd_netlink *nfnl, sd_netlink_message **ret, uint16_t msg_type) {
42 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
43 int r;
44
45 r = message_new(nfnl, &m, NFNL_SUBSYS_NONE << 8 | msg_type);
46 if (r < 0)
47 return r;
48
49 *(struct nfgenmsg*) NLMSG_DATA(m->hdr) = (struct nfgenmsg) {
50 .nfgen_family = AF_UNSPEC,
51 .version = NFNETLINK_V0,
52 .res_id = NFNL_SUBSYS_NFTABLES,
53 };
54
55 *ret = TAKE_PTR(m);
56 return 0;
57 }
58
sd_nfnl_message_batch_begin(sd_netlink * nfnl,sd_netlink_message ** ret)59 int sd_nfnl_message_batch_begin(sd_netlink *nfnl, sd_netlink_message **ret) {
60 return nfnl_message_batch(nfnl, ret, NFNL_MSG_BATCH_BEGIN);
61 }
62
sd_nfnl_message_batch_end(sd_netlink * nfnl,sd_netlink_message ** ret)63 int sd_nfnl_message_batch_end(sd_netlink *nfnl, sd_netlink_message **ret) {
64 return nfnl_message_batch(nfnl, ret, NFNL_MSG_BATCH_END);
65 }
66
sd_nfnl_nft_message_new_basechain(sd_netlink * nfnl,sd_netlink_message ** ret,int family,const char * table,const char * chain,const char * type,uint8_t hook,int prio)67 int sd_nfnl_nft_message_new_basechain(
68 sd_netlink *nfnl,
69 sd_netlink_message **ret,
70 int family,
71 const char *table,
72 const char *chain,
73 const char *type,
74 uint8_t hook,
75 int prio) {
76
77 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
78 int r;
79
80 r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWCHAIN, NLM_F_CREATE);
81 if (r < 0)
82 return r;
83
84 r = sd_netlink_message_append_string(m, NFTA_CHAIN_TABLE, table);
85 if (r < 0)
86 return r;
87
88 r = sd_netlink_message_append_string(m, NFTA_CHAIN_NAME, chain);
89 if (r < 0)
90 return r;
91
92 r = sd_netlink_message_append_string(m, NFTA_CHAIN_TYPE, type);
93 if (r < 0)
94 return r;
95
96 r = sd_netlink_message_open_container(m, NFTA_CHAIN_HOOK);
97 if (r < 0)
98 return r;
99
100 r = sd_netlink_message_append_u32(m, NFTA_HOOK_HOOKNUM, htobe32(hook));
101 if (r < 0)
102 return r;
103
104 r = sd_netlink_message_append_u32(m, NFTA_HOOK_PRIORITY, htobe32(prio));
105 if (r < 0)
106 return r;
107
108 r = sd_netlink_message_close_container(m);
109 if (r < 0)
110 return r;
111
112 *ret = TAKE_PTR(m);
113 return 0;
114 }
115
sd_nfnl_nft_message_del_table(sd_netlink * nfnl,sd_netlink_message ** ret,int family,const char * table)116 int sd_nfnl_nft_message_del_table(
117 sd_netlink *nfnl,
118 sd_netlink_message **ret,
119 int family,
120 const char *table) {
121
122 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
123 int r;
124
125 r = nft_message_new(nfnl, &m, family, NFT_MSG_DELTABLE, NLM_F_CREATE);
126 if (r < 0)
127 return r;
128
129 r = sd_netlink_message_append_string(m, NFTA_TABLE_NAME, table);
130 if (r < 0)
131 return r;
132
133 *ret = TAKE_PTR(m);
134 return r;
135 }
136
sd_nfnl_nft_message_new_table(sd_netlink * nfnl,sd_netlink_message ** ret,int family,const char * table)137 int sd_nfnl_nft_message_new_table(
138 sd_netlink *nfnl,
139 sd_netlink_message **ret,
140 int family,
141 const char *table) {
142
143 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
144 int r;
145
146 r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWTABLE, NLM_F_CREATE | NLM_F_EXCL);
147 if (r < 0)
148 return r;
149
150 r = sd_netlink_message_append_string(m, NFTA_TABLE_NAME, table);
151 if (r < 0)
152 return r;
153
154 *ret = TAKE_PTR(m);
155 return r;
156 }
157
sd_nfnl_nft_message_new_rule(sd_netlink * nfnl,sd_netlink_message ** ret,int family,const char * table,const char * chain)158 int sd_nfnl_nft_message_new_rule(
159 sd_netlink *nfnl,
160 sd_netlink_message **ret,
161 int family,
162 const char *table,
163 const char *chain) {
164
165 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
166 int r;
167
168 r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWRULE, NLM_F_CREATE);
169 if (r < 0)
170 return r;
171
172 r = sd_netlink_message_append_string(m, NFTA_RULE_TABLE, table);
173 if (r < 0)
174 return r;
175
176 r = sd_netlink_message_append_string(m, NFTA_RULE_CHAIN, chain);
177 if (r < 0)
178 return r;
179
180 *ret = TAKE_PTR(m);
181 return r;
182 }
183
sd_nfnl_nft_message_new_set(sd_netlink * nfnl,sd_netlink_message ** ret,int family,const char * table,const char * set_name,uint32_t set_id,uint32_t klen)184 int sd_nfnl_nft_message_new_set(
185 sd_netlink *nfnl,
186 sd_netlink_message **ret,
187 int family,
188 const char *table,
189 const char *set_name,
190 uint32_t set_id,
191 uint32_t klen) {
192
193 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
194 int r;
195
196 r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWSET, NLM_F_CREATE);
197 if (r < 0)
198 return r;
199
200 r = sd_netlink_message_append_string(m, NFTA_SET_TABLE, table);
201 if (r < 0)
202 return r;
203
204 r = sd_netlink_message_append_string(m, NFTA_SET_NAME, set_name);
205 if (r < 0)
206 return r;
207
208 r = sd_netlink_message_append_u32(m, NFTA_SET_ID, ++set_id);
209 if (r < 0)
210 return r;
211
212 r = sd_netlink_message_append_u32(m, NFTA_SET_KEY_LEN, htobe32(klen));
213 if (r < 0)
214 return r;
215
216 *ret = TAKE_PTR(m);
217 return r;
218 }
219
sd_nfnl_nft_message_new_setelems_begin(sd_netlink * nfnl,sd_netlink_message ** ret,int family,const char * table,const char * set_name)220 int sd_nfnl_nft_message_new_setelems_begin(
221 sd_netlink *nfnl,
222 sd_netlink_message **ret,
223 int family,
224 const char *table,
225 const char *set_name) {
226
227 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
228 int r;
229
230 r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWSETELEM, NLM_F_CREATE);
231 if (r < 0)
232 return r;
233
234 r = sd_netlink_message_append_string(m, NFTA_SET_ELEM_LIST_TABLE, table);
235 if (r < 0)
236 return r;
237
238 r = sd_netlink_message_append_string(m, NFTA_SET_ELEM_LIST_SET, set_name);
239 if (r < 0)
240 return r;
241
242 r = sd_netlink_message_open_container(m, NFTA_SET_ELEM_LIST_ELEMENTS);
243 if (r < 0)
244 return r;
245
246 *ret = TAKE_PTR(m);
247 return r;
248 }
249
sd_nfnl_nft_message_del_setelems_begin(sd_netlink * nfnl,sd_netlink_message ** ret,int family,const char * table,const char * set_name)250 int sd_nfnl_nft_message_del_setelems_begin(
251 sd_netlink *nfnl,
252 sd_netlink_message **ret,
253 int family,
254 const char *table,
255 const char *set_name) {
256
257 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
258 int r;
259
260 r = nft_message_new(nfnl, &m, family, NFT_MSG_DELSETELEM, 0);
261 if (r < 0)
262 return r;
263
264 r = sd_netlink_message_append_string(m, NFTA_SET_ELEM_LIST_TABLE, table);
265 if (r < 0)
266 return r;
267
268 r = sd_netlink_message_append_string(m, NFTA_SET_ELEM_LIST_SET, set_name);
269 if (r < 0)
270 return r;
271
272 r = sd_netlink_message_open_container(m, NFTA_SET_ELEM_LIST_ELEMENTS);
273 if (r < 0)
274 return r;
275
276 *ret = TAKE_PTR(m);
277 return r;
278 }
279
sd_nfnl_add_data(sd_netlink_message * m,uint16_t attr,const void * data,uint32_t dlen)280 static int sd_nfnl_add_data(sd_netlink_message *m, uint16_t attr, const void *data, uint32_t dlen) {
281 int r;
282
283 r = sd_netlink_message_open_container(m, attr);
284 if (r < 0)
285 return r;
286
287 r = sd_netlink_message_append_data(m, NFTA_DATA_VALUE, data, dlen);
288 if (r < 0)
289 return r;
290
291 return sd_netlink_message_close_container(m); /* attr */
292 }
293
sd_nfnl_nft_message_add_setelem(sd_netlink_message * m,uint32_t num,const void * key,uint32_t klen,const void * data,uint32_t dlen)294 int sd_nfnl_nft_message_add_setelem(
295 sd_netlink_message *m,
296 uint32_t num,
297 const void *key,
298 uint32_t klen,
299 const void *data,
300 uint32_t dlen) {
301
302 int r;
303
304 r = sd_netlink_message_open_array(m, num);
305 if (r < 0)
306 return r;
307
308 r = sd_nfnl_add_data(m, NFTA_SET_ELEM_KEY, key, klen);
309 if (r < 0)
310 goto cancel;
311
312 if (data) {
313 r = sd_nfnl_add_data(m, NFTA_SET_ELEM_DATA, data, dlen);
314 if (r < 0)
315 goto cancel;
316 }
317
318 return 0;
319
320 cancel:
321 sd_netlink_message_cancel_array(m);
322 return r;
323 }
324
sd_nfnl_nft_message_add_setelem_end(sd_netlink_message * m)325 int sd_nfnl_nft_message_add_setelem_end(sd_netlink_message *m) {
326 return sd_netlink_message_close_container(m); /* NFTA_SET_ELEM_LIST_ELEMENTS */
327 }
328
sd_nfnl_socket_open(sd_netlink ** ret)329 int sd_nfnl_socket_open(sd_netlink **ret) {
330 return netlink_open_family(ret, NETLINK_NETFILTER);
331 }
332