• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &ethproto, 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