Commit dab1e98e authored by Arturo Borrero Gonzalez's avatar Arturo Borrero Gonzalez
Browse files

New upstream version 1.8.1

parent f1f129da
/* ebt_nat
*
* Authors:
* Bart De Schuymer <bdschuym@pandora.be>
*
* June, 2002
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <netinet/ether.h>
#include <xtables.h>
#include <linux/netfilter_bridge/ebt_nat.h>
#include "iptables/nft.h"
#include "iptables/nft-bridge.h"
#define NAT_D '1'
#define NAT_D_TARGET '2'
static const struct option brdnat_opts[] =
{
{ "to-destination", required_argument, 0, NAT_D },
{ "to-dst" , required_argument, 0, NAT_D },
{ "dnat-target" , required_argument, 0, NAT_D_TARGET },
{ 0 }
};
static void brdnat_print_help(void)
{
printf(
"dnat options:\n"
" --to-dst address : MAC address to map destination to\n"
" --dnat-target target : ACCEPT, DROP, RETURN or CONTINUE\n");
}
static void brdnat_init(struct xt_entry_target *target)
{
struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
natinfo->target = EBT_ACCEPT;
}
#define OPT_DNAT 0x01
#define OPT_DNAT_TARGET 0x02
static int brdnat_parse(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_target **target)
{
struct ebt_nat_info *natinfo = (struct ebt_nat_info *)(*target)->data;
struct ether_addr *addr;
switch (c) {
case NAT_D:
EBT_CHECK_OPTION(flags, OPT_DNAT);
if (!(addr = ether_aton(optarg)))
xtables_error(PARAMETER_PROBLEM, "Problem with specified --to-destination mac");
memcpy(natinfo->mac, addr, ETH_ALEN);
break;
case NAT_D_TARGET:
EBT_CHECK_OPTION(flags, OPT_DNAT_TARGET);
if (ebt_fill_target(optarg, (unsigned int *)&natinfo->target))
xtables_error(PARAMETER_PROBLEM, "Illegal --dnat-target target");
break;
default:
return 0;
}
return 1;
}
static void brdnat_final_check(unsigned int flags)
{
if (!flags)
xtables_error(PARAMETER_PROBLEM,
"You must specify proper arguments");
}
static void ebt_print_mac(const unsigned char *mac)
{
printf("%s", ether_ntoa((struct ether_addr *) mac));
}
static void brdnat_print(const void *ip, const struct xt_entry_target *target, int numeric)
{
struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
printf("--to-dst ");
ebt_print_mac(natinfo->mac);
printf(" --dnat-target %s", ebt_target_name(natinfo->target));
}
static const char* brdnat_verdict(int verdict)
{
switch (verdict) {
case EBT_ACCEPT: return "accept";
case EBT_DROP: return "drop";
case EBT_CONTINUE: return "continue";
case EBT_RETURN: return "return";
}
return "";
}
static int brdnat_xlate(struct xt_xlate *xl,
const struct xt_xlate_tg_params *params)
{
const struct ebt_nat_info *natinfo = (const void*)params->target->data;
xt_xlate_add(xl, "ether daddr set %s %s ",
ether_ntoa((struct ether_addr *)natinfo->mac),
brdnat_verdict(natinfo->target));
return 1;
}
static struct xtables_target brdnat_target =
{
.name = "dnat",
.version = XTABLES_VERSION,
.family = NFPROTO_BRIDGE,
.size = XT_ALIGN(sizeof(struct ebt_nat_info)),
.userspacesize = XT_ALIGN(sizeof(struct ebt_nat_info)),
.help = brdnat_print_help,
.init = brdnat_init,
.parse = brdnat_parse,
.final_check = brdnat_final_check,
.print = brdnat_print,
.xlate = brdnat_xlate,
.extra_opts = brdnat_opts,
};
void _init(void)
{
xtables_register_target(&brdnat_target);
}
ebtables-translate -t nat -A PREROUTING -i someport --to-dst de:ad:00:be:ee:ff
nft add rule bridge nat PREROUTING iifname "someport" ether daddr set de:ad:0:be:ee:ff accept counter
ebtables-translate -t nat -A PREROUTING -i someport --to-dst de:ad:00:be:ee:ff --dnat-target ACCEPT
nft add rule bridge nat PREROUTING iifname "someport" ether daddr set de:ad:0:be:ee:ff accept counter
ebtables-translate -t nat -A PREROUTING -i someport --to-dst de:ad:00:be:ee:ff --dnat-target CONTINUE
nft add rule bridge nat PREROUTING iifname "someport" ether daddr set de:ad:0:be:ee:ff continue counter
......@@ -18,8 +18,41 @@
#include <string.h>
#include <getopt.h>
#include <netdb.h>
#include <inttypes.h>
#include <xtables.h>
#include <linux/netfilter_bridge/ebt_ip.h>
#include "libxt_icmp.h"
#define EBT_IP_SOURCE 0x01
#define EBT_IP_DEST 0x02
#define EBT_IP_TOS 0x04
#define EBT_IP_PROTO 0x08
#define EBT_IP_SPORT 0x10
#define EBT_IP_DPORT 0x20
#define EBT_IP_ICMP 0x40
#define EBT_IP_IGMP 0x80
#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO |\
EBT_IP_SPORT | EBT_IP_DPORT | EBT_IP_ICMP | EBT_IP_IGMP)
struct ebt_ip_info {
__be32 saddr;
__be32 daddr;
__be32 smsk;
__be32 dmsk;
__u8 tos;
__u8 protocol;
__u8 bitmask;
__u8 invflags;
union {
__u16 sport[2];
__u8 icmp_type[2];
__u8 igmp_type[2];
};
union {
__u16 dport[2];
__u8 icmp_code[2];
};
};
#define IP_SOURCE '1'
#define IP_DEST '2'
......@@ -27,6 +60,8 @@
#define IP_PROTO '4'
#define IP_SPORT '5'
#define IP_DPORT '6'
#define IP_EBT_ICMP '7'
#define IP_EBT_IGMP '8'
static const struct option brip_opts[] = {
{ .name = "ip-source", .has_arg = true, .val = IP_SOURCE },
......@@ -40,9 +75,73 @@ static const struct option brip_opts[] = {
{ .name = "ip-sport", .has_arg = true, .val = IP_SPORT },
{ .name = "ip-destination-port",.has_arg = true, .val = IP_DPORT },
{ .name = "ip-dport", .has_arg = true, .val = IP_DPORT },
{ .name = "ip-icmp-type", .has_arg = true, .val = IP_EBT_ICMP },
{ .name = "ip-igmp-type", .has_arg = true, .val = IP_EBT_IGMP },
XT_GETOPT_TABLEEND,
};
static const struct xt_icmp_names icmp_codes[] = {
{ "echo-reply", 0, 0, 0xFF },
/* Alias */ { "pong", 0, 0, 0xFF },
{ "destination-unreachable", 3, 0, 0xFF },
{ "network-unreachable", 3, 0, 0 },
{ "host-unreachable", 3, 1, 1 },
{ "protocol-unreachable", 3, 2, 2 },
{ "port-unreachable", 3, 3, 3 },
{ "fragmentation-needed", 3, 4, 4 },
{ "source-route-failed", 3, 5, 5 },
{ "network-unknown", 3, 6, 6 },
{ "host-unknown", 3, 7, 7 },
{ "network-prohibited", 3, 9, 9 },
{ "host-prohibited", 3, 10, 10 },
{ "TOS-network-unreachable", 3, 11, 11 },
{ "TOS-host-unreachable", 3, 12, 12 },
{ "communication-prohibited", 3, 13, 13 },
{ "host-precedence-violation", 3, 14, 14 },
{ "precedence-cutoff", 3, 15, 15 },
{ "source-quench", 4, 0, 0xFF },
{ "redirect", 5, 0, 0xFF },
{ "network-redirect", 5, 0, 0 },
{ "host-redirect", 5, 1, 1 },
{ "TOS-network-redirect", 5, 2, 2 },
{ "TOS-host-redirect", 5, 3, 3 },
{ "echo-request", 8, 0, 0xFF },
/* Alias */ { "ping", 8, 0, 0xFF },
{ "router-advertisement", 9, 0, 0xFF },
{ "router-solicitation", 10, 0, 0xFF },
{ "time-exceeded", 11, 0, 0xFF },
/* Alias */ { "ttl-exceeded", 11, 0, 0xFF },
{ "ttl-zero-during-transit", 11, 0, 0 },
{ "ttl-zero-during-reassembly", 11, 1, 1 },
{ "parameter-problem", 12, 0, 0xFF },
{ "ip-header-bad", 12, 0, 0 },
{ "required-option-missing", 12, 1, 1 },
{ "timestamp-request", 13, 0, 0xFF },
{ "timestamp-reply", 14, 0, 0xFF },
{ "address-mask-request", 17, 0, 0xFF },
{ "address-mask-reply", 18, 0, 0xFF }
};
static const struct xt_icmp_names igmp_types[] = {
{ "membership-query", 0x11 },
{ "membership-report-v1", 0x12 },
{ "membership-report-v2", 0x16 },
{ "leave-group", 0x17 },
{ "membership-report-v3", 0x22 },
};
static void brip_print_help(void)
{
printf(
......@@ -52,7 +151,14 @@ static void brip_print_help(void)
"--ip-tos [!] tos : ip tos specification\n"
"--ip-proto [!] protocol : ip protocol specification\n"
"--ip-sport [!] port[:port] : tcp/udp source port or port range\n"
"--ip-dport [!] port[:port] : tcp/udp destination port or port range\n");
"--ip-dport [!] port[:port] : tcp/udp destination port or port range\n"
"--ip-icmp-type [!] type[[:type]/code[:code]] : icmp type/code or type/code range\n"
"--ip-igmp-type [!] type[:type] : igmp type or type range\n");
printf("\nValid ICMP Types:\n");
xt_print_icmp_types(icmp_codes, ARRAY_SIZE(icmp_codes));
printf("\nValid IGMP Types:\n");
xt_print_icmp_types(igmp_types, ARRAY_SIZE(igmp_types));
}
static void brip_init(struct xt_entry_match *match)
......@@ -161,6 +267,118 @@ static void ebt_parse_ip_address(char *address, uint32_t *addr, uint32_t *msk)
*addr = *addr & *msk;
}
static char *parse_range(const char *str, unsigned int res[])
{
char *next;
if (!xtables_strtoui(str, &next, &res[0], 0, 255))
return NULL;
res[1] = res[0];
if (*next == ':') {
str = next + 1;
if (!xtables_strtoui(str, &next, &res[1], 0, 255))
return NULL;
}
return next;
}
static int ebt_parse_icmp(const struct xt_icmp_names *codes, size_t n_codes,
const char *icmptype, uint8_t type[], uint8_t code[])
{
unsigned int match = n_codes;
unsigned int i, number[2];
for (i = 0; i < n_codes; i++) {
if (strncasecmp(codes[i].name, icmptype, strlen(icmptype)))
continue;
if (match != n_codes)
xtables_error(PARAMETER_PROBLEM, "Ambiguous ICMP type `%s':"
" `%s' or `%s'?",
icmptype, codes[match].name,
codes[i].name);
match = i;
}
if (match < n_codes) {
type[0] = type[1] = codes[match].type;
if (code) {
code[0] = codes[match].code_min;
code[1] = codes[match].code_max;
}
} else {
char *next = parse_range(icmptype, number);
if (!next) {
xtables_error(PARAMETER_PROBLEM, "Unknown ICMP type `%s'",
icmptype);
return -1;
}
type[0] = (uint8_t) number[0];
type[1] = (uint8_t) number[1];
switch (*next) {
case 0:
if (code) {
code[0] = 0;
code[1] = 255;
}
return 0;
case '/':
if (code) {
next = parse_range(next+1, number);
code[0] = (uint8_t) number[0];
code[1] = (uint8_t) number[1];
if (next == NULL)
return -1;
if (next && *next == 0)
return 0;
}
/* fallthrough */
default:
xtables_error(PARAMETER_PROBLEM, "unknown character %c", *next);
return -1;
}
}
return 0;
}
static void print_icmp_code(uint8_t *code)
{
if (!code)
return;
if (code[0] == code[1])
printf("/%"PRIu8 " ", code[0]);
else
printf("/%"PRIu8":%"PRIu8 " ", code[0], code[1]);
}
static void ebt_print_icmp_type(const struct xt_icmp_names *codes,
size_t n_codes, uint8_t *type, uint8_t *code)
{
unsigned int i;
if (type[0] != type[1]) {
printf("%"PRIu8 ":%" PRIu8, type[0], type[1]);
print_icmp_code(code);
return;
}
for (i = 0; i < n_codes; i++) {
if (codes[i].type != type[0])
continue;
if (!code || (codes[i].code_min == code[0] &&
codes[i].code_max == code[1])) {
printf("%s ", codes[i].name);
return;
}
}
printf("%"PRIu8, type[0]);
print_icmp_code(code);
}
static int
brip_parse(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_match **match)
......@@ -192,6 +410,20 @@ brip_parse(int c, char **argv, int invert, unsigned int *flags,
parse_port_range(NULL, optarg, info->dport);
info->bitmask |= EBT_IP_DPORT;
break;
case IP_EBT_ICMP:
if (invert)
info->invflags |= EBT_IP_ICMP;
ebt_parse_icmp(icmp_codes, ARRAY_SIZE(icmp_codes), optarg,
info->icmp_type, info->icmp_code);
info->bitmask |= EBT_IP_ICMP;
break;
case IP_EBT_IGMP:
if (invert)
info->invflags |= EBT_IP_IGMP;
ebt_parse_icmp(igmp_types, ARRAY_SIZE(igmp_types), optarg,
info->igmp_type, NULL);
info->bitmask |= EBT_IP_IGMP;
break;
case IP_EBT_TOS:
if (invert)
info->invflags |= EBT_IP_TOS;
......@@ -205,10 +437,6 @@ brip_parse(int c, char **argv, int invert, unsigned int *flags,
if (invert)
info->invflags |= EBT_IP_PROTO;
info->protocol = xtables_parse_protocol(optarg);
if (info->protocol == -1)
xtables_error(PARAMETER_PROBLEM,
"Unknown specified IP protocol - %s",
optarg);
info->bitmask |= EBT_IP_PROTO;
break;
default:
......@@ -289,6 +517,194 @@ static void brip_print(const void *ip, const struct xt_entry_match *match,
printf("! ");
print_port_range(info->dport);
}
if (info->bitmask & EBT_IP_ICMP) {
printf("--ip-icmp-type ");
if (info->invflags & EBT_IP_ICMP)
printf("! ");
ebt_print_icmp_type(icmp_codes, ARRAY_SIZE(icmp_codes),
info->icmp_type, info->icmp_code);
}
if (info->bitmask & EBT_IP_IGMP) {
printf("--ip-igmp-type ");
if (info->invflags & EBT_IP_IGMP)
printf("! ");
ebt_print_icmp_type(igmp_types, ARRAY_SIZE(igmp_types),
info->igmp_type, NULL);
}
}
static const char *brip_xlate_proto_to_name(uint8_t proto)
{
switch (proto) {
case IPPROTO_TCP:
return "tcp";
case IPPROTO_UDP:
return "udp";
case IPPROTO_UDPLITE:
return "udplite";
case IPPROTO_SCTP:
return "sctp";
case IPPROTO_DCCP:
return "dccp";
default:
return NULL;
}
}
static void brip_xlate_icmp(struct xt_xlate *xl,
const struct ebt_ip_info *info, int bit)
{
if ((info->bitmask & bit) == 0)
return;
xt_xlate_add(xl, "icmp type ");
if (info->invflags & bit)
xt_xlate_add(xl, "!= ");
if (info->icmp_type[0] == info->icmp_type[1])
xt_xlate_add(xl, "%d ", info->icmp_type[0]);
else
xt_xlate_add(xl, "%d-%d ", info->icmp_type[0],
info->icmp_type[1]);
if (info->icmp_code[0] == 0 &&
info->icmp_code[1] == 0xff)
return;
xt_xlate_add(xl, "icmp code ");
if (info->invflags & bit)
xt_xlate_add(xl, "!= ");
if (info->icmp_code[0] == info->icmp_code[1])
xt_xlate_add(xl, "%d ", info->icmp_code[0]);
else
xt_xlate_add(xl, "%d-%d ", info->icmp_code[0],
info->icmp_code[1]);
}
static void brip_xlate_igmp(struct xt_xlate *xl,
const struct ebt_ip_info *info, int bit)
{
if ((info->bitmask & bit) == 0)
return;
xt_xlate_add(xl, "@th,0,8 ");
if (info->invflags & bit)
xt_xlate_add(xl, "!= ");
if (info->icmp_type[0] == info->icmp_type[1])
xt_xlate_add(xl, "%d ", info->icmp_type[0]);
else
xt_xlate_add(xl, "%d-%d ", info->icmp_type[0],
info->icmp_type[1]);
}
static void brip_xlate_th(struct xt_xlate *xl,
const struct ebt_ip_info *info, int bit,
const char *pname)
{
const uint16_t *ports;
if ((info->bitmask & bit) == 0)
return;
switch (bit) {
case EBT_IP_SPORT:
if (pname)
xt_xlate_add(xl, "%s sport ", pname);
else
xt_xlate_add(xl, "@th,0,16 ");
ports = info->sport;
break;
case EBT_IP_DPORT:
if (pname)
xt_xlate_add(xl, "%s dport ", pname);
else
xt_xlate_add(xl, "@th,16,16 ");
ports = info->dport;
break;
default:
return;
}
if (info->invflags & bit)
xt_xlate_add(xl, "!= ");
if (ports[0] == ports[1])
xt_xlate_add(xl, "%d ", ports[0]);
else
xt_xlate_add(xl, "%d-%d ", ports[0], ports[1]);
}
static void brip_xlate_nh(struct xt_xlate *xl,
const struct ebt_ip_info *info, int bit)
{
struct in_addr *addrp, *maskp;
if ((info->bitmask & bit) == 0)
return;
switch (bit) {
case EBT_IP_SOURCE:
xt_xlate_add(xl, "ip saddr ");
addrp = (struct in_addr *)&info->saddr;
maskp = (struct in_addr *)&info->smsk;
break;
case EBT_IP_DEST:
xt_xlate_add(xl, "ip daddr ");
addrp = (struct in_addr *)&info->daddr;
maskp = (struct in_addr *)&info->dmsk;
break;
default:
return;
}
if (info->invflags & bit)
xt_xlate_add(xl, "!= ");
xt_xlate_add(xl, "%s%s ", xtables_ipaddr_to_numeric(addrp),
xtables_ipmask_to_numeric(maskp));
}
static int brip_xlate(struct xt_xlate *xl,
const struct xt_xlate_mt_params *params)
{
const struct ebt_ip_info *info = (const void *)params->match->data;
const char *pname = NULL;
brip_xlate_nh(xl, info, EBT_IP_SOURCE);
brip_xlate_nh(xl, info, EBT_IP_DEST);
if (info->bitmask & EBT_IP_TOS) {
xt_xlate_add(xl, "ip dscp ");
if (info->invflags & EBT_IP_TOS)
xt_xlate_add(xl, "!= ");
xt_xlate_add(xl, "0x%02x ", info->tos & 0x3f); /* remove ECN bits */
}
if (info->bitmask & EBT_IP_PROTO) {
struct protoent *pe;
if (info->bitmask & (EBT_IP_SPORT|EBT_IP_DPORT|EBT_IP_ICMP) &&
(info->invflags & EBT_IP_PROTO) == 0) {
/* port number or icmp given and not inverted, no need to print this */
pname = brip_xlate_proto_to_name(info->protocol);
} else {
xt_xlate_add(xl, "ip protocol ");
if (info->invflags & EBT_IP_PROTO)
xt_xlate_add(xl, "!= ");
pe = getprotobynumber(info->protocol);
if (pe == NULL)
xt_xlate_add(xl, "%d ", info->protocol);
else
xt_xlate_add(xl, "%s ", pe->p_name);
}
}
brip_xlate_th(xl, info, EBT_IP_SPORT, pname);
brip_xlate_th(xl, info, EBT_IP_DPORT, pname);
brip_xlate_icmp(xl, info, EBT_IP_ICMP);
brip_xlate_igmp(xl, info, EBT_IP_IGMP);
return 1;
}
static struct xtables_match brip_match = {
......@@ -303,6 +719,7 @@ static struct xtables_match brip_match = {
.parse = brip_parse,
.final_check = brip_final_check,
.print = brip_print,
.xlate = brip_xlate,
.extra_opts = brip_opts,
};
......
ebtables-translate -A FORWARD -p ip --ip-src ! 192.168.0.0/24 -j ACCEPT
nft add rule bridge filter FORWARD ip saddr != 192.168.0.0/24 counter accept
ebtables-translate -I FORWARD -p ip --ip-dst 10.0.0.1
nft insert rule bridge filter FORWARD ip daddr 10.0.0.1 counter
ebtables-translate -I OUTPUT 3 -p ip -o eth0 --ip-tos 0xff
nft insert rule bridge filter OUTPUT oifname "eth0" ip dscp 0x3f counter
ebtables-translate -A FORWARD -p ip --ip-proto tcp --ip-dport 22
nft add rule bridge filter FORWARD tcp dport 22 counter
ebtables-translate -A FORWARD -p ip --ip-proto udp --ip-sport 1024:65535
nft add rule bridge filter FORWARD udp sport 1024-65535 counter
ebtables-translate -A FORWARD -p ip --ip-proto 253
nft add rule bridge filter FORWARD ip protocol 253 counter
ebtables-translate -A FORWARD -p ip --ip-protocol icmp --ip-icmp-type "echo-request"
nft add rule bridge filter FORWARD icmp type 8 counter
ebtables-translate -A FORWARD -p ip --ip-proto icmp --ip-icmp-type 1/1
nft add rule bridge filter FORWARD icmp type 1 icmp code 1 counter
ebtables-translate -A FORWARD -p ip --ip-protocol icmp --ip-icmp-type ! 1:10
nft add rule bridge filter FORWARD icmp type != 1-10 counter
/* ebt_ip6
*
* Authors:
* Kuo-Lang Tseng <kuo-lang.tseng@intel.com>
* Manohar Castelino <manohar.castelino@intel.com>
*
* Summary:
* This is just a modification of the IPv4 code written by
* Bart De Schuymer <bdschuym@pandora.be>
* with the changes required to support IPv6
*
*/
#include <errno.h>
#include <arpa/inet.h>
#include <inttypes.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <netdb.h>
#include <xtables.h>
#include <linux/netfilter_bridge/ebt_ip6.h>
#include "libxt_icmp.h"
#define IP_SOURCE '1'
#define IP_DEST '2'
#define IP_TCLASS '3'
#define IP_PROTO '4'
#define IP_SPORT '5'
#define IP_DPORT '6'
#define IP_ICMP6 '7'
static const struct option brip6_opts[] = {
{ .name = "ip6-source", .has_arg = true, .val = IP_SOURCE },
{ .name = "ip6-src", .has_arg = true, .val = IP_SOURCE },
{ .name = "ip6-destination", .has_arg = true, .val = IP_DEST },
{ .name = "ip6-dst", .has_arg = true, .val = IP_DEST },
{ .name = "ip6-tclass", .has_arg = true, .val = IP_TCLASS },
{ .name = "ip6-protocol", .has_arg = true, .val = IP_PROTO },
{ .name = "ip6-proto", .has_arg = true, .val = IP_PROTO },
{ .name = "ip6-source-port", .has_arg = true, .val = IP_SPORT },
{ .name = "ip6-sport", .has_arg = true, .val = IP_SPORT },
{ .name = "ip6-destination-port",.has_arg = true,.val = IP_DPORT },
{ .name = "ip6-dport", .has_arg = true, .val = IP_DPORT },
{ .name = "ip6-icmp-type", .has_arg = true, .val = IP_ICMP6 },
XT_GETOPT_TABLEEND,
};
static const struct xt_icmp_names icmpv6_codes[] = {
{ "destination-unreachable", 1, 0, 0xFF },
{ "no-route", 1, 0, 0 },
{ "communication-prohibited", 1, 1, 1 },
{ "address-unreachable", 1, 3, 3 },
{ "port-unreachable", 1, 4, 4 },
{ "packet-too-big", 2, 0, 0xFF },
{ "time-exceeded", 3, 0, 0xFF },
/* Alias */ { "ttl-exceeded", 3, 0, 0xFF },
{ "ttl-zero-during-transit", 3, 0, 0 },
{ "ttl-zero-during-reassembly", 3, 1, 1 },
{ "parameter-problem", 4, 0, 0xFF },
{ "bad-header", 4, 0, 0 },
{ "unknown-header-type", 4, 1, 1 },
{ "unknown-option", 4, 2, 2 },
{ "echo-request", 128, 0, 0xFF },
/* Alias */ { "ping", 128, 0, 0xFF },
{ "echo-reply", 129, 0, 0xFF },
/* Alias */ { "pong", 129, 0, 0xFF },
{ "router-solicitation", 133, 0, 0xFF },
{ "router-advertisement", 134, 0, 0xFF },
{ "neighbour-solicitation", 135, 0, 0xFF },
/* Alias */ { "neighbor-solicitation", 135, 0, 0xFF },
{ "neighbour-advertisement", 136, 0, 0xFF },
/* Alias */ { "neighbor-advertisement", 136, 0, 0xFF },
{ "redirect", 137, 0, 0xFF },
};
static void
parse_port_range(const char *protocol, const char *portstring, uint16_t *ports)
{
char *buffer;
char *cp;
buffer = strdup(portstring);
if ((cp = strchr(buffer, ':')) == NULL)
ports[0] = ports[1] = xtables_parse_port(buffer, NULL);
else {
*cp = '\0';
cp++;
ports[0] = buffer[0] ? xtables_parse_port(buffer, NULL) : 0;
ports[1] = cp[0] ? xtables_parse_port(cp, NULL) : 0xFFFF;
if (ports[0] > ports[1])
xtables_error(PARAMETER_PROBLEM,
"invalid portrange (min > max)");
}
free(buffer);
}
static char *parse_range(const char *str, unsigned int res[])
{
char *next;
if (!xtables_strtoui(str, &next, &res[0], 0, 255))
return NULL;
res[1] = res[0];
if (*next == ':') {
str = next + 1;
if (!xtables_strtoui(str, &next, &res[1], 0, 255))
return NULL;
}
return next;
}
static int
parse_icmpv6(const char *icmpv6type, uint8_t type[], uint8_t code[])
{
static const unsigned int limit = ARRAY_SIZE(icmpv6_codes);
unsigned int match = limit;
unsigned int i, number[2];
for (i = 0; i < limit; i++) {
if (strncasecmp(icmpv6_codes[i].name, icmpv6type, strlen(icmpv6type)))
continue;
if (match != limit)
xtables_error(PARAMETER_PROBLEM, "Ambiguous ICMPv6 type `%s':"
" `%s' or `%s'?",
icmpv6type, icmpv6_codes[match].name,
icmpv6_codes[i].name);
match = i;
}
if (match < limit) {
type[0] = type[1] = icmpv6_codes[match].type;
code[0] = icmpv6_codes[match].code_min;
code[1] = icmpv6_codes[match].code_max;
} else {
char *next = parse_range(icmpv6type, number);
if (!next) {
xtables_error(PARAMETER_PROBLEM, "Unknown ICMPv6 type `%s'",
icmpv6type);
return -1;
}
type[0] = (uint8_t) number[0];
type[1] = (uint8_t) number[1];
switch (*next) {
case 0:
code[0] = 0;
code[1] = 255;
return 0;
case '/':
next = parse_range(next+1, number);
code[0] = (uint8_t) number[0];
code[1] = (uint8_t) number[1];
if (next == NULL)
return -1;
if (next && *next == 0)
return 0;
/* fallthrough */
default:
xtables_error(PARAMETER_PROBLEM, "unknown character %c", *next);
return -1;
}
}
return 0;
}
static void print_port_range(uint16_t *ports)
{
if (ports[0] == ports[1])
printf("%d ", ports[0]);
else
printf("%d:%d ", ports[0], ports[1]);
}
static void print_icmp_code(uint8_t *code)
{
if (code[0] == code[1])
printf("/%"PRIu8 " ", code[0]);
else
printf("/%"PRIu8":%"PRIu8 " ", code[0], code[1]);
}
static void print_icmp_type(uint8_t *type, uint8_t *code)
{
unsigned int i;
if (type[0] != type[1]) {
printf("%"PRIu8 ":%" PRIu8, type[0], type[1]);
print_icmp_code(code);
return;
}
for (i = 0; i < ARRAY_SIZE(icmpv6_codes); i++) {
if (icmpv6_codes[i].type != type[0])
continue;
if (icmpv6_codes[i].code_min == code[0] &&
icmpv6_codes[i].code_max == code[1]) {
printf("%s ", icmpv6_codes[i].name);
return;
}
}
printf("%"PRIu8, type[0]);
print_icmp_code(code);
}
static void brip6_print_help(void)
{
printf(
"ip6 options:\n"
"--ip6-src [!] address[/mask]: ipv6 source specification\n"
"--ip6-dst [!] address[/mask]: ipv6 destination specification\n"
"--ip6-tclass [!] tclass : ipv6 traffic class specification\n"
"--ip6-proto [!] protocol : ipv6 protocol specification\n"
"--ip6-sport [!] port[:port] : tcp/udp source port or port range\n"
"--ip6-dport [!] port[:port] : tcp/udp destination port or port range\n"
"--ip6-icmp-type [!] type[[:type]/code[:code]] : ipv6-icmp type/code or type/code range\n");
printf("Valid ICMPv6 Types:");
xt_print_icmp_types(icmpv6_codes, ARRAY_SIZE(icmpv6_codes));
}
static void brip6_init(struct xt_entry_match *match)
{
struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data;
ipinfo->invflags = 0;
ipinfo->bitmask = 0;
memset(ipinfo->saddr.s6_addr, 0, sizeof(ipinfo->saddr.s6_addr));
memset(ipinfo->smsk.s6_addr, 0, sizeof(ipinfo->smsk.s6_addr));
memset(ipinfo->daddr.s6_addr, 0, sizeof(ipinfo->daddr.s6_addr));
memset(ipinfo->dmsk.s6_addr, 0, sizeof(ipinfo->dmsk.s6_addr));
}
static struct in6_addr *numeric_to_addr(const char *num)
{
static struct in6_addr ap;
int err;
if ((err=inet_pton(AF_INET6, num, &ap)) == 1)
return &ap;
return (struct in6_addr *)NULL;
}
static struct in6_addr *parse_ip6_mask(char *mask)
{
static struct in6_addr maskaddr;
struct in6_addr *addrp;
unsigned int bits;
if (mask == NULL) {
/* no mask at all defaults to 128 bits */
memset(&maskaddr, 0xff, sizeof maskaddr);
return &maskaddr;
}
if ((addrp = numeric_to_addr(mask)) != NULL)
return addrp;
if (!xtables_strtoui(mask, NULL, &bits, 0, 128))
xtables_error(PARAMETER_PROBLEM, "Invalid IPv6 Mask '%s' specified", mask);
if (bits != 0) {
char *p = (char *)&maskaddr;
memset(p, 0xff, bits / 8);
memset(p + (bits / 8) + 1, 0, (128 - bits) / 8);
p[bits / 8] = 0xff << (8 - (bits & 7));
return &maskaddr;
}
memset(&maskaddr, 0, sizeof maskaddr);
return &maskaddr;
}
/* Set the ipv6 mask and address. Callers should check ebt_errormsg[0].
* The string pointed to by address can be altered. */
static void ebt_parse_ip6_address(char *address, struct in6_addr *addr, struct in6_addr *msk)
{
struct in6_addr *tmp_addr;
char buf[256];
char *p;
int i;
int err;
strncpy(buf, address, sizeof(buf) - 1);
/* first the mask */
buf[sizeof(buf) - 1] = '\0';
if ((p = strrchr(buf, '/')) != NULL) {
*p = '\0';
tmp_addr = parse_ip6_mask(p + 1);
} else
tmp_addr = parse_ip6_mask(NULL);
*msk = *tmp_addr;
/* if a null mask is given, the name is ignored, like in "any/0" */
if (!memcmp(msk, &in6addr_any, sizeof(in6addr_any)))
strcpy(buf, "::");
if ((err=inet_pton(AF_INET6, buf, addr)) < 1) {
xtables_error(PARAMETER_PROBLEM, "Invalid IPv6 Address '%s' specified", buf);
return;
}
for (i = 0; i < 4; i++)
addr->s6_addr32[i] &= msk->s6_addr32[i];
}
#define OPT_SOURCE 0x01
#define OPT_DEST 0x02
#define OPT_TCLASS 0x04
#define OPT_PROTO 0x08
#define OPT_SPORT 0x10
#define OPT_DPORT 0x20
static int
brip6_parse(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_match **match)
{
struct ebt_ip6_info *info = (struct ebt_ip6_info *)(*match)->data;
unsigned int i;
char *end;
switch (c) {
case IP_SOURCE:
if (invert)
info->invflags |= EBT_IP6_SOURCE;
ebt_parse_ip6_address(optarg, &info->saddr, &info->smsk);
info->bitmask |= EBT_IP6_SOURCE;
break;
case IP_DEST:
if (invert)
info->invflags |= EBT_IP6_DEST;
ebt_parse_ip6_address(optarg, &info->daddr, &info->dmsk);
info->bitmask |= EBT_IP6_DEST;
break;
case IP_SPORT:
if (invert)
info->invflags |= EBT_IP6_SPORT;
parse_port_range(NULL, optarg, info->sport);
info->bitmask |= EBT_IP6_SPORT;
break;
case IP_DPORT:
if (invert)
info->invflags |= EBT_IP6_DEST;
parse_port_range(NULL, optarg, info->dport);
info->bitmask |= EBT_IP6_DPORT;
break;
case IP_ICMP6:
if (invert)
info->invflags |= EBT_IP6_ICMP6;
if (parse_icmpv6(optarg, info->icmpv6_type, info->icmpv6_code))
return 0;
info->bitmask |= EBT_IP6_ICMP6;
break;
case IP_TCLASS:
if (invert)
info->invflags |= EBT_IP6_TCLASS;
if (!xtables_strtoui(optarg, &end, &i, 0, 255))
xtables_error(PARAMETER_PROBLEM, "Problem with specified IPv6 traffic class '%s'", optarg);
info->tclass = i;
info->bitmask |= EBT_IP6_TCLASS;
break;
case IP_PROTO:
if (invert)
info->invflags |= EBT_IP6_PROTO;
info->protocol = xtables_parse_protocol(optarg);
info->bitmask |= EBT_IP6_PROTO;
break;
default:
return 0;
}
*flags |= info->bitmask;
return 1;
}
static void brip6_final_check(unsigned int flags)
{
if (!flags)
xtables_error(PARAMETER_PROBLEM,
"You must specify proper arguments");
}
static void brip6_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data;
if (ipinfo->bitmask & EBT_IP6_SOURCE) {
printf("--ip6-src ");
if (ipinfo->invflags & EBT_IP6_SOURCE)
printf("! ");
printf("%s", xtables_ip6addr_to_numeric(&ipinfo->saddr));
printf("%s ", xtables_ip6mask_to_numeric(&ipinfo->smsk));
}
if (ipinfo->bitmask & EBT_IP6_DEST) {
printf("--ip6-dst ");
if (ipinfo->invflags & EBT_IP6_DEST)
printf("! ");
printf("%s", xtables_ip6addr_to_numeric(&ipinfo->daddr));
printf("%s ", xtables_ip6mask_to_numeric(&ipinfo->dmsk));
}
if (ipinfo->bitmask & EBT_IP6_TCLASS) {
printf("--ip6-tclass ");
if (ipinfo->invflags & EBT_IP6_TCLASS)
printf("! ");
printf("0x%02X ", ipinfo->tclass);
}
if (ipinfo->bitmask & EBT_IP6_PROTO) {
struct protoent *pe;
printf("--ip6-proto ");
if (ipinfo->invflags & EBT_IP6_PROTO)
printf("! ");
pe = getprotobynumber(ipinfo->protocol);
if (pe == NULL) {
printf("%d ", ipinfo->protocol);
} else {
printf("%s ", pe->p_name);
}
}
if (ipinfo->bitmask & EBT_IP6_SPORT) {
printf("--ip6-sport ");
if (ipinfo->invflags & EBT_IP6_SPORT)
printf("! ");
print_port_range(ipinfo->sport);
}
if (ipinfo->bitmask & EBT_IP6_DPORT) {
printf("--ip6-dport ");
if (ipinfo->invflags & EBT_IP6_DPORT)
printf("! ");
print_port_range(ipinfo->dport);
}
if (ipinfo->bitmask & EBT_IP6_ICMP6) {
printf("--ip6-icmp-type ");
if (ipinfo->invflags & EBT_IP6_ICMP6)
printf("! ");
print_icmp_type(ipinfo->icmpv6_type, ipinfo->icmpv6_code);
}
}
static void brip_xlate_th(struct xt_xlate *xl,
const struct ebt_ip6_info *info, int bit,
const char *pname)
{
const uint16_t *ports;
if ((info->bitmask & bit) == 0)
return;
switch (bit) {
case EBT_IP6_SPORT:
if (pname)
xt_xlate_add(xl, "%s sport ", pname);
else
xt_xlate_add(xl, "@th,0,16 ");
ports = info->sport;
break;
case EBT_IP6_DPORT:
if (pname)
xt_xlate_add(xl, "%s dport ", pname);
else
xt_xlate_add(xl, "@th,16,16 ");
ports = info->dport;
break;
default:
return;
}
if (info->invflags & bit)
xt_xlate_add(xl, "!= ");
if (ports[0] == ports[1])
xt_xlate_add(xl, "%d ", ports[0]);
else
xt_xlate_add(xl, "%d-%d ", ports[0], ports[1]);
}
static void brip_xlate_nh(struct xt_xlate *xl,
const struct ebt_ip6_info *info, int bit)
{
struct in6_addr *addrp, *maskp;
if ((info->bitmask & bit) == 0)
return;
switch (bit) {
case EBT_IP6_SOURCE:
xt_xlate_add(xl, "ip6 saddr ");
addrp = (struct in6_addr *)&info->saddr;
maskp = (struct in6_addr *)&info->smsk;
break;
case EBT_IP6_DEST:
xt_xlate_add(xl, "ip6 daddr ");
addrp = (struct in6_addr *)&info->daddr;
maskp = (struct in6_addr *)&info->dmsk;
break;
default:
return;
}
if (info->invflags & bit)
xt_xlate_add(xl, "!= ");
xt_xlate_add(xl, "%s%s ", xtables_ip6addr_to_numeric(addrp),
xtables_ip6mask_to_numeric(maskp));
}
static const char *brip6_xlate_proto_to_name(uint8_t proto)
{
switch (proto) {
case IPPROTO_TCP:
return "tcp";
case IPPROTO_UDP:
return "udp";
case IPPROTO_UDPLITE:
return "udplite";
case IPPROTO_SCTP:
return "sctp";
case IPPROTO_DCCP:
return "dccp";
default:
return NULL;
}
}
static int brip6_xlate(struct xt_xlate *xl,
const struct xt_xlate_mt_params *params)
{
const struct ebt_ip6_info *info = (const void *)params->match->data;
const char *pname = NULL;
if ((info->bitmask & (EBT_IP6_SOURCE|EBT_IP6_DEST|EBT_IP6_ICMP6|EBT_IP6_TCLASS)) == 0)
xt_xlate_add(xl, "ether type ip6 ");
brip_xlate_nh(xl, info, EBT_IP6_SOURCE);
brip_xlate_nh(xl, info, EBT_IP6_DEST);
if (info->bitmask & EBT_IP6_TCLASS) {
xt_xlate_add(xl, "ip6 dscp ");
if (info->invflags & EBT_IP6_TCLASS)
xt_xlate_add(xl, "!= ");
xt_xlate_add(xl, "0x%02x ", info->tclass & 0x3f); /* remove ECN bits */
}
if (info->bitmask & EBT_IP6_PROTO) {
struct protoent *pe;
if (info->bitmask & (EBT_IP6_SPORT|EBT_IP6_DPORT|EBT_IP6_ICMP6) &&
(info->invflags & EBT_IP6_PROTO) == 0) {
/* port number given and not inverted, no need to
* add explicit 'meta l4proto'.
*/
pname = brip6_xlate_proto_to_name(info->protocol);
} else {
xt_xlate_add(xl, "meta l4proto ");
if (info->invflags & EBT_IP6_PROTO)
xt_xlate_add(xl, "!= ");
pe = getprotobynumber(info->protocol);
if (pe == NULL)
xt_xlate_add(xl, "%d ", info->protocol);
else
xt_xlate_add(xl, "%s ", pe->p_name);
}
}
brip_xlate_th(xl, info, EBT_IP6_SPORT, pname);
brip_xlate_th(xl, info, EBT_IP6_DPORT, pname);
if (info->bitmask & EBT_IP6_ICMP6) {
xt_xlate_add(xl, "icmpv6 type ");
if (info->invflags & EBT_IP6_ICMP6)
xt_xlate_add(xl, "!= ");
if (info->icmpv6_type[0] == info->icmpv6_type[1])
xt_xlate_add(xl, "%d ", info->icmpv6_type[0]);
else
xt_xlate_add(xl, "%d-%d ", info->icmpv6_type[0],
info->icmpv6_type[1]);
if (info->icmpv6_code[0] == 0 &&
info->icmpv6_code[1] == 0xff)
return 1;
xt_xlate_add(xl, "icmpv6 code ");
if (info->invflags & EBT_IP6_ICMP6)
xt_xlate_add(xl, "!= ");
if (info->icmpv6_code[0] == info->icmpv6_code[1])
xt_xlate_add(xl, "%d ", info->icmpv6_code[0]);
else
xt_xlate_add(xl, "%d-%d ", info->icmpv6_code[0],
info->icmpv6_code[1]);
}
return 1;
}
static struct xtables_match brip6_match = {
.name = "ip6",
.revision = 0,
.version = XTABLES_VERSION,
.family = NFPROTO_BRIDGE,
.size = XT_ALIGN(sizeof(struct ebt_ip6_info)),
.userspacesize = XT_ALIGN(sizeof(struct ebt_ip6_info)),
.init = brip6_init,
.help = brip6_print_help,
.parse = brip6_parse,
.final_check = brip6_final_check,
.print = brip6_print,
.xlate = brip6_xlate,
.extra_opts = brip6_opts,
};
void _init(void)
{
xtables_register_match(&brip6_match);
}
ebtables-translate -A FORWARD -p ip6 --ip6-src ! dead::beef/64 -j ACCEPT
nft add rule bridge filter FORWARD ip6 saddr != dead::/64 counter accept
ebtables-translate -A FORWARD -p ip6 ! --ip6-dst dead:beef::/64 -j ACCEPT
nft add rule bridge filter FORWARD ip6 daddr != dead:beef::/64 counter accept
ebtables-translate -I FORWARD -p ip6 --ip6-dst f00:ba::
nft insert rule bridge filter FORWARD ip6 daddr f00:ba:: counter
ebtables-translate -I OUTPUT -o eth0 -p ip6 --ip6-tclass 0xff
nft insert rule bridge filter OUTPUT oifname "eth0" ip6 dscp 0x3f counter
ebtables-translate -A FORWARD -p ip6 --ip6-proto tcp --ip6-dport 22
nft add rule bridge filter FORWARD ether type ip6 tcp dport 22 counter
ebtables-translate -A FORWARD -p ip6 --ip6-proto udp --ip6-sport 1024:65535
nft add rule bridge filter FORWARD ether type ip6 udp sport 1024-65535 counter
ebtables-translate -A FORWARD -p ip6 --ip6-proto 253
nft add rule bridge filter FORWARD ether type ip6 meta l4proto 253 counter
ebtables-translate -A FORWARD -p ip6 --ip6-protocol icmpv6 --ip6-icmp-type "echo-request"
nft add rule bridge filter FORWARD icmpv6 type 128 counter
ebtables-translate -A FORWARD -p ip6 --ip6-protocol icmpv6 --ip6-icmp-type 1/1
nft add rule bridge filter FORWARD icmpv6 type 1 icmpv6 code 1 counter
ebtables-translate -A FORWARD -p ip6 --ip6-protocol icmpv6 --ip6-icmp-type ! 1:10
nft add rule bridge filter FORWARD icmpv6 type != 1-10 counter
/* ebt_limit
*
* Authors:
* Tom Marshall <tommy@home.tig-grr.com>
*
* Mostly copied from iptables' limit match.
*
* September, 2003
*
* Translated to use libxtables for ebtables-compat in 2015 by
* Arturo Borrero Gonzalez <arturo@debian.org>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <errno.h>
#include <xtables.h>
#include <linux/netfilter_bridge/ebt_limit.h>
#include "iptables/nft.h"
#include "iptables/nft-bridge.h"
#define EBT_LIMIT_AVG "3/hour"
#define EBT_LIMIT_BURST 5
#define FLAG_LIMIT 0x01
#define FLAG_LIMIT_BURST 0x02
#define ARG_LIMIT '1'
#define ARG_LIMIT_BURST '2'
static struct option brlimit_opts[] =
{
{ .name = "limit", .has_arg = true, .val = ARG_LIMIT },
{ .name = "limit-burst",.has_arg = true, .val = ARG_LIMIT_BURST },
XT_GETOPT_TABLEEND,
};
static void brlimit_print_help(void)
{
printf(
"limit options:\n"
"--limit avg : max average match rate: default "EBT_LIMIT_AVG"\n"
" [Packets per second unless followed by \n"
" /sec /minute /hour /day postfixes]\n"
"--limit-burst number : number to match in a burst, -1 < number < 10001,\n"
" default %u\n", EBT_LIMIT_BURST);
}
static int parse_rate(const char *rate, uint32_t *val)
{
const char *delim;
uint32_t r;
uint32_t mult = 1; /* Seconds by default. */
delim = strchr(rate, '/');
if (delim) {
if (strlen(delim+1) == 0)
return 0;
if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0)
mult = 1;
else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
mult = 60;
else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
mult = 60*60;
else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
mult = 24*60*60;
else
return 0;
}
r = atoi(rate);
if (!r)
return 0;
/* This would get mapped to infinite (1/day is minimum they
can specify, so we're ok at that end). */
if (r / mult > EBT_LIMIT_SCALE)
return 0;
*val = EBT_LIMIT_SCALE * mult / r;
return 1;
}
static void brlimit_init(struct xt_entry_match *match)
{
struct ebt_limit_info *r = (struct ebt_limit_info *)match->data;
parse_rate(EBT_LIMIT_AVG, &r->avg);
r->burst = EBT_LIMIT_BURST;
}
static int brlimit_parse(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_match **match)
{
struct ebt_limit_info *r = (struct ebt_limit_info *)(*match)->data;
uintmax_t num;
switch (c) {
case ARG_LIMIT:
EBT_CHECK_OPTION(flags, FLAG_LIMIT);
if (invert)
xtables_error(PARAMETER_PROBLEM,
"Unexpected `!' after --limit");
if (!parse_rate(optarg, &r->avg))
xtables_error(PARAMETER_PROBLEM,
"bad rate `%s'", optarg);
break;
case ARG_LIMIT_BURST:
EBT_CHECK_OPTION(flags, FLAG_LIMIT_BURST);
if (invert)
xtables_error(PARAMETER_PROBLEM,
"Unexpected `!' after --limit-burst");
if (!xtables_strtoul(optarg, NULL, &num, 0, 10000))
xtables_error(PARAMETER_PROBLEM,
"bad --limit-burst `%s'", optarg);
r->burst = num;
break;
default:
return 0;
}
return 1;
}
struct rates
{
const char *name;
uint32_t mult;
};
static struct rates g_rates[] =
{
{ "day", EBT_LIMIT_SCALE*24*60*60 },
{ "hour", EBT_LIMIT_SCALE*60*60 },
{ "min", EBT_LIMIT_SCALE*60 },
{ "sec", EBT_LIMIT_SCALE }
};
static void print_rate(uint32_t period)
{
unsigned int i;
for (i = 1; i < sizeof(g_rates)/sizeof(struct rates); i++)
if (period > g_rates[i].mult ||
g_rates[i].mult/period < g_rates[i].mult%period)
break;
printf("%u/%s ", g_rates[i-1].mult / period, g_rates[i-1].name);
}
static void brlimit_print(const void *ip, const struct xt_entry_match *match,
int numeric)
{
struct ebt_limit_info *r = (struct ebt_limit_info *)match->data;
printf("--limit ");
print_rate(r->avg);
printf("--limit-burst %u ", r->burst);
}
static struct xtables_match brlimit_match = {
.name = "limit",
.revision = 0,
.version = XTABLES_VERSION,
.family = NFPROTO_BRIDGE,
.size = XT_ALIGN(sizeof(struct ebt_limit_info)),
.userspacesize = offsetof(struct ebt_limit_info, prev),
.init = brlimit_init,
.help = brlimit_print_help,
.parse = brlimit_parse,
.print = brlimit_print,
.extra_opts = brlimit_opts,
};
void _init(void)
{
xtables_register_match(&brlimit_match);
}
ebtables-translate -A INPUT --limit 3/m --limit-burst 3
nft add rule bridge filter INPUT limit rate 3/minute burst 3 packets counter
ebtables-translate -A INPUT --limit 10/s --limit-burst 5
nft add rule bridge filter INPUT limit rate 10/second burst 5 packets counter
ebtables-translate -A INPUT --limit 10/s --limit-burst 0
nft add rule bridge filter INPUT limit rate 10/second counter
......@@ -27,12 +27,12 @@
#define LOG_LOG '5'
#define LOG_IP6 '6'
typedef struct _code {
struct code {
char *c_name;
int c_val;
} CODE;
};
static CODE eight_priority[] = {
static struct code eight_priority[] = {
{ "emerg", LOG_EMERG },
{ "alert", LOG_ALERT },
{ "crit", LOG_CRIT },
......@@ -92,6 +92,14 @@ static void brlog_init(struct xt_entry_target *t)
loginfo->loglevel = LOG_NOTICE;
}
static unsigned int log_chk_inv(int inv, unsigned int bit, const char *suffix)
{
if (inv)
xtables_error(PARAMETER_PROBLEM,
"Unexpected `!' after --log%s", suffix);
return bit;
}
static int brlog_parse(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_target **target)
{
......@@ -125,26 +133,16 @@ static int brlog_parse(int c, char **argv, int invert, unsigned int *flags,
"Problem with the log-level");
break;
case LOG_IP:
if (invert)
xtables_error(PARAMETER_PROBLEM,
"Unexpected `!' after --log-ip");
loginfo->bitmask |= EBT_LOG_IP;
loginfo->bitmask |= log_chk_inv(invert, EBT_LOG_IP, "-ip");
break;
case LOG_ARP:
if (invert)
xtables_error(PARAMETER_PROBLEM,
"Unexpected `!' after --log-arp");
loginfo->bitmask |= EBT_LOG_ARP;
loginfo->bitmask |= log_chk_inv(invert, EBT_LOG_ARP, "-arp");
break;
case LOG_LOG:
if (invert)
xtables_error(PARAMETER_PROBLEM,
"Unexpected `!' after --log");
loginfo->bitmask |= log_chk_inv(invert, 0, "");
break;
case LOG_IP6:
if (invert)
xtables_error(PARAMETER_PROBLEM,
"Unexpected `!' after --log-ip6");
loginfo->bitmask |= EBT_LOG_IP6;
loginfo->bitmask |= log_chk_inv(invert, EBT_LOG_IP6, "-ip6");
break;
default:
return 0;
......@@ -176,6 +174,27 @@ static void brlog_print(const void *ip, const struct xt_entry_target *target,
printf(" ");
}
static int brlog_xlate(struct xt_xlate *xl,
const struct xt_xlate_tg_params *params)
{
const struct ebt_log_info *loginfo = (const void *)params->target->data;
xt_xlate_add(xl, "log");
if (loginfo->prefix[0]) {
if (params->escape_quotes)
xt_xlate_add(xl, " prefix \\\"%s\\\"", loginfo->prefix);
else
xt_xlate_add(xl, " prefix \"%s\"", loginfo->prefix);
}
if (loginfo->loglevel != LOG_DEFAULT_LEVEL)
xt_xlate_add(xl, " level %s", eight_priority[loginfo->loglevel].c_name);
xt_xlate_add(xl, " flags ether ");
return 1;
}
static struct xtables_target brlog_target = {
.name = "log",
.revision = 0,
......@@ -188,6 +207,7 @@ static struct xtables_target brlog_target = {
.parse = brlog_parse,
.final_check = brlog_final_check,
.print = brlog_print,
.xlate = brlog_xlate,
.extra_opts = brlog_opts,
};
......
ebtables-translate -A INPUT --log
nft add rule bridge filter INPUT log level notice flags ether counter
ebtables-translate -A INPUT --log-level 1
nft add rule bridge filter INPUT log level alert flags ether counter
ebtables-translate -A INPUT --log-level crit
nft add rule bridge filter INPUT log level crit flags ether counter
ebtables-translate -A INPUT --log-level emerg --log-ip --log-arp --log-ip6
nft add rule bridge filter INPUT log level emerg flags ether counter
ebtables-translate -A INPUT --log-level crit --log-ip --log-arp --log-ip6 --log-prefix foo
nft add rule bridge filter INPUT log prefix "foo" level crit flags ether counter
......@@ -18,14 +18,12 @@
#include "iptables/nft.h"
#include "iptables/nft-bridge.h"
static int mark_supplied;
#define MARK_TARGET '1'
#define MARK_SETMARK '2'
#define MARK_ORMARK '3'
#define MARK_ANDMARK '4'
#define MARK_XORMARK '5'
static struct option brmark_opts[] = {
static const struct option brmark_opts[] = {
{ .name = "mark-target",.has_arg = true, .val = MARK_TARGET },
/* an oldtime messup, we should have always used the scheme
* <extension-name>-<option> */
......@@ -54,7 +52,6 @@ static void brmark_init(struct xt_entry_target *target)
info->target = EBT_ACCEPT;
info->mark = 0;
mark_supplied = 0;
}
#define OPT_MARK_TARGET 0x01
......@@ -133,7 +130,6 @@ brmark_parse(int c, char **argv, int invert, unsigned int *flags,
xtables_error(PARAMETER_PROBLEM, "Bad MARK value '%s'",
optarg);
mark_supplied = 1;
return 1;
}
......@@ -162,14 +158,54 @@ static void brmark_print(const void *ip, const struct xt_entry_target *target,
static void brmark_final_check(unsigned int flags)
{
if (mark_supplied == 0)
xtables_error(PARAMETER_PROBLEM, "No mark value supplied");
if (!flags)
xtables_error(PARAMETER_PROBLEM,
"You must specify some option");
}
static const char* brmark_verdict(int verdict)
{
switch (verdict) {
case EBT_ACCEPT: return "accept";
case EBT_DROP: return "drop";
case EBT_CONTINUE: return "continue";
case EBT_RETURN: return "return";
}
return "";
}
static int brmark_xlate(struct xt_xlate *xl,
const struct xt_xlate_tg_params *params)
{
const struct ebt_mark_t_info *info = (const void*)params->target->data;
int tmp;
tmp = info->target & ~EBT_VERDICT_BITS;
xt_xlate_add(xl, "meta mark set ");
switch (tmp) {
case MARK_SET_VALUE:
break;
case MARK_OR_VALUE:
xt_xlate_add(xl, "meta mark or ");
break;
case MARK_XOR_VALUE:
xt_xlate_add(xl, "meta mark xor ");
break;
case MARK_AND_VALUE:
xt_xlate_add(xl, "meta mark and ");
break;
default:
return 0;
}
tmp = info->target & EBT_VERDICT_BITS;
xt_xlate_add(xl, "0x%lx %s ", info->mark, brmark_verdict(tmp));
return 1;
}
static struct xtables_target brmark_target = {
.name = "mark",
.revision = 0,
......@@ -182,6 +218,7 @@ static struct xtables_target brmark_target = {
.parse = brmark_parse,
.final_check = brmark_final_check,
.print = brmark_print,
.xlate = brmark_xlate,
.extra_opts = brmark_opts,
};
......
ebtables-translate -A INPUT --mark-set 42
nft add rule bridge filter INPUT mark set 0x2a counter
ebtables-translate -A INPUT --mark-or 42 --mark-target RETURN
nft add rule bridge filter INPUT mark set mark or 0x2a counter return
ebtables-translate -A INPUT --mark-and 42 --mark-target ACCEPT
nft add rule bridge filter INPUT mark set mark and 0x2a counter accept
ebtables-translate -A INPUT --mark-xor 42 --mark-target DROP
nft add rule bridge filter INPUT mark set mark xor 0x2a counter drop
......@@ -18,7 +18,7 @@
#define MARK '1'
static struct option brmark_m_opts[] = {
static const struct option brmark_m_opts[] = {
{ .name = "mark", .has_arg = true, .val = MARK },
XT_GETOPT_TABLEEND,
};
......@@ -97,6 +97,30 @@ static void brmark_m_print(const void *ip, const struct xt_entry_match *match,
printf("0x%lx ", info->mark);
}
static int brmark_m_xlate(struct xt_xlate *xl,
const struct xt_xlate_mt_params *params)
{
const struct ebt_mark_m_info *info = (const void*)params->match->data;
enum xt_op op = XT_OP_EQ;
if (info->invert)
op = XT_OP_NEQ;
xt_xlate_add(xl, "meta mark ");
if (info->bitmask == EBT_MARK_OR) {
xt_xlate_add(xl, "and 0x%x %s0 ", info->mask,
info->invert ? "" : "!= ");
} else if (info->mask != 0xffffffffU) {
xt_xlate_add(xl, "and 0x%x %s0x%x ", info->mask,
op == XT_OP_EQ ? "" : "!= ", info->mark);
} else {
xt_xlate_add(xl, "%s0x%x ",
op == XT_OP_EQ ? "" : "!= ", info->mark);
}
return 1;
}
static struct xtables_match brmark_m_match = {
.name = "mark_m",
.revision = 0,
......@@ -109,6 +133,7 @@ static struct xtables_match brmark_m_match = {
.parse = brmark_m_parse,
.final_check = brmark_m_final_check,
.print = brmark_m_print,
.xlate = brmark_m_xlate,
.extra_opts = brmark_m_opts,
};
......
ebtables-translate -A INPUT --mark 42
nft add rule bridge filter INPUT meta mark 0x2a counter
ebtables-translate -A INPUT ! --mark 42
nft add rule bridge filter INPUT meta mark != 0x2a counter
ebtables-translate -A INPUT --mark ! 42
nft add rule bridge filter INPUT meta mark != 0x2a counter
ebtables-translate -A INPUT --mark ! 0x1/0xff
nft add rule bridge filter INPUT meta mark and 0xff != 0x1 counter
ebtables-translate -A INPUT --mark /0x02
nft add rule bridge filter INPUT meta mark and 0x2 != 0 counter
......@@ -30,7 +30,7 @@ enum {
NFLOG_NFLOG = 0x16,
};
static struct option brnflog_opts[] = {
static const struct option brnflog_opts[] = {
{ .name = "nflog-group", .has_arg = true, .val = NFLOG_GROUP},
{ .name = "nflog-prefix", .has_arg = true, .val = NFLOG_PREFIX},
{ .name = "nflog-range", .has_arg = true, .val = NFLOG_RANGE},
......@@ -124,6 +124,29 @@ brnflog_print(const void *ip, const struct xt_entry_target *target,
printf("--nflog-threshold %d ", info->threshold);
}
static int brnflog_xlate(struct xt_xlate *xl,
const struct xt_xlate_tg_params *params)
{
const struct ebt_nflog_info *info = (void *)params->target->data;
xt_xlate_add(xl, "log ");
if (info->prefix[0] != '\0') {
if (params->escape_quotes)
xt_xlate_add(xl, "prefix \\\"%s\\\" ", info->prefix);
else
xt_xlate_add(xl, "prefix \"%s\" ", info->prefix);
}
xt_xlate_add(xl, "group %u ", info->group);
if (info->len)
xt_xlate_add(xl, "snaplen %u ", info->len);
if (info->threshold != EBT_NFLOG_DEFAULT_THRESHOLD)
xt_xlate_add(xl, "queue-threshold %u ", info->threshold);
return 1;
}
static struct xtables_target brnflog_watcher = {
.name = "nflog",
.revision = 0,
......@@ -135,6 +158,7 @@ static struct xtables_target brnflog_watcher = {
.help = brnflog_help,
.parse = brnflog_parse,
.print = brnflog_print,
.xlate = brnflog_xlate,
.extra_opts = brnflog_opts,
};
......
ebtables-translate -A INPUT --nflog
nft add rule bridge filter INPUT log group 1 counter
ebtables-translate -A INPUT --nflog-group 42
nft add rule bridge filter INPUT log group 42 counter
ebtables-translate -A INPUT --nflog-range 42
nft add rule bridge filter INPUT log group 1 snaplen 42 counter
ebtables-translate -A INPUT --nflog-threshold 100 --nflog-prefix foo
nft add rule bridge filter INPUT log prefix "foo" group 1 queue-threshold 100 counter
/* ebt_pkttype
*
* Authors:
* Bart De Schuymer <bdschuym@pandora.be>
*
* April, 2003
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <netdb.h>
#include <xtables.h>
#include <linux/if_packet.h>
#include <linux/netfilter_bridge/ebt_pkttype.h>
static const char *classes[] = {
"host",
"broadcast",
"multicast",
"otherhost",
"outgoing",
"loopback",
"fastroute",
};
static const struct option brpkttype_opts[] =
{
{ "pkttype-type" , required_argument, 0, '1' },
{ 0 }
};
static void brpkttype_print_help(void)
{
printf(
"pkttype options:\n"
"--pkttype-type [!] type: class the packet belongs to\n"
"Possible values: broadcast, multicast, host, otherhost, or any other byte value (which would be pretty useless).\n");
}
static int brpkttype_parse(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_match **match)
{
struct ebt_pkttype_info *ptinfo = (struct ebt_pkttype_info *)(*match)->data;
char *end;
long int i;
switch (c) {
case '1':
if (invert)
ptinfo->invert = 1;
i = strtol(optarg, &end, 16);
if (*end != '\0') {
for (i = 0; i < ARRAY_SIZE(classes); i++) {
if (!strcasecmp(optarg, classes[i]))
break;
}
if (i >= ARRAY_SIZE(classes))
xtables_error(PARAMETER_PROBLEM, "Could not parse class '%s'", optarg);
}
if (i < 0 || i > 255)
xtables_error(PARAMETER_PROBLEM, "Problem with specified pkttype class");
ptinfo->pkt_type = (uint8_t)i;
break;
default:
return 0;
}
return 1;
}
static void brpkttype_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
struct ebt_pkttype_info *pt = (struct ebt_pkttype_info *)match->data;
printf("--pkttype-type %s", pt->invert ? "! " : "");
if (pt->pkt_type < ARRAY_SIZE(classes))
printf("%s ", classes[pt->pkt_type]);
else
printf("%d ", pt->pkt_type);
}
static int brpkttype_xlate(struct xt_xlate *xl,
const struct xt_xlate_mt_params *params)
{
const struct ebt_pkttype_info *info = (const void*)params->match->data;
xt_xlate_add(xl, "meta pkttype %s", info->invert ? "!= " : "");
if (info->pkt_type < 3)
xt_xlate_add(xl, "%s ", classes[info->pkt_type]);
else if (info->pkt_type == 3)
xt_xlate_add(xl, "other ");
else
xt_xlate_add(xl, "%d ", info->pkt_type);
return 1;
}
static struct xtables_match brpkttype_match = {
.name = "pkttype",
.version = XTABLES_VERSION,
.family = NFPROTO_BRIDGE,
.size = XT_ALIGN(sizeof(struct ebt_pkttype_info)),
.userspacesize = XT_ALIGN(sizeof(struct ebt_pkttype_info)),
.help = brpkttype_print_help,
.parse = brpkttype_parse,
.print = brpkttype_print,
.xlate = brpkttype_xlate,
.extra_opts = brpkttype_opts,
};
void _init(void)
{
xtables_register_match(&brpkttype_match);
}
ebtables-translate -A INPUT --pkttype-type host
nft add rule bridge filter INPUT meta pkttype host counter
ebtables-translate -A INPUT ! --pkttype-type broadcast
nft add rule bridge filter INPUT meta pkttype != broadcast counter
ebtables-translate -A INPUT --pkttype-type ! multicast
nft add rule bridge filter INPUT meta pkttype != multicast counter
ebtables-translate -A INPUT --pkttype-type otherhost
nft add rule bridge filter INPUT meta pkttype other counter
ebtables-translate -A INPUT --pkttype-type outgoing
nft add rule bridge filter INPUT meta pkttype 4 counter
ebtables-translate -A INPUT --pkttype-type loopback
nft add rule bridge filter INPUT meta pkttype 5 counter
ebtables-translate -A INPUT --pkttype-type fastroute
nft add rule bridge filter INPUT meta pkttype 6 counter
/* ebt_redirect
*
* Authors:
* Bart De Schuymer <bdschuym@pandora.be>
*
* April, 2002
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <xtables.h>
#include <linux/netfilter_bridge/ebt_redirect.h>
#include "iptables/nft.h"
#include "iptables/nft-bridge.h"
#define REDIRECT_TARGET '1'
static const struct option brredir_opts[] =
{
{ "redirect-target", required_argument, 0, REDIRECT_TARGET },
{ 0 }
};
static void brredir_print_help(void)
{
printf(
"redirect option:\n"
" --redirect-target target : ACCEPT, DROP, RETURN or CONTINUE\n");
}
static void brredir_init(struct xt_entry_target *target)
{
struct ebt_redirect_info *redirectinfo =
(struct ebt_redirect_info *)target->data;
redirectinfo->target = EBT_ACCEPT;
}
#define OPT_REDIRECT_TARGET 0x01
static int brredir_parse(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_target **target)
{
struct ebt_redirect_info *redirectinfo =
(struct ebt_redirect_info *)(*target)->data;
switch (c) {
case REDIRECT_TARGET:
EBT_CHECK_OPTION(flags, OPT_REDIRECT_TARGET);
if (ebt_fill_target(optarg, (unsigned int *)&redirectinfo->target))
xtables_error(PARAMETER_PROBLEM, "Illegal --redirect-target target");
break;
default:
return 0;
}
return 1;
}
static void brredir_print(const void *ip, const struct xt_entry_target *target, int numeric)
{
struct ebt_redirect_info *redirectinfo =
(struct ebt_redirect_info *)target->data;
if (redirectinfo->target == EBT_ACCEPT)
return;
printf(" --redirect-target %s", ebt_target_name(redirectinfo->target));
}
static const char* brredir_verdict(int verdict)
{
switch (verdict) {
case EBT_ACCEPT: return "accept";
case EBT_DROP: return "drop";
case EBT_CONTINUE: return "continue";
case EBT_RETURN: return "return";
}
return "";
}
static int brredir_xlate(struct xt_xlate *xl,
const struct xt_xlate_tg_params *params)
{
const struct ebt_redirect_info *red = (const void*)params->target->data;
xt_xlate_add(xl, "meta set pkttype host");
if (red->target != EBT_ACCEPT)
xt_xlate_add(xl, " %s ", brredir_verdict(red->target));
return 0;
}
static struct xtables_target brredirect_target = {
.name = "redirect",
.version = XTABLES_VERSION,
.family = NFPROTO_BRIDGE,
.size = XT_ALIGN(sizeof(struct ebt_redirect_info)),
.userspacesize = XT_ALIGN(sizeof(struct ebt_redirect_info)),
.help = brredir_print_help,
.init = brredir_init,
.parse = brredir_parse,
.print = brredir_print,
.xlate = brredir_xlate,
.extra_opts = brredir_opts,
};
void _init(void)
{
xtables_register_target(&brredirect_target);
}
/* ebt_nat
*
* Authors:
* Bart De Schuymer <bdschuym@pandora.be>
*
* June, 2002
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <netinet/ether.h>
#include <xtables.h>
#include <linux/netfilter_bridge/ebt_nat.h>
#include "iptables/nft.h"
#include "iptables/nft-bridge.h"
#define NAT_S '1'
#define NAT_S_TARGET '2'
#define NAT_S_ARP '3'
static const struct option brsnat_opts[] =
{
{ "to-source" , required_argument, 0, NAT_S },
{ "to-src" , required_argument, 0, NAT_S },
{ "snat-target" , required_argument, 0, NAT_S_TARGET },
{ "snat-arp" , no_argument, 0, NAT_S_ARP },
{ 0 }
};
static void brsnat_print_help(void)
{
printf(
"snat options:\n"
" --to-src address : MAC address to map source to\n"
" --snat-target target : ACCEPT, DROP, RETURN or CONTINUE\n"
" --snat-arp : also change src address in arp msg\n");
}
static void brsnat_init(struct xt_entry_target *target)
{
struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
natinfo->target = EBT_ACCEPT;
}
#define OPT_SNAT 0x01
#define OPT_SNAT_TARGET 0x02
#define OPT_SNAT_ARP 0x04
static int brsnat_parse(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_target **target)
{
struct ebt_nat_info *natinfo = (struct ebt_nat_info *)(*target)->data;
struct ether_addr *addr;
switch (c) {
case NAT_S:
EBT_CHECK_OPTION(flags, OPT_SNAT);
if (!(addr = ether_aton(optarg)))
xtables_error(PARAMETER_PROBLEM, "Problem with specified --to-source mac");
memcpy(natinfo->mac, addr, ETH_ALEN);
break;
case NAT_S_TARGET:
{ unsigned int tmp;
EBT_CHECK_OPTION(flags, OPT_SNAT_TARGET);
if (ebt_fill_target(optarg, &tmp))
xtables_error(PARAMETER_PROBLEM, "Illegal --snat-target target");
natinfo->target = (natinfo->target & ~EBT_VERDICT_BITS) | (tmp & EBT_VERDICT_BITS);
}
break;
case NAT_S_ARP:
EBT_CHECK_OPTION(flags, OPT_SNAT_ARP);
natinfo->target ^= NAT_ARP_BIT;
break;
default:
return 0;
}
return 1;
}
static void brsnat_final_check(unsigned int flags)
{
if (!flags)
xtables_error(PARAMETER_PROBLEM,
"You must specify proper arguments");
}
static void ebt_print_mac(const unsigned char *mac)
{
printf("%s", ether_ntoa((struct ether_addr *) mac));
}
static void brsnat_print(const void *ip, const struct xt_entry_target *target, int numeric)
{
struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
printf("--to-src ");
ebt_print_mac(natinfo->mac);
if (!(natinfo->target&NAT_ARP_BIT))
printf(" --snat-arp");
printf(" --snat-target %s", ebt_target_name((natinfo->target|~EBT_VERDICT_BITS)));
}
static const char* brsnat_verdict(int verdict)
{
switch (verdict) {
case EBT_ACCEPT: return "accept";
case EBT_DROP: return "drop";
case EBT_CONTINUE: return "continue";
case EBT_RETURN: return "return";
}
return "";
}
static int brsnat_xlate(struct xt_xlate *xl,
const struct xt_xlate_tg_params *params)
{
const struct ebt_nat_info *natinfo = (const void*)params->target->data;
xt_xlate_add(xl, "ether saddr set %s ",
ether_ntoa((struct ether_addr *)natinfo->mac));
/* NAT_ARP_BIT set -> no arp mangling, not set -> arp mangling (yes, its inverted) */
if (!(natinfo->target&NAT_ARP_BIT))
return 0;
xt_xlate_add(xl, "%s ", brsnat_verdict(natinfo->target | ~EBT_VERDICT_BITS));
return 1;
}
static struct xtables_target brsnat_target =
{
.name = "snat",
.version = XTABLES_VERSION,
.family = NFPROTO_BRIDGE,
.size = XT_ALIGN(sizeof(struct ebt_nat_info)),
.userspacesize = XT_ALIGN(sizeof(struct ebt_nat_info)),
.help = brsnat_print_help,
.init = brsnat_init,
.parse = brsnat_parse,
.final_check = brsnat_final_check,
.print = brsnat_print,
.xlate = brsnat_xlate,
.extra_opts = brsnat_opts,
};
void _init(void)
{
xtables_register_target(&brsnat_target);
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment