xref: /aosp_15_r20/external/iptables/iptables/nft-ruleparse-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 <stddef.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <netdb.h>
15 #include <net/if.h>
16 //#include <net/if_arp.h>
17 #include <netinet/if_ether.h>
18 
19 #include <libnftnl/rule.h>
20 #include <libnftnl/expr.h>
21 #include <libnftnl/set.h>
22 
23 #include <xtables.h>
24 
25 #include "nft.h" /* just for nft_set_batch_lookup_byid? */
26 #include "nft-bridge.h"
27 #include "nft-cache.h"
28 #include "nft-shared.h"
29 #include "nft-ruleparse.h"
30 #include "xshared.h"
31 
nft_bridge_parse_meta(struct nft_xt_ctx * ctx,const struct nft_xt_ctx_reg * reg,struct nftnl_expr * e,struct iptables_command_state * cs)32 static void nft_bridge_parse_meta(struct nft_xt_ctx *ctx,
33 				  const struct nft_xt_ctx_reg *reg,
34 				  struct nftnl_expr *e,
35 				  struct iptables_command_state *cs)
36 {
37 	struct ebt_entry *fw = &cs->eb;
38 	uint8_t invflags = 0;
39 	char iifname[IFNAMSIZ] = {}, oifname[IFNAMSIZ] = {};
40 
41 	switch (reg->meta_dreg.key) {
42 	case NFT_META_PROTOCOL:
43 		return;
44 	}
45 
46 	if (parse_meta(ctx, e, reg->meta_dreg.key, iifname, NULL, oifname, NULL, &invflags) < 0) {
47 		ctx->errmsg = "unknown meta key";
48 		return;
49 	}
50 
51 	switch (reg->meta_dreg.key) {
52 	case NFT_META_BRI_IIFNAME:
53 		if (invflags & IPT_INV_VIA_IN)
54 			cs->eb.invflags |= EBT_ILOGICALIN;
55 		snprintf(fw->logical_in, sizeof(fw->logical_in), "%s", iifname);
56 		break;
57 	case NFT_META_IIFNAME:
58 		if (invflags & IPT_INV_VIA_IN)
59 			cs->eb.invflags |= EBT_IIN;
60 		snprintf(fw->in, sizeof(fw->in), "%s", iifname);
61 		break;
62 	case NFT_META_BRI_OIFNAME:
63 		if (invflags & IPT_INV_VIA_OUT)
64 			cs->eb.invflags |= EBT_ILOGICALOUT;
65 		snprintf(fw->logical_out, sizeof(fw->logical_out), "%s", oifname);
66 		break;
67 	case NFT_META_OIFNAME:
68 		if (invflags & IPT_INV_VIA_OUT)
69 			cs->eb.invflags |= EBT_IOUT;
70 		snprintf(fw->out, sizeof(fw->out), "%s", oifname);
71 		break;
72 	default:
73 		ctx->errmsg = "unknown bridge meta key";
74 		break;
75 	}
76 }
77 
nft_bridge_parse_payload(struct nft_xt_ctx * ctx,const struct nft_xt_ctx_reg * reg,struct nftnl_expr * e,struct iptables_command_state * cs)78 static void nft_bridge_parse_payload(struct nft_xt_ctx *ctx,
79 				     const struct nft_xt_ctx_reg *reg,
80 				     struct nftnl_expr *e,
81 				     struct iptables_command_state *cs)
82 {
83 	struct ebt_entry *fw = &cs->eb;
84 	unsigned char addr[ETH_ALEN];
85 	unsigned short int ethproto;
86 	uint8_t op;
87 	bool inv;
88 	int i;
89 
90 	switch (reg->payload.offset) {
91 	case offsetof(struct ethhdr, h_dest):
92 		get_cmp_data(e, addr, sizeof(addr), &inv);
93 		for (i = 0; i < ETH_ALEN; i++)
94 			fw->destmac[i] = addr[i];
95 		if (inv)
96 			fw->invflags |= EBT_IDEST;
97 
98 		if (reg->bitwise.set)
99                         memcpy(fw->destmsk, reg->bitwise.mask, ETH_ALEN);
100                 else
101 			memset(&fw->destmsk, 0xff,
102 			       min(reg->payload.len, ETH_ALEN));
103 		fw->bitmask |= EBT_IDEST;
104 		break;
105 	case offsetof(struct ethhdr, h_source):
106 		get_cmp_data(e, addr, sizeof(addr), &inv);
107 		for (i = 0; i < ETH_ALEN; i++)
108 			fw->sourcemac[i] = addr[i];
109 		if (inv)
110 			fw->invflags |= EBT_ISOURCE;
111 		if (reg->bitwise.set)
112                         memcpy(fw->sourcemsk, reg->bitwise.mask, ETH_ALEN);
113                 else
114 			memset(&fw->sourcemsk, 0xff,
115 			       min(reg->payload.len, ETH_ALEN));
116 		fw->bitmask |= EBT_ISOURCE;
117 		break;
118 	case offsetof(struct ethhdr, h_proto):
119 		__get_cmp_data(e, &ethproto, sizeof(ethproto), &op);
120 		if (ethproto == htons(0x0600)) {
121 			fw->bitmask |= EBT_802_3;
122 			inv = (op == NFT_CMP_GTE);
123 		} else {
124 			fw->ethproto = ethproto;
125 			inv = (op == NFT_CMP_NEQ);
126 		}
127 		if (inv)
128 			fw->invflags |= EBT_IPROTO;
129 		fw->bitmask &= ~EBT_NOPROTO;
130 		break;
131 	default:
132 		DEBUGP("unknown payload offset %d\n", reg->payload.offset);
133 		ctx->errmsg = "unknown payload offset";
134 		break;
135 	}
136 }
137 
138 /* return 0 if saddr, 1 if daddr, -1 on error */
139 static int
lookup_check_ether_payload(uint32_t base,uint32_t offset,uint32_t len)140 lookup_check_ether_payload(uint32_t base, uint32_t offset, uint32_t len)
141 {
142 	if (base != 0 || len != ETH_ALEN)
143 		return -1;
144 
145 	switch (offset) {
146 	case offsetof(struct ether_header, ether_dhost):
147 		return 1;
148 	case offsetof(struct ether_header, ether_shost):
149 		return 0;
150 	default:
151 		return -1;
152 	}
153 }
154 
155 /* return 0 if saddr, 1 if daddr, -1 on error */
156 static int
lookup_check_iphdr_payload(uint32_t base,uint32_t offset,uint32_t len)157 lookup_check_iphdr_payload(uint32_t base, uint32_t offset, uint32_t len)
158 {
159 	if (base != 1 || len != 4)
160 		return -1;
161 
162 	switch (offset) {
163 	case offsetof(struct iphdr, daddr):
164 		return 1;
165 	case offsetof(struct iphdr, saddr):
166 		return 0;
167 	default:
168 		return -1;
169 	}
170 }
171 
172 /* Make sure previous payload expression(s) is/are consistent and extract if
173  * matching on source or destination address and if matching on MAC and IP or
174  * only MAC address. */
lookup_analyze_payloads(struct nft_xt_ctx * ctx,enum nft_registers sreg,uint32_t key_len,bool * dst,bool * ip)175 static int lookup_analyze_payloads(struct nft_xt_ctx *ctx,
176 				   enum nft_registers sreg,
177 				   uint32_t key_len,
178 				   bool *dst, bool *ip)
179 {
180 	const struct nft_xt_ctx_reg *reg;
181 	int val, val2 = -1;
182 
183 	reg = nft_xt_ctx_get_sreg(ctx, sreg);
184 	if (!reg)
185 		return -1;
186 
187 	if (reg->type != NFT_XT_REG_PAYLOAD) {
188 		ctx->errmsg = "lookup reg is not payload type";
189 		return -1;
190 	}
191 
192 	switch (key_len) {
193 	case 12: /* ether + ipv4addr */
194 		val = lookup_check_ether_payload(reg->payload.base,
195 						 reg->payload.offset,
196 						 reg->payload.len);
197 		if (val < 0) {
198 			DEBUGP("unknown payload base/offset/len %d/%d/%d\n",
199 			       reg->payload.base, reg->payload.offset,
200 			       reg->payload.len);
201 			return -1;
202 		}
203 
204 		sreg = nft_get_next_reg(sreg, ETH_ALEN);
205 
206 		reg = nft_xt_ctx_get_sreg(ctx, sreg);
207 		if (!reg) {
208 			ctx->errmsg = "next lookup register is invalid";
209 			return -1;
210 		}
211 
212 		if (reg->type != NFT_XT_REG_PAYLOAD) {
213 			ctx->errmsg = "next lookup reg is not payload type";
214 			return -1;
215 		}
216 
217 		val2 = lookup_check_iphdr_payload(reg->payload.base,
218 						  reg->payload.offset,
219 						  reg->payload.len);
220 		if (val2 < 0) {
221 			DEBUGP("unknown payload base/offset/len %d/%d/%d\n",
222 			       reg->payload.base, reg->payload.offset,
223 			       reg->payload.len);
224 			return -1;
225 		} else if (val != val2) {
226 			DEBUGP("mismatching payload match offsets\n");
227 			return -1;
228 		}
229 		break;
230 	case 6: /* ether */
231 		val = lookup_check_ether_payload(reg->payload.base,
232 						 reg->payload.offset,
233 						 reg->payload.len);
234 		if (val < 0) {
235 			DEBUGP("unknown payload base/offset/len %d/%d/%d\n",
236 			       reg->payload.base, reg->payload.offset,
237 			       reg->payload.len);
238 			return -1;
239 		}
240 		break;
241 	default:
242 		ctx->errmsg = "unsupported lookup key length";
243 		return -1;
244 	}
245 
246 	if (dst)
247 		*dst = (val == 1);
248 	if (ip)
249 		*ip = (val2 != -1);
250 	return 0;
251 }
252 
set_elems_to_among_pairs(struct nft_among_pair * pairs,const struct nftnl_set * s,int cnt)253 static int set_elems_to_among_pairs(struct nft_among_pair *pairs,
254 				    const struct nftnl_set *s, int cnt)
255 {
256 	struct nftnl_set_elems_iter *iter = nftnl_set_elems_iter_create(s);
257 	struct nftnl_set_elem *elem;
258 	size_t tmpcnt = 0;
259 	const void *data;
260 	uint32_t datalen;
261 	int ret = -1;
262 
263 	if (!iter) {
264 		fprintf(stderr, "BUG: set elems iter allocation failed\n");
265 		return ret;
266 	}
267 
268 	while ((elem = nftnl_set_elems_iter_next(iter))) {
269 		data = nftnl_set_elem_get(elem, NFTNL_SET_ELEM_KEY, &datalen);
270 		if (!data) {
271 			fprintf(stderr, "BUG: set elem without key\n");
272 			goto err;
273 		}
274 		if (datalen > sizeof(*pairs)) {
275 			fprintf(stderr, "BUG: overlong set elem\n");
276 			goto err;
277 		}
278 		nft_among_insert_pair(pairs, &tmpcnt, data);
279 	}
280 	ret = 0;
281 err:
282 	nftnl_set_elems_iter_destroy(iter);
283 	return ret;
284 }
285 
set_from_lookup_expr(struct nft_xt_ctx * ctx,const struct nftnl_expr * e)286 static struct nftnl_set *set_from_lookup_expr(struct nft_xt_ctx *ctx,
287 					      const struct nftnl_expr *e)
288 {
289 	const char *set_name = nftnl_expr_get_str(e, NFTNL_EXPR_LOOKUP_SET);
290 	uint32_t set_id = nftnl_expr_get_u32(e, NFTNL_EXPR_LOOKUP_SET_ID);
291 	struct nftnl_set_list *slist;
292 	struct nftnl_set *set;
293 
294 	slist = nft_set_list_get(ctx->h, ctx->table, set_name);
295 	if (slist) {
296 		set = nftnl_set_list_lookup_byname(slist, set_name);
297 		if (set)
298 			return set;
299 
300 		set = nft_set_batch_lookup_byid(ctx->h, set_id);
301 		if (set)
302 			return set;
303 	}
304 
305 	return NULL;
306 }
307 
nft_bridge_parse_lookup(struct nft_xt_ctx * ctx,struct nftnl_expr * e)308 static void nft_bridge_parse_lookup(struct nft_xt_ctx *ctx,
309 				    struct nftnl_expr *e)
310 {
311 	struct xtables_match *match = NULL;
312 	struct nft_among_data *among_data;
313 	bool is_dst, have_ip, inv;
314 	struct ebt_match *ematch;
315 	struct nftnl_set *s;
316 	size_t poff, size;
317 	uint32_t cnt;
318 
319 	s = set_from_lookup_expr(ctx, e);
320 	if (!s)
321 		xtables_error(OTHER_PROBLEM,
322 			      "BUG: lookup expression references unknown set");
323 
324 	if (lookup_analyze_payloads(ctx,
325 				    nftnl_expr_get_u32(e, NFTNL_EXPR_LOOKUP_SREG),
326 				    nftnl_set_get_u32(s, NFTNL_SET_KEY_LEN),
327 				    &is_dst, &have_ip))
328 		return;
329 
330 	cnt = nftnl_set_get_u32(s, NFTNL_SET_DESC_SIZE);
331 
332 	for (ematch = ctx->cs->match_list; ematch; ematch = ematch->next) {
333 		if (!ematch->ismatch || strcmp(ematch->u.match->name, "among"))
334 			continue;
335 
336 		match = ematch->u.match;
337 		among_data = (struct nft_among_data *)match->m->data;
338 
339 		size = cnt + among_data->src.cnt + among_data->dst.cnt;
340 		size *= sizeof(struct nft_among_pair);
341 
342 		size += XT_ALIGN(sizeof(struct xt_entry_match)) +
343 			sizeof(struct nft_among_data);
344 
345 		match->m = xtables_realloc(match->m, size);
346 		break;
347 	}
348 	if (!match) {
349 		match = xtables_find_match("among", XTF_TRY_LOAD,
350 					   &ctx->cs->matches);
351 
352 		size = cnt * sizeof(struct nft_among_pair);
353 		size += XT_ALIGN(sizeof(struct xt_entry_match)) +
354 			sizeof(struct nft_among_data);
355 
356 		match->m = xtables_calloc(1, size);
357 		strcpy(match->m->u.user.name, match->name);
358 		match->m->u.user.revision = match->revision;
359 		xs_init_match(match);
360 
361 		if (ctx->h->ops->rule_parse->match != NULL)
362 			ctx->h->ops->rule_parse->match(match, ctx->cs);
363 	}
364 	if (!match)
365 		return;
366 
367 	match->m->u.match_size = size;
368 
369 	inv = !!(nftnl_expr_get_u32(e, NFTNL_EXPR_LOOKUP_FLAGS) &
370 				    NFT_LOOKUP_F_INV);
371 
372 	among_data = (struct nft_among_data *)match->m->data;
373 	poff = nft_among_prepare_data(among_data, is_dst, cnt, inv, have_ip);
374 	if (set_elems_to_among_pairs(among_data->pairs + poff, s, cnt))
375 		xtables_error(OTHER_PROBLEM,
376 			      "ebtables among pair parsing failed");
377 }
378 
parse_watcher(void * object,struct ebt_match ** match_list,bool ismatch)379 static void parse_watcher(void *object, struct ebt_match **match_list,
380 			  bool ismatch)
381 {
382 	struct ebt_match *m = xtables_calloc(1, sizeof(struct ebt_match));
383 
384 	if (ismatch)
385 		m->u.match = object;
386 	else
387 		m->u.watcher = object;
388 
389 	m->ismatch = ismatch;
390 	if (*match_list == NULL)
391 		*match_list = m;
392 	else
393 		(*match_list)->next = m;
394 }
395 
nft_bridge_parse_match(struct xtables_match * m,struct iptables_command_state * cs)396 static void nft_bridge_parse_match(struct xtables_match *m,
397 				   struct iptables_command_state *cs)
398 {
399 	parse_watcher(m, &cs->match_list, true);
400 }
401 
nft_bridge_parse_target(struct xtables_target * t,struct iptables_command_state * cs)402 static void nft_bridge_parse_target(struct xtables_target *t,
403 				    struct iptables_command_state *cs)
404 {
405 	/* harcoded names :-( */
406 	if (strcmp(t->name, "log") == 0 ||
407 	    strcmp(t->name, "nflog") == 0) {
408 		parse_watcher(t, &cs->match_list, false);
409 		cs->jumpto = NULL;
410 		cs->target = NULL;
411 		return;
412 	}
413 }
414 
415 struct nft_ruleparse_ops nft_ruleparse_ops_bridge = {
416 	.meta		= nft_bridge_parse_meta,
417 	.payload	= nft_bridge_parse_payload,
418 	.lookup		= nft_bridge_parse_lookup,
419 	.match		= nft_bridge_parse_match,
420 	.target		= nft_bridge_parse_target,
421 };
422