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