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