xref: /aosp_15_r20/external/iptables/iptables/nft-bridge.c (revision a71a954618bbadd4a345637e5edcf36eec826889)
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, &reg);
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, &params);
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