387 lines
9.8 KiB
C
387 lines
9.8 KiB
C
/* ebt_inat
|
|
*
|
|
* Authors:
|
|
* Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
|
|
*
|
|
* August, 2003
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <netinet/ether.h>
|
|
#include <getopt.h>
|
|
#include <ctype.h>
|
|
#include "../include/ebtables_u.h"
|
|
#include <linux/netfilter_bridge/ebt_inat.h>
|
|
|
|
static int s_sub_supplied, d_sub_supplied;
|
|
|
|
#define NAT_S '1'
|
|
#define NAT_D '1'
|
|
#define NAT_S_SUB '2'
|
|
#define NAT_D_SUB '2'
|
|
#define NAT_S_TARGET '3'
|
|
#define NAT_D_TARGET '3'
|
|
static struct option opts_s[] =
|
|
{
|
|
{ "isnat-list" , required_argument, 0, NAT_S },
|
|
{ "isnat-sub" , required_argument, 0, NAT_S_SUB },
|
|
{ "isnat-default-target" , required_argument, 0, NAT_S_TARGET },
|
|
{ 0 }
|
|
};
|
|
|
|
static struct option opts_d[] =
|
|
{
|
|
{ "idnat-list" , required_argument, 0, NAT_D },
|
|
{ "idnat-sub" , required_argument, 0, NAT_D_SUB },
|
|
{ "idnat-default-target" , required_argument, 0, NAT_D_TARGET },
|
|
{ 0 }
|
|
};
|
|
|
|
static void print_help_common(const char *cas)
|
|
{
|
|
printf(
|
|
"isnat options:\n"
|
|
" --i%1.1snat-list : indexed list of MAC addresses\n"
|
|
" --i%1.1snat-sub : /24 subnet to which the rule apply\n"
|
|
" --i%1.1snat-default-target target : ACCEPT, DROP, RETURN or CONTINUE\n"
|
|
"Indexed list of addresses is as follows:\n"
|
|
"\tlist := chunk\n"
|
|
"\tlist := list chunk\n"
|
|
"\tchunk := pair ','\n"
|
|
"\tpair := index '=' action\n"
|
|
"\taction := mac_addr\n"
|
|
"\taction := mac_addr '+'\n"
|
|
"\taction := '_'\n"
|
|
"where\n"
|
|
"\tindex -- an integer [0..255]\n"
|
|
"\tmac_addr -- a MAC address in format xx:xx:xx:xx:xx:xx\n"
|
|
"If '_' at some index is specified, packets with last %s IP address byte\n"
|
|
"equal to index are DROPped. If there is a MAC address, they are %1.1snatted\n"
|
|
"to this and the target is CONTINUE. If this MAC is followed by '+', the\n"
|
|
"target is ACCEPT.\n"
|
|
"For example,\n"
|
|
"--idnat-list 2=20:21:22:23:24:25,4=_,7=30:31:32:33:34:35,\n"
|
|
"is valid.\n"
|
|
"The subnet MUST be specified. Only packets with 3 first bytes of their\n"
|
|
"%s address are considered. --i%1.1snat-sub parameter must begin with\n"
|
|
"3 integers separated by dots. Only they are considered, the rest is ignored.\n"
|
|
"No matter if you write '192.168.42.', '192.168.42.0', '192.168.42.12',\n"
|
|
"'192.168.42.0/24', '192.168.42.0/23' or '192.168.42.i%1.1snat_sucks!!!',\n"
|
|
"The numbers 192, 168 and 42 are taken and it behaves like a /24 IP subnet.\n"
|
|
"--i%1.1snat-default-target affects only the packet not matching the subnet.\n",
|
|
cas, cas, cas, cas, cas, cas, cas, cas,
|
|
cas, cas, cas, cas, cas, cas, cas, cas
|
|
);
|
|
}
|
|
|
|
static void print_help_s()
|
|
{
|
|
print_help_common("src");
|
|
}
|
|
|
|
static void print_help_d()
|
|
{
|
|
print_help_common("dest");
|
|
}
|
|
|
|
static void init_s(struct ebt_entry_target *target)
|
|
{
|
|
struct ebt_inat_info *natinfo = (struct ebt_inat_info *)target->data;
|
|
|
|
s_sub_supplied = 0;
|
|
memset(natinfo, 0, sizeof(struct ebt_inat_info));
|
|
natinfo->target = EBT_CONTINUE;
|
|
return;
|
|
}
|
|
|
|
static void init_d(struct ebt_entry_target *target)
|
|
{
|
|
struct ebt_inat_info *natinfo = (struct ebt_inat_info *)target->data;
|
|
|
|
d_sub_supplied = 0;
|
|
memset(natinfo, 0, sizeof(struct ebt_inat_info));
|
|
natinfo->target = EBT_CONTINUE;
|
|
return;
|
|
}
|
|
|
|
static void parse_list(const char *arg, struct ebt_inat_info *info)
|
|
{
|
|
int i;
|
|
char c;
|
|
int count = 0;
|
|
int now_index = 1;
|
|
int index;
|
|
char buf[4];
|
|
unsigned char mac[6];
|
|
int ibuf = 0;
|
|
int imac = 0;
|
|
int target;
|
|
memset(buf, 0, 4);
|
|
i = 0;
|
|
while (1) {
|
|
c = arg[i];
|
|
if (now_index) {
|
|
if (isdigit(c)) {
|
|
buf[ibuf++] = c;
|
|
if (ibuf > 3) {
|
|
print_error("Index too long at position %d", i);
|
|
}
|
|
goto next;
|
|
}
|
|
if (c == '=') {
|
|
if (ibuf == 0) {
|
|
print_error("Integer index expected before '=' at position %d", i);
|
|
}
|
|
buf[ibuf] = 0;
|
|
ibuf = 0;
|
|
index = atoi(buf);
|
|
if (index < 0 || 255 < index) {
|
|
print_error("Index out of range [0..255], namely %d", index);
|
|
}
|
|
now_index = 0;
|
|
memset(mac, 0, 6);
|
|
imac = 0;
|
|
target = EBT_CONTINUE;
|
|
goto next;
|
|
}
|
|
if (c == '\0') {
|
|
goto next;
|
|
}
|
|
print_error("Unexpected '%c' where integer or '=' expected", c);
|
|
}
|
|
else {
|
|
if (isxdigit(c)) {
|
|
buf[ibuf++] = c;
|
|
if (ibuf > 2) {
|
|
print_error("MAC address chunk too long at position %d", i);
|
|
}
|
|
goto next;
|
|
}
|
|
if (c == ':' || c == ',' || c == '\0') {
|
|
buf[ibuf] = 0;
|
|
ibuf = 0;
|
|
mac[imac++] = strtol(buf, 0, 16);
|
|
if (c == ',' || c == '\0') {
|
|
info->a[index].enabled = 1;
|
|
info->a[index].target = target;
|
|
memcpy(info->a[index].mac, mac, 6);
|
|
now_index = 1;
|
|
count++;
|
|
goto next;
|
|
}
|
|
if (c == ':' && imac >= 6) {
|
|
print_error("Too many MAC address chunks at position %d", i);
|
|
}
|
|
goto next;
|
|
}
|
|
if (c == '_') {
|
|
target = EBT_DROP;
|
|
goto next;
|
|
}
|
|
if (c == '+') {
|
|
target = EBT_ACCEPT;
|
|
goto next;
|
|
}
|
|
print_error("Unexpected '%c' where hex digit, '_', '+', ',' or end of string expected", c);
|
|
}
|
|
next:
|
|
if (!c) break;
|
|
i++;
|
|
}
|
|
if (count == 0) {
|
|
print_error("List empty");
|
|
}
|
|
}
|
|
|
|
static uint32_t parse_ip(const char *s)
|
|
{
|
|
int a0, a1, a2;
|
|
char ip[4];
|
|
sscanf(s, "%d.%d.%d", &a0, &a1, &a2);
|
|
ip[0] = a0;
|
|
ip[1] = a1;
|
|
ip[2] = a2;
|
|
ip[3] = 0;
|
|
return *(uint32_t*)ip;
|
|
}
|
|
|
|
#define OPT_ISNAT 0x01
|
|
#define OPT_ISNAT_SUB 0x02
|
|
#define OPT_ISNAT_TARGET 0x04
|
|
static int parse_s(int c, char **argv, int argc,
|
|
const struct ebt_u_entry *entry, unsigned int *flags,
|
|
struct ebt_entry_target **target)
|
|
{
|
|
struct ebt_inat_info *natinfo = (struct ebt_inat_info *)(*target)->data;
|
|
|
|
switch (c) {
|
|
case NAT_S:
|
|
check_option(flags, OPT_ISNAT);
|
|
parse_list(optarg, natinfo);
|
|
break;
|
|
case NAT_S_TARGET:
|
|
check_option(flags, OPT_ISNAT_TARGET);
|
|
if (FILL_TARGET(optarg, natinfo->target))
|
|
print_error("Illegal --isnat-default-target target");
|
|
break;
|
|
case NAT_S_SUB:
|
|
natinfo->ip_subnet = parse_ip(optarg);
|
|
s_sub_supplied = 1;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
#define OPT_IDNAT 0x01
|
|
#define OPT_IDNAT_SUB 0x02
|
|
#define OPT_IDNAT_TARGET 0x04
|
|
static int parse_d(int c, char **argv, int argc,
|
|
const struct ebt_u_entry *entry, unsigned int *flags,
|
|
struct ebt_entry_target **target)
|
|
{
|
|
struct ebt_inat_info *natinfo = (struct ebt_inat_info *)(*target)->data;
|
|
|
|
switch (c) {
|
|
case NAT_D:
|
|
check_option(flags, OPT_IDNAT);
|
|
parse_list(optarg, natinfo);
|
|
break;
|
|
case NAT_D_TARGET:
|
|
check_option(flags, OPT_IDNAT_TARGET);
|
|
if (FILL_TARGET(optarg, natinfo->target))
|
|
print_error("Illegal --idnat-default-target target");
|
|
break;
|
|
case NAT_D_SUB:
|
|
natinfo->ip_subnet = parse_ip(optarg);
|
|
d_sub_supplied = 1;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static void final_check_s(const struct ebt_u_entry *entry,
|
|
const struct ebt_entry_target *target, const char *name,
|
|
unsigned int hookmask, unsigned int time)
|
|
{
|
|
struct ebt_inat_info *natinfo = (struct ebt_inat_info *)target->data;
|
|
|
|
if (BASE_CHAIN && natinfo->target == EBT_RETURN)
|
|
print_error("--isnat-default-target RETURN not allowed on base chain");
|
|
CLEAR_BASE_CHAIN_BIT;
|
|
if ((hookmask & ~(1 << NF_BR_POST_ROUTING)) || strcmp(name, "nat"))
|
|
print_error("Wrong chain for isnat");
|
|
if (time == 0 && s_sub_supplied == 0)
|
|
print_error("No isnat subnet supplied");
|
|
}
|
|
|
|
static void final_check_d(const struct ebt_u_entry *entry,
|
|
const struct ebt_entry_target *target, const char *name,
|
|
unsigned int hookmask, unsigned int time)
|
|
{
|
|
struct ebt_inat_info *natinfo = (struct ebt_inat_info *)target->data;
|
|
|
|
if (BASE_CHAIN && natinfo->target == EBT_RETURN)
|
|
print_error("--idnat-default-target RETURN not allowed on base chain");
|
|
CLEAR_BASE_CHAIN_BIT;
|
|
if (((hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))
|
|
|| strcmp(name, "nat")) &&
|
|
((hookmask & ~(1 << NF_BR_BROUTING)) || strcmp(name, "broute")))
|
|
print_error("Wrong chain for idnat");
|
|
if (time == 0 && d_sub_supplied == 0)
|
|
print_error("No idnat subnet supplied");
|
|
}
|
|
|
|
static void print_list(const struct ebt_inat_info *info)
|
|
{
|
|
int i;
|
|
for (i = 0; i < 256; i++) {
|
|
if (info->a[i].enabled) {
|
|
printf("%d=", i);
|
|
if (info->a[i].target == EBT_DROP) {
|
|
printf("_");
|
|
}
|
|
else {
|
|
if (info->a[i].target == EBT_ACCEPT) {
|
|
printf("+");
|
|
}
|
|
print_mac(info->a[i].mac);
|
|
}
|
|
printf(",");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void print_s(const struct ebt_u_entry *entry,
|
|
const struct ebt_entry_target *target)
|
|
{
|
|
struct ebt_inat_info *info = (struct ebt_inat_info *)target->data;
|
|
|
|
unsigned char sub[4];
|
|
*(uint32_t*)sub = info->ip_subnet;
|
|
printf("--isnat-sub %u.%u.%u.0/24", sub[0], sub[1], sub[2]);
|
|
printf(" --isnat-list ");
|
|
print_list(info);
|
|
printf(" --isnat-default-target %s", TARGET_NAME(info->target));
|
|
}
|
|
|
|
static void print_d(const struct ebt_u_entry *entry,
|
|
const struct ebt_entry_target *target)
|
|
{
|
|
struct ebt_inat_info *info = (struct ebt_inat_info *)target->data;
|
|
|
|
unsigned char sub[4];
|
|
*(uint32_t*)sub = info->ip_subnet;
|
|
printf("--idnat-sub %u.%u.%u.0/24", sub[0], sub[1], sub[2]);
|
|
printf(" --idnat-list ");
|
|
print_list(info);
|
|
printf(" --idnat-default-target %s", TARGET_NAME(info->target));
|
|
}
|
|
|
|
static int compare(const struct ebt_entry_target *t1,
|
|
const struct ebt_entry_target *t2)
|
|
{
|
|
struct ebt_inat_info *natinfo1 = (struct ebt_inat_info *)t1->data;
|
|
struct ebt_inat_info *natinfo2 = (struct ebt_inat_info *)t2->data;
|
|
|
|
|
|
return !memcmp(natinfo1, natinfo2, sizeof(struct ebt_inat_info));
|
|
}
|
|
|
|
static struct ebt_u_target isnat_target =
|
|
{
|
|
.name = EBT_ISNAT_TARGET,
|
|
.size = sizeof(struct ebt_inat_info),
|
|
.help = print_help_s,
|
|
.init = init_s,
|
|
.parse = parse_s,
|
|
.final_check = final_check_s,
|
|
.print = print_s,
|
|
.compare = compare,
|
|
.extra_ops = opts_s,
|
|
};
|
|
|
|
static struct ebt_u_target idnat_target =
|
|
{
|
|
.name = EBT_IDNAT_TARGET,
|
|
.size = sizeof(struct ebt_inat_info),
|
|
.help = print_help_d,
|
|
.init = init_d,
|
|
.parse = parse_d,
|
|
.final_check = final_check_d,
|
|
.print = print_d,
|
|
.compare = compare,
|
|
.extra_ops = opts_d,
|
|
};
|
|
|
|
static void _init(void) __attribute__ ((constructor));
|
|
static void _init(void)
|
|
{
|
|
register_target(&isnat_target);
|
|
register_target(&idnat_target);
|
|
}
|