1 /*
2 * (C) 2014 by Giuseppe Longo <[email protected]>
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_meta_broute(struct nftnl_rule * r)68 static int add_meta_broute(struct nftnl_rule *r)
69 {
70 struct nftnl_expr *expr;
71
72 expr = nftnl_expr_alloc("immediate");
73 if (expr == NULL)
74 return -1;
75
76 nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_DREG, NFT_REG32_01);
77 nftnl_expr_set_u8(expr, NFTNL_EXPR_IMM_DATA, 1);
78 nftnl_rule_add_expr(r, expr);
79
80 expr = nftnl_expr_alloc("meta");
81 if (expr == NULL)
82 return -1;
83 nftnl_expr_set_u32(expr, NFTNL_EXPR_META_KEY, NFT_META_BRI_BROUTE);
84 nftnl_expr_set_u32(expr, NFTNL_EXPR_META_SREG, NFT_REG32_01);
85
86 nftnl_rule_add_expr(r, expr);
87 return 0;
88 }
89
_add_action(struct nftnl_rule * r,struct iptables_command_state * cs)90 static int _add_action(struct nftnl_rule *r, struct iptables_command_state *cs)
91 {
92 const char *table = nftnl_rule_get_str(r, NFTNL_RULE_TABLE);
93
94 if (cs->target &&
95 table && strcmp(table, "broute") == 0) {
96 if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0) {
97 int ret = add_meta_broute(r);
98
99 if (ret)
100 return ret;
101
102 cs->jumpto = "ACCEPT";
103 }
104 }
105
106 return add_action(r, cs, false);
107 }
108
109 static int
nft_bridge_add_match(struct nft_handle * h,const struct ebt_entry * fw,struct nft_rule_ctx * ctx,struct nftnl_rule * r,struct xt_entry_match * m)110 nft_bridge_add_match(struct nft_handle *h, const struct ebt_entry *fw,
111 struct nft_rule_ctx *ctx, struct nftnl_rule *r,
112 struct xt_entry_match *m)
113 {
114 if (!strcmp(m->u.user.name, "802_3") && !(fw->bitmask & EBT_802_3))
115 xtables_error(PARAMETER_PROBLEM,
116 "For 802.3 DSAP/SSAP filtering the protocol must be LENGTH");
117
118 if (!strcmp(m->u.user.name, "ip") && fw->ethproto != htons(ETH_P_IP))
119 xtables_error(PARAMETER_PROBLEM,
120 "For IP filtering the protocol must be specified as IPv4.");
121
122 if (!strcmp(m->u.user.name, "ip6") && fw->ethproto != htons(ETH_P_IPV6))
123 xtables_error(PARAMETER_PROBLEM,
124 "For IPv6 filtering the protocol must be specified as IPv6.");
125
126 return add_match(h, ctx, r, m);
127 }
128
nft_bridge_add(struct nft_handle * h,struct nft_rule_ctx * ctx,struct nftnl_rule * r,struct iptables_command_state * cs)129 static int nft_bridge_add(struct nft_handle *h, struct nft_rule_ctx *ctx,
130 struct nftnl_rule *r,
131 struct iptables_command_state *cs)
132 {
133 struct ebt_match *iter;
134 struct ebt_entry *fw = &cs->eb;
135 uint32_t op;
136
137 if (fw->bitmask & EBT_ISOURCE) {
138 op = nft_invflags2cmp(fw->invflags, EBT_ISOURCE);
139 add_addr(h, r, NFT_PAYLOAD_LL_HEADER,
140 offsetof(struct ethhdr, h_source),
141 fw->sourcemac, fw->sourcemsk, ETH_ALEN, op);
142 }
143
144 if (fw->bitmask & EBT_IDEST) {
145 op = nft_invflags2cmp(fw->invflags, EBT_IDEST);
146 add_addr(h, r, NFT_PAYLOAD_LL_HEADER,
147 offsetof(struct ethhdr, h_dest),
148 fw->destmac, fw->destmsk, ETH_ALEN, op);
149 }
150
151 if (fw->in[0] != '\0') {
152 op = nft_invflags2cmp(fw->invflags, EBT_IIN);
153 add_iface(h, r, fw->in, NFT_META_IIFNAME, op);
154 }
155
156 if (fw->out[0] != '\0') {
157 op = nft_invflags2cmp(fw->invflags, EBT_IOUT);
158 add_iface(h, r, fw->out, NFT_META_OIFNAME, op);
159 }
160
161 if (fw->logical_in[0] != '\0') {
162 op = nft_invflags2cmp(fw->invflags, EBT_ILOGICALIN);
163 add_iface(h, r, fw->logical_in, NFT_META_BRI_IIFNAME, op);
164 }
165
166 if (fw->logical_out[0] != '\0') {
167 op = nft_invflags2cmp(fw->invflags, EBT_ILOGICALOUT);
168 add_iface(h, r, fw->logical_out, NFT_META_BRI_OIFNAME, op);
169 }
170
171 if ((fw->bitmask & EBT_NOPROTO) == 0) {
172 uint16_t ethproto = fw->ethproto;
173 uint8_t reg;
174
175 op = nft_invflags2cmp(fw->invflags, EBT_IPROTO);
176 add_payload(h, r, offsetof(struct ethhdr, h_proto), 2,
177 NFT_PAYLOAD_LL_HEADER, ®);
178
179 if (fw->bitmask & EBT_802_3) {
180 op = (op == NFT_CMP_EQ ? NFT_CMP_LT : NFT_CMP_GTE);
181 ethproto = htons(0x0600);
182 }
183
184 add_cmp_u16(r, ethproto, op, reg);
185 }
186
187 add_compat(r, fw->ethproto, fw->invflags & EBT_IPROTO);
188
189 for (iter = cs->match_list; iter; iter = iter->next) {
190 if (iter->ismatch) {
191 if (nft_bridge_add_match(h, fw, ctx, r, iter->u.match->m))
192 break;
193 } else {
194 if (add_target(r, iter->u.watcher->t))
195 break;
196 }
197 }
198
199 if (add_counters(r, cs->counters.pcnt, cs->counters.bcnt) < 0)
200 return -1;
201
202 return _add_action(r, cs);
203 }
204
nft_rule_to_ebtables_command_state(struct nft_handle * h,const struct nftnl_rule * r,struct iptables_command_state * cs)205 static bool nft_rule_to_ebtables_command_state(struct nft_handle *h,
206 const struct nftnl_rule *r,
207 struct iptables_command_state *cs)
208 {
209 cs->eb.bitmask = EBT_NOPROTO;
210 return nft_rule_to_iptables_command_state(h, r, cs);
211 }
212
print_iface(const char * option,const char * name,bool invert)213 static void print_iface(const char *option, const char *name, bool invert)
214 {
215 if (*name)
216 printf("%s%s %s ", option, invert ? " !" : "", name);
217 }
218
nft_bridge_print_table_header(const char * tablename)219 static void nft_bridge_print_table_header(const char *tablename)
220 {
221 printf("Bridge table: %s\n\n", tablename);
222 }
223
nft_bridge_print_header(unsigned int format,const char * chain,const char * pol,const struct xt_counters * counters,int refs,uint32_t entries)224 static void nft_bridge_print_header(unsigned int format, const char *chain,
225 const char *pol,
226 const struct xt_counters *counters,
227 int refs, uint32_t entries)
228 {
229 printf("Bridge chain: %s, entries: %u, policy: %s\n",
230 chain, entries, pol ?: "RETURN");
231 }
232
print_matches_and_watchers(const struct iptables_command_state * cs,unsigned int format)233 static void print_matches_and_watchers(const struct iptables_command_state *cs,
234 unsigned int format)
235 {
236 struct xtables_target *watcherp;
237 struct xtables_match *matchp;
238 struct ebt_match *m;
239
240 for (m = cs->match_list; m; m = m->next) {
241 if (m->ismatch) {
242 matchp = m->u.match;
243 if (matchp->print != NULL) {
244 matchp->print(&cs->eb, matchp->m,
245 format & FMT_NUMERIC);
246 }
247 } else {
248 watcherp = m->u.watcher;
249 if (watcherp->print != NULL) {
250 watcherp->print(&cs->eb, watcherp->t,
251 format & FMT_NUMERIC);
252 }
253 }
254 }
255 }
256
print_mac(char option,const unsigned char * mac,const unsigned char * mask,bool invert)257 static void print_mac(char option, const unsigned char *mac,
258 const unsigned char *mask,
259 bool invert)
260 {
261 printf("-%c ", option);
262 if (invert)
263 printf("! ");
264 ebt_print_mac_and_mask(mac, mask);
265 printf(" ");
266 }
267
268
print_protocol(uint16_t ethproto,bool invert,unsigned int bitmask)269 static void print_protocol(uint16_t ethproto, bool invert, unsigned int bitmask)
270 {
271 struct xt_ethertypeent *ent;
272
273 /* Dont print anything about the protocol if no protocol was
274 * specified, obviously this means any protocol will do. */
275 if (bitmask & EBT_NOPROTO)
276 return;
277
278 printf("-p ");
279 if (invert)
280 printf("! ");
281
282 if (bitmask & EBT_802_3) {
283 printf("Length ");
284 return;
285 }
286
287 ent = xtables_getethertypebynumber(ntohs(ethproto));
288 if (!ent)
289 printf("0x%x ", ntohs(ethproto));
290 else
291 printf("%s ", ent->e_name);
292 }
293
__nft_bridge_save_rule(const struct iptables_command_state * cs,unsigned int format)294 static void __nft_bridge_save_rule(const struct iptables_command_state *cs,
295 unsigned int format)
296 {
297 if (!(cs->eb.bitmask & EBT_NOPROTO))
298 print_protocol(cs->eb.ethproto, cs->eb.invflags & EBT_IPROTO,
299 cs->eb.bitmask);
300 if (cs->eb.bitmask & EBT_ISOURCE)
301 print_mac('s', cs->eb.sourcemac, cs->eb.sourcemsk,
302 cs->eb.invflags & EBT_ISOURCE);
303 if (cs->eb.bitmask & EBT_IDEST)
304 print_mac('d', cs->eb.destmac, cs->eb.destmsk,
305 cs->eb.invflags & EBT_IDEST);
306
307 print_iface("-i", cs->eb.in, cs->eb.invflags & EBT_IIN);
308 print_iface("--logical-in", cs->eb.logical_in,
309 cs->eb.invflags & EBT_ILOGICALIN);
310 print_iface("-o", cs->eb.out, cs->eb.invflags & EBT_IOUT);
311 print_iface("--logical-out", cs->eb.logical_out,
312 cs->eb.invflags & EBT_ILOGICALOUT);
313
314 print_matches_and_watchers(cs, format);
315
316 printf("-j ");
317
318 if (cs->jumpto != NULL) {
319 if (strcmp(cs->jumpto, "") != 0)
320 printf("%s", cs->jumpto);
321 else
322 printf("CONTINUE");
323 }
324 if (cs->target != NULL && cs->target->print != NULL) {
325 printf(" ");
326 cs->target->print(&cs->fw, cs->target->t, format & FMT_NUMERIC);
327 }
328
329 if ((format & (FMT_NOCOUNTS | FMT_C_COUNTS)) == FMT_C_COUNTS) {
330 if (format & FMT_EBT_SAVE)
331 printf(" -c %"PRIu64" %"PRIu64"",
332 (uint64_t)cs->counters.pcnt,
333 (uint64_t)cs->counters.bcnt);
334 else
335 printf(" , pcnt = %"PRIu64" -- bcnt = %"PRIu64"",
336 (uint64_t)cs->counters.pcnt,
337 (uint64_t)cs->counters.bcnt);
338 }
339
340 if (!(format & FMT_NONEWLINE))
341 fputc('\n', stdout);
342 }
343
nft_bridge_save_rule(const struct iptables_command_state * cs,unsigned int format)344 static void nft_bridge_save_rule(const struct iptables_command_state *cs,
345 unsigned int format)
346 {
347 printf(" ");
348 __nft_bridge_save_rule(cs, format);
349 }
350
nft_bridge_print_rule(struct nft_handle * h,struct nftnl_rule * r,unsigned int num,unsigned int format)351 static void nft_bridge_print_rule(struct nft_handle *h, struct nftnl_rule *r,
352 unsigned int num, unsigned int format)
353 {
354 struct iptables_command_state cs = {};
355
356 if (format & FMT_LINENUMBERS)
357 printf("%d ", num);
358
359 nft_rule_to_ebtables_command_state(h, r, &cs);
360 __nft_bridge_save_rule(&cs, format);
361 ebt_cs_clean(&cs);
362 }
363
nft_bridge_save_chain(const struct nftnl_chain * c,const char * policy)364 static void nft_bridge_save_chain(const struct nftnl_chain *c,
365 const char *policy)
366 {
367 const char *chain = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
368
369 printf(":%s %s\n", chain, policy ?: "ACCEPT");
370 }
371
nft_bridge_is_same(const struct iptables_command_state * cs_a,const struct iptables_command_state * cs_b)372 static bool nft_bridge_is_same(const struct iptables_command_state *cs_a,
373 const struct iptables_command_state *cs_b)
374 {
375 const struct ebt_entry *a = &cs_a->eb;
376 const struct ebt_entry *b = &cs_b->eb;
377 int i;
378
379 if (a->ethproto != b->ethproto ||
380 /* FIXME: a->flags != b->flags || */
381 a->invflags != b->invflags) {
382 DEBUGP("different proto/flags/invflags\n");
383 return false;
384 }
385
386 for (i = 0; i < ETH_ALEN; i++) {
387 if (a->sourcemac[i] != b->sourcemac[i]) {
388 DEBUGP("different source mac %x, %x (%d)\n",
389 a->sourcemac[i] & 0xff, b->sourcemac[i] & 0xff, i);
390 return false;
391 }
392
393 if (a->destmac[i] != b->destmac[i]) {
394 DEBUGP("different destination mac %x, %x (%d)\n",
395 a->destmac[i] & 0xff, b->destmac[i] & 0xff, i);
396 return false;
397 }
398 }
399
400 for (i = 0; i < IFNAMSIZ; i++) {
401 if (a->logical_in[i] != b->logical_in[i]) {
402 DEBUGP("different logical iniface %x, %x (%d)\n",
403 a->logical_in[i] & 0xff, b->logical_in[i] & 0xff, i);
404 return false;
405 }
406
407 if (a->logical_out[i] != b->logical_out[i]) {
408 DEBUGP("different logical outiface %x, %x (%d)\n",
409 a->logical_out[i] & 0xff, b->logical_out[i] & 0xff, i);
410 return false;
411 }
412 }
413
414 return strcmp(a->in, b->in) == 0 && strcmp(a->out, b->out) == 0;
415 }
416
xlate_ebmatches(const struct iptables_command_state * cs,struct xt_xlate * xl)417 static int xlate_ebmatches(const struct iptables_command_state *cs, struct xt_xlate *xl)
418 {
419 int ret = 1, numeric = cs->options & OPT_NUMERIC;
420 struct ebt_match *m;
421
422 for (m = cs->match_list; m; m = m->next) {
423 if (m->ismatch) {
424 struct xtables_match *matchp = m->u.match;
425 struct xt_xlate_mt_params mt_params = {
426 .ip = (const void *)&cs->eb,
427 .numeric = numeric,
428 .match = matchp->m,
429 };
430
431 if (!matchp->xlate)
432 return 0;
433
434 ret = matchp->xlate(xl, &mt_params);
435 } else {
436 struct xtables_target *watcherp = m->u.watcher;
437 struct xt_xlate_tg_params wt_params = {
438 .ip = (const void *)&cs->eb,
439 .numeric = numeric,
440 .target = watcherp->t,
441 };
442
443 if (!watcherp->xlate)
444 return 0;
445
446 ret = watcherp->xlate(xl, &wt_params);
447 }
448
449 if (!ret)
450 break;
451 }
452
453 return ret;
454 }
455
xlate_ebaction(const struct iptables_command_state * cs,struct xt_xlate * xl)456 static int xlate_ebaction(const struct iptables_command_state *cs, struct xt_xlate *xl)
457 {
458 int ret = 1, numeric = cs->options & OPT_NUMERIC;
459
460 /* If no target at all, add nothing (default to continue) */
461 if (cs->target != NULL) {
462 /* Standard target? */
463 if (strcmp(cs->jumpto, XTC_LABEL_ACCEPT) == 0)
464 xt_xlate_add(xl, " accept");
465 else if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0)
466 xt_xlate_add(xl, " drop");
467 else if (strcmp(cs->jumpto, XTC_LABEL_RETURN) == 0)
468 xt_xlate_add(xl, " return");
469 else if (cs->target->xlate) {
470 struct xt_xlate_tg_params params = {
471 .ip = (const void *)&cs->eb,
472 .target = cs->target->t,
473 .numeric = numeric,
474 };
475 ret = cs->target->xlate(xl, ¶ms);
476 }
477 else
478 return 0;
479 } else if (cs->jumpto == NULL) {
480 } else if (strlen(cs->jumpto) > 0)
481 xt_xlate_add(xl, " jump %s", cs->jumpto);
482
483 return ret;
484 }
485
xlate_mac(struct xt_xlate * xl,const unsigned char * mac)486 static void xlate_mac(struct xt_xlate *xl, const unsigned char *mac)
487 {
488 int i;
489
490 xt_xlate_add(xl, "%02x", mac[0]);
491
492 for (i=1; i < ETH_ALEN; i++)
493 xt_xlate_add(xl, ":%02x", mac[i]);
494 }
495
nft_bridge_xlate_mac(struct xt_xlate * xl,const char * type,bool invert,const unsigned char * mac,const unsigned char * mask)496 static void nft_bridge_xlate_mac(struct xt_xlate *xl, const char *type, bool invert,
497 const unsigned char *mac, const unsigned char *mask)
498 {
499 char one_msk[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
500
501 xt_xlate_add(xl, "ether %s %s", type, invert ? "!= " : "");
502
503 if (memcmp(mask, one_msk, ETH_ALEN)) {
504 int i;
505 xt_xlate_add(xl, "and");
506
507 xlate_mac(xl, mask);
508
509 xt_xlate_add(xl, " == %02x", mac[0] & mask[0]);
510 for (i=1; i < ETH_ALEN; i++)
511 xt_xlate_add(xl, ":%02x", mac[i] & mask[i]);
512 } else {
513 xlate_mac(xl, mac);
514 }
515 }
516
nft_bridge_xlate(const struct iptables_command_state * cs,struct xt_xlate * xl)517 static int nft_bridge_xlate(const struct iptables_command_state *cs,
518 struct xt_xlate *xl)
519 {
520 int ret;
521
522 xlate_ifname(xl, "iifname", cs->eb.in,
523 cs->eb.invflags & EBT_IIN);
524 xlate_ifname(xl, "meta ibrname", cs->eb.logical_in,
525 cs->eb.invflags & EBT_ILOGICALIN);
526 xlate_ifname(xl, "oifname", cs->eb.out,
527 cs->eb.invflags & EBT_IOUT);
528 xlate_ifname(xl, "meta obrname", cs->eb.logical_out,
529 cs->eb.invflags & EBT_ILOGICALOUT);
530
531 if (cs->eb.bitmask & EBT_802_3) {
532 xt_xlate_add(xl, "ether type %s 0x0600 ",
533 cs->eb.invflags & EBT_IPROTO ? ">=" : "<");
534 } else if ((cs->eb.bitmask & EBT_NOPROTO) == 0) {
535 const char *implicit = NULL;
536
537 switch (ntohs(cs->eb.ethproto)) {
538 case ETH_P_IP:
539 implicit = "ip";
540 break;
541 case ETH_P_IPV6:
542 implicit = "ip6";
543 break;
544 case ETH_P_8021Q:
545 implicit = "vlan";
546 break;
547 default:
548 break;
549 }
550
551 if (!implicit || !xlate_find_match(cs, implicit))
552 xt_xlate_add(xl, "ether type %s0x%x ",
553 cs->eb.invflags & EBT_IPROTO ? "!= " : "",
554 ntohs(cs->eb.ethproto));
555 }
556
557 if (cs->eb.bitmask & EBT_ISOURCE)
558 nft_bridge_xlate_mac(xl, "saddr", cs->eb.invflags & EBT_ISOURCE,
559 cs->eb.sourcemac, cs->eb.sourcemsk);
560 if (cs->eb.bitmask & EBT_IDEST)
561 nft_bridge_xlate_mac(xl, "daddr", cs->eb.invflags & EBT_IDEST,
562 cs->eb.destmac, cs->eb.destmsk);
563 ret = xlate_ebmatches(cs, xl);
564 if (ret == 0)
565 return ret;
566
567 /* Always add counters per rule, as in ebtables */
568 xt_xlate_add(xl, "counter");
569 ret = xlate_ebaction(cs, xl);
570
571 return ret;
572 }
573
574 struct nft_family_ops nft_family_ops_bridge = {
575 .add = nft_bridge_add,
576 .is_same = nft_bridge_is_same,
577 .print_payload = NULL,
578 .rule_parse = &nft_ruleparse_ops_bridge,
579 .print_table_header = nft_bridge_print_table_header,
580 .print_header = nft_bridge_print_header,
581 .print_rule = nft_bridge_print_rule,
582 .save_rule = nft_bridge_save_rule,
583 .save_chain = nft_bridge_save_chain,
584 .rule_to_cs = nft_rule_to_ebtables_command_state,
585 .clear_cs = ebt_cs_clean,
586 .xlate = nft_bridge_xlate,
587 };
588