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 #include <ebtables/ethernetdb.h>
20
21 #include "nft-shared.h"
22 #include "nft-bridge.h"
23 #include "nft.h"
24
ebt_cs_clean(struct ebtables_command_state * cs)25 void ebt_cs_clean(struct ebtables_command_state *cs)
26 {
27 struct ebt_match *m, *nm;
28
29 xtables_rule_matches_free(&cs->matches);
30
31 for (m = cs->match_list; m;) {
32 nm = m->next;
33 if (!m->ismatch)
34 free(m->u.watcher->t);
35 free(m);
36 m = nm;
37 }
38 }
39
40 /* 0: default, print only 2 digits if necessary
41 * 2: always print 2 digits, a printed mac address
42 * then always has the same length
43 */
44 int ebt_printstyle_mac;
45
ebt_print_mac(const unsigned char * mac)46 static void ebt_print_mac(const unsigned char *mac)
47 {
48 if (ebt_printstyle_mac == 2) {
49 int j;
50 for (j = 0; j < ETH_ALEN; j++)
51 printf("%02x%s", mac[j],
52 (j==ETH_ALEN-1) ? "" : ":");
53 } else
54 printf("%s", ether_ntoa((struct ether_addr *) mac));
55 }
56
57 /* 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)58 static void ebt_print_mac_and_mask(const unsigned char *mac, const unsigned char *mask)
59 {
60 char hlpmsk[6] = {};
61
62 if (!memcmp(mac, eb_mac_type_unicast, 6) &&
63 !memcmp(mask, eb_msk_type_unicast, 6))
64 printf("Unicast");
65 else if (!memcmp(mac, eb_mac_type_multicast, 6) &&
66 !memcmp(mask, eb_msk_type_multicast, 6))
67 printf("Multicast");
68 else if (!memcmp(mac, eb_mac_type_broadcast, 6) &&
69 !memcmp(mask, eb_msk_type_broadcast, 6))
70 printf("Broadcast");
71 else if (!memcmp(mac, eb_mac_type_bridge_group, 6) &&
72 !memcmp(mask, eb_msk_type_bridge_group, 6))
73 printf("BGA");
74 else {
75 ebt_print_mac(mac);
76 if (memcmp(mask, hlpmsk, 6)) {
77 printf("/");
78 ebt_print_mac(mask);
79 }
80 }
81 }
82
ipt_to_ebt_flags(uint8_t invflags)83 static uint16_t ipt_to_ebt_flags(uint8_t invflags)
84 {
85 uint16_t result = 0;
86
87 if (invflags & IPT_INV_VIA_IN)
88 result |= EBT_IIN;
89
90 if (invflags & IPT_INV_VIA_OUT)
91 result |= EBT_IOUT;
92
93 if (invflags & IPT_INV_PROTO)
94 result |= EBT_IPROTO;
95
96 return result;
97 }
98
add_logical_iniface(struct nftnl_rule * r,char * iface,uint32_t op)99 static void add_logical_iniface(struct nftnl_rule *r, char *iface, uint32_t op)
100 {
101 int iface_len;
102
103 iface_len = strlen(iface);
104
105 add_meta(r, NFT_META_BRI_IIFNAME);
106 if (iface[iface_len - 1] == '+')
107 add_cmp_ptr(r, op, iface, iface_len - 1);
108 else
109 add_cmp_ptr(r, op, iface, iface_len + 1);
110 }
111
add_logical_outiface(struct nftnl_rule * r,char * iface,uint32_t op)112 static void add_logical_outiface(struct nftnl_rule *r, char *iface, uint32_t op)
113 {
114 int iface_len;
115
116 iface_len = strlen(iface);
117
118 add_meta(r, NFT_META_BRI_OIFNAME);
119 if (iface[iface_len - 1] == '+')
120 add_cmp_ptr(r, op, iface, iface_len - 1);
121 else
122 add_cmp_ptr(r, op, iface, iface_len + 1);
123 }
124
125 /* TODO: Use generic add_action() once we convert this to use
126 * iptables_command_state.
127 */
_add_action(struct nftnl_rule * r,struct ebtables_command_state * cs)128 static int _add_action(struct nftnl_rule *r, struct ebtables_command_state *cs)
129 {
130 int ret = 0;
131
132 if (cs->jumpto == NULL || strcmp(cs->jumpto, "CONTINUE") == 0)
133 return 0;
134
135 /* If no target at all, add nothing (default to continue) */
136 if (cs->target != NULL) {
137 /* Standard target? */
138 if (strcmp(cs->jumpto, XTC_LABEL_ACCEPT) == 0)
139 ret = add_verdict(r, NF_ACCEPT);
140 else if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0)
141 ret = add_verdict(r, NF_DROP);
142 else if (strcmp(cs->jumpto, XTC_LABEL_RETURN) == 0)
143 ret = add_verdict(r, NFT_RETURN);
144 else
145 ret = add_target(r, cs->target->t);
146 } else if (strlen(cs->jumpto) > 0) {
147 /* Not standard, then it's a jump to chain */
148 ret = add_jumpto(r, cs->jumpto, NFT_JUMP);
149 }
150
151 return ret;
152 }
153
nft_bridge_add(struct nftnl_rule * r,void * data)154 static int nft_bridge_add(struct nftnl_rule *r, void *data)
155 {
156 struct ebtables_command_state *cs = data;
157 struct ebt_match *iter;
158 struct ebt_entry *fw = &cs->fw;
159 uint32_t op;
160 char *addr;
161
162 if (fw->in[0] != '\0') {
163 op = nft_invflags2cmp(fw->invflags, EBT_IIN);
164 add_iniface(r, fw->in, op);
165 }
166
167 if (fw->out[0] != '\0') {
168 op = nft_invflags2cmp(fw->invflags, EBT_IOUT);
169 add_outiface(r, fw->out, op);
170 }
171
172 if (fw->logical_in[0] != '\0') {
173 op = nft_invflags2cmp(fw->invflags, EBT_ILOGICALIN);
174 add_logical_iniface(r, fw->logical_in, op);
175 }
176
177 if (fw->logical_out[0] != '\0') {
178 op = nft_invflags2cmp(fw->invflags, EBT_ILOGICALOUT);
179 add_logical_outiface(r, fw->logical_out, op);
180 }
181
182 addr = ether_ntoa((struct ether_addr *) fw->sourcemac);
183 if (strcmp(addr, "0:0:0:0:0:0") != 0) {
184 op = nft_invflags2cmp(fw->invflags, EBT_ISOURCE);
185 add_payload(r, offsetof(struct ethhdr, h_source), 6,
186 NFT_PAYLOAD_LL_HEADER);
187 add_cmp_ptr(r, op, fw->sourcemac, 6);
188 }
189
190 addr = ether_ntoa((struct ether_addr *) fw->destmac);
191 if (strcmp(addr, "0:0:0:0:0:0") != 0) {
192 op = nft_invflags2cmp(fw->invflags, EBT_IDEST);
193 add_payload(r, offsetof(struct ethhdr, h_dest), 6,
194 NFT_PAYLOAD_LL_HEADER);
195 add_cmp_ptr(r, op, fw->destmac, 6);
196 }
197
198 if (fw->ethproto != 0) {
199 op = nft_invflags2cmp(fw->invflags, EBT_IPROTO);
200 add_payload(r, offsetof(struct ethhdr, h_proto), 2,
201 NFT_PAYLOAD_LL_HEADER);
202 add_cmp_u16(r, fw->ethproto, op);
203 }
204
205 add_compat(r, fw->ethproto, fw->invflags);
206
207 for (iter = cs->match_list; iter; iter = iter->next) {
208 if (iter->ismatch) {
209 if (add_match(r, iter->u.match->m))
210 break;
211 } else {
212 if (add_target(r, iter->u.watcher->t))
213 break;
214 }
215 }
216
217 if (add_counters(r, cs->counters.pcnt, cs->counters.bcnt) < 0)
218 return -1;
219
220 return _add_action(r, cs);
221 }
222
nft_bridge_parse_meta(struct nft_xt_ctx * ctx,struct nftnl_expr * e,void * data)223 static void nft_bridge_parse_meta(struct nft_xt_ctx *ctx,
224 struct nftnl_expr *e, void *data)
225 {
226 struct ebtables_command_state *cs = data;
227 struct ebt_entry *fw = &cs->fw;
228 uint8_t flags = 0;
229 int iface = 0;
230 const void *ifname;
231 uint32_t len;
232
233 iface = parse_meta(e, ctx->meta.key, fw->in, fw->in_mask,
234 fw->out, fw->out_mask, &flags);
235 if (!iface)
236 goto out;
237
238 switch (ctx->meta.key) {
239 case NFT_META_BRI_IIFNAME:
240 ifname = nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
241 if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
242 flags |= IPT_INV_VIA_IN;
243
244 memcpy(fw->logical_in, ifname, len);
245
246 if (fw->logical_in[len] == '\0')
247 memset(fw->in_mask, 0xff, len);
248 else {
249 fw->logical_in[len] = '+';
250 fw->logical_in[len+1] = '\0';
251 memset(fw->in_mask, 0xff, len + 1);
252 }
253 break;
254 case NFT_META_BRI_OIFNAME:
255 ifname = nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
256 if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
257 flags |= IPT_INV_VIA_OUT;
258
259 memcpy(fw->logical_out, ifname, len);
260
261 if (fw->logical_out[len] == '\0')
262 memset(fw->out_mask, 0xff, len);
263 else {
264 fw->logical_out[len] = '+';
265 fw->logical_out[len+1] = '\0';
266 memset(fw->out_mask, 0xff, len + 1);
267 }
268 break;
269 default:
270 break;
271 }
272
273 out:
274 fw->invflags |= ipt_to_ebt_flags(flags);
275 }
276
nft_bridge_parse_payload(struct nft_xt_ctx * ctx,struct nftnl_expr * e,void * data)277 static void nft_bridge_parse_payload(struct nft_xt_ctx *ctx,
278 struct nftnl_expr *e, void *data)
279 {
280 struct ebtables_command_state *cs = data;
281 struct ebt_entry *fw = &cs->fw;
282 unsigned char addr[ETH_ALEN];
283 unsigned short int ethproto;
284 bool inv;
285 int i;
286
287 switch (ctx->payload.offset) {
288 case offsetof(struct ethhdr, h_dest):
289 get_cmp_data(e, addr, sizeof(addr), &inv);
290 for (i = 0; i < ETH_ALEN; i++)
291 fw->destmac[i] = addr[i];
292 if (inv)
293 fw->invflags |= EBT_IDEST;
294 break;
295 case offsetof(struct ethhdr, h_source):
296 get_cmp_data(e, addr, sizeof(addr), &inv);
297 for (i = 0; i < ETH_ALEN; i++)
298 fw->sourcemac[i] = addr[i];
299 if (inv)
300 fw->invflags |= EBT_ISOURCE;
301 break;
302 case offsetof(struct ethhdr, h_proto):
303 get_cmp_data(e, ðproto, sizeof(ethproto), &inv);
304 fw->ethproto = ethproto;
305 if (inv)
306 fw->invflags |= EBT_IPROTO;
307 break;
308 }
309 }
310
nft_bridge_parse_immediate(const char * jumpto,bool nft_goto,void * data)311 static void nft_bridge_parse_immediate(const char *jumpto, bool nft_goto,
312 void *data)
313 {
314 struct ebtables_command_state *cs = data;
315
316 cs->jumpto = jumpto;
317 }
318
parse_watcher(void * object,struct ebt_match ** match_list,bool ismatch)319 static void parse_watcher(void *object, struct ebt_match **match_list,
320 bool ismatch)
321 {
322 struct ebt_match *m;
323
324 m = calloc(1, sizeof(struct ebt_match));
325 if (m == NULL)
326 xtables_error(OTHER_PROBLEM, "Can't allocate memory");
327
328 if (ismatch)
329 m->u.match = object;
330 else
331 m->u.watcher = object;
332
333 m->ismatch = ismatch;
334 if (*match_list == NULL)
335 *match_list = m;
336 else
337 (*match_list)->next = m;
338 }
339
nft_bridge_parse_match(struct xtables_match * m,void * data)340 static void nft_bridge_parse_match(struct xtables_match *m, void *data)
341 {
342 struct ebtables_command_state *cs = data;
343
344 parse_watcher(m, &cs->match_list, true);
345 }
346
nft_bridge_parse_target(struct xtables_target * t,void * data)347 static void nft_bridge_parse_target(struct xtables_target *t, void *data)
348 {
349 struct ebtables_command_state *cs = data;
350
351 /* harcoded names :-( */
352 if (strcmp(t->name, "log") == 0 ||
353 strcmp(t->name, "nflog") == 0) {
354 parse_watcher(t, &cs->match_list, false);
355 return;
356 }
357
358 cs->target = t;
359 }
360
nft_rule_to_ebtables_command_state(struct nftnl_rule * r,struct ebtables_command_state * cs)361 void nft_rule_to_ebtables_command_state(struct nftnl_rule *r,
362 struct ebtables_command_state *cs)
363 {
364 struct nftnl_expr_iter *iter;
365 struct nftnl_expr *expr;
366 int family = nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY);
367 struct nft_xt_ctx ctx = {
368 .state.cs_eb = cs,
369 .family = family,
370 };
371
372 iter = nftnl_expr_iter_create(r);
373 if (iter == NULL)
374 return;
375
376 expr = nftnl_expr_iter_next(iter);
377 while (expr != NULL) {
378 const char *name =
379 nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
380
381 if (strcmp(name, "counter") == 0)
382 nft_parse_counter(expr, &cs->counters);
383 else if (strcmp(name, "payload") == 0)
384 nft_parse_payload(&ctx, expr);
385 else if (strcmp(name, "meta") == 0)
386 nft_parse_meta(&ctx, expr);
387 else if (strcmp(name, "bitwise") == 0)
388 nft_parse_bitwise(&ctx, expr);
389 else if (strcmp(name, "cmp") == 0)
390 nft_parse_cmp(&ctx, expr);
391 else if (strcmp(name, "immediate") == 0)
392 nft_parse_immediate(&ctx, expr);
393 else if (strcmp(name, "match") == 0)
394 nft_parse_match(&ctx, expr);
395 else if (strcmp(name, "target") == 0)
396 nft_parse_target(&ctx, expr);
397
398 expr = nftnl_expr_iter_next(iter);
399 }
400
401 nftnl_expr_iter_destroy(iter);
402
403 if (cs->jumpto != NULL)
404 return;
405
406 if (cs->target != NULL && cs->target->name != NULL)
407 cs->target = xtables_find_target(cs->target->name, XTF_TRY_LOAD);
408 else
409 cs->jumpto = "CONTINUE";
410 }
411
print_iface(const char * iface)412 static void print_iface(const char *iface)
413 {
414 char *c;
415
416 if ((c = strchr(iface, IF_WILDCARD)))
417 *c = '+';
418 printf("%s ", iface);
419 if (c)
420 *c = IF_WILDCARD;
421 }
422
nft_bridge_print_table_header(const char * tablename)423 static void nft_bridge_print_table_header(const char *tablename)
424 {
425 printf("Bridge table: %s\n\n", tablename);
426 }
427
nft_bridge_print_header(unsigned int format,const char * chain,const char * pol,const struct xt_counters * counters,bool basechain,uint32_t refs)428 static void nft_bridge_print_header(unsigned int format, const char *chain,
429 const char *pol,
430 const struct xt_counters *counters,
431 bool basechain, uint32_t refs)
432 {
433 printf("Bridge chain: %s, entries: %u, policy: %s\n",
434 chain, refs, basechain ? pol : "RETURN");
435 }
436
nft_bridge_print_firewall(struct nftnl_rule * r,unsigned int num,unsigned int format)437 static void nft_bridge_print_firewall(struct nftnl_rule *r, unsigned int num,
438 unsigned int format)
439 {
440 struct xtables_match *matchp;
441 struct xtables_target *watcherp;
442 struct ebt_match *m;
443 struct ebtables_command_state cs = {};
444 char *addr;
445
446 nft_rule_to_ebtables_command_state(r, &cs);
447
448 if (format & FMT_LINENUMBERS)
449 printf("%d ", num);
450
451 /* Dont print anything about the protocol if no protocol was
452 * specified, obviously this means any protocol will do. */
453 if (cs.fw.ethproto != 0) {
454 printf("-p ");
455 if (cs.fw.invflags & EBT_IPROTO)
456 printf("! ");
457 if (cs.fw.bitmask & EBT_802_3)
458 printf("Length ");
459 else {
460 struct ethertypeent *ent;
461
462 ent = getethertypebynumber(ntohs(cs.fw.ethproto));
463 if (!ent)
464 printf("0x%x ", ntohs(cs.fw.ethproto));
465 else
466 printf("%s ", ent->e_name);
467 }
468 }
469
470 addr = ether_ntoa((struct ether_addr *) cs.fw.sourcemac);
471 if (strcmp(addr, "0:0:0:0:0:0") != 0) {
472 printf("-s ");
473 if (cs.fw.invflags & EBT_ISOURCE)
474 printf("! ");
475 ebt_print_mac_and_mask(cs.fw.sourcemac, cs.fw.sourcemsk);
476 printf(" ");
477 }
478
479 addr = ether_ntoa((struct ether_addr *) cs.fw.destmac);
480 if (strcmp(addr, "0:0:0:0:0:0") != 0) {
481 printf("-d ");
482 if (cs.fw.invflags & EBT_IDEST)
483 printf("! ");
484 ebt_print_mac_and_mask(cs.fw.destmac, cs.fw.destmsk);
485 printf(" ");
486 }
487
488 if (cs.fw.in[0] != '\0') {
489 printf("-i ");
490 if (cs.fw.invflags & EBT_IIN)
491 printf("! ");
492 print_iface(cs.fw.in);
493 }
494
495 if (cs.fw.logical_in[0] != '\0') {
496 printf("--logical-in ");
497 if (cs.fw.invflags & EBT_ILOGICALIN)
498 printf("! ");
499 print_iface(cs.fw.logical_in);
500 }
501
502 if (cs.fw.logical_out[0] != '\0') {
503 printf("--logical-out ");
504 if (cs.fw.invflags & EBT_ILOGICALOUT)
505 printf("! ");
506 print_iface(cs.fw.logical_out);
507 }
508
509 if (cs.fw.out[0] != '\0') {
510 printf("-o ");
511 if (cs.fw.invflags & EBT_IOUT)
512 printf("! ");
513 print_iface(cs.fw.out);
514 }
515
516 for (m = cs.match_list; m; m = m->next) {
517 if (m->ismatch) {
518 matchp = m->u.match;
519 if (matchp->print != NULL) {
520 matchp->print(&cs.fw, matchp->m,
521 format & FMT_NUMERIC);
522 }
523 } else {
524 watcherp = m->u.watcher;
525 if (watcherp->print != NULL) {
526 watcherp->print(&cs.fw, watcherp->t,
527 format & FMT_NUMERIC);
528 }
529 }
530 }
531
532 printf("-j ");
533
534 if (cs.jumpto != NULL)
535 printf("%s", cs.jumpto);
536 else if (cs.target != NULL && cs.target->print != NULL)
537 cs.target->print(&cs.fw, cs.target->t, format & FMT_NUMERIC);
538
539 if (!(format & FMT_NOCOUNTS))
540 printf(" , pcnt = %"PRIu64" -- bcnt = %"PRIu64"",
541 (uint64_t)cs.counters.pcnt, (uint64_t)cs.counters.bcnt);
542
543 if (!(format & FMT_NONEWLINE))
544 fputc('\n', stdout);
545
546 ebt_cs_clean(&cs);
547 }
548
nft_bridge_is_same(const void * data_a,const void * data_b)549 static bool nft_bridge_is_same(const void *data_a, const void *data_b)
550 {
551 const struct ebt_entry *a = data_a;
552 const struct ebt_entry *b = data_b;
553 int i;
554
555 if (a->ethproto != b->ethproto ||
556 /* FIXME: a->flags != b->flags || */
557 a->invflags != b->invflags) {
558 DEBUGP("different proto/flags/invflags\n");
559 return false;
560 }
561
562 for (i = 0; i < ETH_ALEN; i++) {
563 if (a->sourcemac[i] != b->sourcemac[i]) {
564 DEBUGP("different source mac %x, %x (%d)\n",
565 a->sourcemac[i] & 0xff, b->sourcemac[i] & 0xff, i);
566 return false;
567 }
568
569 if (a->destmac[i] != b->destmac[i]) {
570 DEBUGP("different destination mac %x, %x (%d)\n",
571 a->destmac[i] & 0xff, b->destmac[i] & 0xff, i);
572 return false;
573 }
574 }
575
576 for (i = 0; i < IFNAMSIZ; i++) {
577 if (a->logical_in[i] != b->logical_in[i]) {
578 DEBUGP("different logical iniface %x, %x (%d)\n",
579 a->logical_in[i] & 0xff, b->logical_in[i] & 0xff, i);
580 return false;
581 }
582
583 if (a->logical_out[i] != b->logical_out[i]) {
584 DEBUGP("different logical outiface %x, %x (%d)\n",
585 a->logical_out[i] & 0xff, b->logical_out[i] & 0xff, i);
586 return false;
587 }
588 }
589
590 return is_same_interfaces((char *)a->in,
591 (char *)a->out,
592 a->in_mask,
593 a->out_mask,
594 (char *)b->in,
595 (char *)b->out,
596 b->in_mask,
597 b->out_mask);
598 }
599
nft_bridge_rule_find(struct nft_family_ops * ops,struct nftnl_rule * r,void * data)600 static bool nft_bridge_rule_find(struct nft_family_ops *ops, struct nftnl_rule *r,
601 void *data)
602 {
603 struct ebtables_command_state *cs = data;
604 struct ebtables_command_state this = {};
605
606 nft_rule_to_ebtables_command_state(r, &this);
607
608 DEBUGP("comparing with... ");
609
610 if (!nft_bridge_is_same(cs, &this))
611 return false;
612
613 if (!compare_matches(cs->matches, this.matches)) {
614 DEBUGP("Different matches\n");
615 return false;
616 }
617
618 if (!compare_targets(cs->target, this.target)) {
619 DEBUGP("Different target\n");
620 return false;
621 }
622
623 if (cs->jumpto != NULL && strcmp(cs->jumpto, this.jumpto) != 0) {
624 DEBUGP("Different verdict\n");
625 return false;
626 }
627
628 return true;
629 }
630
631 struct nft_family_ops nft_family_ops_bridge = {
632 .add = nft_bridge_add,
633 .is_same = nft_bridge_is_same,
634 .print_payload = NULL,
635 .parse_meta = nft_bridge_parse_meta,
636 .parse_payload = nft_bridge_parse_payload,
637 .parse_immediate = nft_bridge_parse_immediate,
638 .parse_match = nft_bridge_parse_match,
639 .parse_target = nft_bridge_parse_target,
640 .print_table_header = nft_bridge_print_table_header,
641 .print_header = nft_bridge_print_header,
642 .print_firewall = nft_bridge_print_firewall,
643 .save_firewall = NULL,
644 .save_counters = NULL,
645 .post_parse = NULL,
646 .rule_find = nft_bridge_rule_find,
647 };
648