From 58adbd9dd5dbc0d48ad6e77848e316556f7c2ce1 Mon Sep 17 00:00:00 2001 From: gborowiak Date: Sun, 7 Sep 2003 13:10:40 +0000 Subject: Initial release --- .../include/linux/netfilter_bridge/ebt_inat.h | 23 ++ kernel/linux/net/bridge/netfilter/ebt_idnat.c | 127 +++++++ kernel/linux/net/bridge/netfilter/ebt_isnat.c | 122 +++++++ userspace/ebtables2/extensions/ebt_inat.c | 379 +++++++++++++++++++++ 4 files changed, 651 insertions(+) create mode 100644 kernel/linux/include/linux/netfilter_bridge/ebt_inat.h create mode 100644 kernel/linux/net/bridge/netfilter/ebt_idnat.c create mode 100644 kernel/linux/net/bridge/netfilter/ebt_isnat.c create mode 100644 userspace/ebtables2/extensions/ebt_inat.c 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 + * + * September, 2003 + * + */ + +#include +#include +#include +#include +#include +#include +#include + +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 + * + * September, 2003 + * + */ + +#include +#include +#include +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include +#include "../include/ebtables_u.h" +#include + +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); +} -- cgit v1.2.3