| /* |
| * (C) 2014 by Giuseppe Longo <giuseppelng@gmail.com> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <netinet/ether.h> |
| #include <inttypes.h> |
| |
| #include <xtables.h> |
| #include <libiptc/libxtc.h> |
| #include <linux/netfilter/nf_tables.h> |
| |
| #include <libnftnl/set.h> |
| |
| #include "nft-shared.h" |
| #include "nft-bridge.h" |
| #include "nft-cache.h" |
| #include "nft.h" |
| |
| void ebt_cs_clean(struct iptables_command_state *cs) |
| { |
| struct ebt_match *m, *nm; |
| |
| xtables_rule_matches_free(&cs->matches); |
| |
| for (m = cs->match_list; m;) { |
| if (!m->ismatch) { |
| struct xtables_target *target = m->u.watcher; |
| |
| if (target->t) { |
| free(target->t); |
| target->t = NULL; |
| } |
| if (target == target->next) |
| free(target); |
| } |
| |
| nm = m->next; |
| free(m); |
| m = nm; |
| } |
| |
| if (cs->target) { |
| free(cs->target->t); |
| cs->target->t = NULL; |
| |
| if (cs->target == cs->target->next) { |
| free(cs->target); |
| cs->target = NULL; |
| } |
| } |
| } |
| |
| /* Put the mac address into 6 (ETH_ALEN) bytes returns 0 on success. */ |
| static void ebt_print_mac_and_mask(const unsigned char *mac, const unsigned char *mask) |
| { |
| if (xtables_print_well_known_mac_and_mask(mac, mask)) |
| xtables_print_mac_and_mask(mac, mask); |
| } |
| |
| static void add_logical_iniface(struct nftnl_rule *r, char *iface, uint32_t op) |
| { |
| int iface_len; |
| |
| iface_len = strlen(iface); |
| |
| add_meta(r, NFT_META_BRI_IIFNAME); |
| if (iface[iface_len - 1] == '+') |
| add_cmp_ptr(r, op, iface, iface_len - 1); |
| else |
| add_cmp_ptr(r, op, iface, iface_len + 1); |
| } |
| |
| static void add_logical_outiface(struct nftnl_rule *r, char *iface, uint32_t op) |
| { |
| int iface_len; |
| |
| iface_len = strlen(iface); |
| |
| add_meta(r, NFT_META_BRI_OIFNAME); |
| if (iface[iface_len - 1] == '+') |
| add_cmp_ptr(r, op, iface, iface_len - 1); |
| else |
| add_cmp_ptr(r, op, iface, iface_len + 1); |
| } |
| |
| static int _add_action(struct nftnl_rule *r, struct iptables_command_state *cs) |
| { |
| return add_action(r, cs, false); |
| } |
| |
| static int nft_bridge_add(struct nft_handle *h, |
| struct nftnl_rule *r, void *data) |
| { |
| struct iptables_command_state *cs = data; |
| struct ebt_match *iter; |
| struct ebt_entry *fw = &cs->eb; |
| uint32_t op; |
| |
| if (fw->in[0] != '\0') { |
| op = nft_invflags2cmp(fw->invflags, EBT_IIN); |
| add_iniface(r, fw->in, op); |
| } |
| |
| if (fw->out[0] != '\0') { |
| op = nft_invflags2cmp(fw->invflags, EBT_IOUT); |
| add_outiface(r, fw->out, op); |
| } |
| |
| if (fw->logical_in[0] != '\0') { |
| op = nft_invflags2cmp(fw->invflags, EBT_ILOGICALIN); |
| add_logical_iniface(r, fw->logical_in, op); |
| } |
| |
| if (fw->logical_out[0] != '\0') { |
| op = nft_invflags2cmp(fw->invflags, EBT_ILOGICALOUT); |
| add_logical_outiface(r, fw->logical_out, op); |
| } |
| |
| if (fw->bitmask & EBT_ISOURCE) { |
| op = nft_invflags2cmp(fw->invflags, EBT_ISOURCE); |
| add_addr(r, NFT_PAYLOAD_LL_HEADER, |
| offsetof(struct ethhdr, h_source), |
| fw->sourcemac, fw->sourcemsk, ETH_ALEN, op); |
| } |
| |
| if (fw->bitmask & EBT_IDEST) { |
| op = nft_invflags2cmp(fw->invflags, EBT_IDEST); |
| add_addr(r, NFT_PAYLOAD_LL_HEADER, |
| offsetof(struct ethhdr, h_dest), |
| fw->destmac, fw->destmsk, ETH_ALEN, op); |
| } |
| |
| if ((fw->bitmask & EBT_NOPROTO) == 0) { |
| op = nft_invflags2cmp(fw->invflags, EBT_IPROTO); |
| add_payload(r, offsetof(struct ethhdr, h_proto), 2, |
| NFT_PAYLOAD_LL_HEADER); |
| add_cmp_u16(r, fw->ethproto, op); |
| } |
| |
| add_compat(r, fw->ethproto, fw->invflags & EBT_IPROTO); |
| |
| for (iter = cs->match_list; iter; iter = iter->next) { |
| if (iter->ismatch) { |
| if (add_match(h, r, iter->u.match->m)) |
| break; |
| } else { |
| if (add_target(r, iter->u.watcher->t)) |
| break; |
| } |
| } |
| |
| if (add_counters(r, cs->counters.pcnt, cs->counters.bcnt) < 0) |
| return -1; |
| |
| return _add_action(r, cs); |
| } |
| |
| static void nft_bridge_parse_meta(struct nft_xt_ctx *ctx, |
| struct nftnl_expr *e, void *data) |
| { |
| struct iptables_command_state *cs = data; |
| struct ebt_entry *fw = &cs->eb; |
| uint8_t invflags = 0; |
| char iifname[IFNAMSIZ] = {}, oifname[IFNAMSIZ] = {}; |
| |
| parse_meta(e, ctx->meta.key, iifname, NULL, oifname, NULL, &invflags); |
| |
| switch (ctx->meta.key) { |
| case NFT_META_BRI_IIFNAME: |
| if (invflags & IPT_INV_VIA_IN) |
| cs->eb.invflags |= EBT_ILOGICALIN; |
| snprintf(fw->logical_in, sizeof(fw->logical_in), "%s", iifname); |
| break; |
| case NFT_META_IIFNAME: |
| if (invflags & IPT_INV_VIA_IN) |
| cs->eb.invflags |= EBT_IIN; |
| snprintf(fw->in, sizeof(fw->in), "%s", iifname); |
| break; |
| case NFT_META_BRI_OIFNAME: |
| if (invflags & IPT_INV_VIA_OUT) |
| cs->eb.invflags |= EBT_ILOGICALOUT; |
| snprintf(fw->logical_out, sizeof(fw->logical_out), "%s", oifname); |
| break; |
| case NFT_META_OIFNAME: |
| if (invflags & IPT_INV_VIA_OUT) |
| cs->eb.invflags |= EBT_IOUT; |
| snprintf(fw->out, sizeof(fw->out), "%s", oifname); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| static void nft_bridge_parse_payload(struct nft_xt_ctx *ctx, |
| struct nftnl_expr *e, void *data) |
| { |
| struct iptables_command_state *cs = data; |
| struct ebt_entry *fw = &cs->eb; |
| unsigned char addr[ETH_ALEN]; |
| unsigned short int ethproto; |
| bool inv; |
| int i; |
| |
| switch (ctx->payload.offset) { |
| case offsetof(struct ethhdr, h_dest): |
| get_cmp_data(e, addr, sizeof(addr), &inv); |
| for (i = 0; i < ETH_ALEN; i++) |
| fw->destmac[i] = addr[i]; |
| if (inv) |
| fw->invflags |= EBT_IDEST; |
| |
| if (ctx->flags & NFT_XT_CTX_BITWISE) { |
| memcpy(fw->destmsk, ctx->bitwise.mask, ETH_ALEN); |
| ctx->flags &= ~NFT_XT_CTX_BITWISE; |
| } else { |
| memset(&fw->destmsk, 0xff, |
| min(ctx->payload.len, ETH_ALEN)); |
| } |
| fw->bitmask |= EBT_IDEST; |
| break; |
| case offsetof(struct ethhdr, h_source): |
| get_cmp_data(e, addr, sizeof(addr), &inv); |
| for (i = 0; i < ETH_ALEN; i++) |
| fw->sourcemac[i] = addr[i]; |
| if (inv) |
| fw->invflags |= EBT_ISOURCE; |
| if (ctx->flags & NFT_XT_CTX_BITWISE) { |
| memcpy(fw->sourcemsk, ctx->bitwise.mask, ETH_ALEN); |
| ctx->flags &= ~NFT_XT_CTX_BITWISE; |
| } else { |
| memset(&fw->sourcemsk, 0xff, |
| min(ctx->payload.len, ETH_ALEN)); |
| } |
| fw->bitmask |= EBT_ISOURCE; |
| break; |
| case offsetof(struct ethhdr, h_proto): |
| get_cmp_data(e, ðproto, sizeof(ethproto), &inv); |
| fw->ethproto = ethproto; |
| if (inv) |
| fw->invflags |= EBT_IPROTO; |
| fw->bitmask &= ~EBT_NOPROTO; |
| break; |
| } |
| } |
| |
| static void nft_bridge_parse_immediate(const char *jumpto, bool nft_goto, |
| void *data) |
| { |
| struct iptables_command_state *cs = data; |
| |
| cs->jumpto = jumpto; |
| } |
| |
| /* return 0 if saddr, 1 if daddr, -1 on error */ |
| static int |
| lookup_check_ether_payload(uint32_t base, uint32_t offset, uint32_t len) |
| { |
| if (base != 0 || len != ETH_ALEN) |
| return -1; |
| |
| switch (offset) { |
| case offsetof(struct ether_header, ether_dhost): |
| return 1; |
| case offsetof(struct ether_header, ether_shost): |
| return 0; |
| default: |
| return -1; |
| } |
| } |
| |
| /* return 0 if saddr, 1 if daddr, -1 on error */ |
| static int |
| lookup_check_iphdr_payload(uint32_t base, uint32_t offset, uint32_t len) |
| { |
| if (base != 1 || len != 4) |
| return -1; |
| |
| switch (offset) { |
| case offsetof(struct iphdr, daddr): |
| return 1; |
| case offsetof(struct iphdr, saddr): |
| return 0; |
| default: |
| return -1; |
| } |
| } |
| |
| /* Make sure previous payload expression(s) is/are consistent and extract if |
| * matching on source or destination address and if matching on MAC and IP or |
| * only MAC address. */ |
| static int lookup_analyze_payloads(const struct nft_xt_ctx *ctx, |
| bool *dst, bool *ip) |
| { |
| int val, val2 = -1; |
| |
| if (ctx->flags & NFT_XT_CTX_PREV_PAYLOAD) { |
| val = lookup_check_ether_payload(ctx->prev_payload.base, |
| ctx->prev_payload.offset, |
| ctx->prev_payload.len); |
| if (val < 0) { |
| DEBUGP("unknown payload base/offset/len %d/%d/%d\n", |
| ctx->prev_payload.base, ctx->prev_payload.offset, |
| ctx->prev_payload.len); |
| return -1; |
| } |
| if (!(ctx->flags & NFT_XT_CTX_PAYLOAD)) { |
| DEBUGP("Previous but no current payload?\n"); |
| return -1; |
| } |
| val2 = lookup_check_iphdr_payload(ctx->payload.base, |
| ctx->payload.offset, |
| ctx->payload.len); |
| if (val2 < 0) { |
| DEBUGP("unknown payload base/offset/len %d/%d/%d\n", |
| ctx->payload.base, ctx->payload.offset, |
| ctx->payload.len); |
| return -1; |
| } else if (val != val2) { |
| DEBUGP("mismatching payload match offsets\n"); |
| return -1; |
| } |
| } else if (ctx->flags & NFT_XT_CTX_PAYLOAD) { |
| val = lookup_check_ether_payload(ctx->payload.base, |
| ctx->payload.offset, |
| ctx->payload.len); |
| if (val < 0) { |
| DEBUGP("unknown payload base/offset/len %d/%d/%d\n", |
| ctx->payload.base, ctx->payload.offset, |
| ctx->payload.len); |
| return -1; |
| } |
| } else { |
| DEBUGP("unknown LHS of lookup expression\n"); |
| return -1; |
| } |
| |
| if (dst) |
| *dst = (val == 1); |
| if (ip) |
| *ip = (val2 != -1); |
| return 0; |
| } |
| |
| static int set_elems_to_among_pairs(struct nft_among_pair *pairs, |
| const struct nftnl_set *s, int cnt) |
| { |
| struct nftnl_set_elems_iter *iter = nftnl_set_elems_iter_create(s); |
| struct nftnl_set_elem *elem; |
| size_t tmpcnt = 0; |
| const void *data; |
| uint32_t datalen; |
| int ret = -1; |
| |
| if (!iter) { |
| fprintf(stderr, "BUG: set elems iter allocation failed\n"); |
| return ret; |
| } |
| |
| while ((elem = nftnl_set_elems_iter_next(iter))) { |
| data = nftnl_set_elem_get(elem, NFTNL_SET_ELEM_KEY, &datalen); |
| if (!data) { |
| fprintf(stderr, "BUG: set elem without key\n"); |
| goto err; |
| } |
| if (datalen > sizeof(*pairs)) { |
| fprintf(stderr, "BUG: overlong set elem\n"); |
| goto err; |
| } |
| nft_among_insert_pair(pairs, &tmpcnt, data); |
| } |
| ret = 0; |
| err: |
| nftnl_set_elems_iter_destroy(iter); |
| return ret; |
| } |
| |
| static struct nftnl_set *set_from_lookup_expr(struct nft_xt_ctx *ctx, |
| const struct nftnl_expr *e) |
| { |
| const char *set_name = nftnl_expr_get_str(e, NFTNL_EXPR_LOOKUP_SET); |
| uint32_t set_id = nftnl_expr_get_u32(e, NFTNL_EXPR_LOOKUP_SET_ID); |
| struct nftnl_set_list *slist; |
| struct nftnl_set *set; |
| |
| slist = nft_set_list_get(ctx->h, ctx->table, set_name); |
| if (slist) { |
| set = nftnl_set_list_lookup_byname(slist, set_name); |
| if (set) |
| return set; |
| |
| set = nft_set_batch_lookup_byid(ctx->h, set_id); |
| if (set) |
| return set; |
| } |
| |
| return NULL; |
| } |
| |
| static void nft_bridge_parse_lookup(struct nft_xt_ctx *ctx, |
| struct nftnl_expr *e, void *data) |
| { |
| struct xtables_match *match = NULL; |
| struct nft_among_data *among_data; |
| bool is_dst, have_ip, inv; |
| struct ebt_match *ematch; |
| struct nftnl_set *s; |
| size_t poff, size; |
| uint32_t cnt; |
| |
| if (lookup_analyze_payloads(ctx, &is_dst, &have_ip)) |
| return; |
| |
| s = set_from_lookup_expr(ctx, e); |
| if (!s) |
| xtables_error(OTHER_PROBLEM, |
| "BUG: lookup expression references unknown set"); |
| |
| cnt = nftnl_set_get_u32(s, NFTNL_SET_DESC_SIZE); |
| |
| for (ematch = ctx->cs->match_list; ematch; ematch = ematch->next) { |
| if (!ematch->ismatch || strcmp(ematch->u.match->name, "among")) |
| continue; |
| |
| match = ematch->u.match; |
| among_data = (struct nft_among_data *)match->m->data; |
| |
| size = cnt + among_data->src.cnt + among_data->dst.cnt; |
| size *= sizeof(struct nft_among_pair); |
| |
| size += XT_ALIGN(sizeof(struct xt_entry_match)) + |
| sizeof(struct nft_among_data); |
| |
| match->m = xtables_realloc(match->m, size); |
| break; |
| } |
| if (!match) { |
| match = xtables_find_match("among", XTF_TRY_LOAD, |
| &ctx->cs->matches); |
| |
| size = cnt * sizeof(struct nft_among_pair); |
| size += XT_ALIGN(sizeof(struct xt_entry_match)) + |
| sizeof(struct nft_among_data); |
| |
| match->m = xtables_calloc(1, size); |
| strcpy(match->m->u.user.name, match->name); |
| match->m->u.user.revision = match->revision; |
| xs_init_match(match); |
| |
| if (ctx->h->ops->parse_match != NULL) |
| ctx->h->ops->parse_match(match, ctx->cs); |
| } |
| if (!match) |
| return; |
| |
| match->m->u.match_size = size; |
| |
| inv = !!(nftnl_expr_get_u32(e, NFTNL_EXPR_LOOKUP_FLAGS) & |
| NFT_LOOKUP_F_INV); |
| |
| among_data = (struct nft_among_data *)match->m->data; |
| poff = nft_among_prepare_data(among_data, is_dst, cnt, inv, have_ip); |
| if (set_elems_to_among_pairs(among_data->pairs + poff, s, cnt)) |
| xtables_error(OTHER_PROBLEM, |
| "ebtables among pair parsing failed"); |
| |
| ctx->flags &= ~(NFT_XT_CTX_PAYLOAD | NFT_XT_CTX_PREV_PAYLOAD); |
| } |
| |
| static void parse_watcher(void *object, struct ebt_match **match_list, |
| bool ismatch) |
| { |
| struct ebt_match *m; |
| |
| m = calloc(1, sizeof(struct ebt_match)); |
| if (m == NULL) |
| xtables_error(OTHER_PROBLEM, "Can't allocate memory"); |
| |
| if (ismatch) |
| m->u.match = object; |
| else |
| m->u.watcher = object; |
| |
| m->ismatch = ismatch; |
| if (*match_list == NULL) |
| *match_list = m; |
| else |
| (*match_list)->next = m; |
| } |
| |
| static void nft_bridge_parse_match(struct xtables_match *m, void *data) |
| { |
| struct iptables_command_state *cs = data; |
| |
| parse_watcher(m, &cs->match_list, true); |
| } |
| |
| static void nft_bridge_parse_target(struct xtables_target *t, void *data) |
| { |
| struct iptables_command_state *cs = data; |
| |
| /* harcoded names :-( */ |
| if (strcmp(t->name, "log") == 0 || |
| strcmp(t->name, "nflog") == 0) { |
| parse_watcher(t, &cs->match_list, false); |
| return; |
| } |
| |
| cs->target = t; |
| } |
| |
| static void nft_rule_to_ebtables_command_state(struct nft_handle *h, |
| const struct nftnl_rule *r, |
| struct iptables_command_state *cs) |
| { |
| cs->eb.bitmask = EBT_NOPROTO; |
| nft_rule_to_iptables_command_state(h, r, cs); |
| } |
| |
| static void print_iface(const char *option, const char *name, bool invert) |
| { |
| if (*name) |
| printf("%s%s %s ", option, invert ? " !" : "", name); |
| } |
| |
| static void nft_bridge_print_table_header(const char *tablename) |
| { |
| printf("Bridge table: %s\n\n", tablename); |
| } |
| |
| static void 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) |
| { |
| printf("Bridge chain: %s, entries: %u, policy: %s\n", |
| chain, entries, pol ?: "RETURN"); |
| } |
| |
| static void print_matches_and_watchers(const struct iptables_command_state *cs, |
| unsigned int format) |
| { |
| struct xtables_target *watcherp; |
| struct xtables_match *matchp; |
| struct ebt_match *m; |
| |
| for (m = cs->match_list; m; m = m->next) { |
| if (m->ismatch) { |
| matchp = m->u.match; |
| if (matchp->print != NULL) { |
| matchp->print(&cs->eb, matchp->m, |
| format & FMT_NUMERIC); |
| } |
| } else { |
| watcherp = m->u.watcher; |
| if (watcherp->print != NULL) { |
| watcherp->print(&cs->eb, watcherp->t, |
| format & FMT_NUMERIC); |
| } |
| } |
| } |
| } |
| |
| static void print_mac(char option, const unsigned char *mac, |
| const unsigned char *mask, |
| bool invert) |
| { |
| printf("-%c ", option); |
| if (invert) |
| printf("! "); |
| ebt_print_mac_and_mask(mac, mask); |
| printf(" "); |
| } |
| |
| |
| static void print_protocol(uint16_t ethproto, bool invert, unsigned int bitmask) |
| { |
| struct xt_ethertypeent *ent; |
| |
| /* Dont print anything about the protocol if no protocol was |
| * specified, obviously this means any protocol will do. */ |
| if (bitmask & EBT_NOPROTO) |
| return; |
| |
| printf("-p "); |
| if (invert) |
| printf("! "); |
| |
| if (bitmask & EBT_802_3) { |
| printf("length "); |
| return; |
| } |
| |
| ent = xtables_getethertypebynumber(ntohs(ethproto)); |
| if (!ent) |
| printf("0x%x ", ntohs(ethproto)); |
| else |
| printf("%s ", ent->e_name); |
| } |
| |
| static void nft_bridge_save_rule(const void *data, unsigned int format) |
| { |
| const struct iptables_command_state *cs = data; |
| |
| if (cs->eb.ethproto) |
| print_protocol(cs->eb.ethproto, cs->eb.invflags & EBT_IPROTO, |
| cs->eb.bitmask); |
| if (cs->eb.bitmask & EBT_ISOURCE) |
| print_mac('s', cs->eb.sourcemac, cs->eb.sourcemsk, |
| cs->eb.invflags & EBT_ISOURCE); |
| if (cs->eb.bitmask & EBT_IDEST) |
| print_mac('d', cs->eb.destmac, cs->eb.destmsk, |
| cs->eb.invflags & EBT_IDEST); |
| |
| print_iface("-i", cs->eb.in, cs->eb.invflags & EBT_IIN); |
| print_iface("--logical-in", cs->eb.logical_in, |
| cs->eb.invflags & EBT_ILOGICALIN); |
| print_iface("-o", cs->eb.out, cs->eb.invflags & EBT_IOUT); |
| print_iface("--logical-out", cs->eb.logical_out, |
| cs->eb.invflags & EBT_ILOGICALOUT); |
| |
| print_matches_and_watchers(cs, format); |
| |
| printf("-j "); |
| |
| if (cs->jumpto != NULL) { |
| if (strcmp(cs->jumpto, "") != 0) |
| printf("%s", cs->jumpto); |
| else |
| printf("CONTINUE"); |
| } |
| if (cs->target != NULL && cs->target->print != NULL) { |
| printf(" "); |
| cs->target->print(&cs->fw, cs->target->t, format & FMT_NUMERIC); |
| } |
| |
| if ((format & (FMT_NOCOUNTS | FMT_C_COUNTS)) == FMT_C_COUNTS) { |
| if (format & FMT_EBT_SAVE) |
| printf(" -c %"PRIu64" %"PRIu64"", |
| (uint64_t)cs->counters.pcnt, |
| (uint64_t)cs->counters.bcnt); |
| else |
| printf(" , pcnt = %"PRIu64" -- bcnt = %"PRIu64"", |
| (uint64_t)cs->counters.pcnt, |
| (uint64_t)cs->counters.bcnt); |
| } |
| |
| if (!(format & FMT_NONEWLINE)) |
| fputc('\n', stdout); |
| } |
| |
| static void nft_bridge_print_rule(struct nft_handle *h, struct nftnl_rule *r, |
| unsigned int num, unsigned int format) |
| { |
| struct iptables_command_state cs = {}; |
| |
| if (format & FMT_LINENUMBERS) |
| printf("%d ", num); |
| |
| nft_rule_to_ebtables_command_state(h, r, &cs); |
| nft_bridge_save_rule(&cs, format); |
| ebt_cs_clean(&cs); |
| } |
| |
| static void nft_bridge_save_chain(const struct nftnl_chain *c, |
| const char *policy) |
| { |
| const char *chain = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME); |
| |
| printf(":%s %s\n", chain, policy ?: "ACCEPT"); |
| } |
| |
| static bool nft_bridge_is_same(const void *data_a, const void *data_b) |
| { |
| const struct ebt_entry *a = data_a; |
| const struct ebt_entry *b = data_b; |
| int i; |
| |
| if (a->ethproto != b->ethproto || |
| /* FIXME: a->flags != b->flags || */ |
| a->invflags != b->invflags) { |
| DEBUGP("different proto/flags/invflags\n"); |
| return false; |
| } |
| |
| for (i = 0; i < ETH_ALEN; i++) { |
| if (a->sourcemac[i] != b->sourcemac[i]) { |
| DEBUGP("different source mac %x, %x (%d)\n", |
| a->sourcemac[i] & 0xff, b->sourcemac[i] & 0xff, i); |
| return false; |
| } |
| |
| if (a->destmac[i] != b->destmac[i]) { |
| DEBUGP("different destination mac %x, %x (%d)\n", |
| a->destmac[i] & 0xff, b->destmac[i] & 0xff, i); |
| return false; |
| } |
| } |
| |
| for (i = 0; i < IFNAMSIZ; i++) { |
| if (a->logical_in[i] != b->logical_in[i]) { |
| DEBUGP("different logical iniface %x, %x (%d)\n", |
| a->logical_in[i] & 0xff, b->logical_in[i] & 0xff, i); |
| return false; |
| } |
| |
| if (a->logical_out[i] != b->logical_out[i]) { |
| DEBUGP("different logical outiface %x, %x (%d)\n", |
| a->logical_out[i] & 0xff, b->logical_out[i] & 0xff, i); |
| return false; |
| } |
| } |
| |
| return strcmp(a->in, b->in) == 0 && strcmp(a->out, b->out) == 0; |
| } |
| |
| static int xlate_ebmatches(const struct iptables_command_state *cs, struct xt_xlate *xl) |
| { |
| int ret = 1, numeric = cs->options & OPT_NUMERIC; |
| struct ebt_match *m; |
| |
| for (m = cs->match_list; m; m = m->next) { |
| if (m->ismatch) { |
| struct xtables_match *matchp = m->u.match; |
| struct xt_xlate_mt_params mt_params = { |
| .ip = (const void *)&cs->eb, |
| .numeric = numeric, |
| .escape_quotes = false, |
| .match = matchp->m, |
| }; |
| |
| if (!matchp->xlate) |
| return 0; |
| |
| ret = matchp->xlate(xl, &mt_params); |
| } else { |
| struct xtables_target *watcherp = m->u.watcher; |
| struct xt_xlate_tg_params wt_params = { |
| .ip = (const void *)&cs->eb, |
| .numeric = numeric, |
| .escape_quotes = false, |
| .target = watcherp->t, |
| }; |
| |
| if (!watcherp->xlate) |
| return 0; |
| |
| ret = watcherp->xlate(xl, &wt_params); |
| } |
| |
| if (!ret) |
| break; |
| } |
| |
| return ret; |
| } |
| |
| static int xlate_ebaction(const struct iptables_command_state *cs, struct xt_xlate *xl) |
| { |
| int ret = 1, numeric = cs->options & OPT_NUMERIC; |
| |
| /* If no target at all, add nothing (default to continue) */ |
| if (cs->target != NULL) { |
| /* Standard target? */ |
| if (strcmp(cs->jumpto, XTC_LABEL_ACCEPT) == 0) |
| xt_xlate_add(xl, " accept"); |
| else if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0) |
| xt_xlate_add(xl, " drop"); |
| else if (strcmp(cs->jumpto, XTC_LABEL_RETURN) == 0) |
| xt_xlate_add(xl, " return"); |
| else if (cs->target->xlate) { |
| xt_xlate_add(xl, " "); |
| struct xt_xlate_tg_params params = { |
| .ip = (const void *)&cs->eb, |
| .target = cs->target->t, |
| .numeric = numeric, |
| }; |
| ret = cs->target->xlate(xl, ¶ms); |
| } |
| else |
| return 0; |
| } else if (cs->jumpto == NULL) { |
| } else if (strlen(cs->jumpto) > 0) |
| xt_xlate_add(xl, " jump %s", cs->jumpto); |
| |
| return ret; |
| } |
| |
| static void xlate_mac(struct xt_xlate *xl, const unsigned char *mac) |
| { |
| int i; |
| |
| xt_xlate_add(xl, "%02x", mac[0]); |
| |
| for (i=1; i < ETH_ALEN; i++) |
| xt_xlate_add(xl, ":%02x", mac[i]); |
| } |
| |
| static void nft_bridge_xlate_mac(struct xt_xlate *xl, const char *type, bool invert, |
| const unsigned char *mac, const unsigned char *mask) |
| { |
| char one_msk[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; |
| |
| xt_xlate_add(xl, "ether %s %s", type, invert ? "!= " : ""); |
| |
| xlate_mac(xl, mac); |
| |
| if (memcmp(mask, one_msk, ETH_ALEN)) { |
| int i; |
| xt_xlate_add(xl, " and "); |
| |
| xlate_mac(xl, mask); |
| |
| xt_xlate_add(xl, " == %02x", mac[0] & mask[0]); |
| for (i=1; i < ETH_ALEN; i++) |
| xt_xlate_add(xl, ":%02x", mac[i] & mask[i]); |
| } |
| |
| xt_xlate_add(xl, " "); |
| } |
| |
| static int nft_bridge_xlate(const void *data, struct xt_xlate *xl) |
| { |
| const struct iptables_command_state *cs = data; |
| int ret; |
| |
| xlate_ifname(xl, "iifname", cs->eb.in, |
| cs->eb.invflags & EBT_IIN); |
| xlate_ifname(xl, "meta ibrname", cs->eb.logical_in, |
| cs->eb.invflags & EBT_ILOGICALIN); |
| xlate_ifname(xl, "oifname", cs->eb.out, |
| cs->eb.invflags & EBT_IOUT); |
| xlate_ifname(xl, "meta obrname", cs->eb.logical_out, |
| cs->eb.invflags & EBT_ILOGICALOUT); |
| |
| if ((cs->eb.bitmask & EBT_NOPROTO) == 0) { |
| const char *implicit = NULL; |
| |
| switch (ntohs(cs->eb.ethproto)) { |
| case ETH_P_IP: |
| implicit = "ip"; |
| break; |
| case ETH_P_IPV6: |
| implicit = "ip6"; |
| break; |
| case ETH_P_8021Q: |
| implicit = "vlan"; |
| break; |
| default: |
| break; |
| } |
| |
| if (!implicit || !xlate_find_match(cs, implicit)) |
| xt_xlate_add(xl, "ether type %s0x%x ", |
| cs->eb.invflags & EBT_IPROTO ? "!= " : "", |
| ntohs(cs->eb.ethproto)); |
| } |
| |
| if (cs->eb.bitmask & EBT_802_3) |
| return 0; |
| |
| if (cs->eb.bitmask & EBT_ISOURCE) |
| nft_bridge_xlate_mac(xl, "saddr", cs->eb.invflags & EBT_ISOURCE, |
| cs->eb.sourcemac, cs->eb.sourcemsk); |
| if (cs->eb.bitmask & EBT_IDEST) |
| nft_bridge_xlate_mac(xl, "daddr", cs->eb.invflags & EBT_IDEST, |
| cs->eb.destmac, cs->eb.destmsk); |
| ret = xlate_ebmatches(cs, xl); |
| if (ret == 0) |
| return ret; |
| |
| /* Always add counters per rule, as in ebtables */ |
| xt_xlate_add(xl, "counter"); |
| ret = xlate_ebaction(cs, xl); |
| |
| return ret; |
| } |
| |
| struct nft_family_ops nft_family_ops_bridge = { |
| .add = nft_bridge_add, |
| .is_same = nft_bridge_is_same, |
| .print_payload = NULL, |
| .parse_meta = nft_bridge_parse_meta, |
| .parse_payload = nft_bridge_parse_payload, |
| .parse_immediate = nft_bridge_parse_immediate, |
| .parse_lookup = nft_bridge_parse_lookup, |
| .parse_match = nft_bridge_parse_match, |
| .parse_target = nft_bridge_parse_target, |
| .print_table_header = nft_bridge_print_table_header, |
| .print_header = nft_bridge_print_header, |
| .print_rule = nft_bridge_print_rule, |
| .save_rule = nft_bridge_save_rule, |
| .save_chain = nft_bridge_save_chain, |
| .post_parse = NULL, |
| .rule_to_cs = nft_rule_to_ebtables_command_state, |
| .clear_cs = ebt_cs_clean, |
| .xlate = nft_bridge_xlate, |
| }; |