You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
633 lines
16 KiB
633 lines
16 KiB
/* 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 ≈
|
|
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_DPORT;
|
|
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);
|
|
}
|