1 /*
2 * (C) 2012-2013 by Pablo Neira Ayuso <[email protected]>
3 * (C) 2013 by Tomasz Bursztyka <[email protected]>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
11 */
12
13 #include <assert.h>
14 #include <string.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <stdbool.h>
18 #include <netdb.h>
19 #include <errno.h>
20 #include <inttypes.h>
21
22 #include <xtables.h>
23
24 #include <libmnl/libmnl.h>
25 #include <libnftnl/rule.h>
26 #include <libnftnl/expr.h>
27
28 #include "nft-shared.h"
29 #include "nft-bridge.h"
30 #include "xshared.h"
31 #include "nft.h"
32
33 extern struct nft_family_ops nft_family_ops_ipv4;
34 extern struct nft_family_ops nft_family_ops_ipv6;
35 extern struct nft_family_ops nft_family_ops_arp;
36 extern struct nft_family_ops nft_family_ops_bridge;
37
xt_nftnl_expr_alloc(const char * name)38 static struct nftnl_expr *xt_nftnl_expr_alloc(const char *name)
39 {
40 struct nftnl_expr *expr = nftnl_expr_alloc(name);
41
42 if (expr)
43 return expr;
44
45 xtables_error(RESOURCE_PROBLEM,
46 "Failed to allocate nftnl expression '%s'", name);
47 }
48
add_meta(struct nft_handle * h,struct nftnl_rule * r,uint32_t key,uint8_t * dreg)49 void add_meta(struct nft_handle *h, struct nftnl_rule *r, uint32_t key,
50 uint8_t *dreg)
51 {
52 struct nftnl_expr *expr;
53 uint8_t reg;
54
55 expr = xt_nftnl_expr_alloc("meta");
56
57 reg = NFT_REG_1;
58 nftnl_expr_set_u32(expr, NFTNL_EXPR_META_KEY, key);
59 nftnl_expr_set_u32(expr, NFTNL_EXPR_META_DREG, reg);
60 nftnl_rule_add_expr(r, expr);
61
62 *dreg = reg;
63 }
64
add_payload(struct nft_handle * h,struct nftnl_rule * r,int offset,int len,uint32_t base,uint8_t * dreg)65 void add_payload(struct nft_handle *h, struct nftnl_rule *r,
66 int offset, int len, uint32_t base, uint8_t *dreg)
67 {
68 struct nftnl_expr *expr;
69 uint8_t reg;
70
71 expr = xt_nftnl_expr_alloc("payload");
72
73 reg = NFT_REG_1;
74 nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_BASE, base);
75 nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_DREG, reg);
76 nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_OFFSET, offset);
77 nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_LEN, len);
78 nftnl_rule_add_expr(r, expr);
79
80 *dreg = reg;
81 }
82
83 /* bitwise operation is = sreg & mask ^ xor */
add_bitwise_u16(struct nft_handle * h,struct nftnl_rule * r,uint16_t mask,uint16_t xor,uint8_t sreg,uint8_t * dreg)84 void add_bitwise_u16(struct nft_handle *h, struct nftnl_rule *r,
85 uint16_t mask, uint16_t xor, uint8_t sreg, uint8_t *dreg)
86 {
87 struct nftnl_expr *expr;
88 uint8_t reg;
89
90 expr = xt_nftnl_expr_alloc("bitwise");
91
92 reg = NFT_REG_1;
93 nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_SREG, sreg);
94 nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_DREG, reg);
95 nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_LEN, sizeof(uint16_t));
96 nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_MASK, &mask, sizeof(uint16_t));
97 nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_XOR, &xor, sizeof(uint16_t));
98 nftnl_rule_add_expr(r, expr);
99
100 *dreg = reg;
101 }
102
add_bitwise(struct nft_handle * h,struct nftnl_rule * r,uint8_t * mask,size_t len,uint8_t sreg,uint8_t * dreg)103 void add_bitwise(struct nft_handle *h, struct nftnl_rule *r,
104 uint8_t *mask, size_t len, uint8_t sreg, uint8_t *dreg)
105 {
106 struct nftnl_expr *expr;
107 uint32_t xor[4] = { 0 };
108 uint8_t reg = *dreg;
109
110 expr = xt_nftnl_expr_alloc("bitwise");
111
112 nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_SREG, sreg);
113 nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_DREG, reg);
114 nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_LEN, len);
115 nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_MASK, mask, len);
116 nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_XOR, &xor, len);
117 nftnl_rule_add_expr(r, expr);
118
119 *dreg = reg;
120 }
121
add_cmp_ptr(struct nftnl_rule * r,uint32_t op,void * data,size_t len,uint8_t sreg)122 void add_cmp_ptr(struct nftnl_rule *r, uint32_t op, void *data, size_t len,
123 uint8_t sreg)
124 {
125 struct nftnl_expr *expr;
126
127 expr = xt_nftnl_expr_alloc("cmp");
128
129 nftnl_expr_set_u32(expr, NFTNL_EXPR_CMP_SREG, sreg);
130 nftnl_expr_set_u32(expr, NFTNL_EXPR_CMP_OP, op);
131 nftnl_expr_set(expr, NFTNL_EXPR_CMP_DATA, data, len);
132 nftnl_rule_add_expr(r, expr);
133 }
134
add_cmp_u8(struct nftnl_rule * r,uint8_t val,uint32_t op,uint8_t sreg)135 void add_cmp_u8(struct nftnl_rule *r, uint8_t val, uint32_t op, uint8_t sreg)
136 {
137 add_cmp_ptr(r, op, &val, sizeof(val), sreg);
138 }
139
add_cmp_u16(struct nftnl_rule * r,uint16_t val,uint32_t op,uint8_t sreg)140 void add_cmp_u16(struct nftnl_rule *r, uint16_t val, uint32_t op, uint8_t sreg)
141 {
142 add_cmp_ptr(r, op, &val, sizeof(val), sreg);
143 }
144
add_cmp_u32(struct nftnl_rule * r,uint32_t val,uint32_t op,uint8_t sreg)145 void add_cmp_u32(struct nftnl_rule *r, uint32_t val, uint32_t op, uint8_t sreg)
146 {
147 add_cmp_ptr(r, op, &val, sizeof(val), sreg);
148 }
149
add_iface(struct nft_handle * h,struct nftnl_rule * r,char * iface,uint32_t key,uint32_t op)150 void add_iface(struct nft_handle *h, struct nftnl_rule *r,
151 char *iface, uint32_t key, uint32_t op)
152 {
153 int iface_len = strlen(iface);
154 uint8_t reg;
155
156
157 if (iface[iface_len - 1] == '+') {
158 if (iface_len > 1) {
159 iface_len -= 1;
160 } else if (op != NFT_CMP_EQ) {
161 op = NFT_CMP_EQ;
162 iface = "INVAL/D";
163 iface_len = strlen(iface) + 1;
164 } else {
165 return; /* -o + */
166 }
167 } else {
168 iface_len += 1;
169 }
170
171 add_meta(h, r, key, ®);
172 add_cmp_ptr(r, op, iface, iface_len, reg);
173 }
174
add_addr(struct nft_handle * h,struct nftnl_rule * r,enum nft_payload_bases base,int offset,void * data,void * mask,size_t len,uint32_t op)175 void add_addr(struct nft_handle *h, struct nftnl_rule *r,
176 enum nft_payload_bases base, int offset,
177 void *data, void *mask, size_t len, uint32_t op)
178 {
179 const unsigned char *m = mask;
180 bool bitwise = false;
181 uint8_t reg;
182 int i, j;
183
184 for (i = 0; i < len; i++) {
185 if (m[i] != 0xff) {
186 bitwise = m[i] != 0;
187 break;
188 }
189 }
190 for (j = i + 1; !bitwise && j < len; j++)
191 bitwise = !!m[j];
192
193 if (!bitwise)
194 len = i;
195
196 add_payload(h, r, offset, len, base, ®);
197
198 if (bitwise)
199 add_bitwise(h, r, mask, len, reg, ®);
200
201 add_cmp_ptr(r, op, data, len, reg);
202 }
203
add_proto(struct nft_handle * h,struct nftnl_rule * r,int offset,size_t len,uint8_t proto,uint32_t op)204 void add_proto(struct nft_handle *h, struct nftnl_rule *r,
205 int offset, size_t len, uint8_t proto, uint32_t op)
206 {
207 uint8_t reg;
208
209 add_payload(h, r, offset, len, NFT_PAYLOAD_NETWORK_HEADER, ®);
210 add_cmp_u8(r, proto, op, reg);
211 }
212
add_l4proto(struct nft_handle * h,struct nftnl_rule * r,uint8_t proto,uint32_t op)213 void add_l4proto(struct nft_handle *h, struct nftnl_rule *r,
214 uint8_t proto, uint32_t op)
215 {
216 uint8_t reg;
217
218 add_meta(h, r, NFT_META_L4PROTO, ®);
219 add_cmp_u8(r, proto, op, reg);
220 }
221
is_same_interfaces(const char * a_iniface,const char * a_outiface,unsigned const char * a_iniface_mask,unsigned const char * a_outiface_mask,const char * b_iniface,const char * b_outiface,unsigned const char * b_iniface_mask,unsigned const char * b_outiface_mask)222 bool is_same_interfaces(const char *a_iniface, const char *a_outiface,
223 unsigned const char *a_iniface_mask,
224 unsigned const char *a_outiface_mask,
225 const char *b_iniface, const char *b_outiface,
226 unsigned const char *b_iniface_mask,
227 unsigned const char *b_outiface_mask)
228 {
229 int i;
230
231 for (i = 0; i < IFNAMSIZ; i++) {
232 if (a_iniface_mask[i] != b_iniface_mask[i]) {
233 DEBUGP("different iniface mask %x, %x (%d)\n",
234 a_iniface_mask[i] & 0xff, b_iniface_mask[i] & 0xff, i);
235 return false;
236 }
237 if ((a_iniface[i] & a_iniface_mask[i])
238 != (b_iniface[i] & b_iniface_mask[i])) {
239 DEBUGP("different iniface\n");
240 return false;
241 }
242 if (a_outiface_mask[i] != b_outiface_mask[i]) {
243 DEBUGP("different outiface mask\n");
244 return false;
245 }
246 if ((a_outiface[i] & a_outiface_mask[i])
247 != (b_outiface[i] & b_outiface_mask[i])) {
248 DEBUGP("different outiface\n");
249 return false;
250 }
251 }
252
253 return true;
254 }
255
__get_cmp_data(struct nftnl_expr * e,void * data,size_t dlen,uint8_t * op)256 void __get_cmp_data(struct nftnl_expr *e, void *data, size_t dlen, uint8_t *op)
257 {
258 uint32_t len;
259
260 memcpy(data, nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len), dlen);
261 *op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
262 }
263
get_cmp_data(struct nftnl_expr * e,void * data,size_t dlen,bool * inv)264 void get_cmp_data(struct nftnl_expr *e, void *data, size_t dlen, bool *inv)
265 {
266 uint8_t op;
267
268 __get_cmp_data(e, data, dlen, &op);
269 *inv = (op == NFT_CMP_NEQ);
270 }
271
nft_ipv46_save_chain(const struct nftnl_chain * c,const char * policy)272 void nft_ipv46_save_chain(const struct nftnl_chain *c, const char *policy)
273 {
274 const char *chain = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
275 uint64_t pkts = nftnl_chain_get_u64(c, NFTNL_CHAIN_PACKETS);
276 uint64_t bytes = nftnl_chain_get_u64(c, NFTNL_CHAIN_BYTES);
277
278 printf(":%s %s [%"PRIu64":%"PRIu64"]\n",
279 chain, policy ?: "-", pkts, bytes);
280 }
281
save_matches_and_target(const struct iptables_command_state * cs,bool goto_flag,const void * fw,unsigned int format)282 void save_matches_and_target(const struct iptables_command_state *cs,
283 bool goto_flag, const void *fw,
284 unsigned int format)
285 {
286 struct xtables_rule_match *matchp;
287
288 for (matchp = cs->matches; matchp; matchp = matchp->next) {
289 if (matchp->match->alias) {
290 printf(" -m %s",
291 matchp->match->alias(matchp->match->m));
292 } else
293 printf(" -m %s", matchp->match->name);
294
295 if (matchp->match->save != NULL) {
296 /* cs->fw union makes the trick */
297 matchp->match->save(fw, matchp->match->m);
298 }
299 }
300
301 if ((format & (FMT_NOCOUNTS | FMT_C_COUNTS)) == FMT_C_COUNTS)
302 printf(" -c %llu %llu",
303 (unsigned long long)cs->counters.pcnt,
304 (unsigned long long)cs->counters.bcnt);
305
306 if (cs->target != NULL) {
307 if (cs->target->alias) {
308 printf(" -j %s", cs->target->alias(cs->target->t));
309 } else
310 printf(" -j %s", cs->jumpto);
311
312 if (cs->target->save != NULL) {
313 cs->target->save(fw, cs->target->t);
314 }
315 } else if (strlen(cs->jumpto) > 0) {
316 printf(" -%c %s", goto_flag ? 'g' : 'j', cs->jumpto);
317 }
318
319 printf("\n");
320 }
321
print_matches_and_target(struct iptables_command_state * cs,unsigned int format)322 void print_matches_and_target(struct iptables_command_state *cs,
323 unsigned int format)
324 {
325 struct xtables_rule_match *matchp;
326
327 for (matchp = cs->matches; matchp; matchp = matchp->next) {
328 if (matchp->match->print != NULL) {
329 matchp->match->print(&cs->fw, matchp->match->m,
330 format & FMT_NUMERIC);
331 }
332 }
333
334 if (cs->target != NULL) {
335 if (cs->target->print != NULL) {
336 cs->target->print(&cs->fw, cs->target->t,
337 format & FMT_NUMERIC);
338 }
339 }
340 }
341
nft_family_ops_lookup(int family)342 struct nft_family_ops *nft_family_ops_lookup(int family)
343 {
344 switch (family) {
345 case AF_INET:
346 return &nft_family_ops_ipv4;
347 case AF_INET6:
348 return &nft_family_ops_ipv6;
349 case NFPROTO_ARP:
350 return &nft_family_ops_arp;
351 case NFPROTO_BRIDGE:
352 return &nft_family_ops_bridge;
353 default:
354 break;
355 }
356
357 return NULL;
358 }
359
compare_matches(struct xtables_rule_match * mt1,struct xtables_rule_match * mt2)360 bool compare_matches(struct xtables_rule_match *mt1,
361 struct xtables_rule_match *mt2)
362 {
363 struct xtables_rule_match *mp1;
364 struct xtables_rule_match *mp2;
365
366 for (mp1 = mt1, mp2 = mt2; mp1 && mp2; mp1 = mp1->next, mp2 = mp2->next) {
367 struct xt_entry_match *m1 = mp1->match->m;
368 struct xt_entry_match *m2 = mp2->match->m;
369 size_t cmplen = mp1->match->userspacesize;
370
371 if (strcmp(m1->u.user.name, m2->u.user.name) != 0) {
372 DEBUGP("mismatching match name\n");
373 return false;
374 }
375
376 if (m1->u.user.match_size != m2->u.user.match_size) {
377 DEBUGP("mismatching match size\n");
378 return false;
379 }
380
381 if (!strcmp(m1->u.user.name, "among"))
382 cmplen = m1->u.match_size - sizeof(*m1);
383
384 if (memcmp(m1->data, m2->data, cmplen) != 0) {
385 DEBUGP("mismatch match data\n");
386 DEBUG_HEXDUMP("m1->data", m1->data, cmplen);
387 DEBUG_HEXDUMP("m2->data", m2->data, cmplen);
388 return false;
389 }
390 }
391
392 /* Both cursors should be NULL */
393 if (mp1 != mp2) {
394 DEBUGP("mismatch matches amount\n");
395 return false;
396 }
397
398 return true;
399 }
400
compare_targets(struct xtables_target * tg1,struct xtables_target * tg2)401 bool compare_targets(struct xtables_target *tg1, struct xtables_target *tg2)
402 {
403 if (tg1 == NULL && tg2 == NULL)
404 return true;
405
406 if (tg1 == NULL || tg2 == NULL)
407 return false;
408 if (tg1->userspacesize != tg2->userspacesize)
409 return false;
410
411 if (strcmp(tg1->t->u.user.name, tg2->t->u.user.name) != 0)
412 return false;
413
414 if (memcmp(tg1->t->data, tg2->t->data, tg1->userspacesize) != 0)
415 return false;
416
417 return true;
418 }
419
nft_check_xt_legacy(int family,bool is_ipt_save)420 void nft_check_xt_legacy(int family, bool is_ipt_save)
421 {
422 static const char tables6[] = "/proc/net/ip6_tables_names";
423 static const char tables4[] = "/proc/net/ip_tables_names";
424 static const char tablesa[] = "/proc/net/arp_tables_names";
425 const char *prefix = "ip";
426 FILE *fp = NULL;
427 char buf[1024];
428
429 switch (family) {
430 case NFPROTO_IPV4:
431 fp = fopen(tables4, "r");
432 break;
433 case NFPROTO_IPV6:
434 fp = fopen(tables6, "r");
435 prefix = "ip6";
436 break;
437 case NFPROTO_ARP:
438 fp = fopen(tablesa, "r");
439 prefix = "arp";
440 break;
441 default:
442 break;
443 }
444
445 if (!fp)
446 return;
447
448 if (fgets(buf, sizeof(buf), fp))
449 fprintf(stderr, "# Warning: %stables-legacy tables present, use %stables-legacy%s to see them\n",
450 prefix, prefix, is_ipt_save ? "-save" : "");
451 fclose(fp);
452 }
453
nft_get_next_reg(enum nft_registers reg,size_t size)454 enum nft_registers nft_get_next_reg(enum nft_registers reg, size_t size)
455 {
456 /* convert size to NETLINK_ALIGN-sized chunks */
457 size = (size + NETLINK_ALIGN - 1) / NETLINK_ALIGN;
458
459 /* map 16byte reg to 4byte one */
460 if (reg < __NFT_REG_MAX)
461 reg = NFT_REG32_00 + (reg - 1) * NFT_REG_SIZE / NFT_REG32_SIZE;
462
463 reg += size;
464 assert(reg <= NFT_REG32_15);
465
466 return reg;
467 }
468