1 /*
2 * (C) 2014 by Giuseppe Longo <giuseppelng@gmail.com>
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 by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <netinet/ether.h>
14 #include <inttypes.h>
15
16 #include <xtables.h>
17 #include <libiptc/libxtc.h>
18 #include <linux/netfilter/nf_tables.h>
19
20 #include <libnftnl/set.h>
21
22 #include "nft-shared.h"
23 #include "nft-bridge.h"
24 #include "nft-cache.h"
25 #include "nft.h"
26
ebt_cs_clean(struct iptables_command_state * cs)27 void ebt_cs_clean(struct iptables_command_state *cs)
28 {
29 struct ebt_match *m, *nm;
30
31 xtables_rule_matches_free(&cs->matches);
32
33 for (m = cs->match_list; m;) {
34 if (!m->ismatch) {
35 struct xtables_target *target = m->u.watcher;
36
37 if (target->t) {
38 free(target->t);
39 target->t = NULL;
40 }
41 if (target == target->next)
42 free(target);
43 }
44
45 nm = m->next;
46 free(m);
47 m = nm;
48 }
49
50 if (cs->target) {
51 free(cs->target->t);
52 cs->target->t = NULL;
53
54 if (cs->target == cs->target->next) {
55 free(cs->target);
56 cs->target = NULL;
57 }
58 }
59 }
60
61 /* Put the mac address into 6 (ETH_ALEN) bytes returns 0 on success. */
ebt_print_mac_and_mask(const unsigned char * mac,const unsigned char * mask)62 static void ebt_print_mac_and_mask(const unsigned char *mac, const unsigned char *mask)
63 {
64 if (xtables_print_well_known_mac_and_mask(mac, mask))
65 xtables_print_mac_and_mask(mac, mask);
66 }
67
add_logical_iniface(struct nftnl_rule * r,char * iface,uint32_t op)68 static void add_logical_iniface(struct nftnl_rule *r, char *iface, uint32_t op)
69 {
70 int iface_len;
71
72 iface_len = strlen(iface);
73
74 add_meta(r, NFT_META_BRI_IIFNAME);
75 if (iface[iface_len - 1] == '+')
76 add_cmp_ptr(r, op, iface, iface_len - 1);
77 else
78 add_cmp_ptr(r, op, iface, iface_len + 1);
79 }
80
add_logical_outiface(struct nftnl_rule * r,char * iface,uint32_t op)81 static void add_logical_outiface(struct nftnl_rule *r, char *iface, uint32_t op)
82 {
83 int iface_len;
84
85 iface_len = strlen(iface);
86
87 add_meta(r, NFT_META_BRI_OIFNAME);
88 if (iface[iface_len - 1] == '+')
89 add_cmp_ptr(r, op, iface, iface_len - 1);
90 else
91 add_cmp_ptr(r, op, iface, iface_len + 1);
92 }
93
_add_action(struct nftnl_rule * r,struct iptables_command_state * cs)94 static int _add_action(struct nftnl_rule *r, struct iptables_command_state *cs)
95 {
96 return add_action(r, cs, false);
97 }
98
nft_bridge_add(struct nft_handle * h,struct nftnl_rule * r,void * data)99 static int nft_bridge_add(struct nft_handle *h,
100 struct nftnl_rule *r, void *data)
101 {
102 struct iptables_command_state *cs = data;
103 struct ebt_match *iter;
104 struct ebt_entry *fw = &cs->eb;
105 uint32_t op;
106
107 if (fw->in[0] != '\0') {
108 op = nft_invflags2cmp(fw->invflags, EBT_IIN);
109 add_iniface(r, fw->in, op);
110 }
111
112 if (fw->out[0] != '\0') {
113 op = nft_invflags2cmp(fw->invflags, EBT_IOUT);
114 add_outiface(r, fw->out, op);
115 }
116
117 if (fw->logical_in[0] != '\0') {
118 op = nft_invflags2cmp(fw->invflags, EBT_ILOGICALIN);
119 add_logical_iniface(r, fw->logical_in, op);
120 }
121
122 if (fw->logical_out[0] != '\0') {
123 op = nft_invflags2cmp(fw->invflags, EBT_ILOGICALOUT);
124 add_logical_outiface(r, fw->logical_out, op);
125 }
126
127 if (fw->bitmask & EBT_ISOURCE) {
128 op = nft_invflags2cmp(fw->invflags, EBT_ISOURCE);
129 add_addr(r, NFT_PAYLOAD_LL_HEADER,
130 offsetof(struct ethhdr, h_source),
131 fw->sourcemac, fw->sourcemsk, ETH_ALEN, op);
132 }
133
134 if (fw->bitmask & EBT_IDEST) {
135 op = nft_invflags2cmp(fw->invflags, EBT_IDEST);
136 add_addr(r, NFT_PAYLOAD_LL_HEADER,
137 offsetof(struct ethhdr, h_dest),
138 fw->destmac, fw->destmsk, ETH_ALEN, op);
139 }
140
141 if ((fw->bitmask & EBT_NOPROTO) == 0) {
142 op = nft_invflags2cmp(fw->invflags, EBT_IPROTO);
143 add_payload(r, offsetof(struct ethhdr, h_proto), 2,
144 NFT_PAYLOAD_LL_HEADER);
145 add_cmp_u16(r, fw->ethproto, op);
146 }
147
148 add_compat(r, fw->ethproto, fw->invflags & EBT_IPROTO);
149
150 for (iter = cs->match_list; iter; iter = iter->next) {
151 if (iter->ismatch) {
152 if (add_match(h, r, iter->u.match->m))
153 break;
154 } else {
155 if (add_target(r, iter->u.watcher->t))
156 break;
157 }
158 }
159
160 if (add_counters(r, cs->counters.pcnt, cs->counters.bcnt) < 0)
161 return -1;
162
163 return _add_action(r, cs);
164 }
165
nft_bridge_parse_meta(struct nft_xt_ctx * ctx,struct nftnl_expr * e,void * data)166 static void nft_bridge_parse_meta(struct nft_xt_ctx *ctx,
167 struct nftnl_expr *e, void *data)
168 {
169 struct iptables_command_state *cs = data;
170 struct ebt_entry *fw = &cs->eb;
171 uint8_t invflags = 0;
172 char iifname[IFNAMSIZ] = {}, oifname[IFNAMSIZ] = {};
173
174 parse_meta(e, ctx->meta.key, iifname, NULL, oifname, NULL, &invflags);
175
176 switch (ctx->meta.key) {
177 case NFT_META_BRI_IIFNAME:
178 if (invflags & IPT_INV_VIA_IN)
179 cs->eb.invflags |= EBT_ILOGICALIN;
180 snprintf(fw->logical_in, sizeof(fw->logical_in), "%s", iifname);
181 break;
182 case NFT_META_IIFNAME:
183 if (invflags & IPT_INV_VIA_IN)
184 cs->eb.invflags |= EBT_IIN;
185 snprintf(fw->in, sizeof(fw->in), "%s", iifname);
186 break;
187 case NFT_META_BRI_OIFNAME:
188 if (invflags & IPT_INV_VIA_OUT)
189 cs->eb.invflags |= EBT_ILOGICALOUT;
190 snprintf(fw->logical_out, sizeof(fw->logical_out), "%s", oifname);
191 break;
192 case NFT_META_OIFNAME:
193 if (invflags & IPT_INV_VIA_OUT)
194 cs->eb.invflags |= EBT_IOUT;
195 snprintf(fw->out, sizeof(fw->out), "%s", oifname);
196 break;
197 default:
198 break;
199 }
200 }
201
nft_bridge_parse_payload(struct nft_xt_ctx * ctx,struct nftnl_expr * e,void * data)202 static void nft_bridge_parse_payload(struct nft_xt_ctx *ctx,
203 struct nftnl_expr *e, void *data)
204 {
205 struct iptables_command_state *cs = data;
206 struct ebt_entry *fw = &cs->eb;
207 unsigned char addr[ETH_ALEN];
208 unsigned short int ethproto;
209 bool inv;
210 int i;
211
212 switch (ctx->payload.offset) {
213 case offsetof(struct ethhdr, h_dest):
214 get_cmp_data(e, addr, sizeof(addr), &inv);
215 for (i = 0; i < ETH_ALEN; i++)
216 fw->destmac[i] = addr[i];
217 if (inv)
218 fw->invflags |= EBT_IDEST;
219
220 if (ctx->flags & NFT_XT_CTX_BITWISE) {
221 memcpy(fw->destmsk, ctx->bitwise.mask, ETH_ALEN);
222 ctx->flags &= ~NFT_XT_CTX_BITWISE;
223 } else {
224 memset(&fw->destmsk, 0xff,
225 min(ctx->payload.len, ETH_ALEN));
226 }
227 fw->bitmask |= EBT_IDEST;
228 break;
229 case offsetof(struct ethhdr, h_source):
230 get_cmp_data(e, addr, sizeof(addr), &inv);
231 for (i = 0; i < ETH_ALEN; i++)
232 fw->sourcemac[i] = addr[i];
233 if (inv)
234 fw->invflags |= EBT_ISOURCE;
235 if (ctx->flags & NFT_XT_CTX_BITWISE) {
236 memcpy(fw->sourcemsk, ctx->bitwise.mask, ETH_ALEN);
237 ctx->flags &= ~NFT_XT_CTX_BITWISE;
238 } else {
239 memset(&fw->sourcemsk, 0xff,
240 min(ctx->payload.len, ETH_ALEN));
241 }
242 fw->bitmask |= EBT_ISOURCE;
243 break;
244 case offsetof(struct ethhdr, h_proto):
245 get_cmp_data(e, ðproto, sizeof(ethproto), &inv);
246 fw->ethproto = ethproto;
247 if (inv)
248 fw->invflags |= EBT_IPROTO;
249 fw->bitmask &= ~EBT_NOPROTO;
250 break;
251 }
252 }
253
nft_bridge_parse_immediate(const char * jumpto,bool nft_goto,void * data)254 static void nft_bridge_parse_immediate(const char *jumpto, bool nft_goto,
255 void *data)
256 {
257 struct iptables_command_state *cs = data;
258
259 cs->jumpto = jumpto;
260 }
261
262 /* return 0 if saddr, 1 if daddr, -1 on error */
263 static int
lookup_check_ether_payload(uint32_t base,uint32_t offset,uint32_t len)264 lookup_check_ether_payload(uint32_t base, uint32_t offset, uint32_t len)
265 {
266 if (base != 0 || len != ETH_ALEN)
267 return -1;
268
269 switch (offset) {
270 case offsetof(struct ether_header, ether_dhost):
271 return 1;
272 case offsetof(struct ether_header, ether_shost):
273 return 0;
274 default:
275 return -1;
276 }
277 }
278
279 /* return 0 if saddr, 1 if daddr, -1 on error */
280 static int
lookup_check_iphdr_payload(uint32_t base,uint32_t offset,uint32_t len)281 lookup_check_iphdr_payload(uint32_t base, uint32_t offset, uint32_t len)
282 {
283 if (base != 1 || len != 4)
284 return -1;
285
286 switch (offset) {
287 case offsetof(struct iphdr, daddr):
288 return 1;
289 case offsetof(struct iphdr, saddr):
290 return 0;
291 default:
292 return -1;
293 }
294 }
295
296 /* Make sure previous payload expression(s) is/are consistent and extract if
297 * matching on source or destination address and if matching on MAC and IP or
298 * only MAC address. */
lookup_analyze_payloads(const struct nft_xt_ctx * ctx,bool * dst,bool * ip)299 static int lookup_analyze_payloads(const struct nft_xt_ctx *ctx,
300 bool *dst, bool *ip)
301 {
302 int val, val2 = -1;
303
304 if (ctx->flags & NFT_XT_CTX_PREV_PAYLOAD) {
305 val = lookup_check_ether_payload(ctx->prev_payload.base,
306 ctx->prev_payload.offset,
307 ctx->prev_payload.len);
308 if (val < 0) {
309 DEBUGP("unknown payload base/offset/len %d/%d/%d\n",
310 ctx->prev_payload.base, ctx->prev_payload.offset,
311 ctx->prev_payload.len);
312 return -1;
313 }
314 if (!(ctx->flags & NFT_XT_CTX_PAYLOAD)) {
315 DEBUGP("Previous but no current payload?\n");
316 return -1;
317 }
318 val2 = lookup_check_iphdr_payload(ctx->payload.base,
319 ctx->payload.offset,
320 ctx->payload.len);
321 if (val2 < 0) {
322 DEBUGP("unknown payload base/offset/len %d/%d/%d\n",
323 ctx->payload.base, ctx->payload.offset,
324 ctx->payload.len);
325 return -1;
326 } else if (val != val2) {
327 DEBUGP("mismatching payload match offsets\n");
328 return -1;
329 }
330 } else if (ctx->flags & NFT_XT_CTX_PAYLOAD) {
331 val = lookup_check_ether_payload(ctx->payload.base,
332 ctx->payload.offset,
333 ctx->payload.len);
334 if (val < 0) {
335 DEBUGP("unknown payload base/offset/len %d/%d/%d\n",
336 ctx->payload.base, ctx->payload.offset,
337 ctx->payload.len);
338 return -1;
339 }
340 } else {
341 DEBUGP("unknown LHS of lookup expression\n");
342 return -1;
343 }
344
345 if (dst)
346 *dst = (val == 1);
347 if (ip)
348 *ip = (val2 != -1);
349 return 0;
350 }
351
set_elems_to_among_pairs(struct nft_among_pair * pairs,const struct nftnl_set * s,int cnt)352 static int set_elems_to_among_pairs(struct nft_among_pair *pairs,
353 const struct nftnl_set *s, int cnt)
354 {
355 struct nftnl_set_elems_iter *iter = nftnl_set_elems_iter_create(s);
356 struct nftnl_set_elem *elem;
357 size_t tmpcnt = 0;
358 const void *data;
359 uint32_t datalen;
360 int ret = -1;
361
362 if (!iter) {
363 fprintf(stderr, "BUG: set elems iter allocation failed\n");
364 return ret;
365 }
366
367 while ((elem = nftnl_set_elems_iter_next(iter))) {
368 data = nftnl_set_elem_get(elem, NFTNL_SET_ELEM_KEY, &datalen);
369 if (!data) {
370 fprintf(stderr, "BUG: set elem without key\n");
371 goto err;
372 }
373 if (datalen > sizeof(*pairs)) {
374 fprintf(stderr, "BUG: overlong set elem\n");
375 goto err;
376 }
377 nft_among_insert_pair(pairs, &tmpcnt, data);
378 }
379 ret = 0;
380 err:
381 nftnl_set_elems_iter_destroy(iter);
382 return ret;
383 }
384
set_from_lookup_expr(struct nft_xt_ctx * ctx,const struct nftnl_expr * e)385 static struct nftnl_set *set_from_lookup_expr(struct nft_xt_ctx *ctx,
386 const struct nftnl_expr *e)
387 {
388 const char *set_name = nftnl_expr_get_str(e, NFTNL_EXPR_LOOKUP_SET);
389 uint32_t set_id = nftnl_expr_get_u32(e, NFTNL_EXPR_LOOKUP_SET_ID);
390 struct nftnl_set_list *slist;
391 struct nftnl_set *set;
392
393 slist = nft_set_list_get(ctx->h, ctx->table, set_name);
394 if (slist) {
395 set = nftnl_set_list_lookup_byname(slist, set_name);
396 if (set)
397 return set;
398
399 set = nft_set_batch_lookup_byid(ctx->h, set_id);
400 if (set)
401 return set;
402 }
403
404 return NULL;
405 }
406
nft_bridge_parse_lookup(struct nft_xt_ctx * ctx,struct nftnl_expr * e,void * data)407 static void nft_bridge_parse_lookup(struct nft_xt_ctx *ctx,
408 struct nftnl_expr *e, void *data)
409 {
410 struct xtables_match *match = NULL;
411 struct nft_among_data *among_data;
412 bool is_dst, have_ip, inv;
413 struct ebt_match *ematch;
414 struct nftnl_set *s;
415 size_t poff, size;
416 uint32_t cnt;
417
418 if (lookup_analyze_payloads(ctx, &is_dst, &have_ip))
419 return;
420
421 s = set_from_lookup_expr(ctx, e);
422 if (!s)
423 xtables_error(OTHER_PROBLEM,
424 "BUG: lookup expression references unknown set");
425
426 cnt = nftnl_set_get_u32(s, NFTNL_SET_DESC_SIZE);
427
428 for (ematch = ctx->cs->match_list; ematch; ematch = ematch->next) {
429 if (!ematch->ismatch || strcmp(ematch->u.match->name, "among"))
430 continue;
431
432 match = ematch->u.match;
433 among_data = (struct nft_among_data *)match->m->data;
434
435 size = cnt + among_data->src.cnt + among_data->dst.cnt;
436 size *= sizeof(struct nft_among_pair);
437
438 size += XT_ALIGN(sizeof(struct xt_entry_match)) +
439 sizeof(struct nft_among_data);
440
441 match->m = xtables_realloc(match->m, size);
442 break;
443 }
444 if (!match) {
445 match = xtables_find_match("among", XTF_TRY_LOAD,
446 &ctx->cs->matches);
447
448 size = cnt * sizeof(struct nft_among_pair);
449 size += XT_ALIGN(sizeof(struct xt_entry_match)) +
450 sizeof(struct nft_among_data);
451
452 match->m = xtables_calloc(1, size);
453 strcpy(match->m->u.user.name, match->name);
454 match->m->u.user.revision = match->revision;
455 xs_init_match(match);
456
457 if (ctx->h->ops->parse_match != NULL)
458 ctx->h->ops->parse_match(match, ctx->cs);
459 }
460 if (!match)
461 return;
462
463 match->m->u.match_size = size;
464
465 inv = !!(nftnl_expr_get_u32(e, NFTNL_EXPR_LOOKUP_FLAGS) &
466 NFT_LOOKUP_F_INV);
467
468 among_data = (struct nft_among_data *)match->m->data;
469 poff = nft_among_prepare_data(among_data, is_dst, cnt, inv, have_ip);
470 if (set_elems_to_among_pairs(among_data->pairs + poff, s, cnt))
471 xtables_error(OTHER_PROBLEM,
472 "ebtables among pair parsing failed");
473
474 ctx->flags &= ~(NFT_XT_CTX_PAYLOAD | NFT_XT_CTX_PREV_PAYLOAD);
475 }
476
parse_watcher(void * object,struct ebt_match ** match_list,bool ismatch)477 static void parse_watcher(void *object, struct ebt_match **match_list,
478 bool ismatch)
479 {
480 struct ebt_match *m;
481
482 m = calloc(1, sizeof(struct ebt_match));
483 if (m == NULL)
484 xtables_error(OTHER_PROBLEM, "Can't allocate memory");
485
486 if (ismatch)
487 m->u.match = object;
488 else
489 m->u.watcher = object;
490
491 m->ismatch = ismatch;
492 if (*match_list == NULL)
493 *match_list = m;
494 else
495 (*match_list)->next = m;
496 }
497
nft_bridge_parse_match(struct xtables_match * m,void * data)498 static void nft_bridge_parse_match(struct xtables_match *m, void *data)
499 {
500 struct iptables_command_state *cs = data;
501
502 parse_watcher(m, &cs->match_list, true);
503 }
504
nft_bridge_parse_target(struct xtables_target * t,void * data)505 static void nft_bridge_parse_target(struct xtables_target *t, void *data)
506 {
507 struct iptables_command_state *cs = data;
508
509 /* harcoded names :-( */
510 if (strcmp(t->name, "log") == 0 ||
511 strcmp(t->name, "nflog") == 0) {
512 parse_watcher(t, &cs->match_list, false);
513 return;
514 }
515
516 cs->target = t;
517 }
518
nft_rule_to_ebtables_command_state(struct nft_handle * h,const struct nftnl_rule * r,struct iptables_command_state * cs)519 static void nft_rule_to_ebtables_command_state(struct nft_handle *h,
520 const struct nftnl_rule *r,
521 struct iptables_command_state *cs)
522 {
523 cs->eb.bitmask = EBT_NOPROTO;
524 nft_rule_to_iptables_command_state(h, r, cs);
525 }
526
print_iface(const char * option,const char * name,bool invert)527 static void print_iface(const char *option, const char *name, bool invert)
528 {
529 if (*name)
530 printf("%s%s %s ", option, invert ? " !" : "", name);
531 }
532
nft_bridge_print_table_header(const char * tablename)533 static void nft_bridge_print_table_header(const char *tablename)
534 {
535 printf("Bridge table: %s\n\n", tablename);
536 }
537
nft_bridge_print_header(unsigned int format,const char * chain,const char * pol,const struct xt_counters * counters,bool basechain,uint32_t refs,uint32_t entries)538 static void nft_bridge_print_header(unsigned int format, const char *chain,
539 const char *pol,
540 const struct xt_counters *counters,
541 bool basechain, uint32_t refs, uint32_t entries)
542 {
543 printf("Bridge chain: %s, entries: %u, policy: %s\n",
544 chain, entries, pol ?: "RETURN");
545 }
546
print_matches_and_watchers(const struct iptables_command_state * cs,unsigned int format)547 static void print_matches_and_watchers(const struct iptables_command_state *cs,
548 unsigned int format)
549 {
550 struct xtables_target *watcherp;
551 struct xtables_match *matchp;
552 struct ebt_match *m;
553
554 for (m = cs->match_list; m; m = m->next) {
555 if (m->ismatch) {
556 matchp = m->u.match;
557 if (matchp->print != NULL) {
558 matchp->print(&cs->eb, matchp->m,
559 format & FMT_NUMERIC);
560 }
561 } else {
562 watcherp = m->u.watcher;
563 if (watcherp->print != NULL) {
564 watcherp->print(&cs->eb, watcherp->t,
565 format & FMT_NUMERIC);
566 }
567 }
568 }
569 }
570
print_mac(char option,const unsigned char * mac,const unsigned char * mask,bool invert)571 static void print_mac(char option, const unsigned char *mac,
572 const unsigned char *mask,
573 bool invert)
574 {
575 printf("-%c ", option);
576 if (invert)
577 printf("! ");
578 ebt_print_mac_and_mask(mac, mask);
579 printf(" ");
580 }
581
582
print_protocol(uint16_t ethproto,bool invert,unsigned int bitmask)583 static void print_protocol(uint16_t ethproto, bool invert, unsigned int bitmask)
584 {
585 struct xt_ethertypeent *ent;
586
587 /* Dont print anything about the protocol if no protocol was
588 * specified, obviously this means any protocol will do. */
589 if (bitmask & EBT_NOPROTO)
590 return;
591
592 printf("-p ");
593 if (invert)
594 printf("! ");
595
596 if (bitmask & EBT_802_3) {
597 printf("length ");
598 return;
599 }
600
601 ent = xtables_getethertypebynumber(ntohs(ethproto));
602 if (!ent)
603 printf("0x%x ", ntohs(ethproto));
604 else
605 printf("%s ", ent->e_name);
606 }
607
nft_bridge_save_rule(const void * data,unsigned int format)608 static void nft_bridge_save_rule(const void *data, unsigned int format)
609 {
610 const struct iptables_command_state *cs = data;
611
612 if (cs->eb.ethproto)
613 print_protocol(cs->eb.ethproto, cs->eb.invflags & EBT_IPROTO,
614 cs->eb.bitmask);
615 if (cs->eb.bitmask & EBT_ISOURCE)
616 print_mac('s', cs->eb.sourcemac, cs->eb.sourcemsk,
617 cs->eb.invflags & EBT_ISOURCE);
618 if (cs->eb.bitmask & EBT_IDEST)
619 print_mac('d', cs->eb.destmac, cs->eb.destmsk,
620 cs->eb.invflags & EBT_IDEST);
621
622 print_iface("-i", cs->eb.in, cs->eb.invflags & EBT_IIN);
623 print_iface("--logical-in", cs->eb.logical_in,
624 cs->eb.invflags & EBT_ILOGICALIN);
625 print_iface("-o", cs->eb.out, cs->eb.invflags & EBT_IOUT);
626 print_iface("--logical-out", cs->eb.logical_out,
627 cs->eb.invflags & EBT_ILOGICALOUT);
628
629 print_matches_and_watchers(cs, format);
630
631 printf("-j ");
632
633 if (cs->jumpto != NULL) {
634 if (strcmp(cs->jumpto, "") != 0)
635 printf("%s", cs->jumpto);
636 else
637 printf("CONTINUE");
638 }
639 if (cs->target != NULL && cs->target->print != NULL) {
640 printf(" ");
641 cs->target->print(&cs->fw, cs->target->t, format & FMT_NUMERIC);
642 }
643
644 if ((format & (FMT_NOCOUNTS | FMT_C_COUNTS)) == FMT_C_COUNTS) {
645 if (format & FMT_EBT_SAVE)
646 printf(" -c %"PRIu64" %"PRIu64"",
647 (uint64_t)cs->counters.pcnt,
648 (uint64_t)cs->counters.bcnt);
649 else
650 printf(" , pcnt = %"PRIu64" -- bcnt = %"PRIu64"",
651 (uint64_t)cs->counters.pcnt,
652 (uint64_t)cs->counters.bcnt);
653 }
654
655 if (!(format & FMT_NONEWLINE))
656 fputc('\n', stdout);
657 }
658
nft_bridge_print_rule(struct nft_handle * h,struct nftnl_rule * r,unsigned int num,unsigned int format)659 static void nft_bridge_print_rule(struct nft_handle *h, struct nftnl_rule *r,
660 unsigned int num, unsigned int format)
661 {
662 struct iptables_command_state cs = {};
663
664 if (format & FMT_LINENUMBERS)
665 printf("%d ", num);
666
667 nft_rule_to_ebtables_command_state(h, r, &cs);
668 nft_bridge_save_rule(&cs, format);
669 ebt_cs_clean(&cs);
670 }
671
nft_bridge_save_chain(const struct nftnl_chain * c,const char * policy)672 static void nft_bridge_save_chain(const struct nftnl_chain *c,
673 const char *policy)
674 {
675 const char *chain = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
676
677 printf(":%s %s\n", chain, policy ?: "ACCEPT");
678 }
679
nft_bridge_is_same(const void * data_a,const void * data_b)680 static bool nft_bridge_is_same(const void *data_a, const void *data_b)
681 {
682 const struct ebt_entry *a = data_a;
683 const struct ebt_entry *b = data_b;
684 int i;
685
686 if (a->ethproto != b->ethproto ||
687 /* FIXME: a->flags != b->flags || */
688 a->invflags != b->invflags) {
689 DEBUGP("different proto/flags/invflags\n");
690 return false;
691 }
692
693 for (i = 0; i < ETH_ALEN; i++) {
694 if (a->sourcemac[i] != b->sourcemac[i]) {
695 DEBUGP("different source mac %x, %x (%d)\n",
696 a->sourcemac[i] & 0xff, b->sourcemac[i] & 0xff, i);
697 return false;
698 }
699
700 if (a->destmac[i] != b->destmac[i]) {
701 DEBUGP("different destination mac %x, %x (%d)\n",
702 a->destmac[i] & 0xff, b->destmac[i] & 0xff, i);
703 return false;
704 }
705 }
706
707 for (i = 0; i < IFNAMSIZ; i++) {
708 if (a->logical_in[i] != b->logical_in[i]) {
709 DEBUGP("different logical iniface %x, %x (%d)\n",
710 a->logical_in[i] & 0xff, b->logical_in[i] & 0xff, i);
711 return false;
712 }
713
714 if (a->logical_out[i] != b->logical_out[i]) {
715 DEBUGP("different logical outiface %x, %x (%d)\n",
716 a->logical_out[i] & 0xff, b->logical_out[i] & 0xff, i);
717 return false;
718 }
719 }
720
721 return strcmp(a->in, b->in) == 0 && strcmp(a->out, b->out) == 0;
722 }
723
xlate_ebmatches(const struct iptables_command_state * cs,struct xt_xlate * xl)724 static int xlate_ebmatches(const struct iptables_command_state *cs, struct xt_xlate *xl)
725 {
726 int ret = 1, numeric = cs->options & OPT_NUMERIC;
727 struct ebt_match *m;
728
729 for (m = cs->match_list; m; m = m->next) {
730 if (m->ismatch) {
731 struct xtables_match *matchp = m->u.match;
732 struct xt_xlate_mt_params mt_params = {
733 .ip = (const void *)&cs->eb,
734 .numeric = numeric,
735 .escape_quotes = false,
736 .match = matchp->m,
737 };
738
739 if (!matchp->xlate)
740 return 0;
741
742 ret = matchp->xlate(xl, &mt_params);
743 } else {
744 struct xtables_target *watcherp = m->u.watcher;
745 struct xt_xlate_tg_params wt_params = {
746 .ip = (const void *)&cs->eb,
747 .numeric = numeric,
748 .escape_quotes = false,
749 .target = watcherp->t,
750 };
751
752 if (!watcherp->xlate)
753 return 0;
754
755 ret = watcherp->xlate(xl, &wt_params);
756 }
757
758 if (!ret)
759 break;
760 }
761
762 return ret;
763 }
764
xlate_ebaction(const struct iptables_command_state * cs,struct xt_xlate * xl)765 static int xlate_ebaction(const struct iptables_command_state *cs, struct xt_xlate *xl)
766 {
767 int ret = 1, numeric = cs->options & OPT_NUMERIC;
768
769 /* If no target at all, add nothing (default to continue) */
770 if (cs->target != NULL) {
771 /* Standard target? */
772 if (strcmp(cs->jumpto, XTC_LABEL_ACCEPT) == 0)
773 xt_xlate_add(xl, " accept");
774 else if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0)
775 xt_xlate_add(xl, " drop");
776 else if (strcmp(cs->jumpto, XTC_LABEL_RETURN) == 0)
777 xt_xlate_add(xl, " return");
778 else if (cs->target->xlate) {
779 xt_xlate_add(xl, " ");
780 struct xt_xlate_tg_params params = {
781 .ip = (const void *)&cs->eb,
782 .target = cs->target->t,
783 .numeric = numeric,
784 };
785 ret = cs->target->xlate(xl, ¶ms);
786 }
787 else
788 return 0;
789 } else if (cs->jumpto == NULL) {
790 } else if (strlen(cs->jumpto) > 0)
791 xt_xlate_add(xl, " jump %s", cs->jumpto);
792
793 return ret;
794 }
795
xlate_mac(struct xt_xlate * xl,const unsigned char * mac)796 static void xlate_mac(struct xt_xlate *xl, const unsigned char *mac)
797 {
798 int i;
799
800 xt_xlate_add(xl, "%02x", mac[0]);
801
802 for (i=1; i < ETH_ALEN; i++)
803 xt_xlate_add(xl, ":%02x", mac[i]);
804 }
805
nft_bridge_xlate_mac(struct xt_xlate * xl,const char * type,bool invert,const unsigned char * mac,const unsigned char * mask)806 static void nft_bridge_xlate_mac(struct xt_xlate *xl, const char *type, bool invert,
807 const unsigned char *mac, const unsigned char *mask)
808 {
809 char one_msk[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
810
811 xt_xlate_add(xl, "ether %s %s", type, invert ? "!= " : "");
812
813 xlate_mac(xl, mac);
814
815 if (memcmp(mask, one_msk, ETH_ALEN)) {
816 int i;
817 xt_xlate_add(xl, " and ");
818
819 xlate_mac(xl, mask);
820
821 xt_xlate_add(xl, " == %02x", mac[0] & mask[0]);
822 for (i=1; i < ETH_ALEN; i++)
823 xt_xlate_add(xl, ":%02x", mac[i] & mask[i]);
824 }
825
826 xt_xlate_add(xl, " ");
827 }
828
nft_bridge_xlate(const void * data,struct xt_xlate * xl)829 static int nft_bridge_xlate(const void *data, struct xt_xlate *xl)
830 {
831 const struct iptables_command_state *cs = data;
832 int ret;
833
834 xlate_ifname(xl, "iifname", cs->eb.in,
835 cs->eb.invflags & EBT_IIN);
836 xlate_ifname(xl, "meta ibrname", cs->eb.logical_in,
837 cs->eb.invflags & EBT_ILOGICALIN);
838 xlate_ifname(xl, "oifname", cs->eb.out,
839 cs->eb.invflags & EBT_IOUT);
840 xlate_ifname(xl, "meta obrname", cs->eb.logical_out,
841 cs->eb.invflags & EBT_ILOGICALOUT);
842
843 if ((cs->eb.bitmask & EBT_NOPROTO) == 0) {
844 const char *implicit = NULL;
845
846 switch (ntohs(cs->eb.ethproto)) {
847 case ETH_P_IP:
848 implicit = "ip";
849 break;
850 case ETH_P_IPV6:
851 implicit = "ip6";
852 break;
853 case ETH_P_8021Q:
854 implicit = "vlan";
855 break;
856 default:
857 break;
858 }
859
860 if (!implicit || !xlate_find_match(cs, implicit))
861 xt_xlate_add(xl, "ether type %s0x%x ",
862 cs->eb.invflags & EBT_IPROTO ? "!= " : "",
863 ntohs(cs->eb.ethproto));
864 }
865
866 if (cs->eb.bitmask & EBT_802_3)
867 return 0;
868
869 if (cs->eb.bitmask & EBT_ISOURCE)
870 nft_bridge_xlate_mac(xl, "saddr", cs->eb.invflags & EBT_ISOURCE,
871 cs->eb.sourcemac, cs->eb.sourcemsk);
872 if (cs->eb.bitmask & EBT_IDEST)
873 nft_bridge_xlate_mac(xl, "daddr", cs->eb.invflags & EBT_IDEST,
874 cs->eb.destmac, cs->eb.destmsk);
875 ret = xlate_ebmatches(cs, xl);
876 if (ret == 0)
877 return ret;
878
879 /* Always add counters per rule, as in ebtables */
880 xt_xlate_add(xl, "counter");
881 ret = xlate_ebaction(cs, xl);
882
883 return ret;
884 }
885
886 struct nft_family_ops nft_family_ops_bridge = {
887 .add = nft_bridge_add,
888 .is_same = nft_bridge_is_same,
889 .print_payload = NULL,
890 .parse_meta = nft_bridge_parse_meta,
891 .parse_payload = nft_bridge_parse_payload,
892 .parse_immediate = nft_bridge_parse_immediate,
893 .parse_lookup = nft_bridge_parse_lookup,
894 .parse_match = nft_bridge_parse_match,
895 .parse_target = nft_bridge_parse_target,
896 .print_table_header = nft_bridge_print_table_header,
897 .print_header = nft_bridge_print_header,
898 .print_rule = nft_bridge_print_rule,
899 .save_rule = nft_bridge_save_rule,
900 .save_chain = nft_bridge_save_chain,
901 .post_parse = NULL,
902 .rule_to_cs = nft_rule_to_ebtables_command_state,
903 .clear_cs = ebt_cs_clean,
904 .xlate = nft_bridge_xlate,
905 };
906