summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--kernel/linux/include/linux/netfilter_bridge/ebt_inat.h23
-rw-r--r--kernel/linux/net/bridge/netfilter/ebt_idnat.c127
-rw-r--r--kernel/linux/net/bridge/netfilter/ebt_isnat.c122
-rw-r--r--userspace/ebtables2/extensions/ebt_inat.c379
4 files changed, 651 insertions, 0 deletions
diff --git a/kernel/linux/include/linux/netfilter_bridge/ebt_inat.h b/kernel/linux/include/linux/netfilter_bridge/ebt_inat.h
new file mode 100644
index 0000000..d370d7b
--- /dev/null
+++ b/kernel/linux/include/linux/netfilter_bridge/ebt_inat.h
@@ -0,0 +1,23 @@
+#ifndef __LINUX_BRIDGE_EBT_NAT_H
+#define __LINUX_BRIDGE_EBT_NAT_H
+
+struct ebt_inat_tuple
+{
+ int enabled;
+ unsigned char mac[ETH_ALEN];
+ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
+ int target;
+};
+
+struct ebt_inat_info
+{
+ uint32_t ip_subnet;
+ struct ebt_inat_tuple a[256];
+ // EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN
+ int target;
+};
+
+#define EBT_ISNAT_TARGET "isnat"
+#define EBT_IDNAT_TARGET "idnat"
+
+#endif
diff --git a/kernel/linux/net/bridge/netfilter/ebt_idnat.c b/kernel/linux/net/bridge/netfilter/ebt_idnat.c
new file mode 100644
index 0000000..75ea05b
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebt_idnat.c
@@ -0,0 +1,127 @@
+/*
+ * ebt_idnat
+ *
+ * Authors:
+ * Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
+ *
+ * September, 2003
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_inat.h>
+#include <linux/module.h>
+#include <linux/ip.h>
+#include <linux/if_arp.h>
+#include <linux/types.h>
+#include <net/sock.h>
+
+static int get_ip_dst(const struct sk_buff *skb, uint32_t *addr)
+{
+ if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_IP)) {
+ *addr = skb->nh.iph->daddr;
+ return 1;
+ }
+ if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) {
+
+ uint32_t arp_len = sizeof(struct arphdr) +
+ (2 * (((*skb).nh.arph)->ar_hln)) +
+ (2 * (((*skb).nh.arph)->ar_pln));
+
+ // Make sure the packet is long enough.
+ if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
+ return 0;
+ // IPv4 addresses are always 4 bytes.
+ if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
+ return 0;
+
+ memcpy(addr, ((*skb).nh.raw) + sizeof(struct arphdr) +
+ (2*(((*skb).nh.arph)->ar_hln)) +
+ (((*skb).nh.arph)->ar_pln), sizeof(uint32_t));
+
+ return 2;
+ }
+ return 0;
+}
+
+static int ebt_target_idnat(struct sk_buff **pskb, unsigned int hooknr,
+ const struct net_device *in, const struct net_device *out,
+ const void *data, unsigned int datalen)
+{
+ struct ebt_inat_info *info = (struct ebt_inat_info *)data;
+ uint32_t ip;
+ int index;
+ struct ebt_inat_tuple *tuple;
+
+ if (!get_ip_dst(*pskb, &ip)) {
+ /* not an ARP or IPV4 packet */
+ return info->target;
+ }
+
+ if ((ip & __constant_htonl(0xffffff00)) != info->ip_subnet) {
+
+ /* outside our range */
+ return info->target;
+ }
+
+ index = ((unsigned char*)&ip)[3]; /* the last byte; network packets are big endian */
+ tuple = &info->a[index];
+
+ if (!tuple->enabled) {
+ /* we do not want to alter packets with this IP */
+ return info->target;
+ }
+
+ memcpy(((**pskb).mac.ethernet)->h_dest, tuple->mac,
+ ETH_ALEN * sizeof(unsigned char));
+
+ if ((**pskb).mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) {
+ /* change the payload */
+ memcpy(
+ (**pskb).nh.raw + sizeof(struct arphdr) +
+ (**pskb).nh.arph->ar_hln +
+ (**pskb).nh.arph->ar_pln, tuple->mac, ETH_ALEN);
+ }
+
+ return tuple->target;
+}
+
+static int ebt_target_idnat_check(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+ struct ebt_inat_info *info = (struct ebt_inat_info *)data;
+
+ if (BASE_CHAIN && info->target == EBT_RETURN)
+ return -EINVAL;
+ CLEAR_BASE_CHAIN_BIT;
+ if ( (strcmp(tablename, "nat") ||
+ (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
+ (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
+ return -EINVAL;
+ if (datalen != EBT_ALIGN(sizeof(struct ebt_inat_info)))
+ return -EINVAL;
+ if (INVALID_TARGET)
+ return -EINVAL;
+ return 0;
+}
+
+static struct ebt_target idnat =
+{
+ {NULL, NULL}, EBT_IDNAT_TARGET, ebt_target_idnat, ebt_target_idnat_check,
+ NULL, THIS_MODULE
+};
+
+static int __init init(void)
+{
+ return ebt_register_target(&idnat);
+}
+
+static void __exit fini(void)
+{
+ ebt_unregister_target(&idnat);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux/net/bridge/netfilter/ebt_isnat.c b/kernel/linux/net/bridge/netfilter/ebt_isnat.c
new file mode 100644
index 0000000..7f2f844
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebt_isnat.c
@@ -0,0 +1,122 @@
+/*
+ * ebt_isnat
+ *
+ * Authors:
+ * Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
+ *
+ * September, 2003
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_inat.h>
+#include <linux/module.h>
+#include <linux/ip.h>
+#include <linux/if_arp.h>
+#include <linux/types.h>
+#include <net/sock.h>
+
+static int get_ip_src(const struct sk_buff *skb, uint32_t *addr)
+{
+ if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_IP)) {
+ *addr = skb->nh.iph->saddr;
+ return 1;
+ }
+ if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) {
+
+ uint32_t arp_len = sizeof(struct arphdr) +
+ (2 * (((*skb).nh.arph)->ar_hln)) +
+ (2 * (((*skb).nh.arph)->ar_pln));
+
+ // Make sure the packet is long enough.
+ if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
+ return 0;
+ // IPv4 addresses are always 4 bytes.
+ if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
+ return 0;
+
+ memcpy(addr, ((*skb).nh.raw) + sizeof(struct arphdr) +
+ ((((*skb).nh.arph)->ar_hln)), sizeof(uint32_t));
+
+ return 2;
+ }
+ return 0;
+}
+
+static int ebt_target_isnat(struct sk_buff **pskb, unsigned int hooknr,
+ const struct net_device *in, const struct net_device *out,
+ const void *data, unsigned int datalen)
+{
+ struct ebt_inat_info *info = (struct ebt_inat_info *)data;
+ uint32_t ip;
+ int index;
+ struct ebt_inat_tuple *tuple;
+
+ if (!get_ip_src(*pskb, &ip)) {
+ /* not an ARP or IPV4 packet */
+ return info->target;
+ }
+
+ if ((ip & __constant_htonl(0xffffff00)) != info->ip_subnet) {
+
+ /* outside our range */
+ return info->target;
+ }
+
+ index = ((unsigned char*)&ip)[3]; /* the last byte; network packets are big endian */
+ tuple = &info->a[index];
+
+ if (!tuple->enabled) {
+ /* we do not want to alter packets with this IP */
+ return info->target;
+ }
+
+ memcpy(((**pskb).mac.ethernet)->h_source, tuple->mac, ETH_ALEN);
+
+ if ((**pskb).mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) {
+ /* change the payload */
+ memcpy((**pskb).nh.raw + sizeof(struct arphdr), tuple->mac, ETH_ALEN);
+ }
+
+ return tuple->target;
+}
+
+static int ebt_target_isnat_check(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+ struct ebt_inat_info *info = (struct ebt_inat_info *)data;
+
+ if (datalen != sizeof(struct ebt_inat_info))
+ return -EINVAL;
+ if (BASE_CHAIN && info->target == EBT_RETURN)
+ return -EINVAL;
+ CLEAR_BASE_CHAIN_BIT;
+ if (strcmp(tablename, "nat"))
+ return -EINVAL;
+ if (hookmask & ~(1 << NF_BR_POST_ROUTING))
+ return -EINVAL;
+ if (INVALID_TARGET)
+ return -EINVAL;
+ return 0;
+}
+
+static struct ebt_target isnat =
+{
+ {NULL, NULL}, EBT_ISNAT_TARGET, ebt_target_isnat, ebt_target_isnat_check,
+ NULL, THIS_MODULE
+};
+
+static int __init init(void)
+{
+ return ebt_register_target(&isnat);
+}
+
+static void __exit fini(void)
+{
+ ebt_unregister_target(&isnat);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
diff --git a/userspace/ebtables2/extensions/ebt_inat.c b/userspace/ebtables2/extensions/ebt_inat.c
new file mode 100644
index 0000000..4ec16b5
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebt_inat.c
@@ -0,0 +1,379 @@
+#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("+");
+ }
+ printf("%s", ether_ntoa((struct ether_addr *)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 =
+{
+ EBT_ISNAT_TARGET,
+ sizeof(struct ebt_inat_info),
+ print_help_s,
+ init_s,
+ parse_s,
+ final_check_s,
+ print_s,
+ compare,
+ opts_s,
+};
+
+static struct ebt_u_target idnat_target =
+{
+ EBT_IDNAT_TARGET,
+ sizeof(struct ebt_inat_info),
+ print_help_d,
+ init_d,
+ parse_d,
+ final_check_d,
+ print_d,
+ compare,
+ opts_d
+};
+
+static void _init(void) __attribute__ ((constructor));
+static void _init(void)
+{
+ register_target(&isnat_target);
+ register_target(&idnat_target);
+}