configure: Avoid addition assignment operators
[iptables-mirror.git] / iptables / nft-ruleparse-bridge.c
blobaee08b1396c1a31abd2a84e317eb16d2d3d17ccf
1 /*
2 * (C) 2014 by Giuseppe Longo <giuseppelng@gmail.com>
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 */
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>
19 #include <libnftnl/rule.h>
20 #include <libnftnl/expr.h>
21 #include <libnftnl/set.h>
23 #include <xtables.h>
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"
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)
37 struct ebt_entry *fw = &cs->eb;
38 uint8_t invflags = 0;
39 char iifname[IFNAMSIZ] = {}, oifname[IFNAMSIZ] = {};
41 switch (reg->meta_dreg.key) {
42 case NFT_META_PROTOCOL:
43 return;
46 if (parse_meta(ctx, e, reg->meta_dreg.key,
47 iifname, oifname, &invflags) < 0) {
48 ctx->errmsg = "unknown meta key";
49 return;
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;
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)
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;
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;
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);
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;
139 /* return 0 if saddr, 1 if daddr, -1 on error */
140 static int
141 lookup_check_ether_payload(uint32_t base, uint32_t offset, uint32_t len)
143 if (base != 0 || len != ETH_ALEN)
144 return -1;
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;
156 /* return 0 if saddr, 1 if daddr, -1 on error */
157 static int
158 lookup_check_iphdr_payload(uint32_t base, uint32_t offset, uint32_t len)
160 if (base != 1 || len != 4)
161 return -1;
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;
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. */
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)
181 const struct nft_xt_ctx_reg *reg;
182 int val, val2 = -1;
184 reg = nft_xt_ctx_get_sreg(ctx, sreg);
185 if (!reg)
186 return -1;
188 if (reg->type != NFT_XT_REG_PAYLOAD) {
189 ctx->errmsg = "lookup reg is not payload type";
190 return -1;
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;
205 sreg = nft_get_next_reg(sreg, ETH_ALEN);
207 reg = nft_xt_ctx_get_sreg(ctx, sreg);
208 if (!reg) {
209 ctx->errmsg = "next lookup register is invalid";
210 return -1;
213 if (reg->type != NFT_XT_REG_PAYLOAD) {
214 ctx->errmsg = "next lookup reg is not payload type";
215 return -1;
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;
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;
241 break;
242 default:
243 ctx->errmsg = "unsupported lookup key length";
244 return -1;
247 if (dst)
248 *dst = (val == 1);
249 if (ip)
250 *ip = (val2 != -1);
251 return 0;
254 static int set_elems_to_among_pairs(struct nft_among_pair *pairs,
255 const struct nftnl_set *s, int cnt)
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;
264 if (!iter) {
265 fprintf(stderr, "BUG: set elems iter allocation failed\n");
266 return ret;
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;
275 if (datalen > sizeof(*pairs)) {
276 fprintf(stderr, "BUG: overlong set elem\n");
277 goto err;
279 nft_among_insert_pair(pairs, &tmpcnt, data);
281 ret = 0;
282 err:
283 nftnl_set_elems_iter_destroy(iter);
284 return ret;
287 static struct nftnl_set *set_from_lookup_expr(struct nft_xt_ctx *ctx,
288 const struct nftnl_expr *e)
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;
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;
301 set = nft_set_batch_lookup_byid(ctx->h, set_id);
302 if (set)
303 return set;
306 return NULL;
309 static void nft_bridge_parse_lookup(struct nft_xt_ctx *ctx,
310 struct nftnl_expr *e)
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;
320 s = set_from_lookup_expr(ctx, e);
321 if (!s)
322 xtables_error(OTHER_PROBLEM,
323 "BUG: lookup expression references unknown set");
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;
331 cnt = nftnl_set_get_u32(s, NFTNL_SET_DESC_SIZE);
333 for (ematch = ctx->cs->match_list; ematch; ematch = ematch->next) {
334 if (!ematch->ismatch || strcmp(ematch->u.match->name, "among"))
335 continue;
337 match = ematch->u.match;
338 among_data = (struct nft_among_data *)match->m->data;
340 size = cnt + among_data->src.cnt + among_data->dst.cnt;
341 size *= sizeof(struct nft_among_pair);
343 size += XT_ALIGN(sizeof(struct xt_entry_match)) +
344 sizeof(struct nft_among_data);
346 match->m = xtables_realloc(match->m, size);
347 break;
349 if (!match) {
350 match = xtables_find_match("among", XTF_TRY_LOAD,
351 &ctx->cs->matches);
353 size = cnt * sizeof(struct nft_among_pair);
354 size += XT_ALIGN(sizeof(struct xt_entry_match)) +
355 sizeof(struct nft_among_data);
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);
362 if (ctx->h->ops->rule_parse->match != NULL)
363 ctx->h->ops->rule_parse->match(match, ctx->cs);
365 if (!match)
366 return;
368 match->m->u.match_size = size;
370 inv = !!(nftnl_expr_get_u32(e, NFTNL_EXPR_LOOKUP_FLAGS) &
371 NFT_LOOKUP_F_INV);
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");
380 static void parse_watcher(void *object, struct ebt_match **match_list,
381 bool ismatch)
383 struct ebt_match *m = xtables_calloc(1, sizeof(struct ebt_match));
385 if (ismatch)
386 m->u.match = object;
387 else
388 m->u.watcher = object;
390 m->ismatch = ismatch;
391 if (*match_list == NULL)
392 *match_list = m;
393 else
394 (*match_list)->next = m;
397 static void nft_bridge_parse_match(struct xtables_match *m,
398 struct iptables_command_state *cs)
400 parse_watcher(m, &cs->match_list, true);
403 static void nft_bridge_parse_target(struct xtables_target *t,
404 struct iptables_command_state *cs)
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;
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,