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.
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>
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"
32 static void nft_bridge_parse_meta(struct nft_xt_ctx
*ctx
,
33 const struct nft_xt_ctx_reg
*reg
,
35 struct iptables_command_state
*cs
)
37 struct ebt_entry
*fw
= &cs
->eb
;
39 char iifname
[IFNAMSIZ
] = {}, oifname
[IFNAMSIZ
] = {};
41 switch (reg
->meta_dreg
.key
) {
42 case NFT_META_PROTOCOL
:
46 if (parse_meta(ctx
, e
, reg
->meta_dreg
.key
,
47 iifname
, oifname
, &invflags
) < 0) {
48 ctx
->errmsg
= "unknown meta key";
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
);
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
);
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
);
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
);
74 ctx
->errmsg
= "unknown bridge meta key";
79 static void nft_bridge_parse_payload(struct nft_xt_ctx
*ctx
,
80 const struct nft_xt_ctx_reg
*reg
,
82 struct iptables_command_state
*cs
)
84 struct ebt_entry
*fw
= &cs
->eb
;
85 unsigned char addr
[ETH_ALEN
];
86 unsigned short int ethproto
;
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
];
97 fw
->invflags
|= EBT_IDEST
;
100 memcpy(fw
->destmsk
, reg
->bitwise
.mask
, ETH_ALEN
);
102 memset(&fw
->destmsk
, 0xff,
103 min(reg
->payload
.len
, ETH_ALEN
));
104 fw
->bitmask
|= EBT_IDEST
;
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
];
111 fw
->invflags
|= EBT_ISOURCE
;
112 if (reg
->bitwise
.set
)
113 memcpy(fw
->sourcemsk
, reg
->bitwise
.mask
, ETH_ALEN
);
115 memset(&fw
->sourcemsk
, 0xff,
116 min(reg
->payload
.len
, ETH_ALEN
));
117 fw
->bitmask
|= EBT_ISOURCE
;
119 case offsetof(struct ethhdr
, h_proto
):
120 __get_cmp_data(e
, ðproto
, sizeof(ethproto
), &op
);
121 if (ethproto
== htons(0x0600)) {
122 fw
->bitmask
|= EBT_802_3
;
123 inv
= (op
== NFT_CMP_GTE
);
125 fw
->ethproto
= ethproto
;
126 inv
= (op
== NFT_CMP_NEQ
);
129 fw
->invflags
|= EBT_IPROTO
;
130 fw
->bitmask
&= ~EBT_NOPROTO
;
133 DEBUGP("unknown payload offset %d\n", reg
->payload
.offset
);
134 ctx
->errmsg
= "unknown payload offset";
139 /* return 0 if saddr, 1 if daddr, -1 on error */
141 lookup_check_ether_payload(uint32_t base
, uint32_t offset
, uint32_t len
)
143 if (base
!= 0 || len
!= ETH_ALEN
)
147 case offsetof(struct ether_header
, ether_dhost
):
149 case offsetof(struct ether_header
, ether_shost
):
156 /* return 0 if saddr, 1 if daddr, -1 on error */
158 lookup_check_iphdr_payload(uint32_t base
, uint32_t offset
, uint32_t len
)
160 if (base
!= 1 || len
!= 4)
164 case offsetof(struct iphdr
, daddr
):
166 case offsetof(struct iphdr
, saddr
):
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
,
181 const struct nft_xt_ctx_reg
*reg
;
184 reg
= nft_xt_ctx_get_sreg(ctx
, sreg
);
188 if (reg
->type
!= NFT_XT_REG_PAYLOAD
) {
189 ctx
->errmsg
= "lookup reg is not payload type";
194 case 12: /* ether + ipv4addr */
195 val
= lookup_check_ether_payload(reg
->payload
.base
,
199 DEBUGP("unknown payload base/offset/len %d/%d/%d\n",
200 reg
->payload
.base
, reg
->payload
.offset
,
205 sreg
= nft_get_next_reg(sreg
, ETH_ALEN
);
207 reg
= nft_xt_ctx_get_sreg(ctx
, sreg
);
209 ctx
->errmsg
= "next lookup register is invalid";
213 if (reg
->type
!= NFT_XT_REG_PAYLOAD
) {
214 ctx
->errmsg
= "next lookup reg is not payload type";
218 val2
= lookup_check_iphdr_payload(reg
->payload
.base
,
222 DEBUGP("unknown payload base/offset/len %d/%d/%d\n",
223 reg
->payload
.base
, reg
->payload
.offset
,
226 } else if (val
!= val2
) {
227 DEBUGP("mismatching payload match offsets\n");
232 val
= lookup_check_ether_payload(reg
->payload
.base
,
236 DEBUGP("unknown payload base/offset/len %d/%d/%d\n",
237 reg
->payload
.base
, reg
->payload
.offset
,
243 ctx
->errmsg
= "unsupported lookup key length";
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
;
265 fprintf(stderr
, "BUG: set elems iter allocation failed\n");
269 while ((elem
= nftnl_set_elems_iter_next(iter
))) {
270 data
= nftnl_set_elem_get(elem
, NFTNL_SET_ELEM_KEY
, &datalen
);
272 fprintf(stderr
, "BUG: set elem without key\n");
275 if (datalen
> sizeof(*pairs
)) {
276 fprintf(stderr
, "BUG: overlong set elem\n");
279 nft_among_insert_pair(pairs
, &tmpcnt
, data
);
283 nftnl_set_elems_iter_destroy(iter
);
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
);
297 set
= nftnl_set_list_lookup_byname(slist
, set_name
);
301 set
= nft_set_batch_lookup_byid(ctx
->h
, set_id
);
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
;
320 s
= set_from_lookup_expr(ctx
, e
);
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
),
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"))
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
);
350 match
= xtables_find_match("among", XTF_TRY_LOAD
,
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
);
368 match
->m
->u
.match_size
= size
;
370 inv
= !!(nftnl_expr_get_u32(e
, NFTNL_EXPR_LOOKUP_FLAGS
) &
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
,
383 struct ebt_match
*m
= xtables_calloc(1, sizeof(struct ebt_match
));
388 m
->u
.watcher
= object
;
390 m
->ismatch
= ismatch
;
391 if (*match_list
== NULL
)
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);
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
,