• 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 
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, &ethproto, 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, &params);
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