From a4cc047af5fa0160d4b5a94947ec21851ae83f2a Mon Sep 17 00:00:00 2001 From: CN_SZTL Date: Tue, 25 Feb 2020 23:08:37 +0800 Subject: [PATCH] kernel/iptables: add modules for qos-gargoyle --- include/netfilter.mk | 7 + package/kernel/linux/modules/netfilter.mk | 4 + package/network/utils/iptables/Makefile | 26 +- ...add-gargoyle-netfilter-match-modules.patch | 2811 ++++ target/linux/generic/config-4.14 | 4 + target/linux/generic/config-4.19 | 4 + ...nel-gargoyle-netfilter-match-modules.patch | 10864 ++++++++++++++++ ...nel-gargoyle-netfilter-match-modules.patch | 10864 ++++++++++++++++ 8 files changed, 24583 insertions(+), 1 deletion(-) create mode 100644 package/network/utils/iptables/patches/608-add-gargoyle-netfilter-match-modules.patch create mode 100644 target/linux/generic/pending-4.14/608-add-kernel-gargoyle-netfilter-match-modules.patch create mode 100644 target/linux/generic/pending-4.19/608-add-kernel-gargoyle-netfilter-match-modules.patch diff --git a/include/netfilter.mk b/include/netfilter.mk index 50ea76cfe5..7d486e65cc 100644 --- a/include/netfilter.mk +++ b/include/netfilter.mk @@ -114,6 +114,13 @@ $(eval $(call nf_add,IPT_FILTER,CONFIG_NETFILTER_XT_MATCH_BPF, $(P_XT)xt_bpf)) $(eval $(call nf_add,IPT_IMQ,CONFIG_IP_NF_TARGET_IMQ, $(P_V4)ipt_IMQ)) $(eval $(call nf_add,IPT_IMQ,CONFIG_NETFILTER_XT_TARGET_IMQ, $(P_XT)xt_IMQ)) +# gargoyle-qos + +$(eval $(call nf_add,IPT_BANDWIDTH,CONFIG_IP_NF_MATCH_BANDWIDTH, $(P_V4)ipt_bandwidth)) +$(eval $(call nf_add,IPT_TIMERANGE,CONFIG_IP_NF_MATCH_TIMERANGE, $(P_V4)ipt_timerange)) +$(eval $(call nf_add,IPT_WEBMON,CONFIG_IP_NF_MATCH_WEBMON, $(P_V4)ipt_webmon)) +$(eval $(call nf_add,IPT_WEBURL,CONFIG_IP_NF_MATCH_WEBURL, $(P_V4)ipt_weburl)) + # ipopt $(eval $(call nf_add,IPT_IPOPT,CONFIG_NETFILTER_XT_MATCH_DSCP, $(P_XT)xt_dscp)) diff --git a/package/kernel/linux/modules/netfilter.mk b/package/kernel/linux/modules/netfilter.mk index 167bcc9bc5..20ef3ef904 100644 --- a/package/kernel/linux/modules/netfilter.mk +++ b/package/kernel/linux/modules/netfilter.mk @@ -505,6 +505,7 @@ endef $(eval $(call KernelPackage,ipt-imq)) + define KernelPackage/ipt-bandwidth SUBMENU:=$(NF_MENU) TITLE:=bandwidth @@ -516,6 +517,7 @@ endef $(eval $(call KernelPackage,ipt-bandwidth)) + define KernelPackage/ipt-timerange SUBMENU:=$(NF_MENU) TITLE:=timerange @@ -527,6 +529,7 @@ endef $(eval $(call KernelPackage,ipt-timerange)) + define KernelPackage/ipt-webmon SUBMENU:=$(NF_MENU) TITLE:=webmon @@ -538,6 +541,7 @@ endef $(eval $(call KernelPackage,ipt-webmon)) + define KernelPackage/ipt-weburl SUBMENU:=$(NF_MENU) TITLE:=weburl diff --git a/package/network/utils/iptables/Makefile b/package/network/utils/iptables/Makefile index e8955d2254..2ec3f1118d 100644 --- a/package/network/utils/iptables/Makefile +++ b/package/network/utils/iptables/Makefile @@ -171,7 +171,7 @@ endef define Package/iptables-mod-cgroup $(call Package/iptables/Module, +kmod-ipt-cgroup) - TITLE:=cgroup extension + TITLE:=cgroup extension endef define Package/iptables-mod-cgroup/description @@ -187,6 +187,26 @@ $(call Package/iptables/Module, +kmod-ipt-imq) TITLE:=IMQ support endef +define Package/iptables-mod-bandwidth +$(call Package/iptables/Module, +kmod-ipt-bandwidth) + TITLE:=bandwidth option extensions +endef + +define Package/iptables-mod-timerange +$(call Package/iptables/Module, +kmod-ipt-timerange) + TITLE:=timerange option extensions +endef + +define Package/iptables-mod-webmon +$(call Package/iptables/Module, +kmod-ipt-webmon) + TITLE:=webmon option extensions +endef + +define Package/iptables-mod-weburl +$(call Package/iptables/Module, +kmod-ipt-weburl) + TITLE:=weburl option extensions +endef + define Package/iptables-mod-imq/description iptables extension for IMQ support. @@ -706,6 +726,10 @@ $(eval $(call BuildPlugin,iptables-mod-physdev,$(IPT_PHYSDEV-m))) $(eval $(call BuildPlugin,iptables-mod-filter,$(IPT_FILTER-m))) $(eval $(call BuildPlugin,iptables-mod-cgroup,$(IPT_CGROUP-m))) $(eval $(call BuildPlugin,iptables-mod-imq,$(IPT_IMQ-m))) +$(eval $(call BuildPlugin,iptables-mod-bandwidth,$(IPT_BANDWIDTH-m))) +$(eval $(call BuildPlugin,iptables-mod-timerange,$(IPT_TIMERANGE-m))) +$(eval $(call BuildPlugin,iptables-mod-webmon,$(IPT_WEBMON-m))) +$(eval $(call BuildPlugin,iptables-mod-weburl,$(IPT_WEBURL-m))) $(eval $(call BuildPlugin,iptables-mod-ipopt,$(IPT_IPOPT-m))) $(eval $(call BuildPlugin,iptables-mod-ipsec,$(IPT_IPSEC-m))) $(eval $(call BuildPlugin,iptables-mod-nat-extra,$(IPT_NAT_EXTRA-m))) diff --git a/package/network/utils/iptables/patches/608-add-gargoyle-netfilter-match-modules.patch b/package/network/utils/iptables/patches/608-add-gargoyle-netfilter-match-modules.patch new file mode 100644 index 0000000000..17ad17f7e4 --- /dev/null +++ b/package/network/utils/iptables/patches/608-add-gargoyle-netfilter-match-modules.patch @@ -0,0 +1,2811 @@ +--- /dev/null ++++ b/extensions/libipt_bandwidth.c +@@ -0,0 +1,657 @@ ++/* bandwidth -- An iptables extension for bandwidth monitoring/control ++ * Can be used to efficiently monitor bandwidth and/or implement bandwidth quotas ++ * Can be queried using the iptbwctl userspace library ++ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) ++ * ++ * ++ * Copyright © 2009 by Eric Bishop ++ * ++ * This file is free software: you may copy, redistribute and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation, either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This file is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * in iptables 1.4.0 and higher, iptables.h includes xtables.h, which ++ * we can use to check whether we need to deal with the new requirements ++ * in pre-processor directives below ++ */ ++#include ++#include ++ ++#ifdef _XTABLES_H ++ #define iptables_rule_match xtables_rule_match ++ #define iptables_match xtables_match ++ #define iptables_target xtables_target ++ #define ipt_tryload xt_tryload ++#endif ++ ++/* ++ * XTABLES_VERSION_CODE is only defined in versions 1.4.1 and later, which ++ * also require the use of xtables_register_match ++ * ++ * Version 1.4.0 uses register_match like previous versions ++ */ ++#ifdef XTABLES_VERSION_CODE ++ #define register_match xtables_register_match ++#endif ++ ++ ++ ++int get_minutes_west(void); ++void set_kernel_timezone(void); ++int parse_sub(char* subnet_string, uint32_t* subnet, uint32_t* subnet_mask); ++static unsigned long get_pow(unsigned long base, unsigned long pow); ++static void param_problem_exit_error(char* msg); ++ ++ ++/* Function which prints out usage message. */ ++static void help(void) ++{ ++ printf("bandwidth options:\n"); ++ printf(" --id [unique identifier for querying bandwidth]\n"); ++ printf(" --type [combined|individual_src|individual_dst|individual_local|individual_remote]\n"); ++ printf(" --subnet [a.b.c.d/mask] (0 < mask < 32)\n"); ++ printf(" --greater_than [BYTES]\n"); ++ printf(" --less_than [BYTES]\n"); ++ printf(" --current_bandwidth [BYTES]\n"); ++ printf(" --reset_interval [minute|hour|day|week|month]\n"); ++ printf(" --reset_time [OFFSET IN SECONDS]\n"); ++ printf(" --intervals_to_save [NUMBER OF PREVIOS INTERVALS TO STORE IN MEMORY]\n"); ++ printf(" --last_backup_time [UTC SECONDS SINCE 1970]\n"); ++ printf(" --bcheck Check another bandwidth rule without incrementing it\n"); ++ printf(" --bcheck_with_src_dst_swap Check another bandwidth rule without incrementing it, swapping src & dst ips for check\n"); ++} ++ ++static struct option opts[] = ++{ ++ { .name = "id", .has_arg = 1, .flag = 0, .val = BANDWIDTH_ID }, ++ { .name = "type", .has_arg = 1, .flag = 0, .val = BANDWIDTH_TYPE }, ++ { .name = "subnet", .has_arg = 1, .flag = 0, .val = BANDWIDTH_SUBNET }, ++ { .name = "greater_than", .has_arg = 1, .flag = 0, .val = BANDWIDTH_GT }, ++ { .name = "less_than", .has_arg = 1, .flag = 0, .val = BANDWIDTH_LT }, ++ { .name = "current_bandwidth", .has_arg = 1, .flag = 0, .val = BANDWIDTH_CURRENT }, ++ { .name = "reset_interval", .has_arg = 1, .flag = 0, .val = BANDWIDTH_RESET_INTERVAL }, ++ { .name = "reset_time", .has_arg = 1, .flag = 0, .val = BANDWIDTH_RESET_TIME }, ++ { .name = "intervals_to_save", .has_arg = 1, .flag = 0, .val = BANDWIDTH_NUM_INTERVALS }, ++ { .name = "last_backup_time", .has_arg = 1, .flag = 0, .val = BANDWIDTH_LAST_BACKUP}, ++ { .name = "bcheck", .has_arg = 0, .flag = 0, .val = BANDWIDTH_CHECK_NOSWAP }, ++ { .name = "bcheck_with_src_dst_swap", .has_arg = 0, .flag = 0, .val = BANDWIDTH_CHECK_SWAP }, ++ { .name = 0 } ++}; ++ ++ ++/* Function which parses command options; returns true if it ++ ate an option */ ++static int parse( int c, ++ char **argv, ++ int invert, ++ unsigned int *flags, ++#ifdef _XTABLES_H ++ const void *entry, ++#else ++ const struct ipt_entry *entry, ++ unsigned int *nfcache, ++#endif ++ struct ipt_entry_match **match ++ ) ++{ ++ struct ipt_bandwidth_info *info = (struct ipt_bandwidth_info *)(*match)->data; ++ int valid_arg = 0; ++ long int num_read; ++ uint64_t read_64; ++ time_t read_time; ++ ++ /* set defaults first time we get here */ ++ if(*flags == 0) ++ { ++ /* generate random id */ ++ srand ( time(NULL) ); ++ unsigned long id_num = rand(); ++ sprintf(info->id, "%lu", id_num); ++ ++ info->type = BANDWIDTH_COMBINED; ++ info->check_type = BANDWIDTH_CHECK_NOSWAP; ++ info->local_subnet = 0; ++ info->local_subnet_mask = 0; ++ info->cmp = BANDWIDTH_MONITOR; /* don't test greater/less than, just monitor bandwidth */ ++ info->current_bandwidth = 0; ++ info->reset_is_constant_interval = 0; ++ info->reset_interval = BANDWIDTH_NEVER; ++ info->reset_time=0; ++ info->last_backup_time = 0; ++ info->next_reset = 0; ++ ++ info->num_intervals_to_save=0; ++ ++ info->non_const_self = NULL; ++ info->ref_count = NULL; ++ ++ *flags = *flags + BANDWIDTH_INITIALIZED; ++ } ++ ++ switch (c) ++ { ++ case BANDWIDTH_ID: ++ if(strlen(optarg) < BANDWIDTH_MAX_ID_LENGTH) ++ { ++ sprintf(info->id, "%s", optarg); ++ valid_arg = 1; ++ } ++ c=0; ++ break; ++ case BANDWIDTH_TYPE: ++ valid_arg = 1; ++ if(strcmp(optarg, "combined") == 0) ++ { ++ info->type = BANDWIDTH_COMBINED; ++ } ++ else if(strcmp(optarg, "individual_src") == 0) ++ { ++ info->type = BANDWIDTH_INDIVIDUAL_SRC; ++ } ++ else if(strcmp(optarg, "individual_dst") == 0) ++ { ++ info->type = BANDWIDTH_INDIVIDUAL_DST; ++ } ++ else if(strcmp(optarg, "individual_local") == 0) ++ { ++ info->type = BANDWIDTH_INDIVIDUAL_LOCAL; ++ *flags = *flags + BANDWIDTH_REQUIRES_SUBNET; ++ } ++ else if(strcmp(optarg, "individual_remote") == 0) ++ { ++ info->type = BANDWIDTH_INDIVIDUAL_REMOTE; ++ *flags = *flags + BANDWIDTH_REQUIRES_SUBNET; ++ } ++ else ++ { ++ valid_arg = 0; ++ } ++ ++ c=0; ++ break; ++ ++ case BANDWIDTH_SUBNET: ++ valid_arg = parse_sub(optarg, &(info->local_subnet), &(info->local_subnet_mask)); ++ break; ++ case BANDWIDTH_LT: ++ num_read = sscanf(argv[optind-1], "%lld", &read_64); ++ if(num_read > 0 && (*flags & BANDWIDTH_CMP) == 0) ++ { ++ info->cmp = BANDWIDTH_LT; ++ info->bandwidth_cutoff = read_64; ++ valid_arg = 1; ++ } ++ c = BANDWIDTH_CMP; //only need one flag for less_than/greater_than ++ break; ++ case BANDWIDTH_GT: ++ num_read = sscanf(argv[optind-1], "%lld", &read_64); ++ if(num_read > 0 && (*flags & BANDWIDTH_CMP) == 0) ++ { ++ info->cmp = BANDWIDTH_GT; ++ info->bandwidth_cutoff = read_64; ++ valid_arg = 1; ++ } ++ c = BANDWIDTH_CMP; //only need one flag for less_than/greater_than ++ break; ++ case BANDWIDTH_CHECK_NOSWAP: ++ if( (*flags & BANDWIDTH_CMP) == 0 ) ++ { ++ info->cmp = BANDWIDTH_CHECK; ++ info->check_type = BANDWIDTH_CHECK_NOSWAP; ++ valid_arg = 1; ++ } ++ c = BANDWIDTH_CMP; ++ break; ++ case BANDWIDTH_CHECK_SWAP: ++ if( (*flags & BANDWIDTH_CMP) == 0 ) ++ { ++ info->cmp = BANDWIDTH_CHECK; ++ info->check_type = BANDWIDTH_CHECK_SWAP; ++ valid_arg = 1; ++ } ++ c = BANDWIDTH_CMP; ++ break; ++ case BANDWIDTH_CURRENT: ++ num_read = sscanf(argv[optind-1], "%lld", &read_64); ++ if(num_read > 0 ) ++ { ++ info->current_bandwidth = read_64; ++ valid_arg = 1; ++ } ++ break; ++ case BANDWIDTH_RESET_INTERVAL: ++ valid_arg = 1; ++ if(strcmp(argv[optind-1],"minute") ==0) ++ { ++ info->reset_interval = BANDWIDTH_MINUTE; ++ info->reset_is_constant_interval = 0; ++ } ++ else if(strcmp(argv[optind-1],"hour") ==0) ++ { ++ info->reset_interval = BANDWIDTH_HOUR; ++ info->reset_is_constant_interval = 0; ++ } ++ else if(strcmp(argv[optind-1],"day") ==0) ++ { ++ info->reset_interval = BANDWIDTH_DAY; ++ info->reset_is_constant_interval = 0; ++ } ++ else if(strcmp(argv[optind-1],"week") ==0) ++ { ++ info->reset_interval = BANDWIDTH_WEEK; ++ info->reset_is_constant_interval = 0; ++ } ++ else if(strcmp(argv[optind-1],"month") ==0) ++ { ++ info->reset_interval = BANDWIDTH_MONTH; ++ info->reset_is_constant_interval = 0; ++ } ++ else if(strcmp(argv[optind-1],"never") ==0) ++ { ++ info->reset_interval = BANDWIDTH_NEVER; ++ } ++ else if(sscanf(argv[optind-1], "%ld", &read_time) > 0) ++ { ++ info->reset_interval = read_time; ++ info->reset_is_constant_interval = 1; ++ } ++ else ++ { ++ valid_arg = 0; ++ } ++ break; ++ case BANDWIDTH_NUM_INTERVALS: ++ if( sscanf(argv[optind-1], "%ld", &num_read) > 0) ++ { ++ info->num_intervals_to_save = num_read; ++ valid_arg=1; ++ } ++ c=0; ++ break; ++ case BANDWIDTH_RESET_TIME: ++ num_read = sscanf(argv[optind-1], "%ld", &read_time); ++ if(num_read > 0 ) ++ { ++ info->reset_time = read_time; ++ valid_arg = 1; ++ } ++ break; ++ case BANDWIDTH_LAST_BACKUP: ++ num_read = sscanf(argv[optind-1], "%ld", &read_time); ++ if(num_read > 0 ) ++ { ++ info->last_backup_time = read_time; ++ valid_arg = 1; ++ } ++ break; ++ } ++ *flags = *flags + (unsigned int)c; ++ ++ ++ //if we have both reset_interval & reset_time, check reset_time is in valid range ++ if((*flags & BANDWIDTH_RESET_TIME) == BANDWIDTH_RESET_TIME && (*flags & BANDWIDTH_RESET_INTERVAL) == BANDWIDTH_RESET_INTERVAL) ++ { ++ if( (info->reset_interval == BANDWIDTH_NEVER) || ++ (info->reset_interval == BANDWIDTH_MONTH && info->reset_time >= 60*60*24*28) || ++ (info->reset_interval == BANDWIDTH_WEEK && info->reset_time >= 60*60*24*7) || ++ (info->reset_interval == BANDWIDTH_DAY && info->reset_time >= 60*60*24) || ++ (info->reset_interval == BANDWIDTH_HOUR && info->reset_time >= 60*60) || ++ (info->reset_interval == BANDWIDTH_MINUTE && info->reset_time >= 60) ++ ) ++ { ++ valid_arg = 0; ++ param_problem_exit_error("Parameter for '--reset_time' is not in valid range"); ++ } ++ } ++ if(info->type != BANDWIDTH_COMBINED && (*flags & BANDWIDTH_CURRENT) == BANDWIDTH_CURRENT) ++ { ++ valid_arg = 0; ++ param_problem_exit_error("You may only specify current bandwidth for combined type\n Use user-space library for setting bandwidth for individual types"); ++ } ++ ++ return valid_arg; ++} ++ ++ ++ ++static void print_bandwidth_args( struct ipt_bandwidth_info* info ) ++{ ++ if(info->cmp == BANDWIDTH_CHECK) ++ { ++ if(info->check_type == BANDWIDTH_CHECK_NOSWAP) ++ { ++ printf("--bcheck "); ++ } ++ else ++ { ++ printf("--bcheck_with_src_dst_swap "); ++ } ++ } ++ printf("--id %s ", info->id); ++ ++ ++ ++ if(info->cmp != BANDWIDTH_CHECK) ++ { ++ /* determine current time in seconds since epoch, with offset for current timezone */ ++ int minuteswest = get_minutes_west(); ++ time_t now; ++ time(&now); ++ now = now - (minuteswest*60); ++ ++ if(info->type == BANDWIDTH_COMBINED) ++ { ++ printf("--type combined "); ++ } ++ if(info->type == BANDWIDTH_INDIVIDUAL_SRC) ++ { ++ printf("--type individual_src "); ++ } ++ if(info->type == BANDWIDTH_INDIVIDUAL_DST) ++ { ++ printf("--type individual_dst "); ++ } ++ if(info->type == BANDWIDTH_INDIVIDUAL_LOCAL) ++ { ++ printf("--type individual_local "); ++ } ++ if(info->type == BANDWIDTH_INDIVIDUAL_REMOTE) ++ { ++ printf("--type individual_remote "); ++ } ++ ++ ++ if(info->local_subnet != 0) ++ { ++ unsigned char* sub = (unsigned char*)(&(info->local_subnet)); ++ int msk_bits=0; ++ int pow=0; ++ for(pow=0; pow<32; pow++) ++ { ++ uint32_t test = get_pow(2, pow); ++ msk_bits = ( (info->local_subnet_mask & test) == test) ? msk_bits+1 : msk_bits; ++ } ++ printf("--subnet %u.%u.%u.%u/%u ", (unsigned char)sub[0], (unsigned char)sub[1], (unsigned char)sub[2], (unsigned char)sub[3], msk_bits); ++ } ++ if(info->cmp == BANDWIDTH_GT) ++ { ++ printf("--greater_than %lld ", info->bandwidth_cutoff); ++ } ++ if(info->cmp == BANDWIDTH_LT) ++ { ++ printf("--less_than %lld ", info->bandwidth_cutoff); ++ } ++ if (info->type == BANDWIDTH_COMBINED) /* too much data to print for multi types, have to use socket to get/set data */ ++ { ++ if( info->reset_interval != BANDWIDTH_NEVER && info->next_reset != 0 && info->next_reset < now) ++ { ++ /* ++ * current bandwidth only gets reset when first packet after reset interval arrives, so output ++ * zero if we're already past interval, but no packets have arrived ++ */ ++ printf("--current_bandwidth 0 "); ++ } ++ else ++ { ++ printf("--current_bandwidth %lld ", info->current_bandwidth); ++ } ++ } ++ if(info->reset_is_constant_interval) ++ { ++ printf("--reset_interval %ld ", info->reset_interval); ++ } ++ else ++ { ++ if(info->reset_interval == BANDWIDTH_MINUTE) ++ { ++ printf("--reset_interval minute "); ++ } ++ else if(info->reset_interval == BANDWIDTH_HOUR) ++ { ++ printf("--reset_interval hour "); ++ } ++ else if(info->reset_interval == BANDWIDTH_DAY) ++ { ++ printf("--reset_interval day "); ++ } ++ else if(info->reset_interval == BANDWIDTH_WEEK) ++ { ++ printf("--reset_interval week "); ++ } ++ else if(info->reset_interval == BANDWIDTH_MONTH) ++ { ++ printf("--reset_interval month "); ++ } ++ } ++ if(info->reset_time > 0) ++ { ++ printf("--reset_time %ld ", info->reset_time); ++ } ++ if(info->num_intervals_to_save > 0) ++ { ++ printf("--intervals_to_save %d ", info->num_intervals_to_save); ++ } ++ } ++} ++ ++/* ++ * Final check, we can't have reset_time without reset_interval ++ */ ++static void final_check(unsigned int flags) ++{ ++ if (flags == 0) ++ { ++ param_problem_exit_error("You must specify at least one argument. "); ++ } ++ if( (flags & BANDWIDTH_RESET_INTERVAL) == 0 && (flags & BANDWIDTH_RESET_TIME) != 0) ++ { ++ param_problem_exit_error("You may not specify '--reset_time' without '--reset_interval' "); ++ } ++ if( (flags & BANDWIDTH_REQUIRES_SUBNET) == BANDWIDTH_REQUIRES_SUBNET && (flags & BANDWIDTH_SUBNET) == 0 ) ++ { ++ param_problem_exit_error("You must specify a local subnet (--subnet a.b.c.d/mask) to match individual local/remote IPs "); ++ } ++ ++ /* update timezone minutes_west in kernel to match userspace*/ ++ set_kernel_timezone(); ++} ++ ++/* Prints out the matchinfo. */ ++#ifdef _XTABLES_H ++static void print(const void *ip, const struct xt_entry_match *match, int numeric) ++#else ++static void print(const struct ipt_ip *ip, const struct ipt_entry_match *match, int numeric) ++#endif ++{ ++ printf("bandwidth "); ++ struct ipt_bandwidth_info *info = (struct ipt_bandwidth_info *)match->data; ++ ++ print_bandwidth_args(info); ++} ++ ++/* Saves the union ipt_matchinfo in parsable form to stdout. */ ++#ifdef _XTABLES_H ++static void save(const void *ip, const struct xt_entry_match *match) ++#else ++static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match) ++#endif ++{ ++ struct ipt_bandwidth_info *info = (struct ipt_bandwidth_info *)match->data; ++ time_t now; ++ ++ print_bandwidth_args(info); ++ ++ time(&now); ++ printf("--last_backup-time %ld ", now); ++} ++ ++static struct iptables_match bandwidth = ++{ ++ .next = NULL, ++ .name = "bandwidth", ++ #ifdef XTABLES_VERSION_CODE ++ .version = XTABLES_VERSION, ++ #else ++ .version = IPTABLES_VERSION, ++ #endif ++ .size = XT_ALIGN(sizeof(struct ipt_bandwidth_info)), ++ .userspacesize = XT_ALIGN(sizeof(struct ipt_bandwidth_info)), ++ .help = &help, ++ .parse = &parse, ++ .final_check = &final_check, ++ .print = &print, ++ .save = &save, ++ .extra_opts = opts ++}; ++ ++void _init(void) ++{ ++ register_match(&bandwidth); ++} ++ ++static void param_problem_exit_error(char* msg) ++{ ++ #ifdef xtables_error ++ xtables_error(PARAMETER_PROBLEM, "%s", msg); ++ #else ++ exit_error(PARAMETER_PROBLEM, msg); ++ #endif ++} ++ ++/* ++ * implement a simple function to get positive powers of positive integers so we don't have to mess with math.h ++ * all we really need are powers of 2 for calculating netmask ++ * This is only called a couple of times, so speed isn't an issue either ++ */ ++static unsigned long get_pow(unsigned long base, unsigned long pow) ++{ ++ unsigned long ret = pow == 0 ? 1 : base*get_pow(base, pow-1); ++ return ret; ++} ++ ++ ++int parse_sub(char* subnet_string, uint32_t* subnet, uint32_t* subnet_mask) ++{ ++ ++ int valid = 0; ++ unsigned int A,B,C,D,E,F,G,H; ++ int read_int = sscanf(subnet_string, "%u.%u.%u.%u/%u.%u.%u.%u", &A, &B, &C, &D, &E, &F, &G, &H); ++ if(read_int >= 5) ++ { ++ if( A <= 255 && B <= 255 && C <= 255 && D <= 255) ++ { ++ unsigned char* sub = (unsigned char*)(subnet); ++ unsigned char* msk = (unsigned char*)(subnet_mask); ++ ++ *( sub ) = (unsigned char)A; ++ *( sub + 1 ) = (unsigned char)B; ++ *( sub + 2 ) = (unsigned char)C; ++ *( sub + 3 ) = (unsigned char)D; ++ ++ if(read_int == 5) ++ { ++ unsigned int mask = E; ++ if(mask <= 32) ++ { ++ int msk_index; ++ for(msk_index=0; msk_index*8 < mask; msk_index++) ++ { ++ int bit_index; ++ msk[msk_index] = 0; ++ for(bit_index=0; msk_index*8 + bit_index < mask && bit_index < 8; bit_index++) ++ { ++ msk[msk_index] = msk[msk_index] + get_pow(2, 7-bit_index); ++ } ++ } ++ } ++ valid = 1; ++ } ++ if(read_int == 8) ++ { ++ if( E <= 255 && F <= 255 && G <= 255 && H <= 255) ++ *( msk ) = (unsigned char)E; ++ *( msk + 1 ) = (unsigned char)F; ++ *( msk + 2 ) = (unsigned char)G; ++ *( msk + 3 ) = (unsigned char)H; ++ valid = 1; ++ } ++ } ++ } ++ if(valid) ++ { ++ *subnet = (*subnet & *subnet_mask ); ++ } ++ return valid; ++} ++ ++ ++ ++int get_minutes_west(void) ++{ ++ time_t now; ++ struct tm* utc_info; ++ struct tm* tz_info; ++ int utc_day; ++ int utc_hour; ++ int utc_minute; ++ int tz_day; ++ int tz_hour; ++ int tz_minute; ++ int minuteswest; ++ ++ time(&now); ++ utc_info = gmtime(&now); ++ utc_day = utc_info->tm_mday; ++ utc_hour = utc_info->tm_hour; ++ utc_minute = utc_info->tm_min; ++ tz_info = localtime(&now); ++ tz_day = tz_info->tm_mday; ++ tz_hour = tz_info->tm_hour; ++ tz_minute = tz_info->tm_min; ++ ++ utc_day = utc_day < tz_day - 1 ? tz_day + 1 : utc_day; ++ tz_day = tz_day < utc_day - 1 ? utc_day + 1 : tz_day; ++ ++ minuteswest = (24*60*utc_day + 60*utc_hour + utc_minute) - (24*60*tz_day + 60*tz_hour + tz_minute) ; ++ ++ return minuteswest; ++} ++ ++void set_kernel_timezone(void) ++{ ++ struct timeval tv; ++ struct timezone old_tz; ++ struct timezone new_tz; ++ ++ new_tz.tz_minuteswest = get_minutes_west();; ++ new_tz.tz_dsttime = 0; ++ ++ /* Get tv to pass to settimeofday(2) to be sure we avoid hour-sized warp */ ++ /* (see gettimeofday(2) man page, or /usr/src/linux/kernel/time.c) */ ++ gettimeofday(&tv, &old_tz); ++ ++ /* set timezone */ ++ settimeofday(&tv, &new_tz); ++} + +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ipt_bandwidth.h +@@ -0,0 +1,106 @@ ++/* bandwidth -- An iptables extension for bandwidth monitoring/control ++ * Can be used to efficiently monitor bandwidth and/or implement bandwidth quotas ++ * Can be queried using the iptbwctl userspace library ++ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) ++ * ++ * ++ * Copyright © 2009 by Eric Bishop ++ * ++ * This file is free software: you may copy, redistribute and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation, either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This file is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#ifndef _IPT_BANDWIDTH_H ++#define _IPT_BANDWIDTH_H ++ ++/*flags -- first three don't map to parameters the rest do */ ++#define BANDWIDTH_INITIALIZED 1 ++#define BANDWIDTH_REQUIRES_SUBNET 2 ++#define BANDWIDTH_SUBNET 4 ++#define BANDWIDTH_CMP 8 ++#define BANDWIDTH_CURRENT 16 ++#define BANDWIDTH_RESET_INTERVAL 32 ++#define BANDWIDTH_RESET_TIME 64 ++#define BANDWIDTH_LAST_BACKUP 128 ++ ++ ++/* parameter defs that don't map to flag bits */ ++#define BANDWIDTH_TYPE 70 ++#define BANDWIDTH_ID 71 ++#define BANDWIDTH_GT 72 ++#define BANDWIDTH_LT 73 ++#define BANDWIDTH_MONITOR 74 ++#define BANDWIDTH_CHECK 75 ++#define BANDWIDTH_CHECK_NOSWAP 76 ++#define BANDWIDTH_CHECK_SWAP 77 ++#define BANDWIDTH_NUM_INTERVALS 78 ++ ++/* possible reset intervals */ ++#define BANDWIDTH_MINUTE 80 ++#define BANDWIDTH_HOUR 81 ++#define BANDWIDTH_DAY 82 ++#define BANDWIDTH_WEEK 83 ++#define BANDWIDTH_MONTH 84 ++#define BANDWIDTH_NEVER 85 ++ ++/* possible monitoring types */ ++#define BANDWIDTH_COMBINED 90 ++#define BANDWIDTH_INDIVIDUAL_SRC 91 ++#define BANDWIDTH_INDIVIDUAL_DST 92 ++#define BANDWIDTH_INDIVIDUAL_LOCAL 93 ++#define BANDWIDTH_INDIVIDUAL_REMOTE 94 ++ ++ ++ ++/* socket id parameters (for userspace i/o) */ ++#define BANDWIDTH_SET 2048 ++#define BANDWIDTH_GET 2049 ++ ++/* max id length */ ++#define BANDWIDTH_MAX_ID_LENGTH 50 ++ ++/* 4 bytes for total number of entries, 100 entries of 12 bytes each, + 1 byte indicating whether all have been dumped */ ++#define BANDWIDTH_QUERY_LENGTH 1205 ++#define BANDWIDTH_ENTRY_LENGTH 12 ++ ++ ++struct ipt_bandwidth_info ++{ ++ char id[BANDWIDTH_MAX_ID_LENGTH]; ++ unsigned char type; ++ unsigned char check_type; ++ uint32_t local_subnet; ++ uint32_t local_subnet_mask; ++ ++ unsigned char cmp; ++ unsigned char reset_is_constant_interval; ++ time_t reset_interval; //specific fixed type (see above) or interval length in seconds ++ time_t reset_time; //seconds from start of month/week/day/hour/minute to do reset, or start point of interval if it is a constant interval ++ uint64_t bandwidth_cutoff; ++ uint64_t current_bandwidth; ++ time_t next_reset; ++ time_t previous_reset; ++ time_t last_backup_time; ++ ++ uint32_t num_intervals_to_save; ++ ++ ++ unsigned long hashed_id; ++ void* iam; ++ uint64_t* combined_bw; ++ struct ipt_bandwidth_info* non_const_self; ++ unsigned long* ref_count; ++ ++ ++}; ++#endif /*_IPT_BANDWIDTH_H*/ + +--- /dev/null ++++ b/extensions/libipt_timerange.c +@@ -0,0 +1,876 @@ ++/* timerange -- An iptables extension to match multiple timeranges within a week ++ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) ++ * ++ * ++ * Copyright © 2009-2010 by Eric Bishop ++ * ++ * This file is free software: you may copy, redistribute and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation, either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This file is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++/* ++ * in iptables 1.4.0 and higher, iptables.h includes xtables.h, which ++ * we can use to check whether we need to deal with the new requirements ++ * in pre-processor directives below ++ */ ++#include ++#include ++ ++#ifdef _XTABLES_H ++ #define iptables_rule_match xtables_rule_match ++ #define iptables_match xtables_match ++ #define iptables_target xtables_target ++ #define ipt_tryload xt_tryload ++#endif ++ ++/* ++ * XTABLES_VERSION_CODE is only defined in versions 1.4.1 and later, which ++ * also require the use of xtables_register_match ++ * ++ * Version 1.4.0 uses register_match like previous versions ++ */ ++#ifdef XTABLES_VERSION_CODE ++ #define register_match xtables_register_match ++#endif ++ ++/* utility functions necessary for module to work across multiple iptables versions */ ++static int my_check_inverse(const char option[], int* invert, int *my_optind, int argc); ++static void param_problem_exit_error(char* msg); ++ ++ ++long* parse_time_ranges(char* time_ranges, unsigned char is_weekly_range); ++void merge_adjacent_time_ranges(long* time_ranges, unsigned char is_weekly_range); ++unsigned long parse_time(char* time_str); ++long* parse_weekdays(char* wd_str); ++ ++char** split_on_separators(char* line, char* separators, int num_separators, int max_pieces, int include_remainder_at_max); ++void to_lowercase(char* str); ++char* trim_flanking_whitespace(char* str); ++ ++void set_kernel_timezone(void); ++ ++/* Function which prints out usage message. */ ++static void help(void) ++{ ++ printf( "timerange options:\n --hours [HOURLY RANGES] --weekdays [WEEKDAYS ACTIVE] --weekly_ranges [WEEKLY RANGES]\n"); ++} ++ ++static struct option opts[] = ++{ ++ { .name = "hours", .has_arg = 1, .flag = 0, .val = HOURS }, ++ { .name = "weekdays", .has_arg = 1, .flag = 0, .val = WEEKDAYS }, ++ { .name = "weekly_ranges", .has_arg = 1, .flag = 0, .val = WEEKLY_RANGE }, ++ { .name = 0 } ++}; ++ ++ ++/* Function which parses command options; returns true if it ++ ate an option */ ++static int parse( int c, ++ char **argv, ++ int invert, ++ unsigned int *flags, ++#ifdef _XTABLES_H ++ const void *entry, ++#else ++ const struct ipt_entry *entry, ++ unsigned int *nfcache, ++#endif ++ struct ipt_entry_match **match ++ ) ++{ ++ struct ipt_timerange_info *info = (struct ipt_timerange_info *)(*match)->data; ++ int valid_arg = 0; ++ if(*flags == 0) ++ { ++ my_check_inverse(optarg, &invert, &optind, 0); ++ info->invert = invert ? 1 : 0; ++ } ++ ++ long* parsed = NULL; ++ switch (c) ++ { ++ case HOURS: ++ parsed = parse_time_ranges(argv[optind-1], 0); ++ if(parsed != NULL && (*flags & HOURS) == 0 && (*flags & WEEKLY_RANGE) == 0) ++ { ++ int range_index = 0; ++ for(range_index = 0; parsed[range_index] != -1; range_index++) ++ { ++ if(range_index > 100) ++ { ++ return 0; ++ } ++ info->ranges[range_index] = parsed[range_index]; ++ } ++ info->ranges[range_index] = -1; ++ free(parsed); ++ ++ ++ valid_arg = 1; ++ *flags = *flags+ c; ++ info->type = *flags; ++ } ++ break; ++ ++ ++ case WEEKDAYS: ++ parsed = parse_weekdays(argv[optind-1]); ++ if(parsed != NULL && (*flags & WEEKDAYS) == 0 && (*flags & WEEKLY_RANGE) == 0) ++ { ++ int day_index; ++ for(day_index=0; day_index < 7; day_index++) ++ { ++ info->days[day_index] = parsed[day_index]; ++ } ++ free(parsed); ++ ++ valid_arg = 1 ; ++ *flags = *flags + c; ++ info->type = *flags; ++ } ++ break; ++ case WEEKLY_RANGE: ++ parsed = parse_time_ranges(argv[optind-1], 1); ++ if(parsed != NULL && (*flags & HOURS) == 0 && (*flags & WEEKDAYS) == 0 && (*flags & WEEKLY_RANGE) == 0 ) ++ { ++ int range_index = 0; ++ for(range_index = 0; parsed[range_index] != -1; range_index++) ++ { ++ if(range_index > 100) ++ { ++ return 0; ++ } ++ info->ranges[range_index] = parsed[range_index]; ++ ++ } ++ info->ranges[range_index] = -1; ++ free(parsed); ++ ++ valid_arg = 1; ++ *flags = *flags+c; ++ info->type = *flags; ++ } ++ break; ++ } ++ ++ return valid_arg; ++} ++ ++ ++ ++static void print_timerange_args( struct ipt_timerange_info* info ) ++{ ++ int i; ++ ++ if(info->invert == 1) ++ { ++ printf(" ! "); ++ } ++ ++ switch(info->type) ++ { ++ case DAYS_HOURS: ++ case HOURS: ++ printf(" --hours "); ++ for(i=0; info->ranges[i] != -1; i++) ++ { ++ printf("%ld", info->ranges[i]); ++ if(info->ranges[i+1] != -1) ++ { ++ if(i % 2 == 0){ printf("-"); } ++ else { printf(","); } ++ } ++ } ++ if(info->type == HOURS) { break; } ++ case WEEKDAYS: ++ printf(" --weekdays "); ++ for(i=0; i<7; i++) ++ { ++ printf("%d", info->days[i]); ++ if(i != 6){ printf(","); } ++ } ++ break; ++ case WEEKLY_RANGE: ++ printf(" --weekly_ranges "); ++ for(i=0; info->ranges[i] != -1; i++) ++ { ++ printf("%ld", info->ranges[i]); ++ if(info->ranges[i+1] != -1) ++ { ++ if(i % 2 == 0){ printf("-"); } ++ else { printf(","); } ++ } ++ } ++ break; ++ } ++ printf(" "); ++ ++} ++ ++/* Final check; must have specified a test string with either --contains or --contains_regex. */ ++static void final_check(unsigned int flags) ++{ ++ if(flags ==0) ++ { ++ param_problem_exit_error("Invalid arguments to time_range"); ++ } ++ ++ /* update timezone minutes_west in kernel to match userspace*/ ++ set_kernel_timezone(); ++} ++ ++/* Prints out the matchinfo. */ ++#ifdef _XTABLES_H ++static void print(const void *ip, const struct xt_entry_match *match, int numeric) ++#else ++static void print(const struct ipt_ip *ip, const struct ipt_entry_match *match, int numeric) ++#endif ++{ ++ printf("timerange "); ++ struct ipt_timerange_info *info = (struct ipt_timerange_info *)match->data; ++ ++ print_timerange_args(info); ++} ++ ++/* Saves the union ipt_matchinfo in parsable form to stdout. */ ++#ifdef _XTABLES_H ++static void save(const void *ip, const struct xt_entry_match *match) ++#else ++static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match) ++#endif ++{ ++ struct ipt_timerange_info *info = (struct ipt_timerange_info *)match->data; ++ print_timerange_args(info); ++} ++ ++static struct iptables_match timerange = ++{ ++ .next = NULL, ++ .name = "timerange", ++ #ifdef XTABLES_VERSION_CODE ++ .version = XTABLES_VERSION, ++ #else ++ .version = IPTABLES_VERSION, ++ #endif ++ .size = XT_ALIGN(sizeof(struct ipt_timerange_info)), ++ .userspacesize = XT_ALIGN(sizeof(struct ipt_timerange_info)), ++ .help = &help, ++ .parse = &parse, ++ .final_check = &final_check, ++ .print = &print, ++ .save = &save, ++ .extra_opts = opts ++}; ++ ++void _init(void) ++{ ++ register_match(&timerange); ++} ++ ++#ifndef TRUE ++#define TRUE 1 ++#endif ++#ifndef FALSE ++#define FALSE 0 ++#endif ++static int my_check_inverse(const char option[], int* invert, int *my_optind, int argc) ++{ ++ if (option && strcmp(option, "!") == 0) ++ { ++ if (*invert) ++ { ++ param_problem_exit_error("Multiple `!' flags not allowed"); ++ } ++ *invert = TRUE; ++ if (my_optind != NULL) ++ { ++ ++*my_optind; ++ if (argc && *my_optind > argc) ++ { ++ param_problem_exit_error("no argument following `!'"); ++ } ++ } ++ return TRUE; ++ } ++ return FALSE; ++} ++static void param_problem_exit_error(char* msg) ++{ ++ #ifdef xtables_error ++ xtables_error(PARAMETER_PROBLEM, "%s", msg); ++ #else ++ exit_error(PARAMETER_PROBLEM, msg); ++ #endif ++} ++ ++/* takes a string of days e.g. "Monday, Tuesday, Friday", and turns into an array of 7 longs ++ * each 0 or 1, one for each weekday starting with sunday, e.g. [0,1,1,0,0,1,0] for our example ++ */ ++long* parse_weekdays(char* wd_str) ++{ ++ long* weekdays = (long*)malloc(7*sizeof(long)); ++ weekdays[0] = weekdays[1] = weekdays[2] = weekdays[3] = weekdays[4] = weekdays[5] = weekdays[6] = 0; ++ ++ char** days = split_on_separators(wd_str, ",", 1, -1, 0); ++ int day_index; ++ int found = 0; ++ for(day_index=0; days[day_index] != NULL; day_index++) ++ { ++ char day[4]; ++ trim_flanking_whitespace(days[day_index]); ++ memcpy(day, days[day_index], 3); ++ free(days[day_index]); ++ day[3] = '\0'; ++ to_lowercase(day); ++ if(strcmp(day, "sun") == 0) ++ { ++ weekdays[0] = 1; ++ found = 1; ++ } ++ else if(strcmp(day, "mon") ==0) ++ { ++ weekdays[1] = 1; ++ found = 1; ++ } ++ else if(strcmp(day, "tue") ==0) ++ { ++ weekdays[2] = 1; ++ found = 1; ++ } ++ else if(strcmp(day, "wed") ==0) ++ { ++ weekdays[3] = 1; ++ found = 1; ++ } ++ else if(strcmp(day, "thu") ==0) ++ { ++ weekdays[4] = 1; ++ found = 1; ++ } ++ else if(strcmp(day, "fri") ==0) ++ { ++ weekdays[5] = 1; ++ found = 1; ++ } ++ else if(strcmp(day, "sat") ==0) ++ { ++ weekdays[6] = 1; ++ found = 1; ++ } ++ else if(strcmp(day, "all") ==0) ++ { ++ weekdays[0] = weekdays[1] = weekdays[2] = weekdays[3] = weekdays[4] = weekdays[5] = weekdays[6] = 1; ++ found = 1; ++ } ++ } ++ free(days); ++ if(found == 0) ++ { ++ free(weekdays); ++ weekdays = NULL; ++ } ++ return weekdays; ++} ++ ++ ++/* is_weekly_range indicates whether we're parsing hours within a single day or a range over a whole week */ ++long* parse_time_ranges(char* time_ranges, unsigned char is_weekly_range) ++{ ++ char** pieces = split_on_separators(time_ranges, ",", 1, -1, 0); ++ int num_pieces = 0; ++ for(num_pieces = 0; pieces[num_pieces] != NULL; num_pieces++) {}; ++ long *parsed = (long*)malloc( (1+(num_pieces*2)) * sizeof(long)); ++ ++ ++ ++ int piece_index = 0; ++ for(piece_index = 0; pieces[piece_index] != NULL; piece_index++) ++ { ++ trim_flanking_whitespace(pieces[piece_index]); ++ char** times=split_on_separators(pieces[piece_index], "-", 1, 2, 0); ++ int time_count = 0; ++ for(time_count = 0; times[time_count] != 0 ; time_count++){} ++ if( time_count == 2 ) ++ { ++ unsigned long start = parse_time(trim_flanking_whitespace(times[0])); ++ unsigned long end = parse_time(trim_flanking_whitespace(times[1])); ++ parsed[ piece_index*2 ] = (long)start; ++ parsed[ (piece_index*2)+1 ] = (long)end; ++ ++ free( times[1] ); ++ } ++ if( time_count > 0) { free(times[0]); } ++ ++ free(times); ++ free(pieces[piece_index]); ++ } ++ free(pieces); ++ parsed[ (num_pieces*2) ] = -1; // terminated with -1 ++ ++ ++ // make sure there is no overlap -- this will invalidate ranges ++ int range_index = 0; ++ char overlap_found = 0; ++ for(range_index = 0; range_index < num_pieces; range_index++) ++ { ++ // now test for overlap ++ long start1 = parsed[ (range_index*2) ]; ++ long end1 = parsed[ (range_index*2)+1 ]; ++ end1= end1 < start1 ? end1 + (is_weekly_range ? 7*24*60*60 : 24*60*60) : end1; ++ ++ int range_index2 = 0; ++ for(range_index2 = 0; range_index2 < num_pieces; range_index2++) ++ { ++ if(range_index2 != range_index) ++ { ++ long start2 = parsed[ (range_index2*2) ]; ++ long end2 = parsed[ (range_index2*2)+1 ]; ++ end2= end2 < start2 ? end2 + (is_weekly_range ? 7*24*60*60 : 24*60*60) : end2; ++ overlap_found = overlap_found || (start1 < end2 && end1 > start2 ); ++ } ++ } ++ } ++ ++ if(!overlap_found) ++ { ++ // sort ranges ++ int sorted_index = 0; ++ while(parsed[sorted_index] != -1) ++ { ++ int next_start=-1; ++ int next_start_index=-1; ++ int test_index; ++ long tmp1; ++ long tmp2; ++ for(test_index=sorted_index; parsed[test_index] != -1; test_index=test_index+2) ++ { ++ next_start_index = next_start < 0 || next_start > parsed[test_index] ? test_index : next_start_index; ++ next_start = next_start < 0 || next_start > parsed[test_index] ? parsed[test_index] : next_start; ++ } ++ tmp1 = parsed[next_start_index]; ++ tmp2 = parsed[next_start_index+1]; ++ parsed[next_start_index] = parsed[sorted_index]; ++ parsed[next_start_index+1] = parsed[sorted_index+1]; ++ parsed[sorted_index] = tmp1; ++ parsed[sorted_index+1] = tmp2; ++ sorted_index = sorted_index + 2; ++ } ++ } ++ else ++ { ++ // de-allocate parsed, set to NULL ++ free(parsed); ++ parsed = NULL; ++ } ++ ++ // merge time ranges where end of first = start of second ++ merge_adjacent_time_ranges(parsed, is_weekly_range); ++ ++ ++ // if always active, free & return NULL ++ int max_multiple = is_weekly_range ? 7 : 1; ++ if(parsed[0] == 0 && parsed[1] == max_multiple*24*60*60) ++ { ++ free(parsed); ++ parsed = NULL; ++ } ++ ++ ++ //adjust so any range that crosses end of range is split in two ++ int num_range_indices=0; ++ for(num_range_indices=0; parsed[num_range_indices] != -1; num_range_indices++){} ++ ++ long* adjusted_range = (long*)malloc((3+num_range_indices)*sizeof(long)); ++ int ar_index = 0; ++ int old_index = 0; ++ if(parsed[num_range_indices-1] < parsed[0]) ++ { ++ adjusted_range[0] = 0; ++ adjusted_range[1] = parsed[num_range_indices-1]; ++ ar_index = ar_index + 2; ++ parsed[num_range_indices-1] = -1; ++ } ++ for(old_index=0; parsed[old_index] != -1; old_index++) ++ { ++ adjusted_range[ar_index] = parsed[old_index]; ++ ar_index++; ++ } ++ ++ if(ar_index % 2 == 1 ) ++ { ++ adjusted_range[ar_index] = is_weekly_range ? 7*24*60*60 : 24*60*60; ++ ar_index++; ++ } ++ adjusted_range[ar_index] = -1; ++ free(parsed); ++ ++ return adjusted_range; ++} ++ ++ ++ ++void merge_adjacent_time_ranges(long* time_ranges, unsigned char is_weekly_range) ++{ ++ int range_length = 0; ++ while(time_ranges[range_length] != -1){ range_length++; } ++ int* merged_indices = (int*)malloc((range_length+1)*sizeof(int)); ++ ++ int merged_index=0; ++ int next_index; ++ for(next_index=0; time_ranges[next_index] != -1; next_index++) ++ { ++ if(next_index == 0) ++ { ++ merged_indices[merged_index] = next_index; ++ merged_index++; ++ } ++ else if( time_ranges[next_index+1] == -1 ) ++ { ++ merged_indices[merged_index] = next_index; ++ merged_index++; ++ } ++ else if( time_ranges[next_index] != time_ranges[next_index-1] && time_ranges[next_index] != time_ranges[next_index+1] ) ++ { ++ merged_indices[merged_index] = next_index; ++ merged_index++; ++ } ++ } ++ merged_indices[merged_index] = -1; ++ ++ for(next_index=0; merged_indices[next_index] != -1; next_index++) ++ { ++ time_ranges[next_index] = time_ranges[ merged_indices[next_index] ]; ++ } ++ time_ranges[next_index] = -1; ++ free(merged_indices); ++ ++} ++ ++ ++ ++ ++/* ++ * assumes 24hr time, not am/pm, in format: ++ * (Day of week) hours:minutes:seconds ++ * if day of week is present, returns seconds since midnight on Sunday ++ * otherwise, seconds since midnight ++ */ ++unsigned long parse_time(char* time_str) ++{ ++ while((*time_str == ' ' || *time_str == '\t') && *time_str != '\0') { time_str++; } ++ ++ int weekday = -1; ++ if(strlen(time_str) > 3) ++ { ++ char wday_test[4]; ++ memcpy(wday_test, time_str, 3); ++ wday_test[3] = '\0'; ++ to_lowercase(wday_test); ++ if(strcmp(wday_test, "sun") == 0) ++ { ++ weekday = 0; ++ } ++ else if(strcmp(wday_test, "mon") == 0) ++ { ++ weekday = 1; ++ } ++ else if(strcmp(wday_test, "tue") == 0) ++ { ++ weekday = 2; ++ } ++ else if(strcmp(wday_test, "wed") == 0) ++ { ++ weekday = 3; ++ } ++ else if(strcmp(wday_test, "thu") == 0) ++ { ++ weekday = 4; ++ } ++ else if(strcmp(wday_test, "fri") == 0) ++ { ++ weekday = 5; ++ } ++ else if(strcmp(wday_test, "sat") == 0) ++ { ++ weekday = 6; ++ } ++ } ++ ++ if(weekday >= 0) ++ { ++ time_str = time_str + 3; ++ while( (*time_str < 48 || *time_str > 57) && *time_str != '\0') { time_str++; } ++ } ++ ++ char** time_parts=split_on_separators(time_str, ":", 1, -1, 0); ++ unsigned long seconds = weekday < 0 ? 0 : ( ((unsigned long)(weekday))*60*60*24 ); ++ unsigned long tmp; ++ unsigned long multiple = 60*60; ++ ++ int tp_index = 0; ++ for(tp_index=0; time_parts[tp_index] != NULL; tp_index++) ++ { ++ sscanf(time_parts[tp_index], "%ld", &tmp); ++ seconds = seconds + (tmp*multiple); ++ multiple = (unsigned long)(multiple/60); ++ free(time_parts[tp_index]); ++ } ++ free(time_parts); ++ ++ return seconds; ++} ++ ++void to_lowercase(char* str) ++{ ++ int i; ++ for(i = 0; str[i] != '\0'; i++) ++ { ++ str[i] = tolower(str[i]); ++ } ++} ++ ++/* ++ * line_str is the line to be parsed -- it is not modified in any way ++ * max_pieces indicates number of pieces to return, if negative this is determined dynamically ++ * include_remainder_at_max indicates whether the last piece, when max pieces are reached, ++ * should be what it would normally be (0) or the entire remainder of the line (1) ++ * if max_pieces < 0 this parameter is ignored ++ * ++ * ++ * returns all non-separator pieces in a line ++ * result is dynamically allocated, MUST be freed after call-- even if ++ * line is empty (you still get a valid char** pointer to to a NULL char*) ++ */ ++char** split_on_separators(char* line_str, char* separators, int num_separators, int max_pieces, int include_remainder_at_max) ++{ ++ char** split; ++ ++ if(line_str != NULL) ++ { ++ int split_index; ++ int non_separator_found; ++ char* dup_line; ++ char* start; ++ ++ if(max_pieces < 0) ++ { ++ /* count number of separator characters in line -- this count + 1 is an upperbound on number of pieces */ ++ int separator_count = 0; ++ int line_index; ++ for(line_index = 0; line_str[line_index] != '\0'; line_index++) ++ { ++ int sep_index; ++ int found = 0; ++ for(sep_index =0; found == 0 && sep_index < num_separators; sep_index++) ++ { ++ found = separators[sep_index] == line_str[line_index] ? 1 : 0; ++ } ++ separator_count = separator_count+ found; ++ } ++ max_pieces = separator_count + 1; ++ } ++ split = (char**)malloc((1+max_pieces)*sizeof(char*)); ++ split_index = 0; ++ split[split_index] = NULL; ++ ++ ++ dup_line = strdup(line_str); ++ start = dup_line; ++ non_separator_found = 0; ++ while(non_separator_found == 0) ++ { ++ int matches = 0; ++ int sep_index; ++ for(sep_index =0; sep_index < num_separators; sep_index++) ++ { ++ matches = matches == 1 || separators[sep_index] == start[0] ? 1 : 0; ++ } ++ non_separator_found = matches==0 || start[0] == '\0' ? 1 : 0; ++ if(non_separator_found == 0) ++ { ++ start++; ++ } ++ } ++ ++ while(start[0] != '\0' && split_index < max_pieces) ++ { ++ /* find first separator index */ ++ int first_separator_index = 0; ++ int separator_found = 0; ++ while( separator_found == 0 ) ++ { ++ int sep_index; ++ for(sep_index =0; separator_found == 0 && sep_index < num_separators; sep_index++) ++ { ++ separator_found = separators[sep_index] == start[first_separator_index] || start[first_separator_index] == '\0' ? 1 : 0; ++ } ++ if(separator_found == 0) ++ { ++ first_separator_index++; ++ } ++ } ++ ++ /* copy next piece to split array */ ++ if(first_separator_index > 0) ++ { ++ char* next_piece = NULL; ++ if(split_index +1 < max_pieces || include_remainder_at_max <= 0) ++ { ++ next_piece = (char*)malloc((first_separator_index+1)*sizeof(char)); ++ memcpy(next_piece, start, first_separator_index); ++ next_piece[first_separator_index] = '\0'; ++ } ++ else ++ { ++ next_piece = strdup(start); ++ } ++ split[split_index] = next_piece; ++ split[split_index+1] = NULL; ++ split_index++; ++ } ++ ++ ++ /* find next non-separator index, indicating start of next piece */ ++ start = start+ first_separator_index; ++ non_separator_found = 0; ++ while(non_separator_found == 0) ++ { ++ int matches = 0; ++ int sep_index; ++ for(sep_index =0; sep_index < num_separators; sep_index++) ++ { ++ matches = matches == 1 || separators[sep_index] == start[0] ? 1 : 0; ++ } ++ non_separator_found = matches==0 || start[0] == '\0' ? 1 : 0; ++ if(non_separator_found == 0) ++ { ++ start++; ++ } ++ } ++ } ++ free(dup_line); ++ } ++ else ++ { ++ split = (char**)malloc((1)*sizeof(char*)); ++ split[0] = NULL; ++ } ++ return split; ++} ++ ++ ++char* trim_flanking_whitespace(char* str) ++{ ++ int new_start = 0; ++ int new_length = 0; ++ ++ char whitespace[5] = { ' ', '\t', '\n', '\r', '\0' }; ++ int num_whitespace_chars = 4; ++ ++ ++ int str_index = 0; ++ int is_whitespace = 1; ++ int test; ++ while( (test = str[str_index]) != '\0' && is_whitespace == 1) ++ { ++ int whitespace_index; ++ is_whitespace = 0; ++ for(whitespace_index = 0; whitespace_index < num_whitespace_chars && is_whitespace == 0; whitespace_index++) ++ { ++ is_whitespace = test == whitespace[whitespace_index] ? 1 : 0; ++ } ++ str_index = is_whitespace == 1 ? str_index+1 : str_index; ++ } ++ new_start = str_index; ++ ++ ++ str_index = strlen(str) - 1; ++ is_whitespace = 1; ++ while( str_index >= new_start && is_whitespace == 1) ++ { ++ int whitespace_index; ++ is_whitespace = 0; ++ for(whitespace_index = 0; whitespace_index < num_whitespace_chars && is_whitespace == 0; whitespace_index++) ++ { ++ is_whitespace = str[str_index] == whitespace[whitespace_index] ? 1 : 0; ++ } ++ str_index = is_whitespace == 1 ? str_index-1 : str_index; ++ } ++ new_length = str[new_start] == '\0' ? 0 : str_index + 1 - new_start; ++ ++ ++ if(new_start > 0) ++ { ++ for(str_index = 0; str_index < new_length; str_index++) ++ { ++ str[str_index] = str[str_index+new_start]; ++ } ++ } ++ str[new_length] = 0; ++ return str; ++} ++ ++void set_kernel_timezone(void) ++{ ++ time_t now; ++ struct tm* utc_info; ++ struct tm* tz_info; ++ int utc_day; ++ int utc_hour; ++ int utc_minute; ++ int tz_day; ++ int tz_hour; ++ int tz_minute; ++ int minuteswest; ++ ++ struct timeval tv; ++ struct timezone old_tz; ++ struct timezone new_tz; ++ ++ time(&now); ++ utc_info = gmtime(&now); ++ utc_day = utc_info->tm_mday; ++ utc_hour = utc_info->tm_hour; ++ utc_minute = utc_info->tm_min; ++ tz_info = localtime(&now); ++ tz_day = tz_info->tm_mday; ++ tz_hour = tz_info->tm_hour; ++ tz_minute = tz_info->tm_min; ++ ++ utc_day = utc_day < tz_day - 1 ? tz_day + 1 : utc_day; ++ tz_day = tz_day < utc_day - 1 ? utc_day + 1 : tz_day; ++ ++ minuteswest = (24*60*utc_day + 60*utc_hour + utc_minute) - (24*60*tz_day + 60*tz_hour + tz_minute) ; ++ new_tz.tz_minuteswest = minuteswest; ++ new_tz.tz_dsttime = 0; ++ ++ /* Get tv to pass to settimeofday(2) to be sure we avoid hour-sized warp */ ++ /* (see gettimeofday(2) man page, or /usr/src/linux/kernel/time.c) */ ++ gettimeofday(&tv, &old_tz); ++ ++ /* set timezone */ ++ settimeofday(&tv, &new_tz); ++ ++} + +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ipt_timerange.h +@@ -0,0 +1,43 @@ ++/* timerange -- An iptables extension to match multiple timeranges within a week ++ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) ++ * ++ * ++ * Copyright © 2009 by Eric Bishop ++ * ++ * This file is free software: you may copy, redistribute and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation, either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This file is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++ ++ ++ ++#ifndef _IPT_TIMERANGE_H ++#define _IPT_TIMERANGE_H ++ ++ ++#define RANGE_LENGTH 51 ++ ++#define HOURS 1 ++#define WEEKDAYS 2 ++#define DAYS_HOURS (HOURS+WEEKDAYS) ++#define WEEKLY_RANGE 4 ++ ++ ++struct ipt_timerange_info ++{ ++ long ranges[RANGE_LENGTH]; ++ char days[7]; ++ char type; ++ unsigned char invert; ++}; ++#endif /*_IPT_TIMERANGE_H*/ + +--- /dev/null ++++ b/extensions/libipt_webmon.c +@@ -0,0 +1,700 @@ ++/* webmon -- An iptables extension to match URLs in HTTP(S) requests ++ * This module can match using string match or regular expressions ++ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) ++ * ++ * ++ * Copyright © 2008-2011 by Eric Bishop ++ * ++ * This file is free software: you may copy, redistribute and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation, either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This file is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* ++ * in iptables 1.4.0 and higher, iptables.h includes xtables.h, which ++ * we can use to check whether we need to deal with the new requirements ++ * in pre-processor directives below ++ */ ++#include ++#include ++ ++#ifdef _XTABLES_H ++ #define iptables_rule_match xtables_rule_match ++ #define iptables_match xtables_match ++ #define iptables_target xtables_target ++ #define ipt_tryload xt_tryload ++#endif ++ ++/* ++ * XTABLES_VERSION_CODE is only defined in versions 1.4.1 and later, which ++ * also require the use of xtables_register_match ++ * ++ * Version 1.4.0 uses register_match like previous versions ++ */ ++#ifdef XTABLES_VERSION_CODE ++ #define register_match xtables_register_match ++#endif ++ ++ ++#define STRIP "%d.%d.%d.%d" ++#define NIPQUAD(addr) \ ++ ((unsigned char *)&addr)[0], \ ++ ((unsigned char *)&addr)[1], \ ++ ((unsigned char *)&addr)[2], \ ++ ((unsigned char *)&addr)[3] ++ ++ ++ ++/* utility functions necessary for module to work across multiple iptables versions */ ++static void param_problem_exit_error(char* msg); ++ ++ ++void parse_ips_and_ranges(char* addr_str, struct ipt_webmon_info *info); ++ ++char** split_on_separators(char* line, char* separators, int num_separators, int max_pieces, int include_remainder_at_max); ++char* trim_flanking_whitespace(char* str); ++unsigned char* read_entire_file(FILE* in, unsigned long read_block_size, unsigned long *length); ++ ++#define DEFAULT_MAX 300 ++ ++#define SEARCH_LOAD_FILE 100 ++#define DOMAIN_LOAD_FILE 101 ++#define CLEAR_SEARCH 102 ++#define CLEAR_DOMAIN 103 ++ ++static char* domain_load_file = NULL; ++static char* search_load_file = NULL; ++static uint32_t global_max_domains = DEFAULT_MAX; ++static uint32_t global_max_searches = DEFAULT_MAX; ++ ++/* Function which prints out usage message. */ ++static void help(void) ++{ ++ printf( "webmon options:\n"); ++} ++ ++static struct option opts[] = ++{ ++ { .name = "exclude_ips", .has_arg = 1, .flag = 0, .val = WEBMON_EXCLUDE }, ++ { .name = "include_ips", .has_arg = 1, .flag = 0, .val = WEBMON_INCLUDE }, ++ { .name = "max_domains", .has_arg = 1, .flag = 0, .val = WEBMON_MAXDOMAIN }, ++ { .name = "max_searches", .has_arg = 1, .flag = 0, .val = WEBMON_MAXSEARCH }, ++ { .name = "search_load_file", .has_arg = 1, .flag = 0, .val = SEARCH_LOAD_FILE }, ++ { .name = "domain_load_file", .has_arg = 1, .flag = 0, .val = DOMAIN_LOAD_FILE }, ++ { .name = "clear_search", .has_arg = 0, .flag = 0, .val = CLEAR_SEARCH }, ++ { .name = "clear_domain", .has_arg = 0, .flag = 0, .val = CLEAR_DOMAIN }, ++ ++ { .name = 0 } ++}; ++ ++static void webmon_init( ++#ifdef _XTABLES_H ++ struct xt_entry_match *match ++#else ++ struct ipt_entry_match *match, unsigned int *nfcache ++#endif ++ ) ++{ ++ struct ipt_webmon_info *info = (struct ipt_webmon_info *)match->data; ++ info->max_domains=DEFAULT_MAX; ++ info->max_searches=DEFAULT_MAX; ++ info->num_exclude_ips=0; ++ info->num_exclude_ranges=0; ++ info->exclude_type = WEBMON_EXCLUDE; ++ info->ref_count = NULL; ++} ++ ++ ++/* Function which parses command options; returns true if it ate an option */ ++static int parse( int c, ++ char **argv, ++ int invert, ++ unsigned int *flags, ++#ifdef _XTABLES_H ++ const void *entry, ++#else ++ const struct ipt_entry *entry, ++ unsigned int *nfcache, ++#endif ++ struct ipt_entry_match **match ++ ) ++{ ++ struct ipt_webmon_info *info = (struct ipt_webmon_info *)(*match)->data; ++ int valid_arg = 1; ++ long max; ++ switch (c) ++ { ++ case WEBMON_EXCLUDE: ++ parse_ips_and_ranges(optarg, info); ++ info->exclude_type = WEBMON_EXCLUDE; ++ break; ++ case WEBMON_INCLUDE: ++ parse_ips_and_ranges(optarg, info); ++ info->exclude_type = WEBMON_INCLUDE; ++ break; ++ case WEBMON_MAXSEARCH: ++ if( sscanf(argv[optind-1], "%ld", &max) == 0) ++ { ++ info->max_searches = DEFAULT_MAX ; ++ valid_arg = 0; ++ } ++ else ++ { ++ info->max_searches = (uint32_t)max; ++ global_max_searches = info->max_searches; ++ } ++ break; ++ case WEBMON_MAXDOMAIN: ++ if( sscanf(argv[optind-1], "%ld", &max) == 0) ++ { ++ info->max_domains = DEFAULT_MAX ; ++ valid_arg = 0; ++ } ++ else ++ { ++ info->max_domains = (uint32_t)max; ++ global_max_domains = info->max_domains; ++ } ++ break; ++ case SEARCH_LOAD_FILE: ++ search_load_file = strdup(optarg); ++ break; ++ case DOMAIN_LOAD_FILE: ++ domain_load_file = strdup(optarg); ++ break; ++ case CLEAR_SEARCH: ++ search_load_file = strdup("/dev/null"); ++ break; ++ case CLEAR_DOMAIN: ++ domain_load_file = strdup("/dev/null"); ++ break; ++ default: ++ valid_arg = 0; ++ } ++ return valid_arg; ++ ++} ++ ++ ++ ++static void print_webmon_args( struct ipt_webmon_info* info ) ++{ ++ printf("--max_domains %ld ", (unsigned long int)info->max_domains); ++ printf("--max_searches %ld ", (unsigned long int)info->max_searches); ++ if(info->num_exclude_ips > 0 || info->num_exclude_ranges > 0) ++ { ++ int ip_index = 0; ++ char comma[3] = ""; ++ printf("--%s ", (info->exclude_type == WEBMON_EXCLUDE ? "exclude_ips" : "include_ips")); ++ for(ip_index=0; ip_index < info->num_exclude_ips; ip_index++) ++ { ++ printf("%s"STRIP, comma, NIPQUAD((info->exclude_ips)[ip_index]) ); ++ sprintf(comma, ","); ++ } ++ for(ip_index=0; ip_index < info->num_exclude_ranges; ip_index++) ++ { ++ struct ipt_webmon_ip_range r = (info->exclude_ranges)[ip_index]; ++ printf("%s"STRIP"-"STRIP, comma, NIPQUAD(r.start), NIPQUAD(r.end) ); ++ sprintf(comma, ","); ++ } ++ printf(" "); ++ } ++} ++ ++ ++static void do_load(char* file, uint32_t max, unsigned char type) ++{ ++ if(file != NULL) ++ { ++ unsigned char* data = NULL; ++ unsigned long data_length = 0; ++ char* file_data = NULL; ++ if(strcmp(file, "/dev/null") != 0) ++ { ++ FILE* in = fopen(file, "r"); ++ if(in != NULL) ++ { ++ file_data = (char*)read_entire_file(in, 4096, &data_length); ++ fclose(in); ++ } ++ } ++ if(file_data == NULL) ++ { ++ file_data=strdup(""); ++ } ++ ++ if(file_data != NULL) ++ { ++ data_length = strlen(file_data) + sizeof(uint32_t)+2; ++ data = (unsigned char*)malloc(data_length); ++ if(data != NULL) ++ { ++ int sockfd = -1; ++ uint32_t* maxp = (uint32_t*)(data+1); ++ data[0] = type; ++ *maxp = max; ++ sprintf( (data+1+sizeof(uint32_t)), "%s", file_data); ++ ++ sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); ++ if(sockfd >= 0) ++ { ++ setsockopt(sockfd, IPPROTO_IP, WEBMON_SET, data, data_length); ++ close(sockfd); ++ } ++ free(data); ++ } ++ free(file_data); ++ } ++ } ++ ++} ++ ++ ++static void final_check(unsigned int flags) ++{ ++ do_load(domain_load_file, global_max_domains, WEBMON_DOMAIN); ++ do_load(search_load_file, global_max_searches, WEBMON_SEARCH); ++} ++ ++/* Prints out the matchinfo. */ ++#ifdef _XTABLES_H ++static void print(const void *ip, const struct xt_entry_match *match, int numeric) ++#else ++static void print(const struct ipt_ip *ip, const struct ipt_entry_match *match, int numeric) ++#endif ++{ ++ printf("WEBMON "); ++ struct ipt_webmon_info *info = (struct ipt_webmon_info *)match->data; ++ ++ print_webmon_args(info); ++} ++ ++/* Saves the union ipt_matchinfo in parsable form to stdout. */ ++#ifdef _XTABLES_H ++static void save(const void *ip, const struct xt_entry_match *match) ++#else ++static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match) ++#endif ++{ ++ struct ipt_webmon_info *info = (struct ipt_webmon_info *)match->data; ++ print_webmon_args(info); ++} ++ ++static struct iptables_match webmon = ++{ ++ .next = NULL, ++ .name = "webmon", ++ #ifdef XTABLES_VERSION_CODE ++ .version = XTABLES_VERSION, ++ #else ++ .version = IPTABLES_VERSION, ++ #endif ++ .size = XT_ALIGN(sizeof(struct ipt_webmon_info)), ++ .userspacesize = XT_ALIGN(sizeof(struct ipt_webmon_info)), ++ .help = &help, ++ .init = &webmon_init, ++ .parse = &parse, ++ .final_check = &final_check, ++ .print = &print, ++ .save = &save, ++ .extra_opts = opts ++}; ++ ++void _init(void) ++{ ++ register_match(&webmon); ++} ++ ++ ++#ifndef TRUE ++#define TRUE 1 ++#endif ++#ifndef FALSE ++#define FALSE 0 ++#endif ++ ++ ++ ++ ++ ++ ++ ++static void param_problem_exit_error(char* msg) ++{ ++ #ifdef xtables_error ++ xtables_error(PARAMETER_PROBLEM, "%s", msg); ++ #else ++ exit_error(PARAMETER_PROBLEM, msg); ++ #endif ++} ++ ++ ++void parse_ips_and_ranges(char* addr_str, struct ipt_webmon_info *info) ++{ ++ char** addr_parts = split_on_separators(addr_str, ",", 1, -1, 0); ++ ++ info->num_exclude_ips=0; ++ info->num_exclude_ranges = 0; ++ ++ int ip_part_index; ++ for(ip_part_index=0; addr_parts[ip_part_index] != NULL; ip_part_index++) ++ { ++ char* next_str = addr_parts[ip_part_index]; ++ if(strchr(next_str, '-') != NULL) ++ { ++ char** range_parts = split_on_separators(next_str, "-", 1, 2, 1); ++ char* start = trim_flanking_whitespace(range_parts[0]); ++ char* end = trim_flanking_whitespace(range_parts[1]); ++ int start_ip[4]; ++ int end_ip[4]; ++ int start_valid = sscanf(start, "%d.%d.%d.%d", start_ip, start_ip+1, start_ip+2, start_ip+3); ++ int end_valid = sscanf(end, "%d.%d.%d.%d", end_ip, end_ip+1, end_ip+2, end_ip+3); ++ ++ if(start_valid == 4 && end_valid == 4) ++ { ++ struct ipt_webmon_ip_range r; ++ struct in_addr sip, eip; ++ inet_pton(AF_INET, start, &sip); ++ inet_pton(AF_INET, end, &eip); ++ r.start = (uint32_t)sip.s_addr; ++ r.end = (uint32_t)eip.s_addr; ++ ++ if(info->num_exclude_ranges < WEBMON_MAX_IP_RANGES && (unsigned long)ntohl(r.start) < (unsigned long)ntohl(r.end) ) ++ { ++ (info->exclude_ranges)[ info->num_exclude_ranges ] = r; ++ info->num_exclude_ranges = info->num_exclude_ranges + 1; ++ } ++ } ++ ++ free(start); ++ free(end); ++ free(range_parts); ++ } ++ else if(strchr(next_str, '/') != NULL) ++ { ++ char** range_parts = split_on_separators(next_str, "/", 1, 2, 1); ++ char* start = trim_flanking_whitespace(range_parts[0]); ++ char* end = trim_flanking_whitespace(range_parts[1]); ++ int base_ip[4]; ++ int base_valid = sscanf(start, "%d.%d.%d.%d", base_ip, base_ip+1, base_ip+2, base_ip+3); ++ if(base_valid == 4) ++ { ++ int mask_valid = 0; ++ uint32_t mask; ++ if(strchr(end, '.') != NULL) ++ { ++ uint32_t mask_ip[4]; ++ int mask_test = sscanf(end, "%d.%d.%d.%d", mask_ip, mask_ip+1, mask_ip+2, mask_ip+3); ++ if(mask_test == 4) ++ { ++ struct in_addr mask_add; ++ inet_pton(AF_INET, end, &mask_add); ++ mask = (uint32_t)mask_add.s_addr; ++ mask_valid = 1; ++ } ++ } ++ else ++ { ++ int mask_bits; ++ if( sscanf(end, "%d", &mask_bits) > 0) ++ { ++ if(mask_bits >=0 && mask_bits <= 32) ++ { ++ uint32_t byte = 0; ++ mask = 0; ++ for(byte=0; byte < 4; byte++) ++ { ++ unsigned char byte_bits = mask_bits > 8 ? 8 : mask_bits; ++ uint32_t byte_mask = 0; ++ mask_bits = mask_bits - byte_bits; ++ ++ while(byte_bits > 0) ++ { ++ byte_mask = byte_mask | (256 >> byte_bits); ++ byte_bits--; ++ } ++ mask = mask | ((uint32_t)byte_mask << (byte*8)); ++ printf("mask = "STRIP"\n", NIPQUAD(mask)); ++ } ++ mask_valid = 1; ++ } ++ } ++ } ++ if(mask_valid) ++ { ++ struct ipt_webmon_ip_range r; ++ struct in_addr bip; ++ inet_pton(AF_INET, start, &bip); ++ r.start = ( ((uint32_t)bip.s_addr) & mask ); ++ r.end = ( ((uint32_t)bip.s_addr) | (~mask) ); ++ if(info->num_exclude_ranges < WEBMON_MAX_IP_RANGES && ntohl(r.start) <= ntohl(r.end) ) ++ { ++ (info->exclude_ranges)[ info->num_exclude_ranges ] = r; ++ info->num_exclude_ranges = info->num_exclude_ranges + 1; ++ } ++ } ++ } ++ free(start); ++ free(end); ++ free(range_parts); ++ } ++ else ++ { ++ int parsed_ip[4]; ++ int valid = sscanf(next_str, "%d.%d.%d.%d", parsed_ip, parsed_ip+1, parsed_ip+2, parsed_ip+3); ++ if(valid == 4) ++ { ++ struct in_addr ip; ++ trim_flanking_whitespace(next_str); ++ inet_pton(AF_INET, next_str, &ip); ++ ++ if(info->num_exclude_ranges < WEBMON_MAX_IPS) ++ { ++ (info->exclude_ips)[ info->num_exclude_ips ] = (uint32_t)ip.s_addr; ++ info->num_exclude_ips = info->num_exclude_ips + 1; ++ } ++ } ++ } ++ free(next_str); ++ } ++ free(addr_parts); ++ ++} ++ ++ ++ ++/* ++ * line_str is the line to be parsed -- it is not modified in any way ++ * max_pieces indicates number of pieces to return, if negative this is determined dynamically ++ * include_remainder_at_max indicates whether the last piece, when max pieces are reached, ++ * should be what it would normally be (0) or the entire remainder of the line (1) ++ * if max_pieces < 0 this parameter is ignored ++ * ++ * ++ * returns all non-separator pieces in a line ++ * result is dynamically allocated, MUST be freed after call-- even if ++ * line is empty (you still get a valid char** pointer to to a NULL char*) ++ */ ++char** split_on_separators(char* line_str, char* separators, int num_separators, int max_pieces, int include_remainder_at_max) ++{ ++ char** split; ++ ++ if(line_str != NULL) ++ { ++ int split_index; ++ int non_separator_found; ++ char* dup_line; ++ char* start; ++ ++ if(max_pieces < 0) ++ { ++ /* count number of separator characters in line -- this count + 1 is an upperbound on number of pieces */ ++ int separator_count = 0; ++ int line_index; ++ for(line_index = 0; line_str[line_index] != '\0'; line_index++) ++ { ++ int sep_index; ++ int found = 0; ++ for(sep_index =0; found == 0 && sep_index < num_separators; sep_index++) ++ { ++ found = separators[sep_index] == line_str[line_index] ? 1 : 0; ++ } ++ separator_count = separator_count+ found; ++ } ++ max_pieces = separator_count + 1; ++ } ++ split = (char**)malloc((1+max_pieces)*sizeof(char*)); ++ split_index = 0; ++ split[split_index] = NULL; ++ ++ ++ dup_line = strdup(line_str); ++ start = dup_line; ++ non_separator_found = 0; ++ while(non_separator_found == 0) ++ { ++ int matches = 0; ++ int sep_index; ++ for(sep_index =0; sep_index < num_separators; sep_index++) ++ { ++ matches = matches == 1 || separators[sep_index] == start[0] ? 1 : 0; ++ } ++ non_separator_found = matches==0 || start[0] == '\0' ? 1 : 0; ++ if(non_separator_found == 0) ++ { ++ start++; ++ } ++ } ++ ++ while(start[0] != '\0' && split_index < max_pieces) ++ { ++ /* find first separator index */ ++ int first_separator_index = 0; ++ int separator_found = 0; ++ while( separator_found == 0 ) ++ { ++ int sep_index; ++ for(sep_index =0; separator_found == 0 && sep_index < num_separators; sep_index++) ++ { ++ separator_found = separators[sep_index] == start[first_separator_index] || start[first_separator_index] == '\0' ? 1 : 0; ++ } ++ if(separator_found == 0) ++ { ++ first_separator_index++; ++ } ++ } ++ ++ /* copy next piece to split array */ ++ if(first_separator_index > 0) ++ { ++ char* next_piece = NULL; ++ if(split_index +1 < max_pieces || include_remainder_at_max <= 0) ++ { ++ next_piece = (char*)malloc((first_separator_index+1)*sizeof(char)); ++ memcpy(next_piece, start, first_separator_index); ++ next_piece[first_separator_index] = '\0'; ++ } ++ else ++ { ++ next_piece = strdup(start); ++ } ++ split[split_index] = next_piece; ++ split[split_index+1] = NULL; ++ split_index++; ++ } ++ ++ ++ /* find next non-separator index, indicating start of next piece */ ++ start = start+ first_separator_index; ++ non_separator_found = 0; ++ while(non_separator_found == 0) ++ { ++ int matches = 0; ++ int sep_index; ++ for(sep_index =0; sep_index < num_separators; sep_index++) ++ { ++ matches = matches == 1 || separators[sep_index] == start[0] ? 1 : 0; ++ } ++ non_separator_found = matches==0 || start[0] == '\0' ? 1 : 0; ++ if(non_separator_found == 0) ++ { ++ start++; ++ } ++ } ++ } ++ free(dup_line); ++ } ++ else ++ { ++ split = (char**)malloc((1)*sizeof(char*)); ++ split[0] = NULL; ++ } ++ return split; ++} ++ ++ ++ ++char* trim_flanking_whitespace(char* str) ++{ ++ int new_start = 0; ++ int new_length = 0; ++ ++ char whitespace[5] = { ' ', '\t', '\n', '\r', '\0' }; ++ int num_whitespace_chars = 4; ++ ++ ++ int str_index = 0; ++ int is_whitespace = 1; ++ int test; ++ while( (test = str[str_index]) != '\0' && is_whitespace == 1) ++ { ++ int whitespace_index; ++ is_whitespace = 0; ++ for(whitespace_index = 0; whitespace_index < num_whitespace_chars && is_whitespace == 0; whitespace_index++) ++ { ++ is_whitespace = test == whitespace[whitespace_index] ? 1 : 0; ++ } ++ str_index = is_whitespace == 1 ? str_index+1 : str_index; ++ } ++ new_start = str_index; ++ ++ ++ str_index = strlen(str) - 1; ++ is_whitespace = 1; ++ while( str_index >= new_start && is_whitespace == 1) ++ { ++ int whitespace_index; ++ is_whitespace = 0; ++ for(whitespace_index = 0; whitespace_index < num_whitespace_chars && is_whitespace == 0; whitespace_index++) ++ { ++ is_whitespace = str[str_index] == whitespace[whitespace_index] ? 1 : 0; ++ } ++ str_index = is_whitespace == 1 ? str_index-1 : str_index; ++ } ++ new_length = str[new_start] == '\0' ? 0 : str_index + 1 - new_start; ++ ++ ++ if(new_start > 0) ++ { ++ for(str_index = 0; str_index < new_length; str_index++) ++ { ++ str[str_index] = str[str_index+new_start]; ++ } ++ } ++ str[new_length] = 0; ++ return str; ++} ++ ++ ++unsigned char* read_entire_file(FILE* in, unsigned long read_block_size, unsigned long *length) ++{ ++ int max_read_size = read_block_size; ++ unsigned char* read_string = (unsigned char*)malloc(max_read_size+1); ++ unsigned long bytes_read = 0; ++ int end_found = 0; ++ while(end_found == 0) ++ { ++ int nextch = '?'; ++ while(nextch != EOF && bytes_read < max_read_size) ++ { ++ nextch = fgetc(in); ++ if(nextch != EOF) ++ { ++ read_string[bytes_read] = (unsigned char)nextch; ++ bytes_read++; ++ } ++ } ++ read_string[bytes_read] = '\0'; ++ end_found = (nextch == EOF) ? 1 : 0; ++ if(end_found == 0) ++ { ++ unsigned char *new_str; ++ max_read_size = max_read_size + read_block_size; ++ new_str = (unsigned char*)malloc(max_read_size+1); ++ memcpy(new_str, read_string, bytes_read); ++ free(read_string); ++ read_string = new_str; ++ } ++ } ++ *length = bytes_read; ++ return read_string; ++} ++ + +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ipt_webmon.h +@@ -0,0 +1,63 @@ ++/* webmon -- A netfilter module to match URLs in HTTP(S) requests ++ * This module can match using string match or regular expressions ++ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) ++ * ++ * ++ * Copyright © 2008-2010 by Eric Bishop ++ * ++ * This file is free software: you may copy, redistribute and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation, either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This file is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++ ++ ++ ++#ifndef _IPT_WEBMON_H ++#define _IPT_WEBMON_H ++ ++ ++#define WEBMON_MAX_IPS 256 ++#define WEBMON_MAX_IP_RANGES 16 ++ ++#define WEBMON_EXCLUDE 1 ++#define WEBMON_INCLUDE 2 ++ ++#define WEBMON_MAXDOMAIN 4 ++#define WEBMON_MAXSEARCH 8 ++ ++#define WEBMON_DOMAIN 16 ++#define WEBMON_SEARCH 32 ++ ++ ++#define WEBMON_SET 3064 ++ ++struct ipt_webmon_ip_range ++{ ++ uint32_t start; ++ uint32_t end; ++}; ++ ++struct ipt_webmon_info ++{ ++ uint32_t max_domains; ++ uint32_t max_searches; ++ uint32_t exclude_ips[WEBMON_MAX_IPS]; ++ struct ipt_webmon_ip_range exclude_ranges[WEBMON_MAX_IP_RANGES]; ++ uint32_t num_exclude_ips; ++ uint32_t num_exclude_ranges; ++ unsigned char exclude_type; ++ uint32_t* ref_count; ++ ++}; ++ ++#endif /*_IPT_WEBMON_H*/ + +--- /dev/null ++++ b/extensions/libipt_weburl.c +@@ -0,0 +1,290 @@ ++/* weburl -- An iptables extension to match URLs in HTTP(S) requests ++ * This module can match using string match or regular expressions ++ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) ++ * ++ * ++ * Copyright © 2008-2010 by Eric Bishop ++ * ++ * This file is free software: you may copy, redistribute and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation, either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This file is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++ ++ ++/* ++ * in iptables 1.4.0 and higher, iptables.h includes xtables.h, which ++ * we can use to check whether we need to deal with the new requirements ++ * in pre-processor directives below ++ */ ++#include ++#include ++ ++#ifdef _XTABLES_H ++ #define iptables_rule_match xtables_rule_match ++ #define iptables_match xtables_match ++ #define iptables_target xtables_target ++ #define ipt_tryload xt_tryload ++#endif ++ ++/* ++ * XTABLES_VERSION_CODE is only defined in versions 1.4.1 and later, which ++ * also require the use of xtables_register_match ++ * ++ * Version 1.4.0 uses register_match like previous versions ++ */ ++#ifdef XTABLES_VERSION_CODE ++ #define register_match xtables_register_match ++#endif ++ ++ ++/* utility functions necessary for module to work across multiple iptables versions */ ++static int my_check_inverse(const char option[], int* invert, int *my_optind, int argc); ++static void param_problem_exit_error(char* msg); ++ ++ ++ ++/* Function which prints out usage message. */ ++static void help(void) ++{ ++ printf( "weburl options:\n --contains [!] [STRING]\n --contains_regex [!] [REGEX]\n --matches_exactly [!] [STRING]\n --domain_only\n --path_only\n"); ++} ++ ++static struct option opts[] = ++{ ++ { .name = "contains", .has_arg = 1, .flag = 0, .val = WEBURL_CONTAINS_TYPE }, //string ++ { .name = "contains_regex", .has_arg = 1, .flag = 0, .val = WEBURL_REGEX_TYPE }, //regex ++ { .name = "matches_exactly", .has_arg = 1, .flag = 0, .val = WEBURL_EXACT_TYPE }, //exact string match ++ { .name = "domain_only", .has_arg = 0, .flag = 0, .val = WEBURL_DOMAIN_PART }, //only match domain portion of url ++ { .name = "path_only", .has_arg = 0, .flag = 0, .val = WEBURL_PATH_PART }, //only match path portion of url ++ { .name = 0 } ++}; ++ ++ ++/* Function which parses command options; returns true if it ++ ate an option */ ++static int parse( int c, ++ char **argv, ++ int invert, ++ unsigned int *flags, ++#ifdef _XTABLES_H ++ const void *entry, ++#else ++ const struct ipt_entry *entry, ++ unsigned int *nfcache, ++#endif ++ struct ipt_entry_match **match ++ ) ++{ ++ struct ipt_weburl_info *info = (struct ipt_weburl_info *)(*match)->data; ++ int valid_arg = 0; ++ ++ if(*flags < 10) ++ { ++ info->match_part = WEBURL_ALL_PART; ++ } ++ ++ switch (c) ++ { ++ case WEBURL_CONTAINS_TYPE: ++ case WEBURL_REGEX_TYPE: ++ case WEBURL_EXACT_TYPE: ++ info->match_type = c; ++ ++ //test whether to invert rule ++ my_check_inverse(optarg, &invert, &optind, 0); ++ info->invert = invert ? 1 : 0; ++ ++ //test that test string is reasonable length, then to info ++ int testlen = strlen(argv[optind-1]); ++ if(testlen > 0 && testlen < MAX_TEST_STR) ++ { ++ strcpy(info->test_str, argv[optind-1]); ++ } ++ else if(testlen >= MAX_TEST_STR) ++ { ++ char err[100]; ++ sprintf(err, "Parameter definition is too long, must be less than %d characters", MAX_TEST_STR); ++ param_problem_exit_error(err); ++ } ++ else ++ { ++ param_problem_exit_error("Parameter definition is incomplete"); ++ } ++ ++ if(*flags % 10 == 1) ++ { ++ param_problem_exit_error("You may only specify one string/pattern to match"); ++ } ++ *flags = *flags + 1; ++ ++ valid_arg = 1; ++ break; ++ ++ case WEBURL_DOMAIN_PART: ++ case WEBURL_PATH_PART: ++ info->match_part = c; ++ if(*flags >= 10) ++ { ++ param_problem_exit_error("You may specify at most one part of the url to match:\n\t--domain_only, --path_only or neither (to match full url)\n"); ++ } ++ *flags = *flags+10; ++ ++ valid_arg = 1; ++ break; ++ } ++ ++ return valid_arg; ++} ++ ++ ++ ++static void print_weburl_args( struct ipt_weburl_info* info ) ++{ ++ //invert ++ if(info->invert > 0) ++ { ++ printf("! "); ++ } ++ //match type ++ switch (info->match_type) ++ { ++ case WEBURL_CONTAINS_TYPE: ++ printf("--contains "); ++ break; ++ case WEBURL_REGEX_TYPE: ++ printf("--contains_regex "); ++ break; ++ case WEBURL_EXACT_TYPE: ++ printf("--matches_exactly "); ++ break; ++ } ++ //test string ++ printf("%s ", info->test_str); ++ ++ //match part ++ switch(info->match_part) ++ { ++ case WEBURL_DOMAIN_PART: ++ printf("--domain_only "); ++ break; ++ case WEBURL_PATH_PART: ++ printf("--path_only "); ++ break; ++ case WEBURL_ALL_PART: ++ //print nothing ++ break; ++ } ++ ++} ++ ++/* Final check; must have specified a test string with either --contains or --contains_regex. */ ++static void final_check(unsigned int flags) ++{ ++ if (flags %10 == 0) ++ { ++ param_problem_exit_error("You must specify '--contains' or '--contains_regex' or '--matches_exactly'"); ++ } ++} ++ ++/* Prints out the matchinfo. */ ++#ifdef _XTABLES_H ++static void print(const void *ip, const struct xt_entry_match *match, int numeric) ++#else ++static void print(const struct ipt_ip *ip, const struct ipt_entry_match *match, int numeric) ++#endif ++{ ++ printf("WEBURL "); ++ struct ipt_weburl_info *info = (struct ipt_weburl_info *)match->data; ++ ++ print_weburl_args(info); ++} ++ ++/* Saves the union ipt_matchinfo in parsable form to stdout. */ ++#ifdef _XTABLES_H ++static void save(const void *ip, const struct xt_entry_match *match) ++#else ++static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match) ++#endif ++{ ++ struct ipt_weburl_info *info = (struct ipt_weburl_info *)match->data; ++ print_weburl_args(info); ++} ++ ++static struct iptables_match weburl = ++{ ++ .next = NULL, ++ .name = "weburl", ++ #ifdef XTABLES_VERSION_CODE ++ .version = XTABLES_VERSION, ++ #else ++ .version = IPTABLES_VERSION, ++ #endif ++ .size = XT_ALIGN(sizeof(struct ipt_weburl_info)), ++ .userspacesize = XT_ALIGN(sizeof(struct ipt_weburl_info)), ++ .help = &help, ++ .parse = &parse, ++ .final_check = &final_check, ++ .print = &print, ++ .save = &save, ++ .extra_opts = opts ++}; ++ ++void _init(void) ++{ ++ register_match(&weburl); ++} ++ ++ ++#ifndef TRUE ++#define TRUE 1 ++#endif ++#ifndef FALSE ++#define FALSE 0 ++#endif ++static int my_check_inverse(const char option[], int* invert, int *my_optind, int argc) ++{ ++ if (option && strcmp(option, "!") == 0) ++ { ++ if (*invert) ++ { ++ param_problem_exit_error("Multiple `!' flags not allowed"); ++ } ++ *invert = TRUE; ++ if (my_optind != NULL) ++ { ++ ++*my_optind; ++ if (argc && *my_optind > argc) ++ { ++ param_problem_exit_error("no argument following `!'"); ++ } ++ } ++ return TRUE; ++ } ++ return FALSE; ++} ++static void param_problem_exit_error(char* msg) ++{ ++ #ifdef xtables_error ++ xtables_error(PARAMETER_PROBLEM, "%s", msg); ++ #else ++ exit_error(PARAMETER_PROBLEM, msg); ++ #endif ++} ++ ++ + +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ipt_weburl.h +@@ -0,0 +1,45 @@ ++/* weburl -- A netfilter module to match URLs in HTTP(S) requests ++ * This module can match using string match or regular expressions ++ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) ++ * ++ * ++ * Copyright © 2008 by Eric Bishop ++ * ++ * This file is free software: you may copy, redistribute and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation, either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This file is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++ ++ ++ ++#ifndef _IPT_WEBURL_H ++#define _IPT_WEBURL_H ++ ++ ++#define MAX_TEST_STR 1024 ++ ++#define WEBURL_CONTAINS_TYPE 1 ++#define WEBURL_REGEX_TYPE 2 ++#define WEBURL_EXACT_TYPE 3 ++#define WEBURL_ALL_PART 4 ++#define WEBURL_DOMAIN_PART 5 ++#define WEBURL_PATH_PART 6 ++ ++struct ipt_weburl_info ++{ ++ char test_str[MAX_TEST_STR]; ++ unsigned char match_type; ++ unsigned char match_part; ++ unsigned char invert; ++}; ++#endif /*_IPT_WEBURL_H*/ diff --git a/target/linux/generic/config-4.14 b/target/linux/generic/config-4.14 index 6842c5be44..cdca243c8e 100644 --- a/target/linux/generic/config-4.14 +++ b/target/linux/generic/config-4.14 @@ -5812,3 +5812,7 @@ CONFIG_IMQ_NUM_DEVS=2 # CONFIG_IMQ_BEHAVIOR_AB is not set CONFIG_IMQ_BEHAVIOR_BA=y # CONFIG_IMQ_BEHAVIOR_BB is not set +CONFIG_IP_NF_MATCH_WEBURL=m +CONFIG_IP_NF_MATCH_WEBMON=m +CONFIG_IP_NF_MATCH_TIMERANGE=m +CONFIG_IP_NF_MATCH_BANDWIDTH=m diff --git a/target/linux/generic/config-4.19 b/target/linux/generic/config-4.19 index 56310a1126..06322adaae 100644 --- a/target/linux/generic/config-4.19 +++ b/target/linux/generic/config-4.19 @@ -6112,3 +6112,7 @@ CONFIG_IMQ_NUM_DEVS=2 # CONFIG_IMQ_BEHAVIOR_AB is not set CONFIG_IMQ_BEHAVIOR_BA=y # CONFIG_IMQ_BEHAVIOR_BB is not set +CONFIG_IP_NF_MATCH_WEBURL=m +CONFIG_IP_NF_MATCH_WEBMON=m +CONFIG_IP_NF_MATCH_TIMERANGE=m +CONFIG_IP_NF_MATCH_BANDWIDTH=m diff --git a/target/linux/generic/pending-4.14/608-add-kernel-gargoyle-netfilter-match-modules.patch b/target/linux/generic/pending-4.14/608-add-kernel-gargoyle-netfilter-match-modules.patch new file mode 100644 index 0000000000..7fb6078ac8 --- /dev/null +++ b/target/linux/generic/pending-4.14/608-add-kernel-gargoyle-netfilter-match-modules.patch @@ -0,0 +1,10864 @@ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ipt_bandwidth.h +@@ -0,0 +1,106 @@ ++/* bandwidth -- An iptables extension for bandwidth monitoring/control ++ * Can be used to efficiently monitor bandwidth and/or implement bandwidth quotas ++ * Can be queried using the iptbwctl userspace library ++ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) ++ * ++ * ++ * Copyright © 2009 by Eric Bishop ++ * ++ * This file is free software: you may copy, redistribute and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation, either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This file is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#ifndef _IPT_BANDWIDTH_H ++#define _IPT_BANDWIDTH_H ++ ++/*flags -- first three don't map to parameters the rest do */ ++#define BANDWIDTH_INITIALIZED 1 ++#define BANDWIDTH_REQUIRES_SUBNET 2 ++#define BANDWIDTH_SUBNET 4 ++#define BANDWIDTH_CMP 8 ++#define BANDWIDTH_CURRENT 16 ++#define BANDWIDTH_RESET_INTERVAL 32 ++#define BANDWIDTH_RESET_TIME 64 ++#define BANDWIDTH_LAST_BACKUP 128 ++ ++ ++/* parameter defs that don't map to flag bits */ ++#define BANDWIDTH_TYPE 70 ++#define BANDWIDTH_ID 71 ++#define BANDWIDTH_GT 72 ++#define BANDWIDTH_LT 73 ++#define BANDWIDTH_MONITOR 74 ++#define BANDWIDTH_CHECK 75 ++#define BANDWIDTH_CHECK_NOSWAP 76 ++#define BANDWIDTH_CHECK_SWAP 77 ++#define BANDWIDTH_NUM_INTERVALS 78 ++ ++/* possible reset intervals */ ++#define BANDWIDTH_MINUTE 80 ++#define BANDWIDTH_HOUR 81 ++#define BANDWIDTH_DAY 82 ++#define BANDWIDTH_WEEK 83 ++#define BANDWIDTH_MONTH 84 ++#define BANDWIDTH_NEVER 85 ++ ++/* possible monitoring types */ ++#define BANDWIDTH_COMBINED 90 ++#define BANDWIDTH_INDIVIDUAL_SRC 91 ++#define BANDWIDTH_INDIVIDUAL_DST 92 ++#define BANDWIDTH_INDIVIDUAL_LOCAL 93 ++#define BANDWIDTH_INDIVIDUAL_REMOTE 94 ++ ++ ++ ++/* socket id parameters (for userspace i/o) */ ++#define BANDWIDTH_SET 2048 ++#define BANDWIDTH_GET 2049 ++ ++/* max id length */ ++#define BANDWIDTH_MAX_ID_LENGTH 50 ++ ++/* 4 bytes for total number of entries, 100 entries of 12 bytes each, + 1 byte indicating whether all have been dumped */ ++#define BANDWIDTH_QUERY_LENGTH 1205 ++#define BANDWIDTH_ENTRY_LENGTH 12 ++ ++ ++struct ipt_bandwidth_info ++{ ++ char id[BANDWIDTH_MAX_ID_LENGTH]; ++ unsigned char type; ++ unsigned char check_type; ++ uint32_t local_subnet; ++ uint32_t local_subnet_mask; ++ ++ unsigned char cmp; ++ unsigned char reset_is_constant_interval; ++ time_t reset_interval; //specific fixed type (see above) or interval length in seconds ++ time_t reset_time; //seconds from start of month/week/day/hour/minute to do reset, or start point of interval if it is a constant interval ++ uint64_t bandwidth_cutoff; ++ uint64_t current_bandwidth; ++ time_t next_reset; ++ time_t previous_reset; ++ time_t last_backup_time; ++ ++ uint32_t num_intervals_to_save; ++ ++ ++ unsigned long hashed_id; ++ void* iam; ++ uint64_t* combined_bw; ++ struct ipt_bandwidth_info* non_const_self; ++ unsigned long* ref_count; ++ ++ ++}; ++#endif /*_IPT_BANDWIDTH_H*/ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ipt_timerange.h +@@ -0,0 +1,43 @@ ++/* timerange -- An iptables extension to match multiple timeranges within a week ++ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) ++ * ++ * ++ * Copyright © 2009 by Eric Bishop ++ * ++ * This file is free software: you may copy, redistribute and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation, either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This file is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++ ++ ++ ++#ifndef _IPT_TIMERANGE_H ++#define _IPT_TIMERANGE_H ++ ++ ++#define RANGE_LENGTH 51 ++ ++#define HOURS 1 ++#define WEEKDAYS 2 ++#define DAYS_HOURS (HOURS+WEEKDAYS) ++#define WEEKLY_RANGE 4 ++ ++ ++struct ipt_timerange_info ++{ ++ long ranges[RANGE_LENGTH]; ++ char days[7]; ++ char type; ++ unsigned char invert; ++}; ++#endif /*_IPT_TIMERANGE_H*/ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ipt_webmon.h +@@ -0,0 +1,63 @@ ++/* webmon -- A netfilter module to match URLs in HTTP requests ++ * This module can match using string match or regular expressions ++ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) ++ * ++ * ++ * Copyright © 2008-2010 by Eric Bishop ++ * ++ * This file is free software: you may copy, redistribute and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation, either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This file is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++ ++ ++ ++#ifndef _IPT_WEBMON_H ++#define _IPT_WEBMON_H ++ ++ ++#define WEBMON_MAX_IPS 256 ++#define WEBMON_MAX_IP_RANGES 16 ++ ++#define WEBMON_EXCLUDE 1 ++#define WEBMON_INCLUDE 2 ++ ++#define WEBMON_MAXDOMAIN 4 ++#define WEBMON_MAXSEARCH 8 ++ ++#define WEBMON_DOMAIN 16 ++#define WEBMON_SEARCH 32 ++ ++ ++#define WEBMON_SET 3064 ++ ++struct ipt_webmon_ip_range ++{ ++ uint32_t start; ++ uint32_t end; ++}; ++ ++struct ipt_webmon_info ++{ ++ uint32_t max_domains; ++ uint32_t max_searches; ++ uint32_t exclude_ips[WEBMON_MAX_IPS]; ++ struct ipt_webmon_ip_range exclude_ranges[WEBMON_MAX_IP_RANGES]; ++ uint32_t num_exclude_ips; ++ uint32_t num_exclude_ranges; ++ unsigned char exclude_type; ++ uint32_t* ref_count; ++ ++}; ++ ++#endif /*_IPT_WEBMON_H*/ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ipt_weburl.h +@@ -0,0 +1,45 @@ ++/* weburl -- A netfilter module to match URLs in HTTP requests ++ * This module can match using string match or regular expressions ++ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) ++ * ++ * ++ * Copyright © 2008 by Eric Bishop ++ * ++ * This file is free software: you may copy, redistribute and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation, either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This file is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++ ++ ++ ++#ifndef _IPT_WEBURL_H ++#define _IPT_WEBURL_H ++ ++ ++#define MAX_TEST_STR 1024 ++ ++#define WEBURL_CONTAINS_TYPE 1 ++#define WEBURL_REGEX_TYPE 2 ++#define WEBURL_EXACT_TYPE 3 ++#define WEBURL_ALL_PART 4 ++#define WEBURL_DOMAIN_PART 5 ++#define WEBURL_PATH_PART 6 ++ ++struct ipt_weburl_info ++{ ++ char test_str[MAX_TEST_STR]; ++ unsigned char match_type; ++ unsigned char match_part; ++ unsigned char invert; ++}; ++#endif /*_IPT_WEBURL_H*/ +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -417,5 +417,25 @@ config IP_NF_ARP_MANGLE + + endif # IP_NF_ARPTABLES + ++config IP_NF_MATCH_BANDWIDTH ++ tristate "bandwidth match support" ++ depends on IP_NF_IPTABLES ++ help ++ This option enables bandwidth match support. ++config IP_NF_MATCH_TIMERANGE ++ tristate "timerange match support" ++ depends on IP_NF_IPTABLES ++ help ++ This option enables timerange match support. ++config IP_NF_MATCH_WEBMON ++ tristate "webmon match support" ++ depends on IP_NF_IPTABLES ++ help ++ This option enables webmon match support. ++config IP_NF_MATCH_WEBURL ++ tristate "weburl match support" ++ depends on IP_NF_IPTABLES ++ help ++ This option enables weburl match support. + endmenu + +--- a/net/ipv4/netfilter/Makefile ++++ b/net/ipv4/netfilter/Makefile +@@ -55,6 +55,10 @@ obj-$(CONFIG_IP_NF_RAW) += iptable_raw.o + obj-$(CONFIG_IP_NF_SECURITY) += iptable_security.o + + # matches ++obj-$(CONFIG_IP_NF_MATCH_BANDWIDTH) += ipt_bandwidth.o ++obj-$(CONFIG_IP_NF_MATCH_TIMERANGE) += ipt_timerange.o ++obj-$(CONFIG_IP_NF_MATCH_WEBMON) += ipt_webmon.o ++obj-$(CONFIG_IP_NF_MATCH_WEBURL) += ipt_weburl.o + obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o + obj-$(CONFIG_IP_NF_MATCH_RPFILTER) += ipt_rpfilter.o + +--- /dev/null ++++ b/net/ipv4/netfilter/bandwidth_deps/tree_map.h +@@ -0,0 +1,1093 @@ ++/* ++ * Copyright © 2008 by Eric Bishop ++ * ++ * This work 'as-is' we provide. ++ * No warranty, express or implied. ++ * We've done our best, ++ * to debug and test. ++ * Liability for damages denied. ++ * ++ * Permission is granted hereby, ++ * to copy, share, and modify. ++ * Use as is fit, ++ * free or for profit. ++ * On this notice these rights rely. ++ * ++ * ++ * ++ * Note that unlike other portions of Gargoyle this code ++ * does not fall under the GPL, but the rather whimsical ++ * 'Poetic License' above. ++ * ++ * Basically, this library contains a bunch of utilities ++ * that I find useful. I'm sure other libraries exist ++ * that are just as good or better, but I like these tools ++ * because I personally wrote them, so I know their quirks. ++ * (i.e. I know where the bodies are buried). I want to ++ * make sure that I can re-use these utilities for whatever ++ * code I may want to write in the future be it ++ * proprietary or open-source, so I've put them under ++ * a very, very permissive license. ++ * ++ * If you find this code useful, use it. If not, don't. ++ * I really don't care. ++ * ++ */ ++ ++ ++#if __KERNEL__ ++ #define malloc(foo) kmalloc(foo,GFP_ATOMIC) ++ #define free(foo) kfree(foo) ++ #define printf(format,args...) printk(format,##args) ++ ++ /* kernel strdup */ ++ static inline char *kernel_strdup(const char *str); ++ static inline char *kernel_strdup(const char *str) ++ { ++ char *tmp; ++ long int s; ++ s=strlen(str) + 1; ++ tmp = kmalloc(s, GFP_ATOMIC); ++ if (tmp != NULL) ++ { ++ memcpy(tmp, str, s); ++ } ++ return tmp; ++ } ++ #define strdup kernel_strdup ++ ++#endif ++ ++ ++ ++/* tree_map structs / prototypes */ ++typedef struct long_tree_map_node ++{ ++ unsigned long key; ++ void* value; ++ ++ signed char balance; ++ struct long_tree_map_node* left; ++ struct long_tree_map_node* right; ++} long_map_node; ++ ++typedef struct ++{ ++ long_map_node* root; ++ unsigned long num_elements; ++ ++}long_map; ++ ++typedef struct ++{ ++ long_map lm; ++ unsigned char store_keys; ++ unsigned long num_elements; ++ ++}string_map; ++ ++ ++ ++/* long map functions */ ++long_map* initialize_long_map(void); ++void* get_long_map_element(long_map* map, unsigned long key); ++void* get_smallest_long_map_element(long_map* map, unsigned long* smallest_key); ++void* get_largest_long_map_element(long_map* map, unsigned long* largest_key); ++void* remove_smallest_long_map_element(long_map* map, unsigned long* smallest_key); ++void* remove_largest_long_map_element(long_map* map, unsigned long* largest_key); ++void* set_long_map_element(long_map* map, unsigned long key, void* value); ++void* remove_long_map_element(long_map* map, unsigned long key); ++unsigned long* get_sorted_long_map_keys(long_map* map, unsigned long* num_keys_returned); ++void** get_sorted_long_map_values(long_map* map, unsigned long* num_values_returned); ++void** destroy_long_map(long_map* map, int destruction_type, unsigned long* num_destroyed); ++void apply_to_every_long_map_value(long_map* map, void (*apply_func)(unsigned long key, void* value)); ++ ++/* string map functions */ ++string_map* initialize_string_map(unsigned char store_keys); ++void* get_string_map_element(string_map* map, const char* key); ++void* get_string_map_element_with_hashed_key(string_map* map, unsigned long hashed_key); ++void* set_string_map_element(string_map* map, const char* key, void* value); ++void* remove_string_map_element(string_map* map, const char* key); ++char** get_string_map_keys(string_map* map, unsigned long* num_keys_returned); ++void** get_string_map_values(string_map* map, unsigned long* num_values_returned); ++void** destroy_string_map(string_map* map, int destruction_type, unsigned long* num_destroyed); ++void apply_to_every_string_map_value(string_map* map, void (*apply_func)(char* key, void* value)); ++ ++ ++/* ++ * three different ways to deal with values when data structure is destroyed ++ */ ++#define DESTROY_MODE_RETURN_VALUES 20 ++#define DESTROY_MODE_FREE_VALUES 21 ++#define DESTROY_MODE_IGNORE_VALUES 22 ++ ++ ++/* ++ * for convenience & backwards compatibility alias _string_map_ functions to ++ * _map_ functions since string map is used more often than long map ++ */ ++#define initialize_map initialize_string_map ++#define set_map_element set_string_map_element ++#define get_map_element get_string_map_element ++#define remove_map_element remove_string_map_element ++#define get_map_keys get_string_map_keys ++#define get_map_values get_string_map_values ++#define destroy_map destroy_string_map ++ ++ ++/* internal utility structures/ functions */ ++typedef struct stack_node_struct ++{ ++ long_map_node** node_ptr; ++ signed char direction; ++ struct stack_node_struct* previous; ++} stack_node; ++ ++static void free_stack(stack_node* stack); ++static void** destroy_long_map_values(long_map* map, int destruction_type, unsigned long* num_destroyed); ++static void apply_to_every_long_map_node(long_map_node* node, void (*apply_func)(unsigned long key, void* value)); ++static void apply_to_every_string_map_node(long_map_node* node, unsigned char has_key, void (*apply_func)(char* key, void* value)); ++static void get_sorted_node_keys(long_map_node* node, unsigned long* key_list, unsigned long* next_key_index, int depth); ++static void get_sorted_node_values(long_map_node* node, void** value_list, unsigned long* next_value_index, int depth); ++static signed char rebalance (long_map_node** n, signed char direction, signed char update_op); ++static void rotate_right (long_map_node** parent); ++static void rotate_left (long_map_node** parent); ++ ++/* internal for string map */ ++typedef struct ++{ ++ char* key; ++ void* value; ++} string_map_key_value; ++static unsigned long sdbm_string_hash(const char *key); ++ ++ ++ ++ ++/*************************************************** ++ * For testing only ++ ***************************************************/ ++/* ++void print_list(stack_node *l); ++ ++void print_list(stack_node *l) ++{ ++ if(l != NULL) ++ { ++ printf(" list key = %ld, dir=%d, \n", (*(l->node_ptr))->key, l->direction); ++ print_list(l->previous); ++ } ++} ++*/ ++/****************************************************** ++ * End testing Code ++ *******************************************************/ ++ ++ ++ ++ ++/*************************************************** ++ * string_map function definitions ++ ***************************************************/ ++ ++string_map* initialize_string_map(unsigned char store_keys) ++{ ++ string_map* map = (string_map*)malloc(sizeof(string_map)); ++ if(map != NULL) ++ { ++ map->store_keys = store_keys; ++ map->lm.root = NULL; ++ map->lm.num_elements = 0; ++ map->num_elements = map->lm.num_elements; ++ } ++ return map; ++} ++ ++void* get_string_map_element(string_map* map, const char* key) ++{ ++ unsigned long hashed_key = sdbm_string_hash(key); ++ ++ return get_string_map_element_with_hashed_key(map, hashed_key); ++} ++ ++void* get_string_map_element_with_hashed_key(string_map* map, unsigned long hashed_key) ++{ ++ void* return_value; ++ /* printk("doing lookup for key = %lu\n", hashed_key); */ ++ return_value = get_long_map_element( &(map->lm), hashed_key); ++ if(return_value != NULL && map->store_keys) ++ { ++ string_map_key_value* r = (string_map_key_value*)return_value; ++ return_value = r->value; ++ } ++ map->num_elements = map->lm.num_elements; ++ return return_value; ++} ++ ++void* set_string_map_element(string_map* map, const char* key, void* value) ++{ ++ unsigned long hashed_key = sdbm_string_hash(key); ++ void* return_value = NULL; ++ if(map->store_keys) ++ { ++ string_map_key_value* kv = (string_map_key_value*)malloc(sizeof(string_map_key_value)); ++ if(kv == NULL) /* deal with malloc failure */ ++ { ++ return NULL; ++ } ++ kv->key = strdup(key); ++ if(kv->key == NULL) /* deal with malloc failure */ ++ { ++ free(kv); ++ return NULL; ++ } ++ kv->value = value; ++ return_value = set_long_map_element( &(map->lm), hashed_key, kv); ++ if(return_value != NULL) ++ { ++ string_map_key_value* r = (string_map_key_value*)return_value; ++ return_value = r->value; ++ free(r->key); ++ free(r); ++ } ++ } ++ else ++ { ++ return_value = set_long_map_element( &(map->lm), hashed_key, value); ++ } ++ map->num_elements = map->lm.num_elements; ++ return return_value; ++} ++ ++void* remove_string_map_element(string_map* map, const char* key) ++{ ++ unsigned long hashed_key = sdbm_string_hash(key); ++ void* return_value = remove_long_map_element( &(map->lm), hashed_key); ++ ++ if(return_value != NULL && map->store_keys) ++ { ++ string_map_key_value* r = (string_map_key_value*)return_value; ++ return_value = r->value; ++ free(r->key); ++ free(r); ++ } ++ map->num_elements = map->lm.num_elements; ++ return return_value; ++} ++ ++char** get_string_map_keys(string_map* map, unsigned long* num_keys_returned) ++{ ++ char** str_keys; ++ str_keys = (char**)malloc((map->num_elements+1)*sizeof(char*)); ++ if(str_keys == NULL) /* deal with malloc failure */ ++ { ++ return NULL; ++ } ++ str_keys[0] = NULL; ++ *num_keys_returned = 0; ++ if(map->store_keys && map->num_elements > 0) ++ { ++ unsigned long list_length; ++ void** long_values = get_sorted_long_map_values( &(map->lm), &list_length); ++ unsigned long key_index; ++ /*list_length will be 0 on malloc failure in get_sorted_long_map_values, so this code shouldn't seg fault if that happens */ ++ for(key_index = 0; key_index < list_length; key_index++) ++ { ++ str_keys[key_index] = strdup( ((string_map_key_value*)(long_values[key_index]))->key); ++ if(str_keys[key_index] == NULL) /* deal with malloc failure */ ++ { ++ //just return the incomplete list (hey, it's null terminated...) ++ free(long_values); ++ return str_keys; ++ } ++ *num_keys_returned = *num_keys_returned + 1; ++ } ++ str_keys[list_length] = NULL; ++ free(long_values); ++ } ++ return str_keys; ++} ++ ++ ++void** get_string_map_values(string_map* map, unsigned long* num_values_returned) ++{ ++ void** values = NULL; ++ if(map != NULL) ++ { ++ values = get_sorted_long_map_values ( &(map->lm), num_values_returned ); ++ } ++ return values; ++} ++ ++ ++void** destroy_string_map(string_map* map, int destruction_type, unsigned long* num_destroyed) ++{ ++ void** return_values = NULL; ++ if(map != NULL) ++ { ++ if(map->store_keys) ++ { ++ void** kvs = destroy_long_map_values( &(map->lm), DESTROY_MODE_RETURN_VALUES, num_destroyed ); ++ unsigned long kv_index = 0; ++ for(kv_index=0; kv_index < *num_destroyed; kv_index++) ++ { ++ string_map_key_value* kv = (string_map_key_value*)kvs[kv_index]; ++ void* value = kv->value; ++ ++ free(kv->key); ++ free(kv); ++ if(destruction_type == DESTROY_MODE_FREE_VALUES) ++ { ++ free(value); ++ } ++ if(destruction_type == DESTROY_MODE_RETURN_VALUES) ++ { ++ kvs[kv_index] = value; ++ } ++ } ++ if(destruction_type == DESTROY_MODE_RETURN_VALUES) ++ { ++ return_values = kvs; ++ } ++ else ++ { ++ free(kvs); ++ } ++ } ++ else ++ { ++ return_values = destroy_long_map_values( &(map->lm), destruction_type, num_destroyed ); ++ } ++ free(map); ++ } ++ return return_values; ++} ++ ++ ++ ++ ++/*************************************************** ++ * long_map function definitions ++ ***************************************************/ ++ ++long_map* initialize_long_map(void) ++{ ++ long_map* map = (long_map*)malloc(sizeof(long_map)); ++ if(map != NULL) /* test for malloc failure */ ++ { ++ map->root = NULL; ++ map->num_elements = 0; ++ } ++ return map; ++} ++ ++void* get_long_map_element(long_map* map, unsigned long key) ++{ ++ void* value = NULL; ++ ++ if(map->root != NULL) ++ { ++ long_map_node* parent_node = map->root; ++ long_map_node* next_node; ++ while( key != parent_node->key && (next_node = (long_map_node *)(key < parent_node->key ? parent_node->left : parent_node->right)) != NULL) ++ { ++ parent_node = next_node; ++ } ++ if(parent_node->key == key) ++ { ++ value = parent_node->value; ++ } ++ } ++ return value; ++} ++ ++void* get_smallest_long_map_element(long_map* map, unsigned long* smallest_key) ++{ ++ void* value = NULL; ++ if(map->root != NULL) ++ { ++ long_map_node* next_node = map->root; ++ while( next_node->left != NULL) ++ { ++ next_node = next_node->left; ++ } ++ value = next_node->value; ++ *smallest_key = next_node->key; ++ } ++ return value; ++} ++ ++void* get_largest_long_map_element(long_map* map, unsigned long* largest_key) ++{ ++ void* value = NULL; ++ if(map->root != NULL) ++ { ++ long_map_node* next_node = map->root; ++ while( next_node->right != NULL) ++ { ++ next_node = next_node->right; ++ } ++ value = next_node->value; ++ *largest_key = next_node->key; ++ } ++ return value; ++} ++ ++void* remove_smallest_long_map_element(long_map* map, unsigned long* smallest_key) ++{ ++ get_smallest_long_map_element(map, smallest_key); ++ return remove_long_map_element(map, *smallest_key); ++} ++ ++void* remove_largest_long_map_element(long_map* map, unsigned long* largest_key) ++{ ++ get_largest_long_map_element(map, largest_key); ++ return remove_long_map_element(map, *largest_key); ++} ++ ++ ++/* if replacement performed, returns replaced value, otherwise null */ ++void* set_long_map_element(long_map* map, unsigned long key, void* value) ++{ ++ stack_node* parent_list = NULL; ++ void* old_value = NULL; ++ int old_value_found = 0; ++ ++ long_map_node* parent_node; ++ long_map_node* next_node; ++ stack_node* next_parent; ++ stack_node* previous_parent; ++ signed char new_balance; ++ ++ ++ long_map_node* new_node = (long_map_node*)malloc(sizeof(long_map_node)); ++ if(new_node == NULL) ++ { ++ return NULL; ++ } ++ new_node->value = value; ++ new_node->key = key; ++ new_node->left = NULL; ++ new_node->right = NULL; ++ new_node->balance = 0; ++ ++ ++ ++ if(map->root == NULL) ++ { ++ map->root = new_node; ++ } ++ else ++ { ++ parent_node = map->root; ++ ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ free(new_node); ++ return NULL; /* won't insert but won't seg fault */ ++ } ++ next_parent->node_ptr = &(map->root); ++ next_parent->previous = parent_list; ++ parent_list = next_parent; ++ ++ while( key != parent_node->key && (next_node = (key < parent_node->key ? parent_node->left : parent_node->right) ) != NULL) ++ { ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ free(new_node); ++ return NULL; ++ } ++ next_parent->node_ptr = key < parent_node->key ? &(parent_node->left) : &(parent_node->right); ++ next_parent->previous = parent_list; ++ next_parent->previous->direction = key < parent_node->key ? -1 : 1; ++ parent_list = next_parent; ++ ++ parent_node = next_node; ++ } ++ ++ ++ if(key == parent_node->key) ++ { ++ old_value = parent_node->value; ++ old_value_found = 1; ++ parent_node->value = value; ++ free(new_node); ++ /* we merely replaced a node, no need to rebalance */ ++ } ++ else ++ { ++ if(key < parent_node->key) ++ { ++ parent_node->left = (void*)new_node; ++ parent_list->direction = -1; ++ } ++ else ++ { ++ parent_node->right = (void*)new_node; ++ parent_list->direction = 1; ++ } ++ ++ ++ /* we inserted a node, rebalance */ ++ previous_parent = parent_list; ++ new_balance = 1; /* initial value is not used, but must not be 0 for initial loop condition */ ++ ++ ++ while(previous_parent != NULL && new_balance != 0) ++ { ++ new_balance = rebalance(previous_parent->node_ptr, previous_parent->direction, 1); ++ previous_parent = previous_parent->previous; ++ } ++ } ++ } ++ ++ free_stack(parent_list); ++ ++ if(old_value_found == 0) ++ { ++ map->num_elements = map->num_elements + 1; ++ } ++ ++ return old_value; ++} ++ ++ ++void* remove_long_map_element(long_map* map, unsigned long key) ++{ ++ ++ void* value = NULL; ++ ++ long_map_node* root_node = map->root; ++ stack_node* parent_list = NULL; ++ ++ ++ long_map_node* remove_parent; ++ long_map_node* remove_node; ++ long_map_node* next_node; ++ ++ long_map_node* replacement; ++ long_map_node* replacement_parent; ++ long_map_node* replacement_next; ++ ++ stack_node* next_parent; ++ stack_node* previous_parent; ++ stack_node* replacement_stack_node; ++ ++ ++ signed char new_balance; ++ ++ ++ ++ if(root_node != NULL) ++ { ++ remove_parent = root_node; ++ remove_node = key < remove_parent->key ? remove_parent->left : remove_parent->right; ++ ++ if(remove_node != NULL && key != remove_parent->key) ++ { ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ return NULL; ++ } ++ next_parent->node_ptr = &(map->root); ++ next_parent->previous = parent_list; ++ parent_list = next_parent; ++ while( key != remove_node->key && (next_node = (key < remove_node->key ? remove_node->left : remove_node->right)) != NULL) ++ { ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ next_parent->node_ptr = key < remove_parent->key ? &(remove_parent->left) : &(remove_parent->right); ++ next_parent->previous = parent_list; ++ next_parent->previous->direction = key < remove_parent->key ? -1 : 1; ++ parent_list = next_parent; ++ ++ ++ remove_parent = remove_node; ++ remove_node = next_node; ++ } ++ parent_list->direction = key < remove_parent-> key ? -1 : 1; ++ } ++ else ++ { ++ remove_node = remove_parent; ++ } ++ ++ ++ if(key == remove_node->key) ++ { ++ ++ /* find replacement for node we are deleting */ ++ if( remove_node->right == NULL ) ++ { ++ replacement = remove_node->left; ++ } ++ else if( remove_node->right->left == NULL) ++ { ++ ++ replacement = remove_node->right; ++ replacement->left = remove_node->left; ++ replacement->balance = remove_node->balance; ++ ++ /* put pointer to replacement node into list for balance update */ ++ replacement_stack_node = (stack_node*)malloc(sizeof(stack_node)); ++ if(replacement_stack_node == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ replacement_stack_node->previous = parent_list; ++ replacement_stack_node->direction = 1; /* replacement is from right */ ++ if(remove_node == remove_parent) /* special case for root node */ ++ { ++ replacement_stack_node->node_ptr = &(map->root); ++ } ++ else ++ { ++ replacement_stack_node->node_ptr = key < remove_parent-> key ? &(remove_parent->left) : &(remove_parent->right); ++ } ++ parent_list = replacement_stack_node; ++ ++ } ++ else ++ { ++ /* put pointer to replacement node into list for balance update */ ++ replacement_stack_node = (stack_node*)malloc(sizeof(stack_node)); ++ if(replacement_stack_node == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ ++ replacement_stack_node->previous = parent_list; ++ replacement_stack_node->direction = 1; /* we always look for replacement on right */ ++ if(remove_node == remove_parent) /* special case for root node */ ++ { ++ replacement_stack_node->node_ptr = &(map->root); ++ } ++ else ++ { ++ replacement_stack_node->node_ptr = key < remove_parent-> key ? &(remove_parent->left) : &(remove_parent->right); ++ } ++ ++ parent_list = replacement_stack_node; ++ ++ ++ /* ++ * put pointer to replacement node->right into list for balance update ++ * this node will have to be updated with the proper pointer ++ * after we have identified the replacement ++ */ ++ replacement_stack_node = (stack_node*)malloc(sizeof(stack_node)); ++ if(replacement_stack_node == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ ++ replacement_stack_node->previous = parent_list; ++ replacement_stack_node->direction = -1; /* we always look for replacement to left of this node */ ++ parent_list = replacement_stack_node; ++ ++ /* find smallest node on right (large) side of tree */ ++ replacement_parent = remove_node->right; ++ replacement = replacement_parent->left; ++ ++ while((replacement_next = replacement->left) != NULL) ++ { ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ ++ next_parent->node_ptr = &(replacement_parent->left); ++ next_parent->previous = parent_list; ++ next_parent->direction = -1; /* we always go left */ ++ parent_list = next_parent; ++ ++ replacement_parent = replacement; ++ replacement = replacement_next; ++ ++ } ++ ++ replacement_parent->left = replacement->right; ++ ++ replacement->left = remove_node->left; ++ replacement->right = remove_node->right; ++ replacement->balance = remove_node->balance; ++ replacement_stack_node->node_ptr = &(replacement->right); ++ } ++ ++ /* insert replacement at proper location in tree */ ++ if(remove_node == remove_parent) ++ { ++ map->root = replacement; ++ } ++ else ++ { ++ remove_parent->left = remove_node == remove_parent->left ? replacement : remove_parent->left; ++ remove_parent->right = remove_node == remove_parent->right ? replacement : remove_parent->right; ++ } ++ ++ ++ /* rebalance tree */ ++ previous_parent = parent_list; ++ new_balance = 0; ++ while(previous_parent != NULL && new_balance == 0) ++ { ++ new_balance = rebalance(previous_parent->node_ptr, previous_parent->direction, -1); ++ previous_parent = previous_parent->previous; ++ } ++ ++ ++ ++ ++ /* ++ * since we found a value to remove, decrease number of elements in map ++ * set return value to the deleted node's value and free the node ++ */ ++ map->num_elements = map->num_elements - 1; ++ value = remove_node->value; ++ free(remove_node); ++ } ++ } ++ ++ free_stack(parent_list); ++ ++ return value; ++} ++ ++ ++/* note: returned keys are dynamically allocated, you need to free them! */ ++unsigned long* get_sorted_long_map_keys(long_map* map, unsigned long* num_keys_returned) ++{ ++ unsigned long* key_list = (unsigned long*)malloc((map->num_elements)*sizeof(unsigned long)); ++ unsigned long next_key_index; ++ if(key_list == NULL) ++ { ++ *num_keys_returned = 0; ++ return NULL; ++ } ++ next_key_index = 0; ++ get_sorted_node_keys(map->root, key_list, &next_key_index, 0); ++ ++ *num_keys_returned = map->num_elements; ++ ++ return key_list; ++} ++ ++ ++void** get_sorted_long_map_values(long_map* map, unsigned long* num_values_returned) ++{ ++ void** value_list = (void**)malloc((map->num_elements+1)*sizeof(void*)); ++ unsigned long next_value_index; ++ ++ if(value_list == NULL) ++ { ++ *num_values_returned = 0; ++ return NULL; ++ } ++ next_value_index = 0; ++ get_sorted_node_values(map->root, value_list, &next_value_index, 0); ++ value_list[map->num_elements] = NULL; /* since we're dealing with pointers make list null terminated */ ++ ++ *num_values_returned = map->num_elements; ++ return value_list; ++ ++} ++ ++ ++ ++void** destroy_long_map(long_map* map, int destruction_type, unsigned long* num_destroyed) ++{ ++ void** return_values = destroy_long_map_values(map, destruction_type, num_destroyed); ++ free(map); ++ return return_values; ++} ++ ++ ++ ++void apply_to_every_long_map_value(long_map* map, void (*apply_func)(unsigned long key, void* value)) ++{ ++ apply_to_every_long_map_node(map->root, apply_func); ++} ++void apply_to_every_string_map_value(string_map* map, void (*apply_func)(char* key, void* value)) ++{ ++ apply_to_every_string_map_node( (map->lm).root, map->store_keys, apply_func); ++} ++ ++ ++/*************************************************** ++ * internal utility function definitions ++ ***************************************************/ ++static void free_stack(stack_node* stack) ++{ ++ while(stack != NULL) ++ { ++ stack_node* prev_node = stack; ++ stack = prev_node->previous; ++ free(prev_node); ++ } ++ ++} ++ ++static void** destroy_long_map_values(long_map* map, int destruction_type, unsigned long* num_destroyed) ++{ ++ void** return_values = NULL; ++ unsigned long return_index = 0; ++ ++ *num_destroyed = 0; ++ ++ if(destruction_type == DESTROY_MODE_RETURN_VALUES) ++ { ++ return_values = (void**)malloc((map->num_elements+1)*sizeof(void*)); ++ if(return_values == NULL) /* deal with malloc failure */ ++ { ++ destruction_type = DESTROY_MODE_IGNORE_VALUES; /* could cause memory leak, but there's no other way to be sure we won't seg fault */ ++ } ++ else ++ { ++ return_values[map->num_elements] = NULL; ++ } ++ } ++ while(map->num_elements > 0) ++ { ++ unsigned long smallest_key; ++ void* removed_value = remove_smallest_long_map_element(map, &smallest_key); ++ if(destruction_type == DESTROY_MODE_RETURN_VALUES) ++ { ++ return_values[return_index] = removed_value; ++ } ++ if(destruction_type == DESTROY_MODE_FREE_VALUES) ++ { ++ free(removed_value); ++ } ++ return_index++; ++ *num_destroyed = *num_destroyed + 1; ++ } ++ return return_values; ++} ++ ++static void apply_to_every_long_map_node(long_map_node* node, void (*apply_func)(unsigned long key, void* value)) ++{ ++ if(node != NULL) ++ { ++ apply_to_every_long_map_node(node->left, apply_func); ++ ++ apply_func(node->key, node->value); ++ ++ apply_to_every_long_map_node(node->right, apply_func); ++ } ++} ++static void apply_to_every_string_map_node(long_map_node* node, unsigned char has_key, void (*apply_func)(char* key, void* value)) ++{ ++ if(node != NULL) ++ { ++ apply_to_every_string_map_node(node->left, has_key, apply_func); ++ ++ if(has_key) ++ { ++ string_map_key_value* kv = (string_map_key_value*)(node->value); ++ apply_func(kv->key, kv->value); ++ } ++ else ++ { ++ apply_func(NULL, node->value); ++ } ++ apply_to_every_string_map_node(node->right, has_key, apply_func); ++ } ++} ++ ++ ++ ++static void get_sorted_node_keys(long_map_node* node, unsigned long* key_list, unsigned long* next_key_index, int depth) ++{ ++ if(node != NULL) ++ { ++ get_sorted_node_keys(node->left, key_list, next_key_index, depth+1); ++ ++ key_list[ *next_key_index ] = node->key; ++ (*next_key_index)++; ++ ++ get_sorted_node_keys(node->right, key_list, next_key_index, depth+1); ++ } ++} ++ ++static void get_sorted_node_values(long_map_node* node, void** value_list, unsigned long* next_value_index, int depth) ++{ ++ if(node != NULL) ++ { ++ get_sorted_node_values(node->left, value_list, next_value_index, depth+1); ++ ++ value_list[ *next_value_index ] = node->value; ++ (*next_value_index)++; ++ ++ get_sorted_node_values(node->right, value_list, next_value_index, depth+1); ++ } ++} ++ ++ ++ ++/* ++ * direction = -1 indicates left subtree updated, direction = 1 for right subtree ++ * update_op = -1 indicates delete node, update_op = 1 for insert node ++ */ ++static signed char rebalance (long_map_node** n, signed char direction, signed char update_op) ++{ ++ /* ++ printf( "original: key = %ld, balance = %d, update_op=%d, direction=%d\n", (*n)->key, (*n)->balance, update_op, direction); ++ */ ++ ++ (*n)->balance = (*n)->balance + (update_op*direction); ++ ++ if( (*n)->balance < -1) ++ { ++ if((*n)->left->balance < 0) ++ { ++ rotate_right(n); ++ (*n)->right->balance = 0; ++ (*n)->balance = 0; ++ } ++ else if((*n)->left->balance == 0) ++ { ++ rotate_right(n); ++ (*n)->right->balance = -1; ++ (*n)->balance = 1; ++ } ++ else if((*n)->left->balance > 0) ++ { ++ rotate_left( &((*n)->left) ); ++ rotate_right(n); ++ /* ++ if( (*n)->balance < 0 ) ++ { ++ (*n)->left->balance = 0; ++ (*n)->right->balance = 1; ++ } ++ else if( (*n)->balance == 0 ) ++ { ++ (*n)->left->balance = 0; ++ (*n)->right->balance = 0; ++ } ++ else if( (*n)->balance > 0 ) ++ { ++ (*n)->left->balance = -1; ++ (*n)->right->balance = 0; ++ } ++ */ ++ (*n)->left->balance = (*n)->balance > 0 ? -1 : 0; ++ (*n)->right->balance = (*n)->balance < 0 ? 1 : 0; ++ (*n)->balance = 0; ++ } ++ } ++ if( (*n)->balance > 1) ++ { ++ if((*n)->right->balance > 0) ++ { ++ rotate_left(n); ++ (*n)->left->balance = 0; ++ (*n)->balance = 0; ++ } ++ else if ((*n)->right->balance == 0) ++ { ++ rotate_left(n); ++ (*n)->left->balance = 1; ++ (*n)->balance = -1; ++ } ++ else if((*n)->right->balance < 0) ++ { ++ rotate_right( &((*n)->right) ); ++ rotate_left(n); ++ /* ++ if( (*n)->balance < 0 ) ++ { ++ (*n)->left->balance = 0; ++ (*n)->right->balance = 1; ++ } ++ else if( (*n)->balance == 0 ) ++ { ++ (*n)->left->balance = 0; ++ (*n)->right->balance = 0; ++ } ++ else if( (*n)->balance > 0 ) ++ { ++ (*n)->left->balance = -1; ++ (*n)->right->balance = 0; ++ } ++ */ ++ (*n)->left->balance = (*n)->balance > 0 ? -1 : 0; ++ (*n)->right->balance = (*n)->balance < 0 ? 1 : 0; ++ (*n)->balance = 0; ++ } ++ } ++ ++ /* ++ printf( "key = %ld, balance = %d\n", (*n)->key, (*n)->balance); ++ */ ++ ++ return (*n)->balance; ++} ++ ++ ++static void rotate_right (long_map_node** parent) ++{ ++ long_map_node* old_parent = *parent; ++ long_map_node* pivot = old_parent->left; ++ old_parent->left = pivot->right; ++ pivot->right = old_parent; ++ ++ *parent = pivot; ++} ++ ++static void rotate_left (long_map_node** parent) ++{ ++ long_map_node* old_parent = *parent; ++ long_map_node* pivot = old_parent->right; ++ old_parent->right = pivot->left; ++ pivot->left = old_parent; ++ ++ *parent = pivot; ++} ++ ++ ++ ++/*************************************************************************** ++ * This algorithm was created for the sdbm database library (a public-domain ++ * reimplementation of ndbm) and seems to work relatively well in ++ * scrambling bits ++ * ++ * ++ * This code was derived from code found at: ++ * http://www.cse.yorku.ca/~oz/hash.html ++ ***************************************************************************/ ++static unsigned long sdbm_string_hash(const char *key) ++{ ++ unsigned long hashed_key = 0; ++ ++ int index = 0; ++ unsigned int nextch; ++ while(key[index] != '\0') ++ { ++ nextch = key[index]; ++ hashed_key = nextch + (hashed_key << 6) + (hashed_key << 16) - hashed_key; ++ index++; ++ } ++ return hashed_key; ++} ++ ++ +--- /dev/null ++++ b/net/ipv4/netfilter/ipt_bandwidth.c +@@ -0,0 +1,2501 @@ ++/* bandwidth -- An iptables extension for bandwidth monitoring/control ++ * Can be used to efficiently monitor bandwidth and/or implement bandwidth quotas ++ * Can be queried using the iptbwctl userspace library ++ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) ++ * ++ * ++ * Copyright © 2009-2011 by Eric Bishop ++ * ++ * This file is free software: you may copy, redistribute and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation, either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This file is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++ ++ ++#include "bandwidth_deps/tree_map.h" ++#include ++#include ++ ++ ++#include ++#include ++ ++ ++/* #define BANDWIDTH_DEBUG 1 */ ++ ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Eric Bishop"); ++MODULE_DESCRIPTION("Match bandwidth used, designed for use with Gargoyle web interface (www.gargoyle-router.com)"); ++ ++/* ++ * WARNING: accessing the sys_tz variable takes FOREVER, and kills performance ++ * keep a local variable that gets updated from the extern variable ++ */ ++extern struct timezone sys_tz; ++static int local_minutes_west; ++static int local_seconds_west; ++static time_t last_local_mw_update; ++ ++ ++static spinlock_t bandwidth_lock = __SPIN_LOCK_UNLOCKED(bandwidth_lock); ++DEFINE_SEMAPHORE(userspace_lock); ++ ++static string_map* id_map = NULL; ++ ++ ++typedef struct info_and_maps_struct ++{ ++ struct ipt_bandwidth_info* info; ++ long_map* ip_map; ++ long_map* ip_history_map; ++}info_and_maps; ++ ++typedef struct history_struct ++{ ++ time_t first_start; ++ time_t first_end; ++ time_t last_end; /* also beginning of current time frame */ ++ uint32_t max_nodes; ++ uint32_t num_nodes; ++ uint32_t non_zero_nodes; ++ uint32_t current_index; ++ uint64_t* history_data; ++} bw_history; ++ ++ ++ ++static unsigned char set_in_progress = 0; ++static char set_id[BANDWIDTH_MAX_ID_LENGTH] = ""; ++ ++/* ++ * function prototypes ++ * ++ * (prototypes only provided for ++ * functions not part of iptables API) ++ * ++*/ ++ ++ ++static void adjust_ip_for_backwards_time_shift(unsigned long key, void* value); ++static void adjust_id_for_backwards_time_shift(char* key, void* value); ++static void check_for_backwards_time_shift(time_t now); ++ ++ ++static void shift_timezone_of_ip(unsigned long key, void* value); ++static void shift_timezone_of_id(char* key, void* value); ++static void check_for_timezone_shift(time_t now, int already_locked); ++ ++ ++ ++static bw_history* initialize_history(uint32_t max_nodes); ++static unsigned char update_history(bw_history* history, time_t interval_start, time_t interval_end, struct ipt_bandwidth_info* info); ++ ++ ++ ++static void do_reset(unsigned long key, void* value); ++static void set_bandwidth_to_zero(unsigned long key, void* value); ++static void handle_interval_reset(info_and_maps* iam, time_t now); ++ ++static uint64_t pow64(uint64_t base, uint64_t pow); ++static uint64_t get_bw_record_max(void); /* called by init to set global variable */ ++ ++static inline int is_leap(unsigned int y); ++static time_t get_next_reset_time(struct ipt_bandwidth_info *info, time_t now, time_t previous_reset); ++static time_t get_nominal_previous_reset_time(struct ipt_bandwidth_info *info, time_t current_next_reset); ++ ++static uint64_t* initialize_map_entries_for_ip(info_and_maps* iam, unsigned long ip, uint64_t initial_bandwidth); ++ ++ ++ ++ ++static time_t backwards_check = 0; ++static time_t backwards_adjust_current_time = 0; ++static time_t backwards_adjust_info_previous_reset = 0; ++static time_t backwards_adjust_ips_zeroed = 0; ++static info_and_maps* backwards_adjust_iam = NULL; ++ ++/* ++static char print_out_buf[25000]; ++static void print_to_buf(char* outdat); ++static void reset_buf(void); ++static void do_print_buf(void); ++ ++static void print_to_buf(char* outdat) ++{ ++ int buf_len = strlen(print_out_buf); ++ sprintf(print_out_buf+buf_len, "\t%s\n", outdat); ++} ++static void reset_buf(void) ++{ ++ print_out_buf[0] = '\n'; ++ print_out_buf[1] = '\0'; ++} ++static void do_print_buf(void) ++{ ++ char* start = print_out_buf; ++ char* next = strchr(start, '\n'); ++ while(next != NULL) ++ { ++ *next = '\0'; ++ printk("%s\n", start); ++ start = next+1; ++ next = strchr(start, '\n'); ++ } ++ printk("%s\n", start); ++ ++ reset_buf(); ++} ++*/ ++ ++static void adjust_ip_for_backwards_time_shift(unsigned long key, void* value) ++{ ++ bw_history* old_history = (bw_history*)value; ++ ++ if(old_history->num_nodes == 1) ++ { ++ if(backwards_adjust_info_previous_reset > backwards_adjust_current_time) ++ { ++ if(backwards_adjust_ips_zeroed == 0) ++ { ++ apply_to_every_long_map_value(backwards_adjust_iam->ip_map, set_bandwidth_to_zero); ++ backwards_adjust_iam->info->next_reset = get_next_reset_time(backwards_adjust_iam->info, backwards_adjust_current_time, backwards_adjust_current_time); ++ backwards_adjust_iam->info->previous_reset = backwards_adjust_current_time; ++ backwards_adjust_iam->info->current_bandwidth = 0; ++ backwards_adjust_ips_zeroed = 1; ++ } ++ } ++ return; ++ } ++ else if(old_history->last_end < backwards_adjust_current_time) ++ { ++ return; ++ } ++ else ++ { ++ ++ /* ++ * reconstruct new history without newest nodes, to represent data as it was ++ * last time the current time was set to the interval to which we just jumped back ++ */ ++ uint32_t next_old_index; ++ time_t old_next_start = old_history->first_start == 0 ? backwards_adjust_info_previous_reset : old_history->first_start; /* first time point in old history */ ++ bw_history* new_history = initialize_history(old_history->max_nodes); ++ if(new_history == NULL) ++ { ++ printk("ipt_bandwidth: warning, kmalloc failure!\n"); ++ return; ++ } ++ ++ ++ ++ /* oldest index in old history -- we iterate forward through old history using this index */ ++ next_old_index = old_history->num_nodes == old_history->max_nodes ? (old_history->current_index+1) % old_history->max_nodes : 0; ++ ++ ++ /* if first time point is after current time, just completely re-initialize history, otherwise set first time point to old first time point */ ++ (new_history->history_data)[ new_history->current_index ] = old_next_start < backwards_adjust_current_time ? (old_history->history_data)[next_old_index] : 0; ++ backwards_adjust_iam->info->previous_reset = old_next_start < backwards_adjust_current_time ? old_next_start : backwards_adjust_current_time; ++ ++ ++ /* iterate through old history, rebuilding in new history*/ ++ while( old_next_start < backwards_adjust_current_time ) ++ { ++ time_t old_next_end = get_next_reset_time(backwards_adjust_iam->info, old_next_start, old_next_start); /* 2nd param = last reset, 3rd param = current time */ ++ if( old_next_end < backwards_adjust_current_time) ++ { ++ update_history(new_history, old_next_start, old_next_end, backwards_adjust_iam->info); ++ next_old_index++; ++ (new_history->history_data)[ new_history->current_index ] = (old_history->history_data)[next_old_index]; ++ } ++ backwards_adjust_iam->info->previous_reset = old_next_start; /*update previous_reset variable in bw_info as we iterate */ ++ old_next_start = old_next_end; ++ } ++ ++ /* update next_reset variable from previous_reset variable which we've already set */ ++ backwards_adjust_iam->info->next_reset = get_next_reset_time(backwards_adjust_iam->info, backwards_adjust_iam->info->previous_reset, backwards_adjust_iam->info->previous_reset); ++ ++ ++ ++ /* set old_history to be new_history */ ++ kfree(old_history->history_data); ++ old_history->history_data = new_history->history_data; ++ old_history->first_start = new_history->first_start; ++ old_history->first_end = new_history->first_end; ++ old_history->last_end = new_history->last_end; ++ old_history->num_nodes = new_history->num_nodes; ++ old_history->non_zero_nodes = new_history->non_zero_nodes; ++ old_history->current_index = new_history->current_index; ++ set_long_map_element(backwards_adjust_iam->ip_map, key, (void*)(old_history->history_data + old_history->current_index) ); ++ if(key == 0) ++ { ++ backwards_adjust_iam->info->combined_bw = (uint64_t*)(old_history->history_data + old_history->current_index); ++ } ++ ++ /* ++ * free new history (which was just temporary) ++ * note that we don't need to free history_data from new_history ++ * we freed the history_data from old history, and set that to the history_data from new_history ++ * so, this cleanup has already been handled ++ */ ++ kfree(new_history); ++ ++ } ++} ++static void adjust_id_for_backwards_time_shift(char* key, void* value) ++{ ++ info_and_maps* iam = (info_and_maps*)value; ++ if(iam == NULL) ++ { ++ return; ++ } ++ if(iam->info == NULL) ++ { ++ return; ++ } ++ ++ backwards_adjust_iam = iam; ++ if( (iam->info->reset_is_constant_interval == 0 && iam->info->reset_interval == BANDWIDTH_NEVER) || iam->info->cmp == BANDWIDTH_CHECK ) ++ { ++ return; ++ } ++ if(iam->ip_history_map != NULL) ++ { ++ backwards_adjust_info_previous_reset = iam->info->previous_reset; ++ backwards_adjust_ips_zeroed = 0; ++ apply_to_every_long_map_value(iam->ip_history_map, adjust_ip_for_backwards_time_shift); ++ } ++ else ++ { ++ time_t next_reset_after_adjustment = get_next_reset_time(iam->info, backwards_adjust_current_time, backwards_adjust_current_time); ++ if(next_reset_after_adjustment < iam->info->next_reset) ++ { ++ iam->info->previous_reset = backwards_adjust_current_time; ++ iam->info->next_reset = next_reset_after_adjustment; ++ } ++ } ++ backwards_adjust_iam = NULL; ++} ++static void check_for_backwards_time_shift(time_t now) ++{ ++ spin_lock_bh(&bandwidth_lock); ++ if(now < backwards_check && backwards_check != 0) ++ { ++ printk("ipt_bandwidth: backwards time shift detected, adjusting\n"); ++ ++ /* adjust */ ++ down(&userspace_lock); ++ ++ /* This function is always called with absolute time, not time adjusted for timezone. Correct that before adjusting. */ ++ backwards_adjust_current_time = now - local_seconds_west; ++ apply_to_every_string_map_value(id_map, adjust_id_for_backwards_time_shift); ++ up(&userspace_lock); ++ } ++ backwards_check = now; ++ spin_unlock_bh(&bandwidth_lock); ++} ++ ++ ++ ++static int old_minutes_west; ++static time_t shift_timezone_current_time; ++static time_t shift_timezone_info_previous_reset; ++static info_and_maps* shift_timezone_iam = NULL; ++static void shift_timezone_of_ip(unsigned long key, void* value) ++{ ++ #ifdef BANDWIDTH_DEBUG ++ unsigned long* ip = &key; ++ printk("shifting ip = %u.%u.%u.%u\n", *((char*)ip), *(((char*)ip)+1), *(((char*)ip)+2), *(((char*)ip)+3) ); ++ #endif ++ ++ ++ bw_history* history = (bw_history*)value; ++ int32_t timezone_adj = (old_minutes_west-local_minutes_west)*60; ++ #ifdef BANDWIDTH_DEBUG ++ printk(" before jump:\n"); ++ printk(" current time = %ld\n", shift_timezone_current_time); ++ printk(" first_start = %ld\n", history->first_start); ++ printk(" first_end = %ld\n", history->first_end); ++ printk(" last_end = %ld\n", history->last_end); ++ printk("\n"); ++ #endif ++ ++ /* given time after shift, calculate next and previous reset times */ ++ time_t next_reset = get_next_reset_time(shift_timezone_iam->info, shift_timezone_current_time, 0); ++ time_t previous_reset = get_nominal_previous_reset_time(shift_timezone_iam->info, next_reset); ++ shift_timezone_iam->info->next_reset = next_reset; ++ ++ /*if we're resetting on a constant interval, we can just adjust -- no need to worry about relationship to constant boundaries, e.g. end of day */ ++ if(shift_timezone_iam->info->reset_is_constant_interval) ++ { ++ shift_timezone_iam->info->previous_reset = previous_reset; ++ if(history->num_nodes > 1) ++ { ++ history->first_start = history->first_start + timezone_adj; ++ history->first_end = history->first_end + timezone_adj; ++ history->last_end = history->last_end + timezone_adj; ++ } ++ } ++ else ++ { ++ ++ ++ /* next reset will be the newly computed next_reset. */ ++ int node_index=history->num_nodes - 1; ++ if(node_index > 0) ++ { ++ /* based on new, shifted time, iterate back over all nodes in history */ ++ shift_timezone_iam->info->previous_reset = previous_reset ; ++ history->last_end = previous_reset; ++ ++ while(node_index > 1) ++ { ++ previous_reset = get_nominal_previous_reset_time(shift_timezone_iam->info, previous_reset); ++ node_index--; ++ } ++ history->first_end = previous_reset; ++ ++ previous_reset = get_nominal_previous_reset_time(shift_timezone_iam->info, previous_reset); ++ history->first_start = previous_reset > history->first_start + timezone_adj ? previous_reset : history->first_start + timezone_adj; ++ } ++ else ++ { ++ /* ++ * history hasn't really been initialized -- there's only one, current time point. ++ * we only know what's in the current accumulator in info. Just adjust previous reset time and make sure it's valid ++ */ ++ shift_timezone_iam->info->previous_reset = previous_reset > shift_timezone_info_previous_reset + timezone_adj ? previous_reset : shift_timezone_info_previous_reset + timezone_adj; ++ } ++ } ++ ++ ++ #ifdef BANDWIDTH_DEBUG ++ printk("\n"); ++ printk(" after jump:\n"); ++ printk(" first_start = %ld\n", history->first_start); ++ printk(" first_end = %ld\n", history->first_end); ++ printk(" last_end = %ld\n", history->last_end); ++ printk("\n\n"); ++ #endif ++ ++} ++static void shift_timezone_of_id(char* key, void* value) ++{ ++ info_and_maps* iam = (info_and_maps*)value; ++ int history_found = 0; ++ if(iam == NULL) ++ { ++ return; ++ } ++ if(iam->info == NULL) ++ { ++ return; ++ } ++ ++ #ifdef BANDWIDTH_DEBUG ++ printk("shifting id %s\n", key); ++ #endif ++ ++ shift_timezone_iam = iam; ++ if( (iam->info->reset_is_constant_interval == 0 && iam->info->reset_interval == BANDWIDTH_NEVER) || iam->info->cmp == BANDWIDTH_CHECK ) ++ { ++ return; ++ } ++ ++ if(iam->ip_history_map != NULL) ++ { ++ if(iam->ip_history_map->num_elements > 0) ++ { ++ history_found = 1; ++ shift_timezone_info_previous_reset = iam->info->previous_reset; ++ apply_to_every_long_map_value(iam->ip_history_map, shift_timezone_of_ip); ++ } ++ } ++ if(history_found == 0) ++ { ++ iam->info->previous_reset = iam->info->previous_reset + ((old_minutes_west - local_minutes_west )*60); ++ if(iam->info->previous_reset > shift_timezone_current_time) ++ { ++ iam->info->next_reset = get_next_reset_time(iam->info, shift_timezone_current_time, shift_timezone_current_time); ++ iam->info->previous_reset = shift_timezone_current_time; ++ } ++ else ++ { ++ iam->info->next_reset = get_next_reset_time(iam->info, shift_timezone_current_time, iam->info->previous_reset); ++ while (iam->info->next_reset < shift_timezone_current_time) ++ { ++ iam->info->previous_reset = iam->info->next_reset; ++ iam->info->next_reset = get_next_reset_time(iam->info, iam->info->previous_reset, iam->info->previous_reset); ++ } ++ } ++ } ++ shift_timezone_iam = NULL; ++} ++ ++static void check_for_timezone_shift(time_t now, int already_locked) ++{ ++ ++ if(already_locked == 0) { spin_lock_bh(&bandwidth_lock); } ++ if(now != last_local_mw_update ) /* make sure nothing changed while waiting for lock */ ++ { ++ local_minutes_west = sys_tz.tz_minuteswest; ++ local_seconds_west = 60*local_minutes_west; ++ last_local_mw_update = now; ++ if(local_seconds_west > last_local_mw_update) ++ { ++ /* we can't let adjusted time be < 0 -- pretend timezone is still UTC */ ++ local_minutes_west = 0; ++ local_seconds_west = 0; ++ } ++ ++ if(local_minutes_west != old_minutes_west) ++ { ++ int adj_minutes = old_minutes_west-local_minutes_west; ++ adj_minutes = adj_minutes < 0 ? adj_minutes*-1 : adj_minutes; ++ ++ if(already_locked == 0) { down(&userspace_lock); } ++ ++ printk("ipt_bandwidth: timezone shift of %d minutes detected, adjusting\n", adj_minutes); ++ printk(" old minutes west=%d, new minutes west=%d\n", old_minutes_west, local_minutes_west); ++ ++ /* this function is always called with absolute time, not time adjusted for timezone. Correct that before adjusting */ ++ shift_timezone_current_time = now - local_seconds_west; ++ apply_to_every_string_map_value(id_map, shift_timezone_of_id); ++ ++ old_minutes_west = local_minutes_west; ++ ++ ++ if(already_locked == 0) { up(&userspace_lock); } ++ } ++ } ++ if(already_locked == 0) { spin_unlock_bh(&bandwidth_lock); } ++} ++ ++ ++ ++static bw_history* initialize_history(uint32_t max_nodes) ++{ ++ bw_history* new_history = (bw_history*)kmalloc(sizeof(bw_history), GFP_ATOMIC); ++ if(new_history != NULL) ++ { ++ new_history->history_data = (uint64_t*)kmalloc((1+max_nodes)*sizeof(uint64_t), GFP_ATOMIC); /*number to save +1 for current */ ++ if(new_history->history_data == NULL) /* deal with malloc failure */ ++ { ++ kfree(new_history); ++ new_history = NULL; ++ } ++ else ++ { ++ new_history->first_start = 0; ++ new_history->first_end = 0; ++ new_history->last_end = 0; ++ new_history->max_nodes = max_nodes+1; /*number to save +1 for current */ ++ new_history->num_nodes = 1; ++ new_history->non_zero_nodes = 0; /* counts non_zero nodes other than current, so initialize to 0 */ ++ new_history->current_index = 0; ++ memset(new_history->history_data, 0, max_nodes*sizeof(uint64_t)); ++ } ++ } ++ return new_history; /* in case of malloc failure new_history will be NULL, this should be safe */ ++} ++ ++/* returns 1 if there are non-zero nodes in history, 0 if history is empty (all zero) */ ++static unsigned char update_history(bw_history* history, time_t interval_start, time_t interval_end, struct ipt_bandwidth_info* info) ++{ ++ unsigned char history_is_nonzero = 0; ++ if(history != NULL) /* should never be null, but let's be sure */ ++ { ++ ++ /* adjust number of non-zero nodes */ ++ if(history->num_nodes == history->max_nodes) ++ { ++ uint32_t first_index = (history->current_index+1) % history->max_nodes; ++ if( (history->history_data)[first_index] > 0) ++ { ++ history->non_zero_nodes = history->non_zero_nodes -1; ++ } ++ } ++ if( (history->history_data)[history->current_index] > 0 ) ++ { ++ history->non_zero_nodes = history->non_zero_nodes + 1; ++ } ++ history_is_nonzero = history->non_zero_nodes > 0 ? 1 : 0; ++ ++ ++ /* update interval start/end */ ++ if(history->first_start == 0) ++ { ++ history->first_start = interval_start; ++ history->first_end = interval_end; ++ } ++ if(history->num_nodes >= history->max_nodes) ++ { ++ history->first_start = history->first_end; ++ history->first_end = get_next_reset_time(info, history->first_start, history->first_start); ++ } ++ history->last_end = interval_end; ++ ++ ++ history->num_nodes = history->num_nodes < history->max_nodes ? history->num_nodes+1 : history->max_nodes; ++ history->current_index = (history->current_index+1) % history->max_nodes; ++ (history->history_data)[history->current_index] = 0; ++ ++ #ifdef BANDWIDTH_DEBUG ++ printk("after update history->num_nodes = %d\n", history->num_nodes); ++ printk("after update history->current_index = %d\n", history->current_index); ++ #endif ++ } ++ return history_is_nonzero; ++} ++ ++ ++static struct ipt_bandwidth_info* do_reset_info = NULL; ++static long_map* do_reset_ip_map = NULL; ++static long_map* do_reset_delete_ips = NULL; ++static time_t do_reset_interval_start = 0; ++static time_t do_reset_interval_end = 0; ++static void do_reset(unsigned long key, void* value) ++{ ++ bw_history* history = (bw_history*)value; ++ if(history != NULL && do_reset_info != NULL) /* should never be null.. but let's be sure */ ++ { ++ unsigned char history_contains_data = update_history(history, do_reset_interval_start, do_reset_interval_end, do_reset_info); ++ if(history_contains_data == 0 || do_reset_ip_map == NULL) ++ { ++ //schedule data for ip to be deleted (can't delete history while we're traversing history tree data structure!) ++ if(do_reset_delete_ips != NULL) /* should never be null.. but let's be sure */ ++ { ++ set_long_map_element(do_reset_delete_ips, key, (void*)(history->history_data + history->current_index)); ++ } ++ } ++ else ++ { ++ set_long_map_element(do_reset_ip_map, key, (void*)(history->history_data + history->current_index) ); ++ } ++ } ++} ++ ++long_map* clear_ip_map = NULL; ++long_map* clear_ip_history_map = NULL; ++static void clear_ips(unsigned long key, void* value) ++{ ++ if(clear_ip_history_map != NULL && clear_ip_map != NULL) ++ { ++ bw_history* history; ++ ++ #ifdef BANDWIDTH_DEBUG ++ unsigned long* ip = &key; ++ printk("clearing ip = %u.%u.%u.%u\n", *((char*)ip), *(((char*)ip)+1), *(((char*)ip)+2), *(((char*)ip)+3) ); ++ #endif ++ ++ remove_long_map_element(clear_ip_map, key); ++ history = (bw_history*)remove_long_map_element(clear_ip_history_map, key); ++ if(history != NULL) ++ { ++ kfree(history->history_data); ++ kfree(history); ++ } ++ } ++} ++ ++static void set_bandwidth_to_zero(unsigned long key, void* value) ++{ ++ *((uint64_t*)value) = 0; ++} ++ ++ ++long_map* reset_histories_ip_map = NULL; ++static void reset_histories(unsigned long key, void* value) ++{ ++ bw_history* bh = (bw_history*)value; ++ bh->first_start = 0; ++ bh->first_end = 0; ++ bh->last_end = 0; ++ bh->num_nodes = 1; ++ bh->non_zero_nodes = 1; ++ bh->current_index = 0; ++ (bh->history_data)[0] = 0; ++ if(reset_histories_ip_map != NULL) ++ { ++ set_long_map_element(reset_histories_ip_map, key, bh->history_data); ++ } ++} ++ ++ ++static void handle_interval_reset(info_and_maps* iam, time_t now) ++{ ++ struct ipt_bandwidth_info* info; ++ ++ #ifdef BANDWIDTH_DEBUG ++ printk("now, handling interval reset\n"); ++ #endif ++ if(iam == NULL) ++ { ++ #ifdef BANDWIDTH_DEBUG ++ printk("error: doing reset, iam is null \n"); ++ #endif ++ return; ++ } ++ if(iam->ip_map == NULL) ++ { ++ #ifdef BANDWIDTH_DEBUG ++ printk("error: doing reset, ip_map is null\n"); ++ #endif ++ return; ++ } ++ if(iam->info == NULL) ++ { ++ #ifdef BANDWIDTH_DEBUG ++ printk("error: doing reset, info is null\n"); ++ #endif ++ ++ return; ++ } ++ ++ info = iam->info; ++ if(info->num_intervals_to_save == 0) ++ { ++ #ifdef BANDWIDTH_DEBUG ++ printk("doing reset for case where no intervals are saved\n"); ++ #endif ++ ++ if(info->next_reset <= now) ++ { ++ info->next_reset = get_next_reset_time(info, info->previous_reset, info->previous_reset); ++ if(info->next_reset <= now) ++ { ++ info->next_reset = get_next_reset_time(info, now, info->previous_reset); ++ } ++ } ++ apply_to_every_long_map_value(iam->ip_map, set_bandwidth_to_zero); ++ } ++ else ++ { ++ unsigned long num_updates; ++ #ifdef BANDWIDTH_DEBUG ++ printk("doing reset for case where at least one interval is saved\n"); ++ #endif ++ ++ ++ if(iam->ip_history_map == NULL) ++ { ++ #ifdef BANDWIDTH_DEBUG ++ printk("error: doing reset, history_map is null when num_intervals_to_save > 0\n"); ++ #endif ++ return; ++ } ++ ++ do_reset_info = info; ++ do_reset_ip_map = iam->ip_map; ++ clear_ip_map = iam->ip_map; ++ clear_ip_history_map = iam->ip_history_map; ++ ++ ++ /* ++ * at most update as many times as we have intervals to save -- prevents ++ * rediculously long loop if interval length is 2 seconds and time was ++ * reset to 5 years in the future ++ */ ++ num_updates = 0; ++ while(info->next_reset <= now && num_updates < info->num_intervals_to_save) ++ { ++ do_reset_delete_ips = initialize_long_map(); ++ /* ++ * don't check for malloc failure here -- we ++ * include tests for whether do_reset_delete_ips ++ * is null below (reset should still be able to procede) ++ */ ++ ++ do_reset_interval_start = info->previous_reset; ++ do_reset_interval_end = info->next_reset; ++ ++ apply_to_every_long_map_value(iam->ip_history_map, do_reset); ++ ++ ++ info->previous_reset = info->next_reset; ++ info->next_reset = get_next_reset_time(info, info->previous_reset, info->previous_reset); ++ ++ /* free all data for ips whose entire histories contain only zeros to conserve space */ ++ if(do_reset_delete_ips != NULL) ++ { ++ unsigned long num_destroyed; ++ ++ /* only clear ips if this is the last iteration of this update */ ++ if(info->next_reset >= now) ++ { ++ /* ++ * no need to reset iam->info->combined_bw if it gets deleted here. ++ * below, at end of function it will get set to NULL if it gets wiped ++ */ ++ ++ apply_to_every_long_map_value(do_reset_delete_ips, clear_ips); ++ } ++ ++ /* but clear do_reset_delete_ips no matter what, values are just pointers to history data so we can ignore them */ ++ destroy_long_map(do_reset_delete_ips, DESTROY_MODE_IGNORE_VALUES, &num_destroyed); ++ do_reset_delete_ips = NULL; ++ } ++ num_updates++; ++ } ++ do_reset_info = NULL; ++ do_reset_ip_map = NULL; ++ clear_ip_map = NULL; ++ clear_ip_history_map = NULL; ++ ++ do_reset_interval_start = 0; ++ do_reset_interval_end = 0; ++ ++ /* ++ * test if we've cycled past all existing data -- if so wipe all existing histories ++ * and set previous reset time to now, and compute next reset time from ++ * current time ++ */ ++ if(info->next_reset <= now) ++ { ++ reset_histories_ip_map = iam->ip_map; ++ apply_to_every_long_map_value(iam->ip_history_map, reset_histories); ++ reset_histories_ip_map = NULL; ++ ++ info->previous_reset = now; ++ info->next_reset = get_next_reset_time(info, now, info->previous_reset); ++ } ++ } ++ info->combined_bw = (uint64_t*)get_long_map_element(iam->ip_map, 0); ++ info->current_bandwidth = 0; ++} ++ ++/* ++ * set max bandwidth to be max possible using 63 of the ++ * 64 bits in our record. In some systems uint64_t is treated ++ * like signed, so to prevent errors, use only 63 bits ++ */ ++static uint64_t pow64(uint64_t base, uint64_t pow) ++{ ++ uint64_t val = 1; ++ if(pow > 0) ++ { ++ val = base*pow64(base, pow-1); ++ } ++ return val; ++} ++static uint64_t get_bw_record_max(void) /* called by init to set global variable */ ++{ ++ return (pow64(2,62)) + (pow64(2,62)-1); ++} ++static uint64_t bandwidth_record_max; ++ ++ ++#define ADD_UP_TO_MAX(original,add,is_check) (bandwidth_record_max - original > add && is_check== 0) ? original+add : (is_check ? original : bandwidth_record_max); ++ ++ ++/* ++ * Shamelessly yoinked from xt_time.c ++ * "That is so amazingly amazing, I think I'd like to steal it." ++ * -- Zaphod Beeblebrox ++ */ ++ ++static const u_int16_t days_since_year[] = { ++ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, ++}; ++ ++static const u_int16_t days_since_leapyear[] = { ++ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, ++}; ++ ++/* ++ * Since time progresses forward, it is best to organize this array in reverse, ++ * to minimize lookup time. These are days since epoch since start of each year, ++ * going back to 1970 ++ */ ++#define DSE_FIRST 2039 ++static const u_int16_t days_since_epoch_for_each_year_start[] = { ++ /* 2039 - 2030 */ ++ 25202, 24837, 24472, 24106, 23741, 23376, 23011, 22645, 22280, 21915, ++ /* 2029 - 2020 */ ++ 21550, 21184, 20819, 20454, 20089, 19723, 19358, 18993, 18628, 18262, ++ /* 2019 - 2010 */ ++ 17897, 17532, 17167, 16801, 16436, 16071, 15706, 15340, 14975, 14610, ++ /* 2009 - 2000 */ ++ 14245, 13879, 13514, 13149, 12784, 12418, 12053, 11688, 11323, 10957, ++ /* 1999 - 1990 */ ++ 10592, 10227, 9862, 9496, 9131, 8766, 8401, 8035, 7670, 7305, ++ /* 1989 - 1980 */ ++ 6940, 6574, 6209, 5844, 5479, 5113, 4748, 4383, 4018, 3652, ++ /* 1979 - 1970 */ ++ 3287, 2922, 2557, 2191, 1826, 1461, 1096, 730, 365, 0, ++}; ++ ++static inline int is_leap(unsigned int y) ++{ ++ return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0); ++} ++ ++/* end of code yoinked from xt_time */ ++ ++ ++static time_t get_nominal_previous_reset_time(struct ipt_bandwidth_info *info, time_t current_next_reset) ++{ ++ time_t previous_reset = current_next_reset; ++ if(info->reset_is_constant_interval == 0) ++ { ++ /* skip backwards in halves of interval after next, until */ ++ time_t next = get_next_reset_time(info, current_next_reset, 0); ++ time_t half_interval = (next-current_next_reset)/2; ++ time_t half_count, tmp; ++ half_interval = half_interval == 0 ? 1 : half_interval; /* must be at least one second, otherwise we loop forever*/ ++ ++ half_count = 1; ++ tmp = get_next_reset_time(info, (current_next_reset-(half_count*half_interval)),0); ++ while(previous_reset >= current_next_reset) ++ { ++ previous_reset = tmp; ++ half_count++; ++ tmp = get_next_reset_time(info, (current_next_reset-(half_count*half_interval)),0); ++ } ++ } ++ else ++ { ++ previous_reset = current_next_reset - info->reset_interval; ++ } ++ return previous_reset; ++} ++ ++ ++static time_t get_next_reset_time(struct ipt_bandwidth_info *info, time_t now, time_t previous_reset) ++{ ++ //first calculate when next reset would be if reset_time is 0 (which it may be) ++ time_t next_reset = 0; ++ if(info->reset_is_constant_interval == 0) ++ { ++ if(info->reset_interval == BANDWIDTH_MINUTE) ++ { ++ next_reset = ( (long)(now/60) + 1)*60; ++ if(info->reset_time > 0) ++ { ++ time_t alt_reset = next_reset + info->reset_time - 60; ++ next_reset = alt_reset > now ? alt_reset : next_reset+info->reset_time; ++ } ++ } ++ else if(info->reset_interval == BANDWIDTH_HOUR) ++ { ++ next_reset = ( (long)(now/(60*60)) + 1)*60*60; ++ if(info->reset_time > 0) ++ { ++ time_t alt_reset = next_reset + info->reset_time - (60*60); ++ next_reset = alt_reset > now ? alt_reset : next_reset+info->reset_time; ++ } ++ } ++ else if(info->reset_interval == BANDWIDTH_DAY) ++ { ++ next_reset = ( (long)(now/(60*60*24)) + 1)*60*60*24; ++ if(info->reset_time > 0) ++ { ++ time_t alt_reset = next_reset + info->reset_time - (60*60*24); ++ next_reset = alt_reset > now ? alt_reset : next_reset+info->reset_time; ++ } ++ } ++ else if(info->reset_interval == BANDWIDTH_WEEK) ++ { ++ long days_since_epoch = now/(60*60*24); ++ long current_weekday = (4 + days_since_epoch ) % 7 ; ++ next_reset = (days_since_epoch + (7-current_weekday) )*(60*60*24); ++ if(info->reset_time > 0) ++ { ++ time_t alt_reset = next_reset + info->reset_time - (60*60*24*7); ++ next_reset = alt_reset > now ? alt_reset : next_reset+info->reset_time; ++ } ++ } ++ else if(info->reset_interval == BANDWIDTH_MONTH) ++ { ++ /* yeah, most of this is yoinked from xt_time too */ ++ int year; ++ int year_index; ++ int year_day; ++ int month; ++ long days_since_epoch = now/(60*60*24); ++ uint16_t* month_start_days; ++ time_t alt_reset; ++ ++ for (year_index = 0, year = DSE_FIRST; days_since_epoch_for_each_year_start[year_index] > days_since_epoch; year_index++) ++ { ++ year--; ++ } ++ year_day = days_since_epoch - days_since_epoch_for_each_year_start[year_index]; ++ if (is_leap(year)) ++ { ++ month_start_days = (u_int16_t*)days_since_leapyear; ++ } ++ else ++ { ++ month_start_days = (u_int16_t*)days_since_year; ++ } ++ for (month = 11 ; month > 0 && month_start_days[month] > year_day; month--){} ++ ++ /* end majority of yoinkage */ ++ ++ alt_reset = (days_since_epoch_for_each_year_start[year_index] + month_start_days[month])*(60*60*24) + info->reset_time; ++ if(alt_reset > now) ++ { ++ next_reset = alt_reset; ++ } ++ else if(month == 11) ++ { ++ next_reset = days_since_epoch_for_each_year_start[year_index-1]*(60*60*24) + info->reset_time; ++ } ++ else ++ { ++ next_reset = (days_since_epoch_for_each_year_start[year_index] + month_start_days[month+1])*(60*60*24) + info->reset_time; ++ } ++ } ++ } ++ else ++ { ++ if(info->reset_time > 0 && previous_reset > 0 && previous_reset <= now) ++ { ++ unsigned long adj_reset_time = info->reset_time; ++ unsigned long tz_secs = 60 * local_minutes_west; ++ if(adj_reset_time < tz_secs) ++ { ++ unsigned long interval_multiple = 1+(tz_secs/info->reset_interval); ++ adj_reset_time = adj_reset_time + (interval_multiple*info->reset_interval); ++ } ++ adj_reset_time = adj_reset_time - tz_secs; ++ ++ if(info->reset_time > now) ++ { ++ unsigned long whole_intervals = ((info->reset_time - now)/info->reset_interval) + 1; /* add one to make sure integer gets rounded UP (since we're subtracting) */ ++ next_reset = info->reset_time - (whole_intervals*info->reset_interval); ++ while(next_reset <= now) ++ { ++ next_reset = next_reset + info->reset_interval; ++ } ++ ++ } ++ else /* info->reset_time <= now */ ++ { ++ unsigned long whole_intervals = (now-info->reset_time)/info->reset_interval; /* integer gets rounded down */ ++ next_reset = info->reset_time + (whole_intervals*info->reset_interval); ++ while(next_reset <= now) ++ { ++ next_reset = next_reset + info->reset_interval; ++ } ++ } ++ } ++ else if(previous_reset > 0) ++ { ++ next_reset = previous_reset; ++ if(next_reset <= now) /* check just to be sure, if this is not true VERY BAD THINGS will happen */ ++ { ++ unsigned long whole_intervals = (now-next_reset)/info->reset_interval; /* integer gets rounded down */ ++ next_reset = next_reset + (whole_intervals*info->reset_interval); ++ while(next_reset <= now) ++ { ++ next_reset = next_reset + info->reset_interval; ++ } ++ } ++ } ++ else ++ { ++ next_reset = now + info->reset_interval; ++ } ++ } ++ ++ return next_reset; ++} ++ ++ ++ ++static uint64_t* initialize_map_entries_for_ip(info_and_maps* iam, unsigned long ip, uint64_t initial_bandwidth) ++{ ++ #ifdef BANDWIDTH_DEBUG ++ printk("initializing entry for ip, bw=%lld\n", initial_bandwidth); ++ #endif ++ ++ #ifdef BANDWIDTH_DEBUG ++ if(iam == NULL){ printk("error in initialization: iam is null!\n"); } ++ #endif ++ ++ ++ uint64_t* new_bw = NULL; ++ if(iam != NULL) /* should never happen, but let's be certain */ ++ { ++ struct ipt_bandwidth_info *info = iam->info; ++ long_map* ip_map = iam->ip_map; ++ long_map* ip_history_map = iam->ip_history_map; ++ ++ #ifdef BANDWIDTH_DEBUG ++ if(info == NULL){ printk("error in initialization: info is null!\n"); } ++ if(ip_map == NULL){ printk("error in initialization: ip_map is null!\n"); } ++ #endif ++ ++ ++ if(info != NULL && ip_map != NULL) /* again... should never happen but let's be sure */ ++ { ++ if(info->num_intervals_to_save == 0 || ip_history_map == NULL) ++ { ++ #ifdef BANDWIDTH_DEBUG ++ printk(" initializing entry for ip without history\n"); ++ #endif ++ new_bw = (uint64_t*)kmalloc(sizeof(uint64_t), GFP_ATOMIC); ++ } ++ else ++ { ++ #ifdef BANDWIDTH_DEBUG ++ printk(" initializing entry for ip with history\n"); ++ #endif ++ ++ bw_history *new_history = initialize_history(info->num_intervals_to_save); ++ if(new_history != NULL) /* check for kmalloc failure */ ++ { ++ bw_history* old_history; ++ #ifdef BANDWIDTH_DEBUG ++ printk(" malloc succeeded, new history is non-null\n"); ++ #endif ++ ++ new_bw = (uint64_t*)(new_history->history_data + new_history->current_index); ++ old_history = set_long_map_element(ip_history_map, ip, (void*)new_history); ++ if(old_history != NULL) ++ { ++ #ifdef BANDWIDTH_DEBUG ++ printk(" after initialization old_history not null! (something is FUBAR)\n"); ++ #endif ++ kfree(old_history->history_data); ++ kfree(old_history); ++ } ++ ++ #ifdef BANDWIDTH_DEBUG ++ ++ #endif ++ } ++ } ++ if(new_bw != NULL) /* check for kmalloc failure */ ++ { ++ uint64_t* old_bw; ++ *new_bw = initial_bandwidth; ++ old_bw = set_long_map_element(ip_map, ip, (void*)new_bw ); ++ ++ /* only free old_bw if num_intervals_to_save is zero -- otherwise it already got freed above when we wiped the old history */ ++ if(old_bw != NULL && info->num_intervals_to_save == 0) ++ { ++ free(old_bw); ++ } ++ ++ if(ip == 0) ++ { ++ info->combined_bw = new_bw; ++ } ++ ++ #ifdef BANDWIDTH_DEBUG ++ uint64_t *test = (uint64_t*)get_long_map_element(ip_map, ip); ++ if(test == NULL) ++ { ++ printk(" after initialization bw is null!\n"); ++ } ++ else ++ { ++ printk(" after initialization bw is %lld\n", *new_bw); ++ printk(" after initialization test is %lld\n", *test); ++ } ++ #endif ++ } ++ } ++ } ++ ++ return new_bw; ++} ++ ++ ++static bool match(const struct sk_buff *skb, struct xt_action_param *par) ++{ ++ ++ struct ipt_bandwidth_info *info = ((const struct ipt_bandwidth_info*)(par->matchinfo))->non_const_self; ++ ++ time_t now; ++ int match_found; ++ ++ ++ unsigned char is_check = info->cmp == BANDWIDTH_CHECK ? 1 : 0; ++ unsigned char do_src_dst_swap = 0; ++ info_and_maps* iam = NULL; ++ long_map* ip_map = NULL; ++ ++ uint64_t* bws[2] = {NULL, NULL}; ++ ++ /* if we're currently setting this id, ignore new data until set is complete */ ++ if(set_in_progress == 1) ++ { ++ if(strcmp(info->id, set_id) == 0) ++ { ++ return 0; ++ } ++ } ++ ++ ++ ++ ++ /* ++ * BEFORE we lock, check for timezone shift ++ * this will almost always be be very,very quick, ++ * but in the event there IS a shift this ++ * function will lock both kernel update spinlock ++ * and userspace i/o semaphore, and do a lot of ++ * number crunching so we shouldn't ++ * already be locked. ++ */ ++ now = get_seconds(); ++ ++ ++ if(now != last_local_mw_update ) ++ { ++ check_for_timezone_shift(now, 0); ++ check_for_backwards_time_shift(now); ++ } ++ now = now - local_seconds_west; /* Adjust for local timezone */ ++ ++ spin_lock_bh(&bandwidth_lock); ++ ++ if(is_check) ++ { ++ info_and_maps* check_iam; ++ do_src_dst_swap = info->check_type == BANDWIDTH_CHECK_SWAP ? 1 : 0; ++ check_iam = (info_and_maps*)get_string_map_element_with_hashed_key(id_map, info->hashed_id); ++ if(check_iam == NULL) ++ { ++ spin_unlock_bh(&bandwidth_lock); ++ return 0; ++ } ++ info = check_iam->info; ++ } ++ ++ ++ ++ ++ if(info->reset_interval != BANDWIDTH_NEVER) ++ { ++ if(info->next_reset < now) ++ { ++ //do reset ++ //iam = (info_and_maps*)get_string_map_element_with_hashed_key(id_map, info->hashed_id); ++ iam = (info_and_maps*)info->iam; ++ if(iam != NULL) /* should never be null, but let's be sure */ ++ { ++ handle_interval_reset(iam, now); ++ ip_map = iam->ip_map; ++ } ++ else ++ { ++ /* even in case of malloc failure or weird error we can update these params */ ++ info->current_bandwidth = 0; ++ info->next_reset = get_next_reset_time(info, now, info->previous_reset); ++ } ++ } ++ } ++ ++ if(info->type == BANDWIDTH_COMBINED) ++ { ++ if(iam == NULL) ++ { ++ //iam = (info_and_maps*)get_string_map_element_with_hashed_key(id_map, info->hashed_id); ++ iam = (info_and_maps*)info->iam; ++ if(iam != NULL) ++ { ++ ip_map = iam->ip_map; ++ } ++ } ++ if(ip_map != NULL) /* if this ip_map != NULL iam can never be NULL, so we don't need to check this */ ++ { ++ ++ if(info->combined_bw == NULL) ++ { ++ bws[0] = initialize_map_entries_for_ip(iam, 0, skb->len); ++ } ++ else ++ { ++ bws[0] = info->combined_bw; ++ *(bws[0]) = ADD_UP_TO_MAX(*(bws[0]), (uint64_t)skb->len, is_check); ++ } ++ } ++ else ++ { ++ #ifdef BANDWIDTH_DEBUG ++ printk("error: ip_map is null in match!\n"); ++ #endif ++ } ++ info->current_bandwidth = ADD_UP_TO_MAX(info->current_bandwidth, (uint64_t)skb->len, is_check); ++ } ++ else ++ { ++ uint32_t bw_ip, bw_ip_index; ++ uint32_t bw_ips[2] = {0, 0}; ++ struct iphdr* iph = (struct iphdr*)(skb_network_header(skb)); ++ if(info->type == BANDWIDTH_INDIVIDUAL_SRC) ++ { ++ //src ip ++ bw_ips[0] = iph->saddr; ++ if(do_src_dst_swap) ++ { ++ bw_ips[0] = iph->daddr; ++ } ++ } ++ else if (info->type == BANDWIDTH_INDIVIDUAL_DST) ++ { ++ //dst ip ++ bw_ips[0] = iph->daddr; ++ if(do_src_dst_swap) ++ { ++ bw_ips[0] = iph->saddr; ++ } ++ } ++ else if(info->type == BANDWIDTH_INDIVIDUAL_LOCAL || info->type == BANDWIDTH_INDIVIDUAL_REMOTE) ++ { ++ //remote or local ip -- need to test both src && dst ++ uint32_t src_ip = iph->saddr; ++ uint32_t dst_ip = iph->daddr; ++ if(info->type == BANDWIDTH_INDIVIDUAL_LOCAL) ++ { ++ bw_ips[0] = ((info->local_subnet_mask & src_ip) == info->local_subnet) ? src_ip : 0; ++ bw_ips[1] = ((info->local_subnet_mask & dst_ip) == info->local_subnet) ? dst_ip : 0; ++ } ++ else if(info->type == BANDWIDTH_INDIVIDUAL_REMOTE) ++ { ++ bw_ips[0] = ((info->local_subnet_mask & src_ip) != info->local_subnet ) ? src_ip : 0; ++ bw_ips[1] = ((info->local_subnet_mask & dst_ip) != info->local_subnet ) ? dst_ip : 0; ++ } ++ } ++ ++ if(ip_map == NULL) ++ { ++ //iam = (info_and_maps*)get_string_map_element_with_hashed_key(id_map, info->hashed_id); ++ iam = (info_and_maps*)info->iam; ++ if(iam != NULL) ++ { ++ ip_map = iam->ip_map; ++ } ++ } ++ if(!is_check && info->cmp == BANDWIDTH_MONITOR) ++ { ++ uint64_t* combined_oldval = info->combined_bw; ++ if(combined_oldval == NULL) ++ { ++ combined_oldval = initialize_map_entries_for_ip(iam, 0, (uint64_t)skb->len); ++ } ++ else ++ { ++ *combined_oldval = ADD_UP_TO_MAX(*combined_oldval, (uint64_t)skb->len, is_check); ++ } ++ } ++ bw_ip_index = bw_ips[0] == 0 ? 1 : 0; ++ bw_ip = bw_ips[bw_ip_index]; ++ if(bw_ip != 0 && ip_map != NULL) ++ { ++ uint64_t* oldval = get_long_map_element(ip_map, (unsigned long)bw_ip); ++ if(oldval == NULL) ++ { ++ if(!is_check) ++ { ++ /* may return NULL on malloc failure but that's ok */ ++ oldval = initialize_map_entries_for_ip(iam, (unsigned long)bw_ip, (uint64_t)skb->len); ++ } ++ } ++ else ++ { ++ *oldval = ADD_UP_TO_MAX(*oldval, (uint64_t)skb->len, is_check); ++ } ++ ++ /* this is fine, setting bws[bw_ip_index] to NULL on check for undefined value or kmalloc failure won't crash anything */ ++ bws[bw_ip_index] = oldval; ++ } ++ ++ } ++ ++ ++ match_found = 0; ++ if(info->cmp != BANDWIDTH_MONITOR) ++ { ++ if(info->cmp == BANDWIDTH_GT) ++ { ++ match_found = bws[0] != NULL ? ( *(bws[0]) > info->bandwidth_cutoff ? 1 : match_found ) : match_found; ++ match_found = bws[1] != NULL ? ( *(bws[1]) > info->bandwidth_cutoff ? 1 : match_found ) : match_found; ++ match_found = info->current_bandwidth > info->bandwidth_cutoff ? 1 : match_found; ++ } ++ else if(info->cmp == BANDWIDTH_LT) ++ { ++ match_found = bws[0] != NULL ? ( *(bws[0]) < info->bandwidth_cutoff ? 1 : match_found ) : match_found; ++ match_found = bws[1] != NULL ? ( *(bws[1]) < info->bandwidth_cutoff ? 1 : match_found ) : match_found; ++ match_found = info->current_bandwidth < info->bandwidth_cutoff ? 1 : match_found; ++ } ++ } ++ ++ ++ spin_unlock_bh(&bandwidth_lock); ++ ++ ++ ++ ++ ++ return match_found; ++} ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++/********************** ++ * Get functions ++ *********************/ ++ ++#define MAX_IP_STR_LENGTH 16 ++ ++#define ERROR_NONE 0 ++#define ERROR_NO_ID 1 ++#define ERROR_BUFFER_TOO_SHORT 2 ++#define ERROR_NO_HISTORY 3 ++#define ERROR_UNKNOWN 4 ++typedef struct get_req_struct ++{ ++ uint32_t ip; ++ uint32_t next_ip_index; ++ unsigned char return_history; ++ char id[BANDWIDTH_MAX_ID_LENGTH]; ++} get_request; ++ ++static unsigned long* output_ip_list = NULL; ++static unsigned long output_ip_list_length = 0; ++ ++static char add_ip_block( uint32_t ip, ++ unsigned char full_history_requested, ++ info_and_maps* iam, ++ unsigned char* output_buffer, ++ uint32_t* current_output_index, ++ uint32_t buffer_length ++ ); ++static void parse_get_request(unsigned char* request_buffer, get_request* parsed_request); ++static int handle_get_failure(int ret_value, int unlock_user_sem, int unlock_bandwidth_spin, unsigned char error_code, unsigned char* out_buffer, unsigned char* free_buffer ); ++ ++ ++/* ++ * returns whether we succeeded in adding ip block, 0= success, ++ * otherwise error code of problem that we found ++ */ ++static char add_ip_block( uint32_t ip, ++ unsigned char full_history_requested, ++ info_and_maps* iam, ++ unsigned char* output_buffer, ++ uint32_t* current_output_index, ++ uint32_t output_buffer_length ++ ) ++{ ++ #ifdef BANDWIDTH_DEBUG ++ uint32_t *ipp = &ip; ++ printk("doing output for ip = %u.%u.%u.%u\n", *((unsigned char*)ipp), *(((unsigned char*)ipp)+1), *(((unsigned char*)ipp)+2), *(((unsigned char*)ipp)+3) ); ++ #endif ++ ++ if(full_history_requested) ++ { ++ bw_history* history = NULL; ++ if(iam->info->num_intervals_to_save > 0 && iam->ip_history_map != NULL) ++ { ++ history = (bw_history*)get_long_map_element(iam->ip_history_map, ip); ++ } ++ if(history == NULL) ++ { ++ #ifdef BANDWIDTH_DEBUG ++ printk(" no history map for ip, dumping latest value in history format\n" ); ++ #endif ++ ++ ++ uint32_t block_length = (2*4) + (3*8); ++ uint64_t *bw; ++ ++ if(*current_output_index + block_length > output_buffer_length) ++ { ++ return ERROR_BUFFER_TOO_SHORT; ++ } ++ *( (uint32_t*)(output_buffer + *current_output_index) ) = ip; ++ *current_output_index = *current_output_index + 4; ++ ++ *( (uint32_t*)(output_buffer + *current_output_index) ) = 1; ++ *current_output_index = *current_output_index + 4; ++ ++ *( (uint64_t*)(output_buffer + *current_output_index) ) = (uint64_t)iam->info->previous_reset + (60 * local_minutes_west); ++ *current_output_index = *current_output_index + 8; ++ ++ *( (uint64_t*)(output_buffer + *current_output_index) ) = (uint64_t)iam->info->previous_reset + (60 * local_minutes_west); ++ *current_output_index = *current_output_index + 8; ++ ++ *( (uint64_t*)(output_buffer + *current_output_index) ) = (uint64_t)iam->info->previous_reset + (60 * local_minutes_west); ++ *current_output_index = *current_output_index + 8; ++ ++ bw = (uint64_t*)get_long_map_element(iam->ip_map, ip); ++ if(bw == NULL) ++ { ++ *( (uint64_t*)(output_buffer + *current_output_index) ) = 0; ++ } ++ else ++ { ++ *( (uint64_t*)(output_buffer + *current_output_index) ) = *bw; ++ } ++ *current_output_index = *current_output_index + 8; ++ ++ } ++ else ++ { ++ uint32_t block_length = (2*4) + (3*8) + (8*history->num_nodes); ++ uint64_t last_reset; ++ uint32_t node_num; ++ uint32_t next_index; ++ ++ if(*current_output_index + block_length > output_buffer_length) ++ { ++ return ERROR_BUFFER_TOO_SHORT; ++ } ++ ++ *( (uint32_t*)(output_buffer + *current_output_index) ) = ip; ++ *current_output_index = *current_output_index + 4; ++ ++ *( (uint32_t*)(output_buffer + *current_output_index) )= history->num_nodes; ++ *current_output_index = *current_output_index + 4; ++ ++ ++ ++ /* need to return times in regular UTC not the UTC - minutes west, which is useful for processing */ ++ last_reset = (uint64_t)iam->info->previous_reset + (60 * local_minutes_west); ++ *( (uint64_t*)(output_buffer + *current_output_index) ) = history->first_start > 0 ? (uint64_t)history->first_start + (60 * local_minutes_west) : last_reset; ++ #ifdef BANDWIDTH_DEBUG ++ printk(" dumping first start = %lld\n", *( (uint64_t*)(output_buffer + *current_output_index) ) ); ++ #endif ++ *current_output_index = *current_output_index + 8; ++ ++ ++ ++ *( (uint64_t*)(output_buffer + *current_output_index) ) = history->first_end > 0 ? (uint64_t)history->first_end + (60 * local_minutes_west) : last_reset; ++ #ifdef BANDWIDTH_DEBUG ++ printk(" dumping first end = %lld\n", *( (uint64_t*)(output_buffer + *current_output_index) ) ); ++ #endif ++ *current_output_index = *current_output_index + 8; ++ ++ ++ ++ *( (uint64_t*)(output_buffer + *current_output_index) ) = history->last_end > 0 ? (uint64_t)history->last_end + (60 * local_minutes_west) : last_reset; ++ #ifdef BANDWIDTH_DEBUG ++ printk(" dumping last end = %lld\n", *( (uint64_t*)(output_buffer + *current_output_index) ) ); ++ #endif ++ *current_output_index = *current_output_index + 8; ++ ++ ++ ++ node_num = 0; ++ next_index = history->num_nodes == history->max_nodes ? history->current_index+1 : 0; ++ next_index = next_index >= history->max_nodes ? 0 : next_index; ++ for(node_num=0; node_num < history->num_nodes; node_num++) ++ { ++ *( (uint64_t*)(output_buffer + *current_output_index) ) = (history->history_data)[ next_index ]; ++ *current_output_index = *current_output_index + 8; ++ next_index = (next_index + 1) % history->max_nodes; ++ } ++ } ++ } ++ else ++ { ++ uint64_t *bw; ++ if(*current_output_index + 8 > output_buffer_length) ++ { ++ return ERROR_BUFFER_TOO_SHORT; ++ } ++ ++ *( (uint32_t*)(output_buffer + *current_output_index) ) = ip; ++ *current_output_index = *current_output_index + 4; ++ ++ ++ bw = (uint64_t*)get_long_map_element(iam->ip_map, ip); ++ if(bw == NULL) ++ { ++ *( (uint64_t*)(output_buffer + *current_output_index) ) = 0; ++ } ++ else ++ { ++ *( (uint64_t*)(output_buffer + *current_output_index) ) = *bw; ++ } ++ *current_output_index = *current_output_index + 8; ++ } ++ return ERROR_NONE; ++} ++ ++ ++ ++/* ++ * convenience method for cleaning crap up after failed malloc or other ++ * error that we can't recover from in get function ++ */ ++static int handle_get_failure(int ret_value, int unlock_user_sem, int unlock_bandwidth_spin, unsigned char error_code, unsigned char* out_buffer, unsigned char* free_buffer ) ++{ ++ copy_to_user(out_buffer, &error_code, 1); ++ if( free_buffer != NULL ) { kfree(free_buffer); } ++ if(unlock_bandwidth_spin) { spin_unlock_bh(&bandwidth_lock); } ++ if(unlock_user_sem) { up(&userspace_lock); } ++ return ret_value; ++} ++ ++/* ++ * request structure: ++ * bytes 1:4 is ip (uint32_t) ++ * bytes 4:8 is the next ip index (uint32_t) ++ * byte 9 is whether to return full history or just current usage (unsigned char) ++ * bytes 10:10+MAX_ID_LENGTH are the id (a string) ++ */ ++static void parse_get_request(unsigned char* request_buffer, get_request* parsed_request) ++{ ++ uint32_t* ip = (uint32_t*)(request_buffer+0); ++ uint32_t* next_ip_index = (uint32_t*)(request_buffer+4); ++ unsigned char* return_history = (unsigned char*)(request_buffer+8); ++ ++ ++ ++ parsed_request->ip = *ip; ++ parsed_request->next_ip_index = *next_ip_index; ++ parsed_request->return_history = *return_history; ++ memcpy(parsed_request->id, request_buffer+9, BANDWIDTH_MAX_ID_LENGTH); ++ (parsed_request->id)[BANDWIDTH_MAX_ID_LENGTH-1] = '\0'; /* make sure id is null terminated no matter what */ ++ ++ #ifdef BANDWIDTH_DEBUG ++ printk("ip = %u.%u.%u.%u\n", *((char*)ip), *(((char*)ip)+1), *(((char*)ip)+2), *(((char*)ip)+3) ); ++ printk("next ip index = %d\n", *next_ip_index); ++ printk("return_history = %d\n", *return_history); ++ #endif ++} ++ ++ ++static int ipt_bandwidth_get_ctl(struct sock *sk, int cmd, void *user, int *len) ++{ ++ /* check for timezone shift & adjust if necessary */ ++ char* buffer; ++ get_request query; ++ info_and_maps* iam; ++ ++ unsigned char* error; ++ uint32_t* total_ips; ++ uint32_t* start_index; ++ uint32_t* num_ips_in_response; ++ uint64_t* reset_interval; ++ uint64_t* reset_time; ++ unsigned char* reset_is_constant_interval; ++ uint32_t current_output_index; ++ time_t now = get_seconds(); ++ check_for_timezone_shift(now, 0); ++ check_for_backwards_time_shift(now); ++ now = now - local_seconds_west; /* Adjust for local timezone */ ++ ++ ++ down(&userspace_lock); ++ ++ ++ /* first check that query buffer is big enough to hold the info needed to parse the query */ ++ if(*len < BANDWIDTH_MAX_ID_LENGTH + 9) ++ { ++ ++ return handle_get_failure(0, 1, 0, ERROR_BUFFER_TOO_SHORT, user, NULL); ++ } ++ ++ ++ ++ /* copy the query from userspace to kernel space & parse */ ++ buffer = kmalloc(*len, GFP_ATOMIC); ++ if(buffer == NULL) /* check for malloc failure */ ++ { ++ return handle_get_failure(0, 1, 0, ERROR_UNKNOWN, user, NULL); ++ } ++ copy_from_user(buffer, user, *len); ++ parse_get_request(buffer, &query); ++ ++ ++ ++ ++ ++ ++ /* ++ * retrieve data for this id and verify all variables are properly defined, just to be sure ++ * this is a kernel module -- it pays to be paranoid! ++ */ ++ spin_lock_bh(&bandwidth_lock); ++ ++ iam = (info_and_maps*)get_string_map_element(id_map, query.id); ++ ++ if(iam == NULL) ++ { ++ return handle_get_failure(0, 1, 1, ERROR_NO_ID, user, buffer); ++ } ++ if(iam->info == NULL || iam->ip_map == NULL) ++ { ++ return handle_get_failure(0, 1, 1, ERROR_NO_ID, user, buffer); ++ } ++ if(iam->info->num_intervals_to_save > 0 && iam->ip_history_map == NULL) ++ { ++ return handle_get_failure(0, 1, 1, ERROR_NO_ID, user, buffer); ++ } ++ ++ /* allocate ip list if this is first query */ ++ if(query.next_ip_index == 0 && query.ip == 0) ++ { ++ if(output_ip_list != NULL) ++ { ++ kfree(output_ip_list); ++ } ++ if(iam->info->type == BANDWIDTH_COMBINED) ++ { ++ output_ip_list_length = 1; ++ output_ip_list = (unsigned long*)kmalloc(sizeof(unsigned long), GFP_ATOMIC); ++ if(output_ip_list != NULL) { *output_ip_list = 0; } ++ } ++ else ++ { ++ output_ip_list = get_sorted_long_map_keys(iam->ip_map, &output_ip_list_length); ++ } ++ ++ if(output_ip_list == NULL) ++ { ++ return handle_get_failure(0, 1, 1, ERROR_UNKNOWN, user, buffer); ++ } ++ } ++ ++ /* if this is not first query do a sanity check -- make sure it's within bounds of allocated ip list */ ++ if(query.next_ip_index > 0 && (output_ip_list == NULL || query.next_ip_index > output_ip_list_length)) ++ { ++ return handle_get_failure(0, 1, 1, ERROR_UNKNOWN, user, buffer); ++ } ++ ++ ++ ++ ++ /* ++ // values only reset when a packet hits a rule, so ++ // reset may have expired without data being reset. ++ // So, test if we need to reset values to zero ++ */ ++ if(iam->info->reset_interval != BANDWIDTH_NEVER) ++ { ++ if(iam->info->next_reset < now) ++ { ++ //do reset ++ handle_interval_reset(iam, now); ++ } ++ } ++ ++ ++ ++ /* compute response & store it in buffer ++ * ++ * format of response: ++ * byte 1 : error code (0 for ok) ++ * bytes 2-5 : total_num_ips found in query (further gets may be necessary to retrieve them) ++ * bytes 6-9 : start_index, index (in a list of total_num_ips) of first ip in response ++ * bytes 10-13 : num_ips_in_response, number of ips in this response ++ * bytes 14-21 : reset_interval (helps deal with DST shifts in userspace) ++ * bytes 22-29 : reset_time (helps deal with DST shifts in userspace) ++ * byte 30 : reset_is_constant_interval (helps deal with DST shifts in userspace) ++ * remaining bytes contain blocks of ip data ++ * format is dependent on whether history was queried ++ * ++ * if history was NOT queried we have ++ * bytes 1-4 : ip ++ * bytes 5-12 : bandwidth ++ * ++ * if history WAS queried we have ++ * (note we are using 64 bit integers for time here ++ * even though time_t is 32 bits on most 32 bit systems ++ * just to be on the safe side) ++ * bytes 1-4 : ip ++ * bytes 4-8 : history_length number of history values (including current) ++ * bytes 9-16 : first start ++ * bytes 17-24 : first end ++ * bytes 25-32 : recent end ++ * 33 onward : list of 64 bit integers of length history_length ++ * ++ */ ++ error = buffer; ++ total_ips = (uint32_t*)(buffer+1); ++ start_index = (uint32_t*)(buffer+5); ++ num_ips_in_response = (uint32_t*)(buffer+9); ++ reset_interval = (uint64_t*)(buffer+13); ++ reset_time = (uint64_t*)(buffer+21); ++ reset_is_constant_interval = (char*)(buffer+29); ++ ++ *reset_interval = (uint64_t)iam->info->reset_interval; ++ *reset_time = (uint64_t)iam->info->reset_time; ++ *reset_is_constant_interval = iam->info->reset_is_constant_interval; ++ ++ current_output_index = 30; ++ if(query.ip != 0) ++ { ++ *error = add_ip_block( query.ip, ++ query.return_history, ++ iam, ++ buffer, ++ ¤t_output_index, ++ *len ++ ); ++ ++ *total_ips = *error == 0; ++ *start_index = 0; ++ *num_ips_in_response = *error == 0 ? 1 : 0; ++ } ++ else ++ { ++ uint32_t next_index = query.next_ip_index; ++ *error = ERROR_NONE; ++ *total_ips = output_ip_list_length; ++ *start_index = next_index; ++ *num_ips_in_response = 0; ++ while(*error == ERROR_NONE && next_index < output_ip_list_length) ++ { ++ uint32_t next_ip = output_ip_list[next_index]; ++ *error = add_ip_block( next_ip, ++ query.return_history, ++ iam, ++ buffer, ++ ¤t_output_index, ++ *len ++ ); ++ if(*error == ERROR_NONE) ++ { ++ *num_ips_in_response = *num_ips_in_response + 1; ++ next_index++; ++ } ++ } ++ if(*error == ERROR_BUFFER_TOO_SHORT && *num_ips_in_response > 0) ++ { ++ *error = ERROR_NONE; ++ } ++ if(next_index == output_ip_list_length) ++ { ++ kfree(output_ip_list); ++ output_ip_list = NULL; ++ output_ip_list_length = 0; ++ } ++ } ++ ++ spin_unlock_bh(&bandwidth_lock); ++ ++ copy_to_user(user, buffer, *len); ++ kfree(buffer); ++ ++ ++ ++ up(&userspace_lock); ++ ++ ++ return 0; ++} ++ ++ ++ ++ ++ ++/******************** ++ * Set functions ++ ********************/ ++ ++typedef struct set_header_struct ++{ ++ uint32_t total_ips; ++ uint32_t next_ip_index; ++ uint32_t num_ips_in_buffer; ++ unsigned char history_included; ++ unsigned char zero_unset_ips; ++ time_t last_backup; ++ char id[BANDWIDTH_MAX_ID_LENGTH]; ++} set_header; ++ ++static int handle_set_failure(int ret_value, int unlock_user_sem, int unlock_bandwidth_spin, unsigned char* free_buffer ); ++static void parse_set_header(unsigned char* input_buffer, set_header* header); ++static void set_single_ip_data(unsigned char history_included, info_and_maps* iam, unsigned char* buffer, uint32_t* buffer_index, time_t now); ++ ++static int handle_set_failure(int ret_value, int unlock_user_sem, int unlock_bandwidth_spin, unsigned char* free_buffer ) ++{ ++ if( free_buffer != NULL ) { kfree(free_buffer); } ++ set_in_progress = 0; ++ if(unlock_bandwidth_spin) { spin_unlock_bh(&bandwidth_lock); } ++ if(unlock_user_sem) { up(&userspace_lock); } ++ return ret_value; ++} ++ ++static void parse_set_header(unsigned char* input_buffer, set_header* header) ++{ ++ /* ++ * set header structure: ++ * bytes 1-4 : total_ips being set in this and subsequent requests ++ * bytes 5-8 : next_ip_index, first ip being set in this set command ++ * bytes 9-12 : num_ips_in_buffer, the number of ips in this set request ++ * byte 13 : history_included (whether history data is included, or just current data) ++ * byte 14 : zero_unset_ips, whether to zero all ips not included in this and subsequent requests ++ * bytes 15-22 : last_backup time (64 bit) ++ * bytes 23-23+BANDWIDTH_MAX_ID_LENGTH : id ++ * bytes 23+ : ip data ++ */ ++ ++ uint32_t* total_ips = (uint32_t*)(input_buffer+0); ++ uint32_t* next_ip_index = (uint32_t*)(input_buffer+4); ++ uint32_t* num_ips_in_buffer = (uint32_t*)(input_buffer+8); ++ unsigned char* history_included = (unsigned char*)(input_buffer+12); ++ unsigned char* zero_unset_ips = (unsigned char*)(input_buffer+13); ++ uint64_t* last_backup = (uint64_t*)(input_buffer+14); ++ ++ ++ header->total_ips = *total_ips; ++ header->next_ip_index = *next_ip_index; ++ header->num_ips_in_buffer = *num_ips_in_buffer; ++ header->history_included = *history_included; ++ header->zero_unset_ips = *zero_unset_ips; ++ header->last_backup = (time_t)*last_backup; ++ memcpy(header->id, input_buffer+22, BANDWIDTH_MAX_ID_LENGTH); ++ (header->id)[BANDWIDTH_MAX_ID_LENGTH-1] = '\0'; /* make sure id is null terminated no matter what */ ++ ++ #ifdef BANDWIDTH_DEBUG ++ printk("parsed set header:\n"); ++ printk(" total_ips = %d\n", header->total_ips); ++ printk(" next_ip_index = %d\n", header->next_ip_index); ++ printk(" num_ips_in_buffer = %d\n", header->num_ips_in_buffer); ++ printk(" zero_unset_ips = %d\n", header->zero_unset_ips); ++ printk(" last_backup = %ld\n", header->last_backup); ++ printk(" id = %s\n", header->id); ++ #endif ++} ++static void set_single_ip_data(unsigned char history_included, info_and_maps* iam, unsigned char* buffer, uint32_t* buffer_index, time_t now) ++{ ++ /* ++ * note that times stored within the module are adjusted so they are equal to seconds ++ * since unix epoch that corrosponds to the UTC wall-clock time (timezone offset 0) ++ * that is equal to the wall-clock time in the current time-zone. Incoming values must ++ * be adjusted similarly ++ */ ++ uint32_t ip = *( (uint32_t*)(buffer + *buffer_index) ); ++ ++ #ifdef BANDWIDTH_DEBUG ++ uint32_t* ipp = &ip; ++ printk("doing set for ip = %u.%u.%u.%u\n", *((unsigned char*)ipp), *(((unsigned char*)ipp)+1), *(((unsigned char*)ipp)+2), *(((unsigned char*)ipp)+3) ); ++ printk("ip index = %d\n", *buffer_index); ++ #endif ++ ++ if(history_included) ++ { ++ uint32_t num_history_nodes = *( (uint32_t*)(buffer + *buffer_index+4)); ++ if(iam->info->num_intervals_to_save > 0 && iam->ip_history_map != NULL) ++ { ++ time_t first_start = (time_t) *( (uint64_t*)(buffer + *buffer_index+8)); ++ /* time_t first_end = (time_t) *( (uint64_t*)(buffer + *buffer_index+16)); //not used */ ++ /* time_t last_end = (time_t) *( (uint64_t*)(buffer + *buffer_index+24)); //not used */ ++ time_t next_start; ++ time_t next_end; ++ uint32_t node_index; ++ uint32_t zero_count; ++ bw_history* history; ++ ++ ++ #ifdef BANDWIDTH_DEBUG ++ printk("setting history with first start = %ld, now = %ld\n", first_start, now); ++ #endif ++ ++ ++ *buffer_index = *buffer_index + (2*4) + (3*8); ++ ++ /* adjust for timezone */ ++ next_start = first_start - (60 * local_minutes_west); ++ next_end = get_next_reset_time(iam->info, next_start, next_start); ++ node_index=0; ++ zero_count=0; ++ history = NULL; ++ while(next_start < now) ++ { ++ uint64_t next_bw = 0; ++ if(node_index < num_history_nodes) ++ { ++ next_bw = *( (uint64_t*)(buffer + *buffer_index)); ++ *buffer_index = *buffer_index + 8; ++ } ++ zero_count = next_bw == 0 ? zero_count+1 : 0; ++ ++ if(node_index == 0 || history == NULL) ++ { ++ initialize_map_entries_for_ip(iam, ip, next_bw); ++ history = get_long_map_element(iam->ip_history_map, (unsigned long)ip); ++ } ++ else if(next_end < now) /* if this is most recent node, don't do update since last node is current bandwidth */ ++ { ++ update_history(history, next_start, next_end, iam->info); ++ (history->history_data)[ history->current_index ] = next_bw; ++ if(zero_count < history->max_nodes +2) ++ { ++ next_start = next_end; ++ next_end = get_next_reset_time(iam->info, next_start, next_start); ++ } ++ else ++ { ++ /* do history reset */ ++ history->first_start = 0; ++ history->first_end = 0; ++ history->last_end = 0; ++ history->num_nodes = 1; ++ history->non_zero_nodes = 1; ++ history->current_index = 0; ++ (history->history_data)[0] = 0; ++ ++ next_start = now; ++ next_end = get_next_reset_time(iam->info, now, next_start); ++ } ++ } ++ else /* if this is most recent node, we still need to exit loop*/ ++ { ++ break; ++ } ++ node_index++; ++ } ++ while(node_index < num_history_nodes) ++ { ++ *buffer_index = *buffer_index + 8; ++ node_index++; ++ } ++ if(history != NULL) ++ { ++ set_long_map_element(iam->ip_map, ip, (history->history_data + history->current_index) ); ++ iam->info->previous_reset = next_start; ++ iam->info->next_reset = next_end; ++ if(ip == 0) ++ { ++ iam->info->current_bandwidth = (history->history_data)[history->current_index]; ++ } ++ } ++ } ++ else ++ { ++ uint64_t bw; ++ *buffer_index = *buffer_index + (2*4) + (3*8) + ((num_history_nodes-1)*8); ++ bw = *( (uint64_t*)(buffer + *buffer_index)); ++ initialize_map_entries_for_ip(iam, ip, bw); /* automatically frees existing values if they exist */ ++ *buffer_index = *buffer_index + 8; ++ if(ip == 0) ++ { ++ iam->info->current_bandwidth = bw; ++ } ++ } ++ ++ } ++ else ++ { ++ uint64_t bw = *( (uint64_t*)(buffer + *buffer_index+4) ); ++ #ifdef BANDWIDTH_DEBUG ++ printk(" setting bw to %lld\n", bw ); ++ #endif ++ ++ ++ initialize_map_entries_for_ip(iam, ip, bw); /* automatically frees existing values if they exist */ ++ *buffer_index = *buffer_index + 12; ++ ++ if(ip == 0) ++ { ++ iam->info->current_bandwidth = bw; ++ } ++ } ++ ++ ++} ++ ++static int ipt_bandwidth_set_ctl(struct sock *sk, int cmd, void *user, u_int32_t len) ++{ ++ /* check for timezone shift & adjust if necessary */ ++ char* buffer; ++ set_header header; ++ info_and_maps* iam; ++ uint32_t buffer_index; ++ uint32_t next_ip_index; ++ time_t now = get_seconds(); ++ check_for_timezone_shift(now, 0); ++ check_for_backwards_time_shift(now); ++ now = now - local_seconds_west; /* Adjust for local timezone */ ++ ++ ++ /* just return right away if user buffer is too short to contain even the header */ ++ if(len < (3*4) + 2 + 8 + BANDWIDTH_MAX_ID_LENGTH) ++ { ++ #ifdef BANDWIDTH_DEBUG ++ printk("set error: buffer not large enough!\n"); ++ #endif ++ return 0; ++ } ++ ++ down(&userspace_lock); ++ set_in_progress = 1; ++ ++ buffer = kmalloc(len, GFP_ATOMIC); ++ if(buffer == NULL) /* check for malloc failure */ ++ { ++ return handle_set_failure(0, 1, 0, NULL); ++ } ++ copy_from_user(buffer, user, len); ++ parse_set_header(buffer, &header); ++ ++ ++ ++ ++ /* ++ * retrieve data for this id and verify all variables are properly defined, just to be sure ++ * this is a kernel module -- it pays to be paranoid! ++ */ ++ spin_lock_bh(&bandwidth_lock); ++ ++ ++ iam = (info_and_maps*)get_string_map_element(id_map, header.id); ++ if(iam == NULL) ++ { ++ return handle_set_failure(0, 1, 1, buffer); ++ } ++ if(iam->info == NULL || iam->ip_map == NULL) ++ { ++ return handle_set_failure(0, 1, 1, buffer); ++ } ++ if(iam->info->num_intervals_to_save > 0 && iam->ip_history_map == NULL) ++ { ++ return handle_set_failure(0, 1, 1, buffer); ++ } ++ ++ /* ++ * during set unconditionally set combined_bw to NULL ++ * if combined data (ip=0) exists after set exits cleanly, we will restore it ++ */ ++ iam->info->combined_bw = NULL; ++ ++ //if zero_unset_ips == 1 && next_ip_index == 0 ++ //then clear data for all ips for this id ++ if(header.zero_unset_ips && header.next_ip_index == 0) ++ { ++ //clear data ++ if(iam->info->num_intervals_to_save > 0) ++ { ++ while(iam->ip_map->num_elements > 0) ++ { ++ unsigned long key; ++ remove_smallest_long_map_element(iam->ip_map, &key); ++ /* ignore return value -- it's actually malloced in history, not here */ ++ } ++ while(iam->ip_history_map->num_elements > 0) ++ { ++ unsigned long key; ++ bw_history* history = remove_smallest_long_map_element(iam->ip_history_map, &key); ++ kfree(history->history_data); ++ kfree(history); ++ } ++ } ++ else ++ { ++ while(iam->ip_map->num_elements > 0) ++ { ++ unsigned long key; ++ uint64_t *bw = remove_smallest_long_map_element(iam->ip_map, &key); ++ kfree(bw); ++ } ++ } ++ } ++ ++ /* ++ * last_backup parameter is only relevant for case where we are not setting history ++ * and when we don't have a constant interval length or a specified reset_time (since in this case start time gets reset when rule is inserted and there is therefore no constant end) ++ * If num_intervals_to_save =0 and is_constant_interval=0, check it. If it's nonzero (0=ignore) and invalid, return. ++ */ ++ if(header.last_backup > 0 && iam->info->num_intervals_to_save == 0 && (iam->info->reset_is_constant_interval == 0 || iam->info->reset_time != 0) ) ++ { ++ time_t adjusted_last_backup_time = header.last_backup - (60 * local_minutes_west); ++ time_t next_reset_of_last_backup = get_next_reset_time(iam->info, adjusted_last_backup_time, adjusted_last_backup_time); ++ if(next_reset_of_last_backup != iam->info->next_reset) ++ { ++ return handle_set_failure(0, 1, 1, buffer); ++ } ++ } ++ ++ ++ /* ++ * iterate over each ip block in buffer, ++ * loading data into necessary kerenel-space data structures ++ */ ++ buffer_index = (3*4) + 1 + 1 + 8 + BANDWIDTH_MAX_ID_LENGTH; ++ next_ip_index = header.next_ip_index; ++ ++ while(next_ip_index < header.num_ips_in_buffer) ++ { ++ set_single_ip_data(header.history_included, iam, buffer, &buffer_index, now); ++ next_ip_index++; ++ } ++ ++ if (next_ip_index == header.total_ips) ++ { ++ set_in_progress = 0; ++ } ++ ++ /* set combined_bw */ ++ iam->info->combined_bw = (uint64_t*)get_long_map_element(iam->ip_map, 0); ++ ++ kfree(buffer); ++ spin_unlock_bh(&bandwidth_lock); ++ up(&userspace_lock); ++ return 0; ++} ++static int checkentry(const struct xt_mtchk_param *par) ++{ ++ ++ ++ struct ipt_bandwidth_info *info = (struct ipt_bandwidth_info*)(par->matchinfo); ++ ++ ++ ++ #ifdef BANDWIDTH_DEBUG ++ printk("checkentry called\n"); ++ #endif ++ ++ ++ ++ ++ ++ if(info->ref_count == NULL) /* first instance, we're inserting rule */ ++ { ++ struct ipt_bandwidth_info *master_info = (struct ipt_bandwidth_info*)kmalloc(sizeof(struct ipt_bandwidth_info), GFP_ATOMIC); ++ info->ref_count = (unsigned long*)kmalloc(sizeof(unsigned long), GFP_ATOMIC); ++ ++ if(info->ref_count == NULL) /* deal with kmalloc failure */ ++ { ++ printk("ipt_bandwidth: kmalloc failure in checkentry!\n"); ++ return 0; ++ } ++ *(info->ref_count) = 1; ++ info->non_const_self = master_info; ++ info->hashed_id = sdbm_string_hash(info->id); ++ info->iam = NULL; ++ info->combined_bw = NULL; ++ ++ memcpy(master_info->id, info->id, BANDWIDTH_MAX_ID_LENGTH); ++ master_info->type = info->type; ++ master_info->check_type = info->check_type; ++ master_info->local_subnet = info->local_subnet; ++ master_info->local_subnet_mask = info->local_subnet_mask; ++ master_info->cmp = info->cmp; ++ master_info->reset_is_constant_interval = info->reset_is_constant_interval; ++ master_info->reset_interval = info->reset_interval; ++ master_info->reset_time = info->reset_time; ++ master_info->bandwidth_cutoff = info->bandwidth_cutoff; ++ master_info->current_bandwidth = info->current_bandwidth; ++ master_info->next_reset = info->next_reset; ++ master_info->previous_reset = info->previous_reset; ++ master_info->last_backup_time = info->last_backup_time; ++ master_info->num_intervals_to_save = info->num_intervals_to_save; ++ ++ master_info->hashed_id = info->hashed_id; ++ master_info->iam = info->iam; ++ master_info->combined_bw = info->combined_bw; ++ master_info->non_const_self = info->non_const_self; ++ master_info->ref_count = info->ref_count; ++ ++ #ifdef BANDWIDTH_DEBUG ++ printk(" after increment, ref count = %ld\n", *(info->ref_count) ); ++ #endif ++ ++ if(info->cmp != BANDWIDTH_CHECK) ++ { ++ info_and_maps *iam; ++ ++ down(&userspace_lock); ++ spin_lock_bh(&bandwidth_lock); ++ ++ ++ ++ iam = (info_and_maps*)get_string_map_element(id_map, info->id); ++ if(iam != NULL) ++ { ++ printk("ipt_bandwidth: error, \"%s\" is a duplicate id\n", info->id); ++ spin_unlock_bh(&bandwidth_lock); ++ up(&userspace_lock); ++ return 0; ++ } ++ ++ if(info->reset_interval != BANDWIDTH_NEVER) ++ { ++ time_t now = get_seconds(); ++ if(now != last_local_mw_update ) ++ { ++ check_for_timezone_shift(now, 1); ++ } ++ ++ ++ now = now - (60 * local_minutes_west); /* Adjust for local timezone */ ++ info->previous_reset = now; ++ master_info->previous_reset = now; ++ if(info->next_reset == 0) ++ { ++ info->next_reset = get_next_reset_time(info, now, now); ++ master_info->next_reset = info->next_reset; ++ /* ++ * if we specify last backup time, check that next reset is consistent, ++ * otherwise reset current_bandwidth to 0 ++ * ++ * only applies to combined type -- otherwise we need to handle setting bandwidth ++ * through userspace library ++ */ ++ if(info->last_backup_time != 0 && info->type == BANDWIDTH_COMBINED) ++ { ++ time_t adjusted_last_backup_time = info->last_backup_time - (60 * local_minutes_west); ++ time_t next_reset_of_last_backup = get_next_reset_time(info, adjusted_last_backup_time, adjusted_last_backup_time); ++ if(next_reset_of_last_backup != info->next_reset) ++ { ++ info->current_bandwidth = 0; ++ master_info->current_bandwidth = 0; ++ } ++ info->last_backup_time = 0; ++ master_info->last_backup_time = 0; ++ } ++ } ++ } ++ ++ iam = (info_and_maps*)kmalloc( sizeof(info_and_maps), GFP_ATOMIC); ++ if(iam == NULL) /* handle kmalloc failure */ ++ { ++ printk("ipt_bandwidth: kmalloc failure in checkentry!\n"); ++ spin_unlock_bh(&bandwidth_lock); ++ up(&userspace_lock); ++ return 0; ++ } ++ iam->ip_map = initialize_long_map(); ++ if(iam->ip_map == NULL) /* handle kmalloc failure */ ++ { ++ printk("ipt_bandwidth: kmalloc failure in checkentry!\n"); ++ spin_unlock_bh(&bandwidth_lock); ++ up(&userspace_lock); ++ return 0; ++ } ++ iam->ip_history_map = NULL; ++ if(info->num_intervals_to_save > 0) ++ { ++ iam->ip_history_map = initialize_long_map(); ++ if(iam->ip_history_map == NULL) /* handle kmalloc failure */ ++ { ++ printk("ipt_bandwidth: kmalloc failure in checkentry!\n"); ++ spin_unlock_bh(&bandwidth_lock); ++ up(&userspace_lock); ++ return 0; ++ } ++ } ++ ++ ++ iam->info = master_info; ++ set_string_map_element(id_map, info->id, iam); ++ ++ info->iam = (void*)iam; ++ master_info->iam = (void*)iam; ++ ++ ++ spin_unlock_bh(&bandwidth_lock); ++ up(&userspace_lock); ++ } ++ } ++ ++ else ++ { ++ /* info->non_const_self = info; */ ++ ++ ++ *(info->ref_count) = *(info->ref_count) + 1; ++ #ifdef BANDWIDTH_DEBUG ++ printk(" after increment, ref count = %ld\n", *(info->ref_count) ); ++ #endif ++ ++ ++ /* ++ if(info->cmp != BANDWIDTH_CHECK) ++ { ++ info_and_maps* iam; ++ down(&userspace_lock); ++ spin_lock_bh(&bandwidth_lock); ++ iam = (info_and_maps*)get_string_map_element(id_map, info->id); ++ if(iam != NULL) ++ { ++ iam->info = info; ++ } ++ spin_unlock_bh(&bandwidth_lock); ++ up(&userspace_lock); ++ } ++ */ ++ } ++ ++ #ifdef BANDWIDTH_DEBUG ++ printk("checkentry complete\n"); ++ #endif ++ return 0; ++} ++ ++static void destroy(const struct xt_mtdtor_param *par) ++{ ++ ++ struct ipt_bandwidth_info *info = (struct ipt_bandwidth_info*)(par->matchinfo); ++ ++ #ifdef BANDWIDTH_DEBUG ++ printk("destroy called\n"); ++ #endif ++ ++ *(info->ref_count) = *(info->ref_count) - 1; ++ ++ #ifdef BANDWIDTH_DEBUG ++ printk(" after decrement refcount = %ld\n", *(info->ref_count)); ++ #endif ++ ++ if(*(info->ref_count) == 0) ++ { ++ info_and_maps* iam; ++ down(&userspace_lock); ++ spin_lock_bh(&bandwidth_lock); ++ ++ info->combined_bw = NULL; ++ iam = (info_and_maps*)remove_string_map_element(id_map, info->id); ++ if(iam != NULL && info->cmp != BANDWIDTH_CHECK) ++ { ++ unsigned long num_destroyed; ++ if(iam->ip_map != NULL && iam->ip_history_map != NULL) ++ { ++ unsigned long history_index = 0; ++ bw_history** histories_to_free; ++ ++ destroy_long_map(iam->ip_map, DESTROY_MODE_IGNORE_VALUES, &num_destroyed); ++ ++ histories_to_free = (bw_history**)destroy_long_map(iam->ip_history_map, DESTROY_MODE_RETURN_VALUES, &num_destroyed); ++ ++ /* num_destroyed will be 0 if histories_to_free is null after malloc failure, so this is safe */ ++ for(history_index = 0; history_index < num_destroyed; history_index++) ++ { ++ bw_history* h = histories_to_free[history_index]; ++ if(h != NULL) ++ { ++ kfree(h->history_data); ++ kfree(h); ++ } ++ } ++ ++ } ++ else if(iam->ip_map != NULL) ++ { ++ destroy_long_map(iam->ip_map, DESTROY_MODE_FREE_VALUES, &num_destroyed); ++ } ++ kfree(iam); ++ /* info portion of iam gets taken care of automatically */ ++ } ++ kfree(info->ref_count); ++ kfree(info->non_const_self); ++ ++ spin_unlock_bh(&bandwidth_lock); ++ up(&userspace_lock); ++ } ++ ++ #ifdef BANDWIDTH_DEBUG ++ printk("destroy complete\n"); ++ #endif ++} ++ ++static struct nf_sockopt_ops ipt_bandwidth_sockopts = ++{ ++ .pf = PF_INET, ++ .set_optmin = BANDWIDTH_SET, ++ .set_optmax = BANDWIDTH_SET+1, ++ .set = ipt_bandwidth_set_ctl, ++ .get_optmin = BANDWIDTH_GET, ++ .get_optmax = BANDWIDTH_GET+1, ++ .get = ipt_bandwidth_get_ctl ++}; ++ ++ ++static struct xt_match bandwidth_match __read_mostly = ++{ ++ .name = "bandwidth", ++ .match = match, ++ .family = AF_INET, ++ .matchsize = sizeof(struct ipt_bandwidth_info), ++ .checkentry = checkentry, ++ .destroy = destroy, ++ .me = THIS_MODULE, ++}; ++ ++static int __init init(void) ++{ ++ /* Register setsockopt */ ++ if (nf_register_sockopt(&ipt_bandwidth_sockopts) < 0) ++ { ++ printk("ipt_bandwidth: Can't register sockopts. Aborting\n"); ++ } ++ bandwidth_record_max = get_bw_record_max(); ++ local_minutes_west = old_minutes_west = sys_tz.tz_minuteswest; ++ local_seconds_west = local_minutes_west*60; ++ last_local_mw_update = get_seconds(); ++ if(local_seconds_west > last_local_mw_update) ++ { ++ /* we can't let adjusted time be < 0 -- pretend timezone is still UTC */ ++ local_minutes_west = 0; ++ local_seconds_west = 0; ++ } ++ ++ id_map = initialize_string_map(0); ++ if(id_map == NULL) /* deal with kmalloc failure */ ++ { ++ printk("id map is null, returning -1\n"); ++ return -1; ++ } ++ ++ ++ return xt_register_match(&bandwidth_match); ++} ++ ++static void __exit fini(void) ++{ ++ down(&userspace_lock); ++ spin_lock_bh(&bandwidth_lock); ++ if(id_map != NULL) ++ { ++ unsigned long num_returned; ++ info_and_maps **iams = (info_and_maps**)destroy_string_map(id_map, DESTROY_MODE_RETURN_VALUES, &num_returned); ++ int iam_index; ++ for(iam_index=0; iam_index < num_returned; iam_index++) ++ { ++ info_and_maps* iam = iams[iam_index]; ++ long_map* ip_map = iam->ip_map; ++ unsigned long num_destroyed; ++ destroy_long_map(ip_map, DESTROY_MODE_FREE_VALUES, &num_destroyed); ++ kfree(iam); ++ /* info portion of iam gets taken care of automatically */ ++ } ++ } ++ nf_unregister_sockopt(&ipt_bandwidth_sockopts); ++ xt_unregister_match(&bandwidth_match); ++ spin_unlock_bh(&bandwidth_lock); ++ up(&userspace_lock); ++ ++} ++ ++module_init(init); ++module_exit(fini); ++ + +--- /dev/null ++++ b/net/ipv4/netfilter/ipt_bandwidth.mod.c +@@ -0,0 +1,20 @@ ++#include ++#include ++#include ++ ++MODULE_INFO(vermagic, VERMAGIC_STRING); ++ ++struct module __this_module ++__attribute__((section(".gnu.linkonce.this_module"))) = { ++ .name = KBUILD_MODNAME, ++ .init = init_module, ++#ifdef CONFIG_MODULE_UNLOAD ++ .exit = cleanup_module, ++#endif ++}; ++ ++static const char __module_depends[] ++__attribute_used__ ++__attribute__((section(".modinfo"))) = ++"depends="; ++ +--- /dev/null ++++ b/net/ipv4/netfilter/ipt_timerange.c +@@ -0,0 +1,142 @@ ++/* timerange -- An iptables extension to match multiple timeranges within a week ++ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) ++ * ++ * ++ * Copyright © 2009-2010 by Eric Bishop ++ * ++ * This file is free software: you may copy, redistribute and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation, either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This file is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++ ++ ++#include ++ ++#include ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Eric Bishop"); ++MODULE_DESCRIPTION("Match time ranges, designed for use with Gargoyle web interface (www.gargoyle-router.com)"); ++ ++ ++extern struct timezone sys_tz; ++ ++ ++static bool match(const struct sk_buff *skb, struct xt_action_param *par) ++{ ++ const struct ipt_timerange_info *info = (const struct ipt_timerange_info*)(par->matchinfo); ++ ++ ++ time_t stamp_time; ++ int weekday; ++ int seconds_since_midnight; ++ int test_index; ++ int match_found; ++ ++ struct timeval test_time; ++ ++ do_gettimeofday(&test_time); ++ stamp_time = test_time.tv_sec; ++ stamp_time = stamp_time - (60 * sys_tz.tz_minuteswest); /* Adjust for local timezone */ ++ seconds_since_midnight = stamp_time % 86400; /* 86400 seconds per day */ ++ weekday = (4 + (stamp_time/86400)) % 7; /* 1970-01-01 (time=0) was a Thursday (4). */ ++ ++ /* ++ printk("time=%d, since midnight = %d, day=%d, minuteswest=%d\n", stamp_time, seconds_since_midnight, weekday, sys_tz.tz_minuteswest); ++ */ ++ ++ match_found = 0; ++ if(info->type == HOURS) ++ { ++ for(test_index=0; info->ranges[test_index] != -1 && match_found == 0 && seconds_since_midnight >= info->ranges[test_index]; test_index=test_index+2) ++ { ++ match_found = seconds_since_midnight >= info->ranges[test_index] && seconds_since_midnight <= info->ranges[test_index+1] ? 1 : match_found; ++ } ++ } ++ else if(info->type == WEEKDAYS) ++ { ++ match_found = info->days[weekday]; ++ } ++ else if(info->type == DAYS_HOURS) ++ { ++ match_found = info->days[weekday]; ++ if(match_found == 1) ++ { ++ match_found = 0; ++ for(test_index=0; info->ranges[test_index] != -1 && match_found == 0 && seconds_since_midnight >= info->ranges[test_index]; test_index=test_index+2) ++ { ++ match_found = seconds_since_midnight >= info->ranges[test_index] && seconds_since_midnight <= info->ranges[test_index+1] ? 1 : match_found; ++ } ++ } ++ } ++ else if(info->type == WEEKLY_RANGE) ++ { ++ time_t seconds_since_sunday_midnight = seconds_since_midnight + (weekday*86400); ++ for(test_index=0; info->ranges[test_index] != -1 && match_found == 0 && seconds_since_sunday_midnight >= info->ranges[test_index]; test_index=test_index+2) ++ { ++ match_found = seconds_since_sunday_midnight >= info->ranges[test_index] && seconds_since_sunday_midnight <= info->ranges[test_index+1] ? 1 : match_found; ++ } ++ ++ } ++ ++ match_found = info->invert == 0 ? match_found : !match_found; ++ return match_found; ++} ++ ++ ++static int checkentry(const struct xt_mtchk_param *par) ++{ ++ return 0; ++} ++ ++ ++static struct xt_match timerange_match __read_mostly = ++{ ++ .name = "timerange", ++ .match = match, ++ .family = AF_INET, ++ .matchsize = sizeof(struct ipt_timerange_info), ++ .checkentry = checkentry, ++ .me = THIS_MODULE, ++}; ++ ++static int __init init(void) ++{ ++ return xt_register_match(&timerange_match); ++} ++ ++static void __exit fini(void) ++{ ++ xt_unregister_match(&timerange_match); ++} ++ ++module_init(init); ++module_exit(fini); ++ + +--- /dev/null ++++ b/net/ipv4/netfilter/ipt_timerange.mod.c +@@ -0,0 +1,20 @@ ++#include ++#include ++#include ++ ++MODULE_INFO(vermagic, VERMAGIC_STRING); ++ ++struct module __this_module ++__attribute__((section(".gnu.linkonce.this_module"))) = { ++ .name = KBUILD_MODNAME, ++ .init = init_module, ++#ifdef CONFIG_MODULE_UNLOAD ++ .exit = cleanup_module, ++#endif ++}; ++ ++static const char __module_depends[] ++__attribute_used__ ++__attribute__((section(".modinfo"))) = ++"depends="; ++ +--- /dev/null ++++ b/net/ipv4/netfilter/ipt_webmon.c +@@ -0,0 +1,1319 @@ ++/* webmon -- A netfilter module to match URLs in HTTP(S) requests ++ * This module can match using string match or regular expressions ++ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) ++ * ++ * ++ * Copyright © 2008-2011 by Eric Bishop ++ * ++ * This file is free software: you may copy, redistribute and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation, either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This file is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "webmon_deps/tree_map.h" ++ ++ ++#include ++ ++ ++#include ++#include ++ ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Eric Bishop"); ++MODULE_DESCRIPTION("Monitor URL in HTTP(S) Requests, designed for use with Gargoyle web interface (www.gargoyle-router.com)"); ++ ++#define NIPQUAD(addr) \ ++ ((unsigned char *)&addr)[0], \ ++ ((unsigned char *)&addr)[1], \ ++ ((unsigned char *)&addr)[2], \ ++ ((unsigned char *)&addr)[3] ++#define STRIP "%u.%u.%u.%u" ++ ++typedef struct qn ++{ ++ uint32_t src_ip; ++ char* value; ++ struct timeval time; ++ struct qn* next; ++ struct qn* previous; ++} queue_node; ++ ++typedef struct ++{ ++ queue_node* first; ++ queue_node* last; ++ int length; ++} queue; ++ ++static string_map* domain_map = NULL; ++static queue* recent_domains = NULL; ++ ++static string_map* search_map = NULL; ++static queue* recent_searches = NULL; ++ ++ ++static int max_domain_queue_length = 5; ++static int max_search_queue_length = 5; ++ ++static spinlock_t webmon_lock = __SPIN_LOCK_UNLOCKED(webmon_lock);; ++ ++ ++static void update_queue_node_time(queue_node* update_node, queue* full_queue) ++{ ++ struct timeval t; ++ do_gettimeofday(&t); ++ update_node->time = t; ++ ++ /* move to front of queue if not already at front of queue */ ++ if(update_node->previous != NULL) ++ { ++ queue_node* p = update_node->previous; ++ queue_node* n = update_node->next; ++ p->next = n; ++ if(n != NULL) ++ { ++ n->previous = p; ++ } ++ else ++ { ++ full_queue->last = p; ++ } ++ update_node->previous = NULL; ++ update_node->next = full_queue->first; ++ full_queue->first->previous = update_node; ++ full_queue->first = update_node; ++ } ++} ++ ++void add_queue_node(uint32_t src_ip, char* value, queue* full_queue, string_map* queue_index, char* queue_index_key, uint32_t max_queue_length ) ++{ ++ ++ queue_node *new_node = (queue_node*)kmalloc(sizeof(queue_node), GFP_ATOMIC); ++ char* dyn_value = kernel_strdup(value); ++ struct timeval t; ++ ++ ++ if(new_node == NULL || dyn_value == NULL) ++ { ++ if(dyn_value) { kfree(dyn_value); } ++ if(new_node) { kfree(new_node); }; ++ ++ return; ++ } ++ set_map_element(queue_index, queue_index_key, (void*)new_node); ++ ++ ++ do_gettimeofday(&t); ++ new_node->time = t; ++ new_node->src_ip = src_ip; ++ new_node->value = dyn_value; ++ new_node->previous = NULL; ++ ++ new_node->next = full_queue->first; ++ if(full_queue->first != NULL) ++ { ++ full_queue->first->previous = new_node; ++ } ++ full_queue->first = new_node; ++ full_queue->last = (full_queue->last == NULL) ? new_node : full_queue->last ; ++ full_queue->length = full_queue->length + 1; ++ ++ if( full_queue->length > max_queue_length ) ++ { ++ queue_node *old_node = full_queue->last; ++ full_queue->last = old_node->previous; ++ full_queue->last->next = NULL; ++ full_queue->first = old_node->previous == NULL ? NULL : full_queue->first; /*shouldn't be needed, but just in case...*/ ++ full_queue->length = full_queue->length - 1; ++ ++ sprintf(queue_index_key, STRIP"@%s", NIPQUAD(old_node->src_ip), old_node->value); ++ remove_map_element(queue_index, queue_index_key); ++ ++ kfree(old_node->value); ++ kfree(old_node); ++ } ++ ++ /* ++ queue_node* n = full_queue->first; ++ while(n != NULL) ++ { ++ printf("%ld\t%s\t%s\t%s\n", (unsigned long)n->time, n->src_ip, n->dst_ip, n->domain); ++ n = (queue_node*)n->next; ++ } ++ printf("\n\n"); ++ */ ++} ++ ++void destroy_queue(queue* q) ++{ ++ queue_node *last_node = q->last; ++ while(last_node != NULL) ++ { ++ queue_node *previous_node = last_node->previous; ++ free(last_node->value); ++ free(last_node); ++ last_node = previous_node; ++ } ++ free(q); ++} ++ ++ ++int strnicmp(const char * cs,const char * ct,size_t count) ++{ ++ register signed char __res = 0; ++ ++ while (count) ++ { ++ if ((__res = toupper( *cs ) - toupper( *ct++ ) ) != 0 || !*cs++) ++ { ++ break; ++ } ++ count--; ++ } ++ return __res; ++} ++ ++char *strnistr(const char *s, const char *find, size_t slen) ++{ ++ char c, sc; ++ size_t len; ++ ++ ++ if ((c = *find++) != '\0') ++ { ++ len = strlen(find); ++ do ++ { ++ do ++ { ++ if (slen < 1 || (sc = *s) == '\0') ++ { ++ return (NULL); ++ } ++ --slen; ++ ++s; ++ } ++ while ( toupper(sc) != toupper(c)); ++ ++ if (len > slen) ++ { ++ return (NULL); ++ } ++ } ++ while (strnicmp(s, find, len) != 0); ++ ++ s--; ++ } ++ return ((char *)s); ++} ++ ++/* NOTE: This is not quite real edit distance -- all differences are assumed to be in one contiguous block ++ * If differences are not in a contiguous block computed edit distance will be greater than real edit distance. ++ * Edit distance computed here is an upper bound on real edit distance. ++ */ ++int within_edit_distance(char *s1, char *s2, int max_edit) ++{ ++ int ret = 0; ++ if(s1 != NULL && s2 != NULL) ++ { ++ int edit1 = strlen(s1); ++ int edit2 = strlen(s2); ++ char* s1sp = s1; ++ char* s2sp = s2; ++ char* s1ep = s1 + (edit1-1); ++ char* s2ep = s2 + (edit2-1); ++ while(*s1sp != '\0' && *s2sp != '\0' && *s1sp == *s2sp) ++ { ++ s1sp++; ++ s2sp++; ++ edit1--; ++ edit2--; ++ } ++ ++ /* if either is zero we got to the end of one of the strings */ ++ while(s1ep > s1sp && s2ep > s2sp && *s1ep == *s2ep) ++ { ++ s1ep--; ++ s2ep--; ++ edit1--; ++ edit2--; ++ } ++ ret = edit1 <= max_edit && edit2 <= max_edit ? 1 : 0; ++ } ++ return ret; ++} ++ ++ ++/* ++ * line is the line to be parsed -- it is not modified in any way ++ * max_pieces indicates number of pieces to return, if negative this is determined dynamically ++ * include_remainder_at_max indicates whether the last piece, when max pieces are reached, ++ * should be what it would normally be (0) or the entire remainder of the line (1) ++ * if max_pieces < 0 this parameter is ignored ++ * ++ * ++ * returns all non-separator pieces in a line ++ * result is dynamically allocated, MUST be freed after call-- even if ++ * line is empty (you still get a valid char** pointer to to a NULL char*) ++ */ ++char** split_on_separators(char* line, char* separators, int num_separators, int max_pieces, int include_remainder_at_max, unsigned long *num_pieces) ++{ ++ char** split; ++ ++ *num_pieces = 0; ++ if(line != NULL) ++ { ++ int split_index; ++ int non_separator_found; ++ char* dup_line; ++ char* start; ++ ++ if(max_pieces < 0) ++ { ++ /* count number of separator characters in line -- this count + 1 is an upperbound on number of pieces */ ++ int separator_count = 0; ++ int line_index; ++ for(line_index = 0; line[line_index] != '\0'; line_index++) ++ { ++ int sep_index; ++ int found = 0; ++ for(sep_index =0; found == 0 && sep_index < num_separators; sep_index++) ++ { ++ found = separators[sep_index] == line[line_index] ? 1 : 0; ++ } ++ separator_count = separator_count+ found; ++ } ++ max_pieces = separator_count + 1; ++ } ++ split = (char**)malloc((1+max_pieces)*sizeof(char*)); ++ split_index = 0; ++ split[split_index] = NULL; ++ ++ ++ dup_line = strdup(line); ++ start = dup_line; ++ non_separator_found = 0; ++ while(non_separator_found == 0) ++ { ++ int matches = 0; ++ int sep_index; ++ for(sep_index =0; sep_index < num_separators; sep_index++) ++ { ++ matches = matches == 1 || separators[sep_index] == start[0] ? 1 : 0; ++ } ++ non_separator_found = matches==0 || start[0] == '\0' ? 1 : 0; ++ if(non_separator_found == 0) ++ { ++ start++; ++ } ++ } ++ ++ while(start[0] != '\0' && split_index < max_pieces) ++ { ++ /* find first separator index */ ++ int first_separator_index = 0; ++ int separator_found = 0; ++ while( separator_found == 0 ) ++ { ++ int sep_index; ++ for(sep_index =0; separator_found == 0 && sep_index < num_separators; sep_index++) ++ { ++ separator_found = separators[sep_index] == start[first_separator_index] || start[first_separator_index] == '\0' ? 1 : 0; ++ } ++ if(separator_found == 0) ++ { ++ first_separator_index++; ++ } ++ } ++ ++ /* copy next piece to split array */ ++ if(first_separator_index > 0) ++ { ++ char* next_piece = NULL; ++ if(split_index +1 < max_pieces || include_remainder_at_max <= 0) ++ { ++ next_piece = (char*)malloc((first_separator_index+1)*sizeof(char)); ++ memcpy(next_piece, start, first_separator_index); ++ next_piece[first_separator_index] = '\0'; ++ } ++ else ++ { ++ next_piece = strdup(start); ++ } ++ split[split_index] = next_piece; ++ split[split_index+1] = NULL; ++ split_index++; ++ } ++ ++ ++ /* find next non-separator index, indicating start of next piece */ ++ start = start+ first_separator_index; ++ non_separator_found = 0; ++ while(non_separator_found == 0) ++ { ++ int matches = 0; ++ int sep_index; ++ for(sep_index =0; sep_index < num_separators; sep_index++) ++ { ++ matches = matches == 1 || separators[sep_index] == start[0] ? 1 : 0; ++ } ++ non_separator_found = matches==0 || start[0] == '\0' ? 1 : 0; ++ if(non_separator_found == 0) ++ { ++ start++; ++ } ++ } ++ } ++ free(dup_line); ++ *num_pieces = split_index; ++ } ++ else ++ { ++ split = (char**)malloc((1)*sizeof(char*)); ++ split[0] = NULL; ++ } ++ return split; ++} ++ ++ ++ ++static void extract_url(const unsigned char* packet_data, int packet_length, char* domain, char* path) ++{ ++ ++ int path_start_index; ++ int path_end_index; ++ int last_header_index; ++ char last_two_buf[2]; ++ int end_found; ++ char* domain_match; ++ char* start_ptr; ++ ++ domain[0] = '\0'; ++ path[0] = '\0'; ++ ++ ++ /* get path portion of URL */ ++ start_ptr = strnistr((char*)packet_data, " ", packet_length); ++ if(start_ptr == NULL) ++ { ++ return; ++ } ++ ++ path_start_index = (int)(start_ptr - (char*)packet_data); ++ start_ptr = strnistr((char*)(packet_data+path_start_index), " ", packet_length-(path_start_index+2)); ++ if(start_ptr == NULL) ++ { ++ return; ++ } ++ ++ while( packet_data[path_start_index] == ' ') ++ { ++ path_start_index++; ++ } ++ path_end_index= (int)(strstr( (char*)(packet_data+path_start_index), " ") - (char*)packet_data); ++ if(path_end_index > 0) ++ { ++ int path_length = path_end_index-path_start_index; ++ path_length = path_length < 625 ? path_length : 624; /* prevent overflow */ ++ memcpy(path, packet_data+path_start_index, path_length); ++ path[ path_length] = '\0'; ++ } ++ else ++ { ++ return; ++ } ++ ++ /* get header length */ ++ last_header_index = 2; ++ memcpy(last_two_buf,(char*)packet_data, 2); ++ end_found = 0; ++ while(end_found == 0 && last_header_index < packet_length) ++ { ++ char next = (char)packet_data[last_header_index]; ++ if(next == '\n') ++ { ++ end_found = last_two_buf[1] == '\n' || (last_two_buf[0] == '\n' && last_two_buf[1] == '\r') ? 1 : 0; ++ } ++ if(end_found == 0) ++ { ++ last_two_buf[0] = last_two_buf[1]; ++ last_two_buf[1] = next; ++ last_header_index++; ++ } ++ } ++ ++ /* get domain portion of URL */ ++ domain_match = strnistr( (char*)packet_data, "Host:", last_header_index); ++ if(domain_match != NULL) ++ { ++ int domain_end_index; ++ domain_match = domain_match + 5; /* character after "Host:" */ ++ while(domain_match[0] == ' ' && ( (char*)domain_match - (char*)packet_data) < last_header_index) ++ { ++ domain_match = domain_match+1; ++ } ++ ++ domain_end_index = 0; ++ while( domain_match[domain_end_index] != '\n' && ++ domain_match[domain_end_index] != '\r' && ++ domain_match[domain_end_index] != ' ' && ++ domain_match[domain_end_index] != ':' && ++ ((char*)domain_match - (char*)packet_data)+domain_end_index < last_header_index ++ ) ++ { ++ domain_end_index++; ++ } ++ domain_end_index = domain_end_index < 625 ? domain_end_index : 624; /* prevent overflow */ ++ memcpy(domain, domain_match, domain_end_index); ++ domain[domain_end_index] = '\0'; ++ ++ for(domain_end_index=0; domain[domain_end_index] != '\0'; domain_end_index++) ++ { ++ domain[domain_end_index] = (char)tolower(domain[domain_end_index]); ++ } ++ } ++} ++ ++static void extract_url_https(const unsigned char* packet_data, int packet_length, char* domain) ++{ ++ //TLSv1.2 Record Layer - All calculations based on this ++ //We want to abuse the SNI (Server Name Indication) extension to harvest likely URLs ++ //Content Type = 0x16 (22) is a "Handshake", HandShake Type 0x01 (1) is a "Client Hello" ++ int x, packet_limit; ++ unsigned short cslen, ext_type, ext_len, maxextlen; ++ unsigned char conttype, hndshktype, sidlen, cmplen; ++ unsigned char* packet_ptr; ++ ++ domain[0] = '\0'; ++ packet_ptr = packet_data; ++ ++ if (packet_length < 43) ++ { ++ /*printk("Packet less than 43 bytes, exiting\n");*/ ++ return; ++ } ++ conttype = packet_data[0]; ++ hndshktype = packet_data[5]; ++ sidlen = packet_data[43]; ++ /*printk("conttype=%d, hndshktype=%d, sidlen=%d ",conttype,hndshktype,sidlen);*/ ++ if(conttype != 22) ++ { ++ /*printk("conttype not 22, exiting\n");*/ ++ return; ++ } ++ if(hndshktype != 1) ++ { ++ /*printk("hndshktype not 1, exiting\n");*/ ++ return; //We aren't in a Client Hello ++ } ++ ++ packet_ptr = packet_data + 1 + 43 + sidlen; //Skip to Cipher Suites Length ++ cslen = ntohs(*(unsigned short*)packet_ptr); //Length of Cipher Suites (2 byte) ++ packet_ptr = packet_ptr + 2 + cslen; //Skip to Compression Methods ++ cmplen = *packet_ptr; //Length of Compression Methods (1 byte) ++ packet_ptr = packet_ptr + 1 + cmplen; //Skip to Extensions Length **IMPORTANT** ++ maxextlen = ntohs(*(unsigned short*)packet_ptr); //Length of extensions (2 byte) ++ packet_ptr = packet_ptr + 2; //Skip to beginning of first extension and start looping ++ ext_type = 1; ++ /*printk("cslen=%d, cmplen=%d, maxextlen=%d, pktlen=%d,ptrpos=%d\n",cslen,cmplen,maxextlen,packet_length,packet_ptr - packet_data);*/ ++ //Limit the pointer bounds to the smaller of either the extensions length or the packet length ++ packet_limit = ((packet_ptr - packet_data) + maxextlen) < packet_length ? ((packet_ptr - packet_data) + maxextlen) : packet_length; ++ ++ //Extension Type and Extension Length are both 2 byte. SNI Extension is "0" ++ while(((packet_ptr - packet_data) < packet_limit) && (ext_type != 0)) ++ { ++ ext_type = ntohs(*(unsigned short*)packet_ptr); ++ packet_ptr = packet_ptr + 2; ++ ext_len = ntohs(*(unsigned short*)packet_ptr); ++ packet_ptr = packet_ptr + 2; ++ /*printk("ext_type=%d, ext_len=%d\n",ext_type,ext_len);*/ ++ if(ext_type == 0) ++ { ++ unsigned short snilen; ++ /*printk("FOUND SNI EXT\n");*/ ++ packet_ptr = packet_ptr + 3; //Skip to length of SNI ++ snilen = ntohs(*(unsigned short*)packet_ptr); ++ /*printk("snilen=%d\n",snilen);*/ ++ packet_ptr = packet_ptr + 2; //Skip to beginning of SNI ++ if((((packet_ptr - packet_data) + snilen) < packet_limit) && (snilen > 0)) ++ { ++ /*printk("FOUND SNI\n");*/ ++ snilen = snilen < 625 ? snilen : 624; // prevent overflow ++ memcpy(domain, packet_ptr, snilen); ++ domain[snilen] = '\0'; ++ for(x=0; domain[x] != '\0'; x++) ++ { ++ domain[x] = (char)tolower(domain[x]); ++ } ++ /*printk("sni=%s\n",domain);*/ ++ } ++ } ++ else ++ { ++ packet_ptr = packet_ptr + ext_len; ++ } ++ } ++} ++ ++#ifdef CONFIG_PROC_FS ++ ++static void *webmon_proc_start(struct seq_file *seq, loff_t *loff_pos) ++{ ++ static unsigned long counter = 0; ++ ++ /* beginning a new sequence ? */ ++ if ( *loff_pos == 0 ) ++ { ++ /* yes => return a non null value to begin the sequence */ ++ return &counter; ++ } ++ else ++ { ++ /* no => it's the end of the sequence, return end to stop reading */ ++ *loff_pos = 0; ++ return NULL; ++ } ++} ++ ++static void *webmon_proc_next(struct seq_file *seq, void *v, loff_t *pos) ++{ ++ return NULL; ++} ++ ++ ++static void webmon_proc_stop(struct seq_file *seq, void *v) ++{ ++ //don't need to do anything ++} ++ ++ ++static int webmon_proc_domain_show(struct seq_file *s, void *v) ++{ ++ queue_node* next_node; ++ spin_lock_bh(&webmon_lock); ++ ++ next_node = recent_domains->last; ++ while(next_node != NULL) ++ { ++ seq_printf(s, "%ld\t"STRIP"\t%s\n", (unsigned long)(next_node->time).tv_sec, NIPQUAD(next_node->src_ip), next_node->value); ++ next_node = (queue_node*)next_node->previous; ++ } ++ spin_unlock_bh(&webmon_lock); ++ ++ return 0; ++} ++ ++static int webmon_proc_search_show(struct seq_file *s, void *v) ++{ ++ queue_node* next_node; ++ spin_lock_bh(&webmon_lock); ++ ++ next_node = recent_searches->last; ++ while(next_node != NULL) ++ { ++ seq_printf(s, "%ld\t"STRIP"\t%s\n", (unsigned long)(next_node->time).tv_sec, NIPQUAD(next_node->src_ip), next_node->value); ++ next_node = (queue_node*)next_node->previous; ++ } ++ spin_unlock_bh(&webmon_lock); ++ ++ return 0; ++} ++ ++ ++static struct seq_operations webmon_proc_domain_sops = { ++ .start = webmon_proc_start, ++ .next = webmon_proc_next, ++ .stop = webmon_proc_stop, ++ .show = webmon_proc_domain_show ++}; ++ ++static struct seq_operations webmon_proc_search_sops = { ++ .start = webmon_proc_start, ++ .next = webmon_proc_next, ++ .stop = webmon_proc_stop, ++ .show = webmon_proc_search_show ++}; ++ ++ ++static int webmon_proc_domain_open(struct inode *inode, struct file* file) ++{ ++ return seq_open(file, &webmon_proc_domain_sops); ++} ++static int webmon_proc_search_open(struct inode *inode, struct file* file) ++{ ++ return seq_open(file, &webmon_proc_search_sops); ++} ++ ++ ++ ++static struct file_operations webmon_proc_domain_fops = { ++ .owner = THIS_MODULE, ++ .open = webmon_proc_domain_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = seq_release ++}; ++static struct file_operations webmon_proc_search_fops = { ++ .owner = THIS_MODULE, ++ .open = webmon_proc_search_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = seq_release ++}; ++ ++ ++#endif ++ ++ ++ ++ ++ ++ ++static int ipt_webmon_set_ctl(struct sock *sk, int cmd, void *user, u_int32_t len) ++{ ++ ++ char* buffer = kmalloc(len, GFP_ATOMIC); ++ if(buffer == NULL) /* check for malloc failure */ ++ { ++ return 0; ++ } ++ spin_lock_bh(&webmon_lock); ++ copy_from_user(buffer, user, len); ++ ++ if(len > 1 + sizeof(uint32_t)) ++ { ++ unsigned char type = buffer[0]; ++ uint32_t max_queue_length = *((uint32_t*)(buffer+1)); ++ char* data = buffer+1+sizeof(uint32_t); ++ char newline_terminator[] = { '\n', '\r' }; ++ char whitespace_chars[] = { '\t', ' ' }; ++ ++ if(type == WEBMON_DOMAIN || type == WEBMON_SEARCH ) ++ { ++ unsigned long num_destroyed; ++ ++ ++ /* destroy and re-initialize queue and map */ ++ if(type == WEBMON_DOMAIN ) ++ { ++ destroy_map(domain_map, DESTROY_MODE_IGNORE_VALUES, &num_destroyed); ++ destroy_queue(recent_domains); ++ recent_domains = (queue*)malloc(sizeof(queue)); ++ recent_domains->first = NULL; ++ recent_domains->last = NULL; ++ recent_domains->length = 0; ++ domain_map = initialize_map(0); ++ ++ max_domain_queue_length = max_queue_length; ++ } ++ else if(type == WEBMON_SEARCH) ++ { ++ destroy_map(search_map, DESTROY_MODE_IGNORE_VALUES, &num_destroyed); ++ destroy_queue(recent_searches); ++ recent_searches = (queue*)malloc(sizeof(queue)); ++ recent_searches->first = NULL; ++ recent_searches->last = NULL; ++ recent_searches->length = 0; ++ search_map = initialize_map(0); ++ ++ max_search_queue_length = max_queue_length; ++ } ++ ++ if(data[0] != '\0') ++ { ++ unsigned long num_lines; ++ unsigned long line_index; ++ char** lines = split_on_separators(data, newline_terminator, 2, -1, 0, &num_lines); ++ for(line_index=0; line_index < num_lines; line_index++) ++ { ++ char* line = lines[line_index]; ++ unsigned long num_pieces; ++ char** split = split_on_separators(line, whitespace_chars, 2, -1, 0, &num_pieces); ++ ++ //check that there are 3 pieces (time, src_ip, value) ++ int length; ++ for(length=0; split[length] != NULL ; length++){} ++ if(length == 3) ++ { ++ time_t time; ++ int parsed_ip[4]; ++ int valid_ip = sscanf(split[1], "%d.%d.%d.%d", parsed_ip, parsed_ip+1, parsed_ip+2, parsed_ip+3); ++ if(valid_ip == 4) ++ { ++ valid_ip = parsed_ip[0] <= 255 && parsed_ip[1] <= 255 && parsed_ip[2] <= 255 && parsed_ip[3] <= 255 ? valid_ip : 0; ++ } ++ if(sscanf(split[0], "%ld", &time) > 0 && valid_ip == 4) ++ { ++ char* value = split[2]; ++ char value_key[700]; ++ uint32_t ip = (parsed_ip[0]<<24) + (parsed_ip[1]<<16) + (parsed_ip[2]<<8) + (parsed_ip[3]) ; ++ ip = htonl(ip); ++ sprintf(value_key, STRIP"@%s", NIPQUAD(ip), value); ++ if(type == WEBMON_DOMAIN) ++ { ++ add_queue_node(ip, value, recent_domains, domain_map, value_key, max_domain_queue_length ); ++ (recent_domains->first->time).tv_sec = time; ++ } ++ else if(type == WEBMON_SEARCH) ++ { ++ add_queue_node(ip, value, recent_searches, search_map, value_key, max_search_queue_length ); ++ (recent_searches->first->time).tv_sec = time; ++ } ++ } ++ } ++ ++ for(length=0; split[length] != NULL ; length++) ++ { ++ free(split[length]); ++ } ++ free(split); ++ free(line); ++ } ++ free(lines); ++ } ++ } ++ } ++ kfree(buffer); ++ spin_unlock_bh(&webmon_lock); ++ ++ ++ return 1; ++} ++static struct nf_sockopt_ops ipt_webmon_sockopts = ++{ ++ .pf = PF_INET, ++ .set_optmin = WEBMON_SET, ++ .set_optmax = WEBMON_SET+1, ++ .set = ipt_webmon_set_ctl, ++}; ++ ++ ++ ++ ++static bool match(const struct sk_buff *skb, struct xt_action_param *par) ++{ ++ ++ const struct ipt_webmon_info *info = (const struct ipt_webmon_info*)(par->matchinfo); ++ ++ ++ struct iphdr* iph; ++ ++ /* linearize skb if necessary */ ++ struct sk_buff *linear_skb; ++ int skb_copied; ++ if(skb_is_nonlinear(skb)) ++ { ++ linear_skb = skb_copy(skb, GFP_ATOMIC); ++ skb_copied = 1; ++ } ++ else ++ { ++ linear_skb = (struct sk_buff*)skb; ++ skb_copied = 0; ++ } ++ ++ ++ ++ /* ignore packets that are not TCP */ ++ iph = (struct iphdr*)(skb_network_header(skb)); ++ if(iph->protocol == IPPROTO_TCP) ++ { ++ /* get payload */ ++ struct tcphdr* tcp_hdr = (struct tcphdr*)( ((unsigned char*)iph) + (iph->ihl*4) ); ++ unsigned short payload_offset = (tcp_hdr->doff*4) + (iph->ihl*4); ++ unsigned char* payload = ((unsigned char*)iph) + payload_offset; ++ unsigned short payload_length = ntohs(iph->tot_len) - payload_offset; ++ ++ ++ ++ /* if payload length <= 10 bytes don't bother doing a check, otherwise check for match */ ++ if(payload_length > 10) ++ { ++ /* are we dealing with a web page request */ ++ if(strnicmp((char*)payload, "GET ", 4) == 0 || strnicmp( (char*)payload, "POST ", 5) == 0 || strnicmp((char*)payload, "HEAD ", 5) == 0) ++ { ++ char domain[650]; ++ char path[650]; ++ char domain_key[700]; ++ unsigned char save = info->exclude_type == WEBMON_EXCLUDE ? 1 : 0; ++ uint32_t ip_index; ++ ++ ++ for(ip_index = 0; ip_index < info->num_exclude_ips; ip_index++) ++ { ++ if( (info->exclude_ips)[ip_index] == iph->saddr ) ++ { ++ save = info->exclude_type == WEBMON_EXCLUDE ? 0 : 1; ++ } ++ } ++ for(ip_index=0; ip_index < info->num_exclude_ranges; ip_index++) ++ { ++ struct ipt_webmon_ip_range r = (info->exclude_ranges)[ip_index]; ++ if( (unsigned long)ntohl( r.start) <= (unsigned long)ntohl(iph->saddr) && (unsigned long)ntohl(r.end) >= (unsigned long)ntohl(iph->saddr) ) ++ { ++ save = info->exclude_type == WEBMON_EXCLUDE ? 0 : 1; ++ } ++ } ++ ++ ++ if(save) ++ { ++ extract_url(payload, payload_length, domain, path); ++ ++ ++ sprintf(domain_key, STRIP"@%s", NIPQUAD(iph->saddr), domain); ++ ++ if(strlen(domain) > 0) ++ { ++ char *search_part = NULL; ++ spin_lock_bh(&webmon_lock); ++ ++ ++ ++ if(get_string_map_element(domain_map, domain_key)) ++ { ++ //update time ++ update_queue_node_time( (queue_node*)get_map_element(domain_map, domain_key), recent_domains ); ++ } ++ else ++ { ++ //add ++ add_queue_node(iph->saddr, domain, recent_domains, domain_map, domain_key, max_domain_queue_length ); ++ } ++ ++ ++ /* printk("domain,path=\"%s\", \"%s\"\n", domain, path); */ ++ ++ if(strnistr(domain, "google.", 625) != NULL) ++ { ++ search_part = strstr(path, "&q="); ++ search_part = search_part == NULL ? strstr(path, "#q=") : search_part; ++ search_part = search_part == NULL ? strstr(path, "?q=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+3; ++ } ++ else if(strstr(domain, "bing.") != NULL) ++ { ++ search_part = strstr(path, "?q="); ++ search_part = search_part == NULL ? strstr(path, "&q=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+3; ++ } ++ else if(strstr(domain, "yahoo.") != NULL) ++ { ++ search_part = strstr(path, "?p="); ++ search_part = search_part == NULL ? strstr(path, "&p=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+3; ++ } ++ else if(strstr(domain, "lycos.") != NULL) ++ { ++ search_part = strstr(path, "&query="); ++ search_part = search_part == NULL ? strstr(path, "?query=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+7; ++ } ++ else if(strstr(domain, "altavista.") != NULL) ++ { ++ search_part = strstr(path, "&q="); ++ search_part = search_part == NULL ? strstr(path, "?q=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+3; ++ } ++ else if(strstr(domain, "duckduckgo.") != NULL) ++ { ++ search_part = strstr(path, "?q="); ++ search_part = search_part == NULL ? strstr(path, "&q=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+3; ++ } ++ else if(strstr(domain, "baidu.") != NULL) ++ { ++ search_part = strstr(path, "?wd="); ++ search_part = search_part == NULL ? strstr(path, "&wd=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+4; ++ } ++ else if(strstr(domain, "search.") != NULL) ++ { ++ search_part = strstr(path, "?q="); ++ search_part = search_part == NULL ? strstr(path, "&q=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+3; ++ } ++ else if(strstr(domain, "aol.") != NULL) ++ { ++ search_part = strstr(path, "&q="); ++ search_part = search_part == NULL ? strstr(path, "?q=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+3; ++ } ++ else if(strstr(domain, "ask.") != NULL) ++ { ++ search_part = strstr(path, "?q="); ++ search_part = search_part == NULL ? strstr(path, "&q=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+3; ++ } ++ else if(strstr(domain, "yandex.") != NULL) ++ { ++ search_part = strstr(path, "?text="); ++ search_part = search_part == NULL ? strstr(path, "&text=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+6; ++ } ++ else if(strstr(domain, "naver.") != NULL) ++ { ++ search_part = strstr(path, "&query="); ++ search_part = search_part == NULL ? strstr(path, "?query=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+7; ++ } ++ else if(strstr(domain, "daum.") != NULL) ++ { ++ search_part = strstr(path, "&q="); ++ search_part = search_part == NULL ? strstr(path, "?q=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+3; ++ } ++ else if(strstr(domain, "cuil.") != NULL) ++ { ++ search_part = strstr(path, "?q="); ++ search_part = search_part == NULL ? strstr(path, "&q=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+3; ++ } ++ else if(strstr(domain, "kosmix.") != NULL) ++ { ++ search_part = strstr(path, "/topic/"); ++ search_part = search_part == NULL ? search_part : search_part+7; ++ } ++ else if(strstr(domain, "yebol.") != NULL) ++ { ++ search_part = strstr(path, "?key="); ++ search_part = search_part == NULL ? strstr(path, "&key=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+5; ++ } ++ else if(strstr(domain, "sogou.") != NULL) ++ { ++ search_part = strstr(path, "&query="); ++ search_part = search_part == NULL ? strstr(path, "?query=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+7; ++ } ++ else if(strstr(domain, "youdao.") != NULL) ++ { ++ search_part = strstr(path, "?q="); ++ search_part = search_part == NULL ? strstr(path, "&q=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+3; ++ } ++ else if(strstr(domain, "metacrawler.") != NULL) ++ { ++ search_part = strstr(path, "/ws/results/Web/"); ++ search_part = search_part == NULL ? search_part : search_part+16; ++ } ++ else if(strstr(domain, "webcrawler.") != NULL) ++ { ++ search_part = strstr(path, "/ws/results/Web/"); ++ search_part = search_part == NULL ? search_part : search_part+16; ++ } ++ else if(strstr(domain, "thepiratebay.") != NULL) ++ { ++ search_part = strstr(path, "/search/"); ++ search_part = search_part == NULL ? search_part : search_part+8; ++ } ++ ++ ++ if(search_part != NULL) ++ { ++ int spi, si; ++ char search_key[700]; ++ char search[650]; ++ queue_node *recent_node = recent_searches->first; ++ ++ /*unescape, replacing whitespace with + */ ++ si = 0; ++ for(spi=0; search_part[spi] != '\0' && search_part[spi] != '&' && search_part[spi] != '/'; spi++) ++ { ++ int parsed_hex = 0; ++ if( search_part[spi] == '%') ++ { ++ if(search_part[spi+1] != '\0' && search_part[spi+1] != '&' && search_part[spi+1] != '/') ++ { ++ if(search_part[spi+2] != '\0' && search_part[spi+2] != '&' && search_part[spi+2] != '/') ++ { ++ char enc[3]; ++ int hex; ++ enc[0] = search_part[spi+1]; ++ enc[1] = search_part[spi+2]; ++ enc[2] = '\0'; ++ if(sscanf(enc, "%x", &hex) > 0) ++ { ++ parsed_hex = 1; ++ search[si] = hex == ' ' || hex == '\t' || hex == '\r' || hex == '\n' ? '+' : (char)hex; ++ spi = spi+2; ++ } ++ } ++ } ++ } ++ if(parsed_hex == 0) ++ { ++ search[si] = search_part[spi]; ++ } ++ si++; ++ } ++ search[si] = '\0'; ++ ++ ++ ++ sprintf(search_key, STRIP"@%s", NIPQUAD(iph->saddr), search); ++ ++ ++ /* Often times search engines will initiate a search as you type it in, but these intermediate queries aren't the real search query ++ * So, if the most recent query is a substring of the current one, discard it in favor of this one ++ */ ++ if(recent_node != NULL) ++ { ++ if(recent_node->src_ip == iph->saddr) ++ { ++ struct timeval t; ++ do_gettimeofday(&t); ++ if( (recent_node->time).tv_sec + 1 >= t.tv_sec || ((recent_node->time).tv_sec + 5 >= t.tv_sec && within_edit_distance(search, recent_node->value, 2))) ++ { ++ char recent_key[700]; ++ ++ sprintf(recent_key, STRIP"@%s", NIPQUAD(recent_node->src_ip), recent_node->value); ++ remove_map_element(search_map, recent_key); ++ ++ recent_searches->first = recent_node->next; ++ recent_searches->last = recent_searches->first == NULL ? NULL : recent_searches->last; ++ if(recent_searches->first != NULL) ++ { ++ recent_searches->first->previous = NULL; ++ } ++ recent_searches->length = recent_searches->length - 1 ; ++ free(recent_node->value); ++ free(recent_node); ++ } ++ } ++ } ++ ++ ++ ++ if(get_string_map_element(search_map, search_key)) ++ { ++ //update time ++ update_queue_node_time( (queue_node*)get_map_element(search_map, search_key), recent_searches ); ++ } ++ else ++ { ++ //add ++ add_queue_node(iph->saddr, search, recent_searches, search_map, search_key, max_search_queue_length ); ++ } ++ } ++ spin_unlock_bh(&webmon_lock); ++ } ++ } ++ } ++ else if ((unsigned short)ntohs(tcp_hdr->dest) == 443) // broad assumption that traffic on 443 is HTTPS. make effort to return fast as soon as we know we are wrong to not slow down processing ++ { ++ char domain[650]; ++ char domain_key[700]; ++ unsigned char save = info->exclude_type == WEBMON_EXCLUDE ? 1 : 0; ++ uint32_t ip_index; ++ ++ for(ip_index = 0; ip_index < info->num_exclude_ips; ip_index++) ++ { ++ if( (info->exclude_ips)[ip_index] == iph->saddr ) ++ { ++ save = info->exclude_type == WEBMON_EXCLUDE ? 0 : 1; ++ } ++ } ++ for(ip_index=0; ip_index < info->num_exclude_ranges; ip_index++) ++ { ++ struct ipt_webmon_ip_range r = (info->exclude_ranges)[ip_index]; ++ if( (unsigned long)ntohl( r.start) <= (unsigned long)ntohl(iph->saddr) && (unsigned long)ntohl(r.end) >= (unsigned long)ntohl(iph->saddr) ) ++ { ++ save = info->exclude_type == WEBMON_EXCLUDE ? 0 : 1; ++ } ++ } ++ ++ ++ if(save) ++ { ++ extract_url_https(payload, payload_length, domain); ++ ++ sprintf(domain_key, STRIP"@%s", NIPQUAD(iph->saddr), domain); ++ ++ if(strlen(domain) > 0) ++ { ++ spin_lock_bh(&webmon_lock); ++ ++ if(get_string_map_element(domain_map, domain_key)) ++ { ++ //update time ++ update_queue_node_time( (queue_node*)get_map_element(domain_map, domain_key), recent_domains ); ++ } ++ else ++ { ++ //add ++ add_queue_node(iph->saddr, domain, recent_domains, domain_map, domain_key, max_domain_queue_length ); ++ } ++ ++ spin_unlock_bh(&webmon_lock); ++ } ++ } ++ } ++ } ++ } ++ ++ /* free skb if we made a copy to linearize it */ ++ if(skb_copied == 1) ++ { ++ kfree_skb(linear_skb); ++ } ++ ++ ++ /* printk("returning %d from webmon\n\n\n", test); */ ++ return 0; ++} ++ ++ ++ ++static int checkentry(const struct xt_mtchk_param *par) ++{ ++ ++ struct ipt_webmon_info *info = (struct ipt_webmon_info*)(par->matchinfo); ++ ++ ++ spin_lock_bh(&webmon_lock); ++ if(info->ref_count == NULL) /* first instance, we're inserting rule */ ++ { ++ info->ref_count = (uint32_t*)kmalloc(sizeof(uint32_t), GFP_ATOMIC); ++ if(info->ref_count == NULL) /* deal with kmalloc failure */ ++ { ++ printk("ipt_webmon: kmalloc failure in checkentry!\n"); ++ return 0; ++ } ++ *(info->ref_count) = 1; ++ ++ ++ max_search_queue_length = info->max_searches; ++ max_domain_queue_length = info->max_domains; ++ ++ ++ } ++ else ++ { ++ *(info->ref_count) = *(info->ref_count) + 1; ++ } ++ spin_unlock_bh(&webmon_lock); ++ ++ return 0; ++} ++ ++static void destroy( const struct xt_mtdtor_param *par ) ++{ ++ struct ipt_webmon_info *info = (struct ipt_webmon_info*)(par->matchinfo); ++ ++ spin_lock_bh(&webmon_lock); ++ *(info->ref_count) = *(info->ref_count) - 1; ++ if(*(info->ref_count) == 0) ++ { ++ kfree(info->ref_count); ++ } ++ spin_unlock_bh(&webmon_lock); ++ ++} ++ ++static struct xt_match webmon_match __read_mostly = ++{ ++ ++ .name = "webmon", ++ .match = match, ++ .family = AF_INET, ++ .matchsize = sizeof(struct ipt_webmon_info), ++ .checkentry = checkentry, ++ .destroy = destroy, ++ .me = THIS_MODULE, ++}; ++ ++static int __init init(void) ++{ ++ ++ #ifdef CONFIG_PROC_FS ++ struct proc_dir_entry *proc_webmon_recent_domains; ++ struct proc_dir_entry *proc_webmon_recent_searches; ++ #endif ++ ++ spin_lock_bh(&webmon_lock); ++ ++ recent_domains = (queue*)malloc(sizeof(queue)); ++ recent_domains->first = NULL; ++ recent_domains->last = NULL; ++ recent_domains->length = 0; ++ domain_map = initialize_string_map(0); ++ ++ recent_searches = (queue*)malloc(sizeof(queue)); ++ recent_searches->first = NULL; ++ recent_searches->last = NULL; ++ recent_searches->length = 0; ++ search_map = initialize_string_map(0); ++ ++ ++ ++ #ifdef CONFIG_PROC_FS ++ proc_create("webmon_recent_domains", 0, NULL, &webmon_proc_domain_fops); ++ proc_create("webmon_recent_searches", 0, NULL, &webmon_proc_search_fops); ++ #endif ++ ++ if (nf_register_sockopt(&ipt_webmon_sockopts) < 0) ++ { ++ printk("ipt_webmon: Can't register sockopts. Aborting\n"); ++ spin_unlock_bh(&webmon_lock); ++ return -1; ++ } ++ spin_unlock_bh(&webmon_lock); ++ ++ return xt_register_match(&webmon_match); ++} ++ ++static void __exit fini(void) ++{ ++ ++ unsigned long num_destroyed; ++ ++ spin_lock_bh(&webmon_lock); ++ ++ ++ #ifdef CONFIG_PROC_FS ++ remove_proc_entry("webmon_recent_domains", NULL); ++ remove_proc_entry("webmon_recent_searches", NULL); ++ #endif ++ nf_unregister_sockopt(&ipt_webmon_sockopts); ++ xt_unregister_match(&webmon_match); ++ destroy_map(domain_map, DESTROY_MODE_IGNORE_VALUES, &num_destroyed); ++ destroy_map(search_map, DESTROY_MODE_IGNORE_VALUES, &num_destroyed); ++ destroy_queue(recent_domains); ++ destroy_queue(recent_searches); ++ ++ spin_unlock_bh(&webmon_lock); ++ ++ ++} ++ ++module_init(init); ++module_exit(fini); + +--- /dev/null ++++ b/net/ipv4/netfilter/ipt_weburl.c +@@ -0,0 +1,537 @@ ++/* weburl -- A netfilter module to match URLs in HTTP(S) requests ++ * This module can match using string match or regular expressions ++ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) ++ * ++ * ++ * Copyright © 2008-2010 by Eric Bishop ++ * ++ * This file is free software: you may copy, redistribute and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation, either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This file is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "weburl_deps/regexp.c" ++#include "weburl_deps/tree_map.h" ++ ++ ++#include ++ ++ ++#include ++ ++ ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Eric Bishop"); ++MODULE_DESCRIPTION("Match URL in HTTP(S) requests, designed for use with Gargoyle web interface (www.gargoyle-router.com)"); ++ ++string_map* compiled_map = NULL; ++ ++int strnicmp(const char * cs,const char * ct,size_t count) ++{ ++ register signed char __res = 0; ++ ++ while (count) ++ { ++ if ((__res = toupper( *cs ) - toupper( *ct++ ) ) != 0 || !*cs++) ++ { ++ break; ++ } ++ count--; ++ } ++ return __res; ++} ++ ++char *strnistr(const char *s, const char *find, size_t slen) ++{ ++ char c, sc; ++ size_t len; ++ ++ ++ if ((c = *find++) != '\0') ++ { ++ len = strlen(find); ++ do ++ { ++ do ++ { ++ if (slen < 1 || (sc = *s) == '\0') ++ { ++ return (NULL); ++ } ++ --slen; ++ ++s; ++ } ++ while ( toupper(sc) != toupper(c)); ++ ++ if (len > slen) ++ { ++ return (NULL); ++ } ++ } ++ while (strnicmp(s, find, len) != 0); ++ ++ s--; ++ } ++ return ((char *)s); ++} ++ ++ ++int do_match_test(unsigned char match_type, const char* reference, char* query) ++{ ++ int matches = 0; ++ struct regexp* r; ++ switch(match_type) ++ { ++ case WEBURL_CONTAINS_TYPE: ++ matches = (strstr(query, reference) != NULL); ++ break; ++ case WEBURL_REGEX_TYPE: ++ ++ if(compiled_map == NULL) ++ { ++ compiled_map = initialize_map(0); ++ if(compiled_map == NULL) /* test for malloc failure */ ++ { ++ return 0; ++ } ++ } ++ r = (struct regexp*)get_map_element(compiled_map, reference); ++ if(r == NULL) ++ { ++ int rlen = strlen(reference); ++ r= regcomp((char*)reference, &rlen); ++ if(r == NULL) /* test for malloc failure */ ++ { ++ return 0; ++ } ++ set_map_element(compiled_map, reference, (void*)r); ++ } ++ matches = regexec(r, query); ++ break; ++ case WEBURL_EXACT_TYPE: ++ matches = (strstr(query, reference) != NULL) && strlen(query) == strlen(reference); ++ break; ++ } ++ return matches; ++} ++ ++int http_match(const struct ipt_weburl_info* info, const unsigned char* packet_data, int packet_length) ++{ ++ int test = 0; ++ ++ /* printk("found a http web page request\n"); */ ++ char path[625] = ""; ++ char host[625] = ""; ++ int path_start_index; ++ int path_end_index; ++ int last_header_index; ++ char last_two_buf[2]; ++ int end_found; ++ char* host_match; ++ char* test_prefixes[6]; ++ int prefix_index; ++ ++ /* get path portion of URL */ ++ path_start_index = (int)(strstr((char*)packet_data, " ") - (char*)packet_data); ++ while( packet_data[path_start_index] == ' ') ++ { ++ path_start_index++; ++ } ++ path_end_index= (int)(strstr( (char*)(packet_data+path_start_index), " ") - (char*)packet_data); ++ if(path_end_index > 0) ++ { ++ int path_length = path_end_index-path_start_index; ++ path_length = path_length < 625 ? path_length : 624; /* prevent overflow */ ++ memcpy(path, packet_data+path_start_index, path_length); ++ path[ path_length] = '\0'; ++ } ++ ++ /* get header length */ ++ last_header_index = 2; ++ memcpy(last_two_buf,(char*)packet_data, 2); ++ end_found = 0; ++ while(end_found == 0 && last_header_index < packet_length) ++ { ++ char next = (char)packet_data[last_header_index]; ++ if(next == '\n') ++ { ++ end_found = last_two_buf[1] == '\n' || (last_two_buf[0] == '\n' && last_two_buf[1] == '\r') ? 1 : 0; ++ } ++ if(end_found == 0) ++ { ++ last_two_buf[0] = last_two_buf[1]; ++ last_two_buf[1] = next; ++ last_header_index++; ++ } ++ } ++ ++ /* get host portion of URL */ ++ host_match = strnistr( (char*)packet_data, "Host:", last_header_index); ++ if(host_match != NULL) ++ { ++ int host_end_index; ++ host_match = host_match + 5; /* character after "Host:" */ ++ while(host_match[0] == ' ') ++ { ++ host_match = host_match+1; ++ } ++ ++ host_end_index = 0; ++ while( host_match[host_end_index] != '\n' && ++ host_match[host_end_index] != '\r' && ++ host_match[host_end_index] != ' ' && ++ host_match[host_end_index] != ':' && ++ ((char*)host_match - (char*)packet_data)+host_end_index < last_header_index ++ ) ++ { ++ host_end_index++; ++ } ++ memcpy(host, host_match, host_end_index); ++ host_end_index = host_end_index < 625 ? host_end_index : 624; /* prevent overflow */ ++ host[host_end_index] = '\0'; ++ ++ ++ } ++ ++ /* printk("host = \"%s\", path =\"%s\"\n", host, path); */ ++ ++ ++ switch(info->match_part) ++ { ++ case WEBURL_DOMAIN_PART: ++ test = do_match_test(info->match_type, info->test_str, host); ++ if(!test && strstr(host, "www.") == host) ++ { ++ test = do_match_test(info->match_type, info->test_str, ((char*)host+4) ); ++ } ++ break; ++ case WEBURL_PATH_PART: ++ test = do_match_test(info->match_type, info->test_str, path); ++ if( !test && path[0] == '/' ) ++ { ++ test = do_match_test(info->match_type, info->test_str, ((char*)path+1) ); ++ } ++ break; ++ case WEBURL_ALL_PART: ++ ++ test_prefixes[0] = "http://"; ++ test_prefixes[1] = ""; ++ test_prefixes[2] = NULL; ++ ++ ++ for(prefix_index=0; test_prefixes[prefix_index] != NULL && test == 0; prefix_index++) ++ { ++ char test_url[1250]; ++ test_url[0] = '\0'; ++ strcat(test_url, test_prefixes[prefix_index]); ++ strcat(test_url, host); ++ if(strcmp(path, "/") != 0) ++ { ++ strcat(test_url, path); ++ } ++ test = do_match_test(info->match_type, info->test_str, test_url); ++ if(!test && strcmp(path, "/") == 0) ++ { ++ strcat(test_url, path); ++ test = do_match_test(info->match_type, info->test_str, test_url); ++ } ++ ++ /* printk("test_url = \"%s\", test=%d\n", test_url, test); */ ++ } ++ if(!test && strstr(host, "www.") == host) ++ { ++ char* www_host = ((char*)host+4); ++ for(prefix_index=0; test_prefixes[prefix_index] != NULL && test == 0; prefix_index++) ++ { ++ char test_url[1250]; ++ test_url[0] = '\0'; ++ strcat(test_url, test_prefixes[prefix_index]); ++ strcat(test_url, www_host); ++ if(strcmp(path, "/") != 0) ++ { ++ strcat(test_url, path); ++ } ++ test = do_match_test(info->match_type, info->test_str, test_url); ++ if(!test && strcmp(path, "/") == 0) ++ { ++ strcat(test_url, path); ++ test = do_match_test(info->match_type, info->test_str, test_url); ++ } ++ ++ /* printk("test_url = \"%s\", test=%d\n", test_url, test); */ ++ } ++ } ++ break; ++ ++ } ++ ++ ++ /* ++ * If invert flag is set, return true if it didn't match ++ */ ++ test = info->invert ? !test : test; ++ ++ return test; ++} ++ ++int https_match(const struct ipt_weburl_info* info, const unsigned char* packet_data, int packet_length) ++{ ++ int test = 0; ++ ++ /* printk("found a https web page request\n"); */ ++ char host[625] = ""; ++ char* test_prefixes[6]; ++ int prefix_index, x, packet_limit; ++ unsigned short cslen, ext_type, ext_len, maxextlen; ++ unsigned char conttype, hndshktype, sidlen, cmplen; ++ unsigned char* packet_ptr; ++ ++ host[0] = '\0'; ++ packet_ptr = packet_data; ++ ++ if (packet_length < 43) ++ { ++ /*printk("Packet less than 43 bytes, exiting\n");*/ ++ return test; ++ } ++ conttype = packet_data[0]; ++ hndshktype = packet_data[5]; ++ sidlen = packet_data[43]; ++ /*printk("conttype=%d, hndshktype=%d, sidlen=%d ",conttype,hndshktype,sidlen);*/ ++ if(conttype != 22) ++ { ++ /*printk("conttype not 22, exiting\n");*/ ++ return test; ++ } ++ if(hndshktype != 1) ++ { ++ /*printk("hndshktype not 1, exiting\n");*/ ++ return test; //We aren't in a Client Hello ++ } ++ ++ packet_ptr = packet_data + 1 + 43 + sidlen; //Skip to Cipher Suites Length ++ cslen = ntohs(*(unsigned short*)packet_ptr); //Length of Cipher Suites (2 byte) ++ packet_ptr = packet_ptr + 2 + cslen; //Skip to Compression Methods ++ cmplen = *packet_ptr; //Length of Compression Methods (1 byte) ++ packet_ptr = packet_ptr + 1 + cmplen; //Skip to Extensions Length **IMPORTANT** ++ maxextlen = ntohs(*(unsigned short*)packet_ptr); //Length of extensions (2 byte) ++ packet_ptr = packet_ptr + 2; //Skip to beginning of first extension and start looping ++ ext_type = 1; ++ /*printk("cslen=%d, cmplen=%d, maxextlen=%d, pktlen=%d,ptrpos=%d\n",cslen,cmplen,maxextlen,packet_length,packet_ptr - packet_data);*/ ++ //Limit the pointer bounds to the smaller of either the extensions length or the packet length ++ packet_limit = ((packet_ptr - packet_data) + maxextlen) < packet_length ? ((packet_ptr - packet_data) + maxextlen) : packet_length; ++ ++ //Extension Type and Extension Length are both 2 byte. SNI Extension is "0" ++ while(((packet_ptr - packet_data) < packet_limit) && (ext_type != 0)) ++ { ++ ext_type = ntohs(*(unsigned short*)packet_ptr); ++ packet_ptr = packet_ptr + 2; ++ ext_len = ntohs(*(unsigned short*)packet_ptr); ++ packet_ptr = packet_ptr + 2; ++ /*printk("ext_type=%d, ext_len=%d\n",ext_type,ext_len);*/ ++ if(ext_type == 0) ++ { ++ unsigned short snilen; ++ /*printk("FOUND SNI EXT\n");*/ ++ packet_ptr = packet_ptr + 3; //Skip to length of SNI ++ snilen = ntohs(*(unsigned short*)packet_ptr); ++ /*printk("snilen=%d\n",snilen);*/ ++ packet_ptr = packet_ptr + 2; //Skip to beginning of SNI ++ if((((packet_ptr - packet_data) + snilen) < packet_limit) && (snilen > 0)) ++ { ++ /*printk("FOUND SNI\n");*/ ++ snilen = snilen < 625 ? snilen : 624; // prevent overflow ++ memcpy(host, packet_ptr, snilen); ++ host[snilen] = '\0'; ++ for(x=0; host[x] != '\0'; x++) ++ { ++ host[x] = (char)tolower(host[x]); ++ } ++ /*printk("sni=%s\n",host);*/ ++ } ++ } ++ else ++ { ++ packet_ptr = packet_ptr + ext_len; ++ } ++ } ++ ++ /* printk("host = \"%s\"\n", host); */ ++ ++ switch(info->match_part) ++ { ++ case WEBURL_DOMAIN_PART: ++ test = do_match_test(info->match_type, info->test_str, host); ++ if(!test && strstr(host, "www.") == host) ++ { ++ test = do_match_test(info->match_type, info->test_str, ((char*)host+4) ); ++ } ++ break; ++ case WEBURL_PATH_PART: ++ test = 0; //we will never have a Path for HTTPS ++ break; ++ case WEBURL_ALL_PART: ++ test_prefixes[0] = "https://"; ++ test_prefixes[1] = ""; ++ test_prefixes[2] = NULL; ++ ++ for(prefix_index=0; test_prefixes[prefix_index] != NULL && test == 0; prefix_index++) ++ { ++ char test_url[1250]; ++ test_url[0] = '\0'; ++ strcat(test_url, test_prefixes[prefix_index]); ++ strcat(test_url, host); ++ ++ test = do_match_test(info->match_type, info->test_str, test_url); ++ ++ /* printk("test_url = \"%s\", test=%d\n", test_url, test); */ ++ } ++ if(!test && strstr(host, "www.") == host) ++ { ++ char* www_host = ((char*)host+4); ++ for(prefix_index=0; test_prefixes[prefix_index] != NULL && test == 0; prefix_index++) ++ { ++ char test_url[1250]; ++ test_url[0] = '\0'; ++ strcat(test_url, test_prefixes[prefix_index]); ++ strcat(test_url, www_host); ++ ++ test = do_match_test(info->match_type, info->test_str, test_url); ++ ++ /* printk("test_url = \"%s\", test=%d\n", test_url, test); */ ++ } ++ } ++ break; ++ } ++ ++ /* ++ * If invert flag is set, return true if it didn't match ++ */ ++ test = info->invert ? !test : test; ++ ++ return test; ++} ++ ++ ++static bool match(const struct sk_buff *skb, struct xt_action_param *par) ++{ ++ ++ const struct ipt_weburl_info *info = (const struct ipt_weburl_info*)(par->matchinfo); ++ ++ ++ int test = 0; ++ struct iphdr* iph; ++ ++ /* linearize skb if necessary */ ++ struct sk_buff *linear_skb; ++ int skb_copied; ++ if(skb_is_nonlinear(skb)) ++ { ++ linear_skb = skb_copy(skb, GFP_ATOMIC); ++ skb_copied = 1; ++ } ++ else ++ { ++ linear_skb = (struct sk_buff*)skb; ++ skb_copied = 0; ++ } ++ ++ ++ ++ /* ignore packets that are not TCP */ ++ iph = (struct iphdr*)(skb_network_header(skb)); ++ if(iph->protocol == IPPROTO_TCP) ++ { ++ /* get payload */ ++ struct tcphdr* tcp_hdr = (struct tcphdr*)( ((unsigned char*)iph) + (iph->ihl*4) ); ++ unsigned short payload_offset = (tcp_hdr->doff*4) + (iph->ihl*4); ++ unsigned char* payload = ((unsigned char*)iph) + payload_offset; ++ unsigned short payload_length = ntohs(iph->tot_len) - payload_offset; ++ ++ ++ ++ /* if payload length <= 10 bytes don't bother doing a check, otherwise check for match */ ++ if(payload_length > 10) ++ { ++ if(strnicmp((char*)payload, "GET ", 4) == 0 || strnicmp( (char*)payload, "POST ", 5) == 0 || strnicmp((char*)payload, "HEAD ", 5) == 0) ++ { ++ test = http_match(info, payload, payload_length); ++ } ++ else if ((unsigned short)ntohs(tcp_hdr->dest) == 443) ++ { ++ test = https_match(info, payload, payload_length); ++ } ++ } ++ } ++ ++ /* free skb if we made a copy to linearize it */ ++ if(skb_copied == 1) ++ { ++ kfree_skb(linear_skb); ++ } ++ ++ ++ /* printk("returning %d from weburl\n\n\n", test); */ ++ return test; ++} ++ ++ ++static int checkentry(const struct xt_mtchk_param *par) ++{ ++ return 0; ++} ++ ++ ++static struct xt_match weburl_match __read_mostly = ++{ ++ .name = "weburl", ++ .match = match, ++ .family = AF_INET, ++ .matchsize = sizeof(struct ipt_weburl_info), ++ .checkentry = checkentry, ++ .me = THIS_MODULE, ++}; ++ ++static int __init init(void) ++{ ++ compiled_map = NULL; ++ return xt_register_match(&weburl_match); ++ ++} ++ ++static void __exit fini(void) ++{ ++ xt_unregister_match(&weburl_match); ++ if(compiled_map != NULL) ++ { ++ unsigned long num_destroyed; ++ destroy_map(compiled_map, DESTROY_MODE_FREE_VALUES, &num_destroyed); ++ } ++} ++ ++module_init(init); ++module_exit(fini); + +--- /dev/null ++++ b/net/ipv4/netfilter/ipt_weburl.mod.c +@@ -0,0 +1,20 @@ ++#include ++#include ++#include ++ ++MODULE_INFO(vermagic, VERMAGIC_STRING); ++ ++struct module __this_module ++__attribute__((section(".gnu.linkonce.this_module"))) = { ++ .name = KBUILD_MODNAME, ++ .init = init_module, ++#ifdef CONFIG_MODULE_UNLOAD ++ .exit = cleanup_module, ++#endif ++}; ++ ++static const char __module_depends[] ++__attribute_used__ ++__attribute__((section(".modinfo"))) = ++"depends="; ++ +--- /dev/null ++++ b/net/ipv4/netfilter/regexp/regexp.c +@@ -0,0 +1,1197 @@ ++/* ++ * regcomp and regexec -- regsub and regerror are elsewhere ++ * @(#)regexp.c 1.3 of 18 April 87 ++ * ++ * Copyright (c) 1986 by University of Toronto. ++ * Written by Henry Spencer. Not derived from licensed software. ++ * ++ * Permission is granted to anyone to use this software for any ++ * purpose on any computer system, and to redistribute it freely, ++ * subject to the following restrictions: ++ * ++ * 1. The author is not responsible for the consequences of use of ++ * this software, no matter how awful, even if they arise ++ * from defects in it. ++ * ++ * 2. The origin of this software must not be misrepresented, either ++ * by explicit claim or by omission. ++ * ++ * 3. Altered versions must be plainly marked as such, and must not ++ * be misrepresented as being the original software. ++ * ++ * Beware that some of this code is subtly aware of the way operator ++ * precedence is structured in regular expressions. Serious changes in ++ * regular-expression syntax might require a total rethink. ++ * ++ * This code was modified by Ethan Sommer to work within the kernel ++ * (it now uses kmalloc etc..) ++ * ++ * Modified slightly by Matthew Strait to use more modern C. ++ */ ++ ++#include "regexp.h" ++#include "regmagic.h" ++ ++/* added by ethan and matt. Lets it work in both kernel and user space. ++(So iptables can use it, for instance.) Yea, it goes both ways... */ ++#if __KERNEL__ ++ #define malloc(foo) kmalloc(foo,GFP_ATOMIC) ++#else ++ #define printk(format,args...) printf(format,##args) ++#endif ++ ++void regerror(char * s) ++{ ++ printk("<3>Regexp: %s\n", s); ++ /* NOTREACHED */ ++} ++ ++/* ++ * The "internal use only" fields in regexp.h are present to pass info from ++ * compile to execute that permits the execute phase to run lots faster on ++ * simple cases. They are: ++ * ++ * regstart char that must begin a match; '\0' if none obvious ++ * reganch is the match anchored (at beginning-of-line only)? ++ * regmust string (pointer into program) that match must include, or NULL ++ * regmlen length of regmust string ++ * ++ * Regstart and reganch permit very fast decisions on suitable starting points ++ * for a match, cutting down the work a lot. Regmust permits fast rejection ++ * of lines that cannot possibly match. The regmust tests are costly enough ++ * that regcomp() supplies a regmust only if the r.e. contains something ++ * potentially expensive (at present, the only such thing detected is * or + ++ * at the start of the r.e., which can involve a lot of backup). Regmlen is ++ * supplied because the test in regexec() needs it and regcomp() is computing ++ * it anyway. ++ */ ++ ++/* ++ * Structure for regexp "program". This is essentially a linear encoding ++ * of a nondeterministic finite-state machine (aka syntax charts or ++ * "railroad normal form" in parsing technology). Each node is an opcode ++ * plus a "next" pointer, possibly plus an operand. "Next" pointers of ++ * all nodes except BRANCH implement concatenation; a "next" pointer with ++ * a BRANCH on both ends of it is connecting two alternatives. (Here we ++ * have one of the subtle syntax dependencies: an individual BRANCH (as ++ * opposed to a collection of them) is never concatenated with anything ++ * because of operator precedence.) The operand of some types of node is ++ * a literal string; for others, it is a node leading into a sub-FSM. In ++ * particular, the operand of a BRANCH node is the first node of the branch. ++ * (NB this is *not* a tree structure: the tail of the branch connects ++ * to the thing following the set of BRANCHes.) The opcodes are: ++ */ ++ ++/* definition number opnd? meaning */ ++#define END 0 /* no End of program. */ ++#define BOL 1 /* no Match "" at beginning of line. */ ++#define EOL 2 /* no Match "" at end of line. */ ++#define ANY 3 /* no Match any one character. */ ++#define ANYOF 4 /* str Match any character in this string. */ ++#define ANYBUT 5 /* str Match any character not in this string. */ ++#define BRANCH 6 /* node Match this alternative, or the next... */ ++#define BACK 7 /* no Match "", "next" ptr points backward. */ ++#define EXACTLY 8 /* str Match this string. */ ++#define NOTHING 9 /* no Match empty string. */ ++#define STAR 10 /* node Match this (simple) thing 0 or more times. */ ++#define PLUS 11 /* node Match this (simple) thing 1 or more times. */ ++#define OPEN 20 /* no Mark this point in input as start of #n. */ ++ /* OPEN+1 is number 1, etc. */ ++#define CLOSE 30 /* no Analogous to OPEN. */ ++ ++/* ++ * Opcode notes: ++ * ++ * BRANCH The set of branches constituting a single choice are hooked ++ * together with their "next" pointers, since precedence prevents ++ * anything being concatenated to any individual branch. The ++ * "next" pointer of the last BRANCH in a choice points to the ++ * thing following the whole choice. This is also where the ++ * final "next" pointer of each individual branch points; each ++ * branch starts with the operand node of a BRANCH node. ++ * ++ * BACK Normal "next" pointers all implicitly point forward; BACK ++ * exists to make loop structures possible. ++ * ++ * STAR,PLUS '?', and complex '*' and '+', are implemented as circular ++ * BRANCH structures using BACK. Simple cases (one character ++ * per match) are implemented with STAR and PLUS for speed ++ * and to minimize recursive plunges. ++ * ++ * OPEN,CLOSE ...are numbered at compile time. ++ */ ++ ++/* ++ * A node is one char of opcode followed by two chars of "next" pointer. ++ * "Next" pointers are stored as two 8-bit pieces, high order first. The ++ * value is a positive offset from the opcode of the node containing it. ++ * An operand, if any, simply follows the node. (Note that much of the ++ * code generation knows about this implicit relationship.) ++ * ++ * Using two bytes for the "next" pointer is vast overkill for most things, ++ * but allows patterns to get big without disasters. ++ */ ++#define OP(p) (*(p)) ++#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377)) ++#define OPERAND(p) ((p) + 3) ++ ++/* ++ * See regmagic.h for one further detail of program structure. ++ */ ++ ++ ++/* ++ * Utility definitions. ++ */ ++#ifndef CHARBITS ++#define UCHARAT(p) ((int)*(unsigned char *)(p)) ++#else ++#define UCHARAT(p) ((int)*(p)&CHARBITS) ++#endif ++ ++#define FAIL(m) { regerror(m); return(NULL); } ++#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?') ++#define META "^$.[()|?+*\\" ++ ++/* ++ * Flags to be passed up and down. ++ */ ++#define HASWIDTH 01 /* Known never to match null string. */ ++#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */ ++#define SPSTART 04 /* Starts with * or +. */ ++#define WORST 0 /* Worst case. */ ++ ++/* ++ * Global work variables for regcomp(). ++ */ ++struct match_globals { ++char *reginput; /* String-input pointer. */ ++char *regbol; /* Beginning of input, for ^ check. */ ++char **regstartp; /* Pointer to startp array. */ ++char **regendp; /* Ditto for endp. */ ++char *regparse; /* Input-scan pointer. */ ++int regnpar; /* () count. */ ++char regdummy; ++char *regcode; /* Code-emit pointer; ®dummy = don't. */ ++long regsize; /* Code size. */ ++}; ++ ++/* ++ * Forward declarations for regcomp()'s friends. ++ */ ++#ifndef STATIC ++#define STATIC static ++#endif ++STATIC char *reg(struct match_globals *g, int paren,int *flagp); ++STATIC char *regbranch(struct match_globals *g, int *flagp); ++STATIC char *regpiece(struct match_globals *g, int *flagp); ++STATIC char *regatom(struct match_globals *g, int *flagp); ++STATIC char *regnode(struct match_globals *g, char op); ++STATIC char *regnext(struct match_globals *g, char *p); ++STATIC void regc(struct match_globals *g, char b); ++STATIC void reginsert(struct match_globals *g, char op, char *opnd); ++STATIC void regtail(struct match_globals *g, char *p, char *val); ++STATIC void regoptail(struct match_globals *g, char *p, char *val); ++ ++ ++__kernel_size_t my_strcspn(const char *s1,const char *s2) ++{ ++ char *scan1; ++ char *scan2; ++ int count; ++ ++ count = 0; ++ for (scan1 = (char *)s1; *scan1 != '\0'; scan1++) { ++ for (scan2 = (char *)s2; *scan2 != '\0';) /* ++ moved down. */ ++ if (*scan1 == *scan2++) ++ return(count); ++ count++; ++ } ++ return(count); ++} ++ ++/* ++ - regcomp - compile a regular expression into internal code ++ * ++ * We can't allocate space until we know how big the compiled form will be, ++ * but we can't compile it (and thus know how big it is) until we've got a ++ * place to put the code. So we cheat: we compile it twice, once with code ++ * generation turned off and size counting turned on, and once "for real". ++ * This also means that we don't allocate space until we are sure that the ++ * thing really will compile successfully, and we never have to move the ++ * code and thus invalidate pointers into it. (Note that it has to be in ++ * one piece because free() must be able to free it all.) ++ * ++ * Beware that the optimization-preparation code in here knows about some ++ * of the structure of the compiled regexp. ++ */ ++regexp * ++regcomp(char *exp,int *patternsize) ++{ ++ register regexp *r; ++ register char *scan; ++ register char *longest; ++ register int len; ++ int flags; ++ struct match_globals g; ++ ++ /* commented out by ethan ++ extern char *malloc(); ++ */ ++ ++ if (exp == NULL) ++ FAIL("NULL argument"); ++ ++ /* First pass: determine size, legality. */ ++ g.regparse = exp; ++ g.regnpar = 1; ++ g.regsize = 0L; ++ g.regcode = &g.regdummy; ++ regc(&g, MAGIC); ++ if (reg(&g, 0, &flags) == NULL) ++ return(NULL); ++ ++ /* Small enough for pointer-storage convention? */ ++ if (g.regsize >= 32767L) /* Probably could be 65535L. */ ++ FAIL("regexp too big"); ++ ++ /* Allocate space. */ ++ *patternsize=sizeof(regexp) + (unsigned)g.regsize; ++ r = (regexp *)malloc(sizeof(regexp) + (unsigned)g.regsize); ++ if (r == NULL) ++ FAIL("out of space"); ++ ++ /* Second pass: emit code. */ ++ g.regparse = exp; ++ g.regnpar = 1; ++ g.regcode = r->program; ++ regc(&g, MAGIC); ++ if (reg(&g, 0, &flags) == NULL) ++ return(NULL); ++ ++ /* Dig out information for optimizations. */ ++ r->regstart = '\0'; /* Worst-case defaults. */ ++ r->reganch = 0; ++ r->regmust = NULL; ++ r->regmlen = 0; ++ scan = r->program+1; /* First BRANCH. */ ++ if (OP(regnext(&g, scan)) == END) { /* Only one top-level choice. */ ++ scan = OPERAND(scan); ++ ++ /* Starting-point info. */ ++ if (OP(scan) == EXACTLY) ++ r->regstart = *OPERAND(scan); ++ else if (OP(scan) == BOL) ++ r->reganch++; ++ ++ /* ++ * If there's something expensive in the r.e., find the ++ * longest literal string that must appear and make it the ++ * regmust. Resolve ties in favor of later strings, since ++ * the regstart check works with the beginning of the r.e. ++ * and avoiding duplication strengthens checking. Not a ++ * strong reason, but sufficient in the absence of others. ++ */ ++ if (flags&SPSTART) { ++ longest = NULL; ++ len = 0; ++ for (; scan != NULL; scan = regnext(&g, scan)) ++ if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { ++ longest = OPERAND(scan); ++ len = strlen(OPERAND(scan)); ++ } ++ r->regmust = longest; ++ r->regmlen = len; ++ } ++ } ++ ++ return(r); ++} ++ ++/* ++ - reg - regular expression, i.e. main body or parenthesized thing ++ * ++ * Caller must absorb opening parenthesis. ++ * ++ * Combining parenthesis handling with the base level of regular expression ++ * is a trifle forced, but the need to tie the tails of the branches to what ++ * follows makes it hard to avoid. ++ */ ++static char * ++reg(struct match_globals *g, int paren, int *flagp /* Parenthesized? */ ) ++{ ++ register char *ret; ++ register char *br; ++ register char *ender; ++ register int parno = 0; /* 0 makes gcc happy */ ++ int flags; ++ ++ *flagp = HASWIDTH; /* Tentatively. */ ++ ++ /* Make an OPEN node, if parenthesized. */ ++ if (paren) { ++ if (g->regnpar >= NSUBEXP) ++ FAIL("too many ()"); ++ parno = g->regnpar; ++ g->regnpar++; ++ ret = regnode(g, OPEN+parno); ++ } else ++ ret = NULL; ++ ++ /* Pick up the branches, linking them together. */ ++ br = regbranch(g, &flags); ++ if (br == NULL) ++ return(NULL); ++ if (ret != NULL) ++ regtail(g, ret, br); /* OPEN -> first. */ ++ else ++ ret = br; ++ if (!(flags&HASWIDTH)) ++ *flagp &= ~HASWIDTH; ++ *flagp |= flags&SPSTART; ++ while (*g->regparse == '|') { ++ g->regparse++; ++ br = regbranch(g, &flags); ++ if (br == NULL) ++ return(NULL); ++ regtail(g, ret, br); /* BRANCH -> BRANCH. */ ++ if (!(flags&HASWIDTH)) ++ *flagp &= ~HASWIDTH; ++ *flagp |= flags&SPSTART; ++ } ++ ++ /* Make a closing node, and hook it on the end. */ ++ ender = regnode(g, (paren) ? CLOSE+parno : END); ++ regtail(g, ret, ender); ++ ++ /* Hook the tails of the branches to the closing node. */ ++ for (br = ret; br != NULL; br = regnext(g, br)) ++ regoptail(g, br, ender); ++ ++ /* Check for proper termination. */ ++ if (paren && *g->regparse++ != ')') { ++ FAIL("unmatched ()"); ++ } else if (!paren && *g->regparse != '\0') { ++ if (*g->regparse == ')') { ++ FAIL("unmatched ()"); ++ } else ++ FAIL("junk on end"); /* "Can't happen". */ ++ /* NOTREACHED */ ++ } ++ ++ return(ret); ++} ++ ++/* ++ - regbranch - one alternative of an | operator ++ * ++ * Implements the concatenation operator. ++ */ ++static char * ++regbranch(struct match_globals *g, int *flagp) ++{ ++ register char *ret; ++ register char *chain; ++ register char *latest; ++ int flags; ++ ++ *flagp = WORST; /* Tentatively. */ ++ ++ ret = regnode(g, BRANCH); ++ chain = NULL; ++ while (*g->regparse != '\0' && *g->regparse != '|' && *g->regparse != ')') { ++ latest = regpiece(g, &flags); ++ if (latest == NULL) ++ return(NULL); ++ *flagp |= flags&HASWIDTH; ++ if (chain == NULL) /* First piece. */ ++ *flagp |= flags&SPSTART; ++ else ++ regtail(g, chain, latest); ++ chain = latest; ++ } ++ if (chain == NULL) /* Loop ran zero times. */ ++ (void) regnode(g, NOTHING); ++ ++ return(ret); ++} ++ ++/* ++ - regpiece - something followed by possible [*+?] ++ * ++ * Note that the branching code sequences used for ? and the general cases ++ * of * and + are somewhat optimized: they use the same NOTHING node as ++ * both the endmarker for their branch list and the body of the last branch. ++ * It might seem that this node could be dispensed with entirely, but the ++ * endmarker role is not redundant. ++ */ ++static char * ++regpiece(struct match_globals *g, int *flagp) ++{ ++ register char *ret; ++ register char op; ++ register char *next; ++ int flags; ++ ++ ret = regatom(g, &flags); ++ if (ret == NULL) ++ return(NULL); ++ ++ op = *g->regparse; ++ if (!ISMULT(op)) { ++ *flagp = flags; ++ return(ret); ++ } ++ ++ if (!(flags&HASWIDTH) && op != '?') ++ FAIL("*+ operand could be empty"); ++ *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH); ++ ++ if (op == '*' && (flags&SIMPLE)) ++ reginsert(g, STAR, ret); ++ else if (op == '*') { ++ /* Emit x* as (x&|), where & means "self". */ ++ reginsert(g, BRANCH, ret); /* Either x */ ++ regoptail(g, ret, regnode(g, BACK)); /* and loop */ ++ regoptail(g, ret, ret); /* back */ ++ regtail(g, ret, regnode(g, BRANCH)); /* or */ ++ regtail(g, ret, regnode(g, NOTHING)); /* null. */ ++ } else if (op == '+' && (flags&SIMPLE)) ++ reginsert(g, PLUS, ret); ++ else if (op == '+') { ++ /* Emit x+ as x(&|), where & means "self". */ ++ next = regnode(g, BRANCH); /* Either */ ++ regtail(g, ret, next); ++ regtail(g, regnode(g, BACK), ret); /* loop back */ ++ regtail(g, next, regnode(g, BRANCH)); /* or */ ++ regtail(g, ret, regnode(g, NOTHING)); /* null. */ ++ } else if (op == '?') { ++ /* Emit x? as (x|) */ ++ reginsert(g, BRANCH, ret); /* Either x */ ++ regtail(g, ret, regnode(g, BRANCH)); /* or */ ++ next = regnode(g, NOTHING); /* null. */ ++ regtail(g, ret, next); ++ regoptail(g, ret, next); ++ } ++ g->regparse++; ++ if (ISMULT(*g->regparse)) ++ FAIL("nested *?+"); ++ ++ return(ret); ++} ++ ++/* ++ - regatom - the lowest level ++ * ++ * Optimization: gobbles an entire sequence of ordinary characters so that ++ * it can turn them into a single node, which is smaller to store and ++ * faster to run. Backslashed characters are exceptions, each becoming a ++ * separate node; the code is simpler that way and it's not worth fixing. ++ */ ++static char * ++regatom(struct match_globals *g, int *flagp) ++{ ++ register char *ret; ++ int flags; ++ ++ *flagp = WORST; /* Tentatively. */ ++ ++ switch (*g->regparse++) { ++ case '^': ++ ret = regnode(g, BOL); ++ break; ++ case '$': ++ ret = regnode(g, EOL); ++ break; ++ case '.': ++ ret = regnode(g, ANY); ++ *flagp |= HASWIDTH|SIMPLE; ++ break; ++ case '[': { ++ register int class; ++ register int classend; ++ ++ if (*g->regparse == '^') { /* Complement of range. */ ++ ret = regnode(g, ANYBUT); ++ g->regparse++; ++ } else ++ ret = regnode(g, ANYOF); ++ if (*g->regparse == ']' || *g->regparse == '-') ++ regc(g, *g->regparse++); ++ while (*g->regparse != '\0' && *g->regparse != ']') { ++ if (*g->regparse == '-') { ++ g->regparse++; ++ if (*g->regparse == ']' || *g->regparse == '\0') ++ regc(g, '-'); ++ else { ++ class = UCHARAT(g->regparse-2)+1; ++ classend = UCHARAT(g->regparse); ++ if (class > classend+1) ++ FAIL("invalid [] range"); ++ for (; class <= classend; class++) ++ regc(g, class); ++ g->regparse++; ++ } ++ } else ++ regc(g, *g->regparse++); ++ } ++ regc(g, '\0'); ++ if (*g->regparse != ']') ++ FAIL("unmatched []"); ++ g->regparse++; ++ *flagp |= HASWIDTH|SIMPLE; ++ } ++ break; ++ case '(': ++ ret = reg(g, 1, &flags); ++ if (ret == NULL) ++ return(NULL); ++ *flagp |= flags&(HASWIDTH|SPSTART); ++ break; ++ case '\0': ++ case '|': ++ case ')': ++ FAIL("internal urp"); /* Supposed to be caught earlier. */ ++ break; ++ case '?': ++ case '+': ++ case '*': ++ FAIL("?+* follows nothing"); ++ break; ++ case '\\': ++ if (*g->regparse == '\0') ++ FAIL("trailing \\"); ++ ret = regnode(g, EXACTLY); ++ regc(g, *g->regparse++); ++ regc(g, '\0'); ++ *flagp |= HASWIDTH|SIMPLE; ++ break; ++ default: { ++ register int len; ++ register char ender; ++ ++ g->regparse--; ++ len = my_strcspn((const char *)g->regparse, (const char *)META); ++ if (len <= 0) ++ FAIL("internal disaster"); ++ ender = *(g->regparse+len); ++ if (len > 1 && ISMULT(ender)) ++ len--; /* Back off clear of ?+* operand. */ ++ *flagp |= HASWIDTH; ++ if (len == 1) ++ *flagp |= SIMPLE; ++ ret = regnode(g, EXACTLY); ++ while (len > 0) { ++ regc(g, *g->regparse++); ++ len--; ++ } ++ regc(g, '\0'); ++ } ++ break; ++ } ++ ++ return(ret); ++} ++ ++/* ++ - regnode - emit a node ++ */ ++static char * /* Location. */ ++regnode(struct match_globals *g, char op) ++{ ++ register char *ret; ++ register char *ptr; ++ ++ ret = g->regcode; ++ if (ret == &g->regdummy) { ++ g->regsize += 3; ++ return(ret); ++ } ++ ++ ptr = ret; ++ *ptr++ = op; ++ *ptr++ = '\0'; /* Null "next" pointer. */ ++ *ptr++ = '\0'; ++ g->regcode = ptr; ++ ++ return(ret); ++} ++ ++/* ++ - regc - emit (if appropriate) a byte of code ++ */ ++static void ++regc(struct match_globals *g, char b) ++{ ++ if (g->regcode != &g->regdummy) ++ *g->regcode++ = b; ++ else ++ g->regsize++; ++} ++ ++/* ++ - reginsert - insert an operator in front of already-emitted operand ++ * ++ * Means relocating the operand. ++ */ ++static void ++reginsert(struct match_globals *g, char op, char* opnd) ++{ ++ register char *src; ++ register char *dst; ++ register char *place; ++ ++ if (g->regcode == &g->regdummy) { ++ g->regsize += 3; ++ return; ++ } ++ ++ src = g->regcode; ++ g->regcode += 3; ++ dst = g->regcode; ++ while (src > opnd) ++ *--dst = *--src; ++ ++ place = opnd; /* Op node, where operand used to be. */ ++ *place++ = op; ++ *place++ = '\0'; ++ *place++ = '\0'; ++} ++ ++/* ++ - regtail - set the next-pointer at the end of a node chain ++ */ ++static void ++regtail(struct match_globals *g, char *p, char *val) ++{ ++ register char *scan; ++ register char *temp; ++ register int offset; ++ ++ if (p == &g->regdummy) ++ return; ++ ++ /* Find last node. */ ++ scan = p; ++ for (;;) { ++ temp = regnext(g, scan); ++ if (temp == NULL) ++ break; ++ scan = temp; ++ } ++ ++ if (OP(scan) == BACK) ++ offset = scan - val; ++ else ++ offset = val - scan; ++ *(scan+1) = (offset>>8)&0377; ++ *(scan+2) = offset&0377; ++} ++ ++/* ++ - regoptail - regtail on operand of first argument; nop if operandless ++ */ ++static void ++regoptail(struct match_globals *g, char *p, char *val) ++{ ++ /* "Operandless" and "op != BRANCH" are synonymous in practice. */ ++ if (p == NULL || p == &g->regdummy || OP(p) != BRANCH) ++ return; ++ regtail(g, OPERAND(p), val); ++} ++ ++/* ++ * regexec and friends ++ */ ++ ++ ++/* ++ * Forwards. ++ */ ++STATIC int regtry(struct match_globals *g, regexp *prog, char *string); ++STATIC int regmatch(struct match_globals *g, char *prog); ++STATIC int regrepeat(struct match_globals *g, char *p); ++ ++#ifdef DEBUG ++int regnarrate = 0; ++void regdump(); ++STATIC char *regprop(char *op); ++#endif ++ ++/* ++ - regexec - match a regexp against a string ++ */ ++int ++regexec(regexp *prog, char *string) ++{ ++ register char *s; ++ struct match_globals g; ++ ++ /* Be paranoid... */ ++ if (prog == NULL || string == NULL) { ++ printk("<3>Regexp: NULL parameter\n"); ++ return(0); ++ } ++ ++ /* Check validity of program. */ ++ if (UCHARAT(prog->program) != MAGIC) { ++ printk("<3>Regexp: corrupted program\n"); ++ return(0); ++ } ++ ++ /* If there is a "must appear" string, look for it. */ ++ if (prog->regmust != NULL) { ++ s = string; ++ while ((s = strchr(s, prog->regmust[0])) != NULL) { ++ if (strncmp(s, prog->regmust, prog->regmlen) == 0) ++ break; /* Found it. */ ++ s++; ++ } ++ if (s == NULL) /* Not present. */ ++ return(0); ++ } ++ ++ /* Mark beginning of line for ^ . */ ++ g.regbol = string; ++ ++ /* Simplest case: anchored match need be tried only once. */ ++ if (prog->reganch) ++ return(regtry(&g, prog, string)); ++ ++ /* Messy cases: unanchored match. */ ++ s = string; ++ if (prog->regstart != '\0') ++ /* We know what char it must start with. */ ++ while ((s = strchr(s, prog->regstart)) != NULL) { ++ if (regtry(&g, prog, s)) ++ return(1); ++ s++; ++ } ++ else ++ /* We don't -- general case. */ ++ do { ++ if (regtry(&g, prog, s)) ++ return(1); ++ } while (*s++ != '\0'); ++ ++ /* Failure. */ ++ return(0); ++} ++ ++/* ++ - regtry - try match at specific point ++ */ ++static int /* 0 failure, 1 success */ ++regtry(struct match_globals *g, regexp *prog, char *string) ++{ ++ register int i; ++ register char **sp; ++ register char **ep; ++ ++ g->reginput = string; ++ g->regstartp = prog->startp; ++ g->regendp = prog->endp; ++ ++ sp = prog->startp; ++ ep = prog->endp; ++ for (i = NSUBEXP; i > 0; i--) { ++ *sp++ = NULL; ++ *ep++ = NULL; ++ } ++ if (regmatch(g, prog->program + 1)) { ++ prog->startp[0] = string; ++ prog->endp[0] = g->reginput; ++ return(1); ++ } else ++ return(0); ++} ++ ++/* ++ - regmatch - main matching routine ++ * ++ * Conceptually the strategy is simple: check to see whether the current ++ * node matches, call self recursively to see whether the rest matches, ++ * and then act accordingly. In practice we make some effort to avoid ++ * recursion, in particular by going through "ordinary" nodes (that don't ++ * need to know whether the rest of the match failed) by a loop instead of ++ * by recursion. ++ */ ++static int /* 0 failure, 1 success */ ++regmatch(struct match_globals *g, char *prog) ++{ ++ register char *scan = prog; /* Current node. */ ++ char *next; /* Next node. */ ++ ++#ifdef DEBUG ++ if (scan != NULL && regnarrate) ++ fprintf(stderr, "%s(\n", regprop(scan)); ++#endif ++ while (scan != NULL) { ++#ifdef DEBUG ++ if (regnarrate) ++ fprintf(stderr, "%s...\n", regprop(scan)); ++#endif ++ next = regnext(g, scan); ++ ++ switch (OP(scan)) { ++ case BOL: ++ if (g->reginput != g->regbol) ++ return(0); ++ break; ++ case EOL: ++ if (*g->reginput != '\0') ++ return(0); ++ break; ++ case ANY: ++ if (*g->reginput == '\0') ++ return(0); ++ g->reginput++; ++ break; ++ case EXACTLY: { ++ register int len; ++ register char *opnd; ++ ++ opnd = OPERAND(scan); ++ /* Inline the first character, for speed. */ ++ if (*opnd != *g->reginput) ++ return(0); ++ len = strlen(opnd); ++ if (len > 1 && strncmp(opnd, g->reginput, len) != 0) ++ return(0); ++ g->reginput += len; ++ } ++ break; ++ case ANYOF: ++ if (*g->reginput == '\0' || strchr(OPERAND(scan), *g->reginput) == NULL) ++ return(0); ++ g->reginput++; ++ break; ++ case ANYBUT: ++ if (*g->reginput == '\0' || strchr(OPERAND(scan), *g->reginput) != NULL) ++ return(0); ++ g->reginput++; ++ break; ++ case NOTHING: ++ case BACK: ++ break; ++ case OPEN+1: ++ case OPEN+2: ++ case OPEN+3: ++ case OPEN+4: ++ case OPEN+5: ++ case OPEN+6: ++ case OPEN+7: ++ case OPEN+8: ++ case OPEN+9: { ++ register int no; ++ register char *save; ++ ++ no = OP(scan) - OPEN; ++ save = g->reginput; ++ ++ if (regmatch(g, next)) { ++ /* ++ * Don't set startp if some later ++ * invocation of the same parentheses ++ * already has. ++ */ ++ if (g->regstartp[no] == NULL) ++ g->regstartp[no] = save; ++ return(1); ++ } else ++ return(0); ++ } ++ break; ++ case CLOSE+1: ++ case CLOSE+2: ++ case CLOSE+3: ++ case CLOSE+4: ++ case CLOSE+5: ++ case CLOSE+6: ++ case CLOSE+7: ++ case CLOSE+8: ++ case CLOSE+9: ++ { ++ register int no; ++ register char *save; ++ ++ no = OP(scan) - CLOSE; ++ save = g->reginput; ++ ++ if (regmatch(g, next)) { ++ /* ++ * Don't set endp if some later ++ * invocation of the same parentheses ++ * already has. ++ */ ++ if (g->regendp[no] == NULL) ++ g->regendp[no] = save; ++ return(1); ++ } else ++ return(0); ++ } ++ break; ++ case BRANCH: { ++ register char *save; ++ ++ if (OP(next) != BRANCH) /* No choice. */ ++ next = OPERAND(scan); /* Avoid recursion. */ ++ else { ++ do { ++ save = g->reginput; ++ if (regmatch(g, OPERAND(scan))) ++ return(1); ++ g->reginput = save; ++ scan = regnext(g, scan); ++ } while (scan != NULL && OP(scan) == BRANCH); ++ return(0); ++ /* NOTREACHED */ ++ } ++ } ++ break; ++ case STAR: ++ case PLUS: { ++ register char nextch; ++ register int no; ++ register char *save; ++ register int min; ++ ++ /* ++ * Lookahead to avoid useless match attempts ++ * when we know what character comes next. ++ */ ++ nextch = '\0'; ++ if (OP(next) == EXACTLY) ++ nextch = *OPERAND(next); ++ min = (OP(scan) == STAR) ? 0 : 1; ++ save = g->reginput; ++ no = regrepeat(g, OPERAND(scan)); ++ while (no >= min) { ++ /* If it could work, try it. */ ++ if (nextch == '\0' || *g->reginput == nextch) ++ if (regmatch(g, next)) ++ return(1); ++ /* Couldn't or didn't -- back up. */ ++ no--; ++ g->reginput = save + no; ++ } ++ return(0); ++ } ++ break; ++ case END: ++ return(1); /* Success! */ ++ break; ++ default: ++ printk("<3>Regexp: memory corruption\n"); ++ return(0); ++ break; ++ } ++ ++ scan = next; ++ } ++ ++ /* ++ * We get here only if there's trouble -- normally "case END" is ++ * the terminating point. ++ */ ++ printk("<3>Regexp: corrupted pointers\n"); ++ return(0); ++} ++ ++/* ++ - regrepeat - repeatedly match something simple, report how many ++ */ ++static int ++regrepeat(struct match_globals *g, char *p) ++{ ++ register int count = 0; ++ register char *scan; ++ register char *opnd; ++ ++ scan = g->reginput; ++ opnd = OPERAND(p); ++ switch (OP(p)) { ++ case ANY: ++ count = strlen(scan); ++ scan += count; ++ break; ++ case EXACTLY: ++ while (*opnd == *scan) { ++ count++; ++ scan++; ++ } ++ break; ++ case ANYOF: ++ while (*scan != '\0' && strchr(opnd, *scan) != NULL) { ++ count++; ++ scan++; ++ } ++ break; ++ case ANYBUT: ++ while (*scan != '\0' && strchr(opnd, *scan) == NULL) { ++ count++; ++ scan++; ++ } ++ break; ++ default: /* Oh dear. Called inappropriately. */ ++ printk("<3>Regexp: internal foulup\n"); ++ count = 0; /* Best compromise. */ ++ break; ++ } ++ g->reginput = scan; ++ ++ return(count); ++} ++ ++/* ++ - regnext - dig the "next" pointer out of a node ++ */ ++static char* ++regnext(struct match_globals *g, char *p) ++{ ++ register int offset; ++ ++ if (p == &g->regdummy) ++ return(NULL); ++ ++ offset = NEXT(p); ++ if (offset == 0) ++ return(NULL); ++ ++ if (OP(p) == BACK) ++ return(p-offset); ++ else ++ return(p+offset); ++} ++ ++#ifdef DEBUG ++ ++STATIC char *regprop(); ++ ++/* ++ - regdump - dump a regexp onto stdout in vaguely comprehensible form ++ */ ++void ++regdump(regexp *r) ++{ ++ register char *s; ++ register char op = EXACTLY; /* Arbitrary non-END op. */ ++ register char *next; ++ /* extern char *strchr(); */ ++ ++ ++ s = r->program + 1; ++ while (op != END) { /* While that wasn't END last time... */ ++ op = OP(s); ++ printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */ ++ next = regnext(s); ++ if (next == NULL) /* Next ptr. */ ++ printf("(0)"); ++ else ++ printf("(%d)", (s-r->program)+(next-s)); ++ s += 3; ++ if (op == ANYOF || op == ANYBUT || op == EXACTLY) { ++ /* Literal string, where present. */ ++ while (*s != '\0') { ++ putchar(*s); ++ s++; ++ } ++ s++; ++ } ++ putchar('\n'); ++ } ++ ++ /* Header fields of interest. */ ++ if (r->regstart != '\0') ++ printf("start `%c' ", r->regstart); ++ if (r->reganch) ++ printf("anchored "); ++ if (r->regmust != NULL) ++ printf("must have \"%s\"", r->regmust); ++ printf("\n"); ++} ++ ++/* ++ - regprop - printable representation of opcode ++ */ ++static char * ++regprop(char *op) ++{ ++#define BUFLEN 50 ++ register char *p; ++ static char buf[BUFLEN]; ++ ++ strcpy(buf, ":"); ++ ++ switch (OP(op)) { ++ case BOL: ++ p = "BOL"; ++ break; ++ case EOL: ++ p = "EOL"; ++ break; ++ case ANY: ++ p = "ANY"; ++ break; ++ case ANYOF: ++ p = "ANYOF"; ++ break; ++ case ANYBUT: ++ p = "ANYBUT"; ++ break; ++ case BRANCH: ++ p = "BRANCH"; ++ break; ++ case EXACTLY: ++ p = "EXACTLY"; ++ break; ++ case NOTHING: ++ p = "NOTHING"; ++ break; ++ case BACK: ++ p = "BACK"; ++ break; ++ case END: ++ p = "END"; ++ break; ++ case OPEN+1: ++ case OPEN+2: ++ case OPEN+3: ++ case OPEN+4: ++ case OPEN+5: ++ case OPEN+6: ++ case OPEN+7: ++ case OPEN+8: ++ case OPEN+9: ++ snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "OPEN%d", OP(op)-OPEN); ++ p = NULL; ++ break; ++ case CLOSE+1: ++ case CLOSE+2: ++ case CLOSE+3: ++ case CLOSE+4: ++ case CLOSE+5: ++ case CLOSE+6: ++ case CLOSE+7: ++ case CLOSE+8: ++ case CLOSE+9: ++ snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "CLOSE%d", OP(op)-CLOSE); ++ p = NULL; ++ break; ++ case STAR: ++ p = "STAR"; ++ break; ++ case PLUS: ++ p = "PLUS"; ++ break; ++ default: ++ printk("<3>Regexp: corrupted opcode\n"); ++ break; ++ } ++ if (p != NULL) ++ strncat(buf, p, BUFLEN-strlen(buf)); ++ return(buf); ++} ++#endif ++ ++ +--- /dev/null ++++ b/net/ipv4/netfilter/regexp/regexp.h +@@ -0,0 +1,41 @@ ++/* ++ * Definitions etc. for regexp(3) routines. ++ * ++ * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof], ++ * not the System V one. ++ */ ++ ++#ifndef REGEXP_H ++#define REGEXP_H ++ ++ ++/* ++http://www.opensource.apple.com/darwinsource/10.3/expect-1/expect/expect.h , ++which contains a version of this library, says: ++ ++ * ++ * NSUBEXP must be at least 10, and no greater than 117 or the parser ++ * will not work properly. ++ * ++ ++However, it looks rather like this library is limited to 10. If you think ++otherwise, let us know. ++*/ ++ ++#define NSUBEXP 10 ++typedef struct regexp { ++ char *startp[NSUBEXP]; ++ char *endp[NSUBEXP]; ++ char regstart; /* Internal use only. */ ++ char reganch; /* Internal use only. */ ++ char *regmust; /* Internal use only. */ ++ int regmlen; /* Internal use only. */ ++ char program[1]; /* Unwarranted chumminess with compiler. */ ++} regexp; ++ ++regexp * regcomp(char *exp, int *patternsize); ++int regexec(regexp *prog, char *string); ++void regsub(regexp *prog, char *source, char *dest); ++void regerror(char *s); ++ ++#endif +--- /dev/null ++++ b/net/ipv4/netfilter/regexp/regmagic.h +@@ -0,0 +1,5 @@ ++/* ++ * The first byte of the regexp internal "program" is actually this magic ++ * number; the start node begins in the second byte. ++ */ ++#define MAGIC 0234 +--- /dev/null ++++ b/net/ipv4/netfilter/regexp/regsub.c +@@ -0,0 +1,95 @@ ++/* ++ * regsub ++ * @(#)regsub.c 1.3 of 2 April 86 ++ * ++ * Copyright (c) 1986 by University of Toronto. ++ * Written by Henry Spencer. Not derived from licensed software. ++ * ++ * Permission is granted to anyone to use this software for any ++ * purpose on any computer system, and to redistribute it freely, ++ * subject to the following restrictions: ++ * ++ * 1. The author is not responsible for the consequences of use of ++ * this software, no matter how awful, even if they arise ++ * from defects in it. ++ * ++ * 2. The origin of this software must not be misrepresented, either ++ * by explicit claim or by omission. ++ * ++ * 3. Altered versions must be plainly marked as such, and must not ++ * be misrepresented as being the original software. ++ * ++ * ++ * This code was modified by Ethan Sommer to work within the kernel ++ * (it now uses kmalloc etc..) ++ * ++ */ ++#include "regexp.h" ++#include "regmagic.h" ++#include ++ ++ ++#ifndef CHARBITS ++#define UCHARAT(p) ((int)*(unsigned char *)(p)) ++#else ++#define UCHARAT(p) ((int)*(p)&CHARBITS) ++#endif ++ ++#if 0 ++//void regerror(char * s) ++//{ ++// printk("regexp(3): %s", s); ++// /* NOTREACHED */ ++//} ++#endif ++ ++/* ++ - regsub - perform substitutions after a regexp match ++ */ ++void ++regsub(regexp * prog, char * source, char * dest) ++{ ++ register char *src; ++ register char *dst; ++ register char c; ++ register int no; ++ register int len; ++ ++ /* Not necessary and gcc doesn't like it -MLS */ ++ /*extern char *strncpy();*/ ++ ++ if (prog == NULL || source == NULL || dest == NULL) { ++ regerror("NULL parm to regsub"); ++ return; ++ } ++ if (UCHARAT(prog->program) != MAGIC) { ++ regerror("damaged regexp fed to regsub"); ++ return; ++ } ++ ++ src = source; ++ dst = dest; ++ while ((c = *src++) != '\0') { ++ if (c == '&') ++ no = 0; ++ else if (c == '\\' && '0' <= *src && *src <= '9') ++ no = *src++ - '0'; ++ else ++ no = -1; ++ ++ if (no < 0) { /* Ordinary character. */ ++ if (c == '\\' && (*src == '\\' || *src == '&')) ++ c = *src++; ++ *dst++ = c; ++ } else if (prog->startp[no] != NULL && prog->endp[no] != NULL) { ++ len = prog->endp[no] - prog->startp[no]; ++ (void) strncpy(dst, prog->startp[no], len); ++ dst += len; ++ if (len != 0 && *(dst-1) == '\0') { /* strncpy hit NUL. */ ++ regerror("damaged match string"); ++ return; ++ } ++ } ++ } ++ *dst++ = '\0'; ++} +--- /dev/null ++++ b/net/ipv4/netfilter/webmon_deps/tree_map.h +@@ -0,0 +1,1084 @@ ++/* ++ * Copyright © 2008 by Eric Bishop ++ * ++ * This work 'as-is' we provide. ++ * No warranty, express or implied. ++ * We've done our best, ++ * to debug and test. ++ * Liability for damages denied. ++ * ++ * Permission is granted hereby, ++ * to copy, share, and modify. ++ * Use as is fit, ++ * free or for profit. ++ * On this notice these rights rely. ++ * ++ * ++ * ++ * Note that unlike other portions of Gargoyle this code ++ * does not fall under the GPL, but the rather whimsical ++ * 'Poetic License' above. ++ * ++ * Basically, this library contains a bunch of utilities ++ * that I find useful. I'm sure other libraries exist ++ * that are just as good or better, but I like these tools ++ * because I personally wrote them, so I know their quirks. ++ * (i.e. I know where the bodies are buried). I want to ++ * make sure that I can re-use these utilities for whatever ++ * code I may want to write in the future be it ++ * proprietary or open-source, so I've put them under ++ * a very, very permissive license. ++ * ++ * If you find this code useful, use it. If not, don't. ++ * I really don't care. ++ * ++ */ ++ ++ ++#if __KERNEL__ ++ #define malloc(foo) kmalloc(foo,GFP_ATOMIC) ++ #define free(foo) kfree(foo) ++ #define printf(format,args...) printk(format,##args) ++ ++ /* kernel strdup */ ++ static inline char *kernel_strdup(const char *str); ++ static inline char *kernel_strdup(const char *str) ++ { ++ char *tmp; ++ long int s; ++ s=strlen(str) + 1; ++ tmp = kmalloc(s, GFP_ATOMIC); ++ if (tmp != NULL) ++ { ++ memcpy(tmp, str, s); ++ } ++ return tmp; ++ } ++ #define strdup kernel_strdup ++ ++#endif ++ ++ ++ ++/* tree_map structs / prototypes */ ++typedef struct long_tree_map_node ++{ ++ unsigned long key; ++ void* value; ++ ++ signed char balance; ++ struct long_tree_map_node* left; ++ struct long_tree_map_node* right; ++} long_map_node; ++ ++typedef struct ++{ ++ long_map_node* root; ++ unsigned long num_elements; ++ ++}long_map; ++ ++typedef struct ++{ ++ long_map lm; ++ unsigned char store_keys; ++ unsigned long num_elements; ++ ++}string_map; ++ ++ ++ ++/* long map functions */ ++long_map* initialize_long_map(void); ++void* get_long_map_element(long_map* map, unsigned long key); ++void* get_smallest_long_map_element(long_map* map, unsigned long* smallest_key); ++void* get_largest_long_map_element(long_map* map, unsigned long* largest_key); ++void* remove_smallest_long_map_element(long_map* map, unsigned long* smallest_key); ++void* remove_largest_long_map_element(long_map* map, unsigned long* largest_key); ++void* set_long_map_element(long_map* map, unsigned long key, void* value); ++void* remove_long_map_element(long_map* map, unsigned long key); ++unsigned long* get_sorted_long_map_keys(long_map* map, unsigned long* num_keys_returned); ++void** get_sorted_long_map_values(long_map* map, unsigned long* num_values_returned); ++void** destroy_long_map(long_map* map, int destruction_type, unsigned long* num_destroyed); ++void apply_to_every_long_map_value(long_map* map, void (*apply_func)(unsigned long key, void* value)); ++ ++/* string map functions */ ++string_map* initialize_string_map(unsigned char store_keys); ++void* get_string_map_element(string_map* map, const char* key); ++void* set_string_map_element(string_map* map, const char* key, void* value); ++void* remove_string_map_element(string_map* map, const char* key); ++char** get_string_map_keys(string_map* map, unsigned long* num_keys_returned); ++void** get_string_map_values(string_map* map, unsigned long* num_values_returned); ++void** destroy_string_map(string_map* map, int destruction_type, unsigned long* num_destroyed); ++void apply_to_every_string_map_value(string_map* map, void (*apply_func)(char* key, void* value)); ++ ++ ++/* ++ * three different ways to deal with values when data structure is destroyed ++ */ ++#define DESTROY_MODE_RETURN_VALUES 20 ++#define DESTROY_MODE_FREE_VALUES 21 ++#define DESTROY_MODE_IGNORE_VALUES 22 ++ ++ ++/* ++ * for convenience & backwards compatibility alias _string_map_ functions to ++ * _map_ functions since string map is used more often than long map ++ */ ++#define initialize_map initialize_string_map ++#define set_map_element set_string_map_element ++#define get_map_element get_string_map_element ++#define remove_map_element remove_string_map_element ++#define get_map_keys get_string_map_keys ++#define get_map_values get_string_map_values ++#define destroy_map destroy_string_map ++ ++ ++/* internal utility structures/ functions */ ++typedef struct stack_node_struct ++{ ++ long_map_node** node_ptr; ++ signed char direction; ++ struct stack_node_struct* previous; ++} stack_node; ++ ++static void free_stack(stack_node* stack); ++static void** destroy_long_map_values(long_map* map, int destruction_type, unsigned long* num_destroyed); ++static void apply_to_every_long_map_node(long_map_node* node, void (*apply_func)(unsigned long key, void* value)); ++static void apply_to_every_string_map_node(long_map_node* node, unsigned char has_key, void (*apply_func)(char* key, void* value)); ++static void get_sorted_node_keys(long_map_node* node, unsigned long* key_list, unsigned long* next_key_index, int depth); ++static void get_sorted_node_values(long_map_node* node, void** value_list, unsigned long* next_value_index, int depth); ++static signed char rebalance (long_map_node** n, signed char direction, signed char update_op); ++static void rotate_right (long_map_node** parent); ++static void rotate_left (long_map_node** parent); ++ ++/* internal for string map */ ++typedef struct ++{ ++ char* key; ++ void* value; ++} string_map_key_value; ++static unsigned long sdbm_string_hash(const char *key); ++ ++ ++ ++ ++/*************************************************** ++ * For testing only ++ ***************************************************/ ++/* ++void print_list(stack_node *l); ++ ++void print_list(stack_node *l) ++{ ++ if(l != NULL) ++ { ++ printf(" list key = %ld, dir=%d, \n", (*(l->node_ptr))->key, l->direction); ++ print_list(l->previous); ++ } ++} ++*/ ++/****************************************************** ++ * End testing Code ++ *******************************************************/ ++ ++ ++ ++ ++/*************************************************** ++ * string_map function definitions ++ ***************************************************/ ++ ++string_map* initialize_string_map(unsigned char store_keys) ++{ ++ string_map* map = (string_map*)malloc(sizeof(string_map)); ++ if(map != NULL) ++ { ++ map->store_keys = store_keys; ++ map->lm.root = NULL; ++ map->lm.num_elements = 0; ++ map->num_elements = map->lm.num_elements; ++ } ++ return map; ++} ++ ++void* get_string_map_element(string_map* map, const char* key) ++{ ++ unsigned long hashed_key = sdbm_string_hash(key); ++ void* return_value = get_long_map_element( &(map->lm), hashed_key); ++ if(return_value != NULL && map->store_keys) ++ { ++ string_map_key_value* r = (string_map_key_value*)return_value; ++ return_value = r->value; ++ } ++ map->num_elements = map->lm.num_elements; ++ return return_value; ++} ++ ++void* set_string_map_element(string_map* map, const char* key, void* value) ++{ ++ unsigned long hashed_key = sdbm_string_hash(key); ++ void* return_value = NULL; ++ if(map->store_keys) ++ { ++ string_map_key_value* kv = (string_map_key_value*)malloc(sizeof(string_map_key_value)); ++ if(kv == NULL) /* deal with malloc failure */ ++ { ++ return NULL; ++ } ++ kv->key = strdup(key); ++ if(kv->key == NULL) /* deal with malloc failure */ ++ { ++ free(kv); ++ return NULL; ++ } ++ kv->value = value; ++ return_value = set_long_map_element( &(map->lm), hashed_key, kv); ++ if(return_value != NULL) ++ { ++ string_map_key_value* r = (string_map_key_value*)return_value; ++ return_value = r->value; ++ free(r->key); ++ free(r); ++ } ++ } ++ else ++ { ++ return_value = set_long_map_element( &(map->lm), hashed_key, value); ++ } ++ map->num_elements = map->lm.num_elements; ++ return return_value; ++} ++ ++void* remove_string_map_element(string_map* map, const char* key) ++{ ++ unsigned long hashed_key = sdbm_string_hash(key); ++ void* return_value = remove_long_map_element( &(map->lm), hashed_key); ++ ++ if(return_value != NULL && map->store_keys) ++ { ++ string_map_key_value* r = (string_map_key_value*)return_value; ++ return_value = r->value; ++ free(r->key); ++ free(r); ++ } ++ map->num_elements = map->lm.num_elements; ++ return return_value; ++} ++ ++char** get_string_map_keys(string_map* map, unsigned long* num_keys_returned) ++{ ++ char** str_keys; ++ str_keys = (char**)malloc((map->num_elements+1)*sizeof(char*)); ++ if(str_keys == NULL) /* deal with malloc failure */ ++ { ++ return NULL; ++ } ++ str_keys[0] = NULL; ++ *num_keys_returned = 0; ++ if(map->store_keys && map->num_elements > 0) ++ { ++ unsigned long list_length; ++ void** long_values = get_sorted_long_map_values( &(map->lm), &list_length); ++ unsigned long key_index; ++ /*list_length will be 0 on malloc failure in get_sorted_long_map_values, so this code shouldn't seg fault if that happens */ ++ for(key_index = 0; key_index < list_length; key_index++) ++ { ++ str_keys[key_index] = strdup( ((string_map_key_value*)(long_values[key_index]))->key); ++ if(str_keys[key_index] == NULL) /* deal with malloc failure */ ++ { ++ //just return the incomplete list (hey, it's null terminated...) ++ free(long_values); ++ return str_keys; ++ } ++ *num_keys_returned = *num_keys_returned + 1; ++ } ++ str_keys[list_length] = NULL; ++ free(long_values); ++ } ++ return str_keys; ++} ++ ++ ++void** get_string_map_values(string_map* map, unsigned long* num_values_returned) ++{ ++ void** values = NULL; ++ if(map != NULL) ++ { ++ values = get_sorted_long_map_values ( &(map->lm), num_values_returned ); ++ } ++ return values; ++} ++ ++ ++void** destroy_string_map(string_map* map, int destruction_type, unsigned long* num_destroyed) ++{ ++ void** return_values = NULL; ++ if(map != NULL) ++ { ++ if(map->store_keys) ++ { ++ void** kvs = destroy_long_map_values( &(map->lm), DESTROY_MODE_RETURN_VALUES, num_destroyed ); ++ unsigned long kv_index = 0; ++ for(kv_index=0; kv_index < *num_destroyed; kv_index++) ++ { ++ string_map_key_value* kv = (string_map_key_value*)kvs[kv_index]; ++ void* value = kv->value; ++ ++ free(kv->key); ++ free(kv); ++ if(destruction_type == DESTROY_MODE_FREE_VALUES) ++ { ++ free(value); ++ } ++ if(destruction_type == DESTROY_MODE_RETURN_VALUES) ++ { ++ kvs[kv_index] = value; ++ } ++ } ++ if(destruction_type == DESTROY_MODE_RETURN_VALUES) ++ { ++ return_values = kvs; ++ } ++ else ++ { ++ free(kvs); ++ } ++ } ++ else ++ { ++ return_values = destroy_long_map_values( &(map->lm), destruction_type, num_destroyed ); ++ } ++ free(map); ++ } ++ return return_values; ++} ++ ++ ++ ++ ++/*************************************************** ++ * long_map function definitions ++ ***************************************************/ ++ ++long_map* initialize_long_map(void) ++{ ++ long_map* map = (long_map*)malloc(sizeof(long_map)); ++ if(map != NULL) /* test for malloc failure */ ++ { ++ map->root = NULL; ++ map->num_elements = 0; ++ } ++ return map; ++} ++ ++void* get_long_map_element(long_map* map, unsigned long key) ++{ ++ void* value = NULL; ++ ++ if(map->root != NULL) ++ { ++ long_map_node* parent_node = map->root; ++ long_map_node* next_node; ++ while( key != parent_node->key && (next_node = (long_map_node *)(key < parent_node->key ? parent_node->left : parent_node->right)) != NULL) ++ { ++ parent_node = next_node; ++ } ++ if(parent_node->key == key) ++ { ++ value = parent_node->value; ++ } ++ } ++ return value; ++} ++ ++void* get_smallest_long_map_element(long_map* map, unsigned long* smallest_key) ++{ ++ void* value = NULL; ++ if(map->root != NULL) ++ { ++ long_map_node* next_node = map->root; ++ while( next_node->left != NULL) ++ { ++ next_node = next_node->left; ++ } ++ value = next_node->value; ++ *smallest_key = next_node->key; ++ } ++ return value; ++} ++ ++void* get_largest_long_map_element(long_map* map, unsigned long* largest_key) ++{ ++ void* value = NULL; ++ if(map->root != NULL) ++ { ++ long_map_node* next_node = map->root; ++ while( next_node->right != NULL) ++ { ++ next_node = next_node->right; ++ } ++ value = next_node->value; ++ *largest_key = next_node->key; ++ } ++ return value; ++} ++ ++void* remove_smallest_long_map_element(long_map* map, unsigned long* smallest_key) ++{ ++ get_smallest_long_map_element(map, smallest_key); ++ return remove_long_map_element(map, *smallest_key); ++} ++ ++void* remove_largest_long_map_element(long_map* map, unsigned long* largest_key) ++{ ++ get_largest_long_map_element(map, largest_key); ++ return remove_long_map_element(map, *largest_key); ++} ++ ++ ++/* if replacement performed, returns replaced value, otherwise null */ ++void* set_long_map_element(long_map* map, unsigned long key, void* value) ++{ ++ stack_node* parent_list = NULL; ++ void* old_value = NULL; ++ int old_value_found = 0; ++ ++ long_map_node* parent_node; ++ long_map_node* next_node; ++ stack_node* next_parent; ++ stack_node* previous_parent; ++ signed char new_balance; ++ ++ ++ long_map_node* new_node = (long_map_node*)malloc(sizeof(long_map_node)); ++ if(new_node == NULL) ++ { ++ return NULL; ++ } ++ new_node->value = value; ++ new_node->key = key; ++ new_node->left = NULL; ++ new_node->right = NULL; ++ new_node->balance = 0; ++ ++ ++ ++ if(map->root == NULL) ++ { ++ map->root = new_node; ++ } ++ else ++ { ++ parent_node = map->root; ++ ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ free(new_node); ++ return NULL; /* won't insert but won't seg fault */ ++ } ++ next_parent->node_ptr = &(map->root); ++ next_parent->previous = parent_list; ++ parent_list = next_parent; ++ ++ while( key != parent_node->key && (next_node = (key < parent_node->key ? parent_node->left : parent_node->right) ) != NULL) ++ { ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ free(new_node); ++ return NULL; ++ } ++ next_parent->node_ptr = key < parent_node->key ? &(parent_node->left) : &(parent_node->right); ++ next_parent->previous = parent_list; ++ next_parent->previous->direction = key < parent_node->key ? -1 : 1; ++ parent_list = next_parent; ++ ++ parent_node = next_node; ++ } ++ ++ ++ if(key == parent_node->key) ++ { ++ old_value = parent_node->value; ++ old_value_found = 1; ++ parent_node->value = value; ++ free(new_node); ++ /* we merely replaced a node, no need to rebalance */ ++ } ++ else ++ { ++ if(key < parent_node->key) ++ { ++ parent_node->left = (void*)new_node; ++ parent_list->direction = -1; ++ } ++ else ++ { ++ parent_node->right = (void*)new_node; ++ parent_list->direction = 1; ++ } ++ ++ ++ /* we inserted a node, rebalance */ ++ previous_parent = parent_list; ++ new_balance = 1; /* initial value is not used, but must not be 0 for initial loop condition */ ++ ++ ++ while(previous_parent != NULL && new_balance != 0) ++ { ++ new_balance = rebalance(previous_parent->node_ptr, previous_parent->direction, 1); ++ previous_parent = previous_parent->previous; ++ } ++ } ++ } ++ ++ free_stack(parent_list); ++ ++ if(old_value_found == 0) ++ { ++ map->num_elements = map->num_elements + 1; ++ } ++ ++ return old_value; ++} ++ ++ ++void* remove_long_map_element(long_map* map, unsigned long key) ++{ ++ ++ void* value = NULL; ++ ++ long_map_node* root_node = map->root; ++ stack_node* parent_list = NULL; ++ ++ ++ long_map_node* remove_parent; ++ long_map_node* remove_node; ++ long_map_node* next_node; ++ ++ long_map_node* replacement; ++ long_map_node* replacement_parent; ++ long_map_node* replacement_next; ++ ++ stack_node* next_parent; ++ stack_node* previous_parent; ++ stack_node* replacement_stack_node; ++ ++ ++ signed char new_balance; ++ ++ ++ ++ if(root_node != NULL) ++ { ++ remove_parent = root_node; ++ remove_node = key < remove_parent->key ? remove_parent->left : remove_parent->right; ++ ++ if(remove_node != NULL && key != remove_parent->key) ++ { ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ return NULL; ++ } ++ next_parent->node_ptr = &(map->root); ++ next_parent->previous = parent_list; ++ parent_list = next_parent; ++ while( key != remove_node->key && (next_node = (key < remove_node->key ? remove_node->left : remove_node->right)) != NULL) ++ { ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ next_parent->node_ptr = key < remove_parent->key ? &(remove_parent->left) : &(remove_parent->right); ++ next_parent->previous = parent_list; ++ next_parent->previous->direction = key < remove_parent->key ? -1 : 1; ++ parent_list = next_parent; ++ ++ ++ remove_parent = remove_node; ++ remove_node = next_node; ++ } ++ parent_list->direction = key < remove_parent-> key ? -1 : 1; ++ } ++ else ++ { ++ remove_node = remove_parent; ++ } ++ ++ ++ if(key == remove_node->key) ++ { ++ ++ /* find replacement for node we are deleting */ ++ if( remove_node->right == NULL ) ++ { ++ replacement = remove_node->left; ++ } ++ else if( remove_node->right->left == NULL) ++ { ++ ++ replacement = remove_node->right; ++ replacement->left = remove_node->left; ++ replacement->balance = remove_node->balance; ++ ++ /* put pointer to replacement node into list for balance update */ ++ replacement_stack_node = (stack_node*)malloc(sizeof(stack_node)); ++ if(replacement_stack_node == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ replacement_stack_node->previous = parent_list; ++ replacement_stack_node->direction = 1; /* replacement is from right */ ++ if(remove_node == remove_parent) /* special case for root node */ ++ { ++ replacement_stack_node->node_ptr = &(map->root); ++ } ++ else ++ { ++ replacement_stack_node->node_ptr = key < remove_parent-> key ? &(remove_parent->left) : &(remove_parent->right); ++ } ++ parent_list = replacement_stack_node; ++ ++ } ++ else ++ { ++ /* put pointer to replacement node into list for balance update */ ++ replacement_stack_node = (stack_node*)malloc(sizeof(stack_node)); ++ if(replacement_stack_node == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ ++ replacement_stack_node->previous = parent_list; ++ replacement_stack_node->direction = 1; /* we always look for replacement on right */ ++ if(remove_node == remove_parent) /* special case for root node */ ++ { ++ replacement_stack_node->node_ptr = &(map->root); ++ } ++ else ++ { ++ replacement_stack_node->node_ptr = key < remove_parent-> key ? &(remove_parent->left) : &(remove_parent->right); ++ } ++ ++ parent_list = replacement_stack_node; ++ ++ ++ /* ++ * put pointer to replacement node->right into list for balance update ++ * this node will have to be updated with the proper pointer ++ * after we have identified the replacement ++ */ ++ replacement_stack_node = (stack_node*)malloc(sizeof(stack_node)); ++ if(replacement_stack_node == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ ++ replacement_stack_node->previous = parent_list; ++ replacement_stack_node->direction = -1; /* we always look for replacement to left of this node */ ++ parent_list = replacement_stack_node; ++ ++ /* find smallest node on right (large) side of tree */ ++ replacement_parent = remove_node->right; ++ replacement = replacement_parent->left; ++ ++ while((replacement_next = replacement->left) != NULL) ++ { ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ ++ next_parent->node_ptr = &(replacement_parent->left); ++ next_parent->previous = parent_list; ++ next_parent->direction = -1; /* we always go left */ ++ parent_list = next_parent; ++ ++ replacement_parent = replacement; ++ replacement = replacement_next; ++ ++ } ++ ++ replacement_parent->left = replacement->right; ++ ++ replacement->left = remove_node->left; ++ replacement->right = remove_node->right; ++ replacement->balance = remove_node->balance; ++ replacement_stack_node->node_ptr = &(replacement->right); ++ } ++ ++ /* insert replacement at proper location in tree */ ++ if(remove_node == remove_parent) ++ { ++ map->root = replacement; ++ } ++ else ++ { ++ remove_parent->left = remove_node == remove_parent->left ? replacement : remove_parent->left; ++ remove_parent->right = remove_node == remove_parent->right ? replacement : remove_parent->right; ++ } ++ ++ ++ /* rebalance tree */ ++ previous_parent = parent_list; ++ new_balance = 0; ++ while(previous_parent != NULL && new_balance == 0) ++ { ++ new_balance = rebalance(previous_parent->node_ptr, previous_parent->direction, -1); ++ previous_parent = previous_parent->previous; ++ } ++ ++ ++ ++ ++ /* ++ * since we found a value to remove, decrease number of elements in map ++ * set return value to the deleted node's value and free the node ++ */ ++ map->num_elements = map->num_elements - 1; ++ value = remove_node->value; ++ free(remove_node); ++ } ++ } ++ ++ free_stack(parent_list); ++ ++ return value; ++} ++ ++ ++/* note: returned keys are dynamically allocated, you need to free them! */ ++unsigned long* get_sorted_long_map_keys(long_map* map, unsigned long* num_keys_returned) ++{ ++ unsigned long* key_list = (unsigned long*)malloc((map->num_elements)*sizeof(unsigned long)); ++ unsigned long next_key_index; ++ if(key_list == NULL) ++ { ++ *num_keys_returned = 0; ++ return NULL; ++ } ++ next_key_index = 0; ++ get_sorted_node_keys(map->root, key_list, &next_key_index, 0); ++ ++ *num_keys_returned = map->num_elements; ++ ++ return key_list; ++} ++ ++ ++void** get_sorted_long_map_values(long_map* map, unsigned long* num_values_returned) ++{ ++ void** value_list = (void**)malloc((map->num_elements+1)*sizeof(void*)); ++ unsigned long next_value_index; ++ ++ if(value_list == NULL) ++ { ++ *num_values_returned = 0; ++ return NULL; ++ } ++ next_value_index = 0; ++ get_sorted_node_values(map->root, value_list, &next_value_index, 0); ++ value_list[map->num_elements] = NULL; /* since we're dealing with pointers make list null terminated */ ++ ++ *num_values_returned = map->num_elements; ++ return value_list; ++ ++} ++ ++ ++ ++void** destroy_long_map(long_map* map, int destruction_type, unsigned long* num_destroyed) ++{ ++ void** return_values = destroy_long_map_values(map, destruction_type, num_destroyed); ++ free(map); ++ return return_values; ++} ++ ++ ++ ++void apply_to_every_long_map_value(long_map* map, void (*apply_func)(unsigned long key, void* value)) ++{ ++ apply_to_every_long_map_node(map->root, apply_func); ++} ++void apply_to_every_string_map_value(string_map* map, void (*apply_func)(char* key, void* value)) ++{ ++ apply_to_every_string_map_node( (map->lm).root, map->store_keys, apply_func); ++} ++ ++ ++/*************************************************** ++ * internal utility function definitions ++ ***************************************************/ ++static void free_stack(stack_node* stack) ++{ ++ while(stack != NULL) ++ { ++ stack_node* prev_node = stack; ++ stack = prev_node->previous; ++ free(prev_node); ++ } ++ ++} ++ ++static void** destroy_long_map_values(long_map* map, int destruction_type, unsigned long* num_destroyed) ++{ ++ void** return_values = NULL; ++ unsigned long return_index = 0; ++ ++ *num_destroyed = 0; ++ ++ if(destruction_type == DESTROY_MODE_RETURN_VALUES) ++ { ++ return_values = (void**)malloc((map->num_elements+1)*sizeof(void*)); ++ if(return_values == NULL) /* deal with malloc failure */ ++ { ++ destruction_type = DESTROY_MODE_IGNORE_VALUES; /* could cause memory leak, but there's no other way to be sure we won't seg fault */ ++ } ++ else ++ { ++ return_values[map->num_elements] = NULL; ++ } ++ } ++ while(map->num_elements > 0) ++ { ++ unsigned long smallest_key; ++ void* removed_value = remove_smallest_long_map_element(map, &smallest_key); ++ if(destruction_type == DESTROY_MODE_RETURN_VALUES) ++ { ++ return_values[return_index] = removed_value; ++ } ++ if(destruction_type == DESTROY_MODE_FREE_VALUES) ++ { ++ free(removed_value); ++ } ++ return_index++; ++ *num_destroyed = *num_destroyed + 1; ++ } ++ return return_values; ++} ++ ++static void apply_to_every_long_map_node(long_map_node* node, void (*apply_func)(unsigned long key, void* value)) ++{ ++ if(node != NULL) ++ { ++ apply_to_every_long_map_node(node->left, apply_func); ++ ++ apply_func(node->key, node->value); ++ ++ apply_to_every_long_map_node(node->right, apply_func); ++ } ++} ++static void apply_to_every_string_map_node(long_map_node* node, unsigned char has_key, void (*apply_func)(char* key, void* value)) ++{ ++ if(node != NULL) ++ { ++ apply_to_every_string_map_node(node->left, has_key, apply_func); ++ ++ if(has_key) ++ { ++ string_map_key_value* kv = (string_map_key_value*)(node->value); ++ apply_func(kv->key, kv->value); ++ } ++ else ++ { ++ apply_func(NULL, node->value); ++ } ++ apply_to_every_string_map_node(node->right, has_key, apply_func); ++ } ++} ++ ++ ++ ++static void get_sorted_node_keys(long_map_node* node, unsigned long* key_list, unsigned long* next_key_index, int depth) ++{ ++ if(node != NULL) ++ { ++ get_sorted_node_keys(node->left, key_list, next_key_index, depth+1); ++ ++ key_list[ *next_key_index ] = node->key; ++ (*next_key_index)++; ++ ++ get_sorted_node_keys(node->right, key_list, next_key_index, depth+1); ++ } ++} ++ ++static void get_sorted_node_values(long_map_node* node, void** value_list, unsigned long* next_value_index, int depth) ++{ ++ if(node != NULL) ++ { ++ get_sorted_node_values(node->left, value_list, next_value_index, depth+1); ++ ++ value_list[ *next_value_index ] = node->value; ++ (*next_value_index)++; ++ ++ get_sorted_node_values(node->right, value_list, next_value_index, depth+1); ++ } ++} ++ ++ ++ ++/* ++ * direction = -1 indicates left subtree updated, direction = 1 for right subtree ++ * update_op = -1 indicates delete node, update_op = 1 for insert node ++ */ ++static signed char rebalance (long_map_node** n, signed char direction, signed char update_op) ++{ ++ /* ++ printf( "original: key = %ld, balance = %d, update_op=%d, direction=%d\n", (*n)->key, (*n)->balance, update_op, direction); ++ */ ++ ++ (*n)->balance = (*n)->balance + (update_op*direction); ++ ++ if( (*n)->balance < -1) ++ { ++ if((*n)->left->balance < 0) ++ { ++ rotate_right(n); ++ (*n)->right->balance = 0; ++ (*n)->balance = 0; ++ } ++ else if((*n)->left->balance == 0) ++ { ++ rotate_right(n); ++ (*n)->right->balance = -1; ++ (*n)->balance = 1; ++ } ++ else if((*n)->left->balance > 0) ++ { ++ rotate_left( &((*n)->left) ); ++ rotate_right(n); ++ /* ++ if( (*n)->balance < 0 ) ++ { ++ (*n)->left->balance = 0; ++ (*n)->right->balance = 1; ++ } ++ else if( (*n)->balance == 0 ) ++ { ++ (*n)->left->balance = 0; ++ (*n)->right->balance = 0; ++ } ++ else if( (*n)->balance > 0 ) ++ { ++ (*n)->left->balance = -1; ++ (*n)->right->balance = 0; ++ } ++ */ ++ (*n)->left->balance = (*n)->balance > 0 ? -1 : 0; ++ (*n)->right->balance = (*n)->balance < 0 ? 1 : 0; ++ (*n)->balance = 0; ++ } ++ } ++ if( (*n)->balance > 1) ++ { ++ if((*n)->right->balance > 0) ++ { ++ rotate_left(n); ++ (*n)->left->balance = 0; ++ (*n)->balance = 0; ++ } ++ else if ((*n)->right->balance == 0) ++ { ++ rotate_left(n); ++ (*n)->left->balance = 1; ++ (*n)->balance = -1; ++ } ++ else if((*n)->right->balance < 0) ++ { ++ rotate_right( &((*n)->right) ); ++ rotate_left(n); ++ /* ++ if( (*n)->balance < 0 ) ++ { ++ (*n)->left->balance = 0; ++ (*n)->right->balance = 1; ++ } ++ else if( (*n)->balance == 0 ) ++ { ++ (*n)->left->balance = 0; ++ (*n)->right->balance = 0; ++ } ++ else if( (*n)->balance > 0 ) ++ { ++ (*n)->left->balance = -1; ++ (*n)->right->balance = 0; ++ } ++ */ ++ (*n)->left->balance = (*n)->balance > 0 ? -1 : 0; ++ (*n)->right->balance = (*n)->balance < 0 ? 1 : 0; ++ (*n)->balance = 0; ++ } ++ } ++ ++ /* ++ printf( "key = %ld, balance = %d\n", (*n)->key, (*n)->balance); ++ */ ++ ++ return (*n)->balance; ++} ++ ++ ++static void rotate_right (long_map_node** parent) ++{ ++ long_map_node* old_parent = *parent; ++ long_map_node* pivot = old_parent->left; ++ old_parent->left = pivot->right; ++ pivot->right = old_parent; ++ ++ *parent = pivot; ++} ++ ++static void rotate_left (long_map_node** parent) ++{ ++ long_map_node* old_parent = *parent; ++ long_map_node* pivot = old_parent->right; ++ old_parent->right = pivot->left; ++ pivot->left = old_parent; ++ ++ *parent = pivot; ++} ++ ++ ++ ++/*************************************************************************** ++ * This algorithm was created for the sdbm database library (a public-domain ++ * reimplementation of ndbm) and seems to work relatively well in ++ * scrambling bits ++ * ++ * ++ * This code was derived from code found at: ++ * http://www.cse.yorku.ca/~oz/hash.html ++ ***************************************************************************/ ++static unsigned long sdbm_string_hash(const char *key) ++{ ++ unsigned long hashed_key = 0; ++ ++ int index = 0; ++ unsigned int nextch; ++ while(key[index] != '\0') ++ { ++ nextch = key[index]; ++ hashed_key = nextch + (hashed_key << 6) + (hashed_key << 16) - hashed_key; ++ index++; ++ } ++ return hashed_key; ++} ++ ++ +--- /dev/null ++++ b/net/ipv4/netfilter/weburl_deps/regexp.c +@@ -0,0 +1,1197 @@ ++/* ++ * regcomp and regexec -- regsub and regerror are elsewhere ++ * @(#)regexp.c 1.3 of 18 April 87 ++ * ++ * Copyright (c) 1986 by University of Toronto. ++ * Written by Henry Spencer. Not derived from licensed software. ++ * ++ * Permission is granted to anyone to use this software for any ++ * purpose on any computer system, and to redistribute it freely, ++ * subject to the following restrictions: ++ * ++ * 1. The author is not responsible for the consequences of use of ++ * this software, no matter how awful, even if they arise ++ * from defects in it. ++ * ++ * 2. The origin of this software must not be misrepresented, either ++ * by explicit claim or by omission. ++ * ++ * 3. Altered versions must be plainly marked as such, and must not ++ * be misrepresented as being the original software. ++ * ++ * Beware that some of this code is subtly aware of the way operator ++ * precedence is structured in regular expressions. Serious changes in ++ * regular-expression syntax might require a total rethink. ++ * ++ * This code was modified by Ethan Sommer to work within the kernel ++ * (it now uses kmalloc etc..) ++ * ++ * Modified slightly by Matthew Strait to use more modern C. ++ */ ++ ++#include "regexp.h" ++#include "regmagic.h" ++ ++/* added by ethan and matt. Lets it work in both kernel and user space. ++(So iptables can use it, for instance.) Yea, it goes both ways... */ ++#if __KERNEL__ ++ #define malloc(foo) kmalloc(foo,GFP_ATOMIC) ++#else ++ #define printk(format,args...) printf(format,##args) ++#endif ++ ++void regerror(char * s) ++{ ++ printk("<3>Regexp: %s\n", s); ++ /* NOTREACHED */ ++} ++ ++/* ++ * The "internal use only" fields in regexp.h are present to pass info from ++ * compile to execute that permits the execute phase to run lots faster on ++ * simple cases. They are: ++ * ++ * regstart char that must begin a match; '\0' if none obvious ++ * reganch is the match anchored (at beginning-of-line only)? ++ * regmust string (pointer into program) that match must include, or NULL ++ * regmlen length of regmust string ++ * ++ * Regstart and reganch permit very fast decisions on suitable starting points ++ * for a match, cutting down the work a lot. Regmust permits fast rejection ++ * of lines that cannot possibly match. The regmust tests are costly enough ++ * that regcomp() supplies a regmust only if the r.e. contains something ++ * potentially expensive (at present, the only such thing detected is * or + ++ * at the start of the r.e., which can involve a lot of backup). Regmlen is ++ * supplied because the test in regexec() needs it and regcomp() is computing ++ * it anyway. ++ */ ++ ++/* ++ * Structure for regexp "program". This is essentially a linear encoding ++ * of a nondeterministic finite-state machine (aka syntax charts or ++ * "railroad normal form" in parsing technology). Each node is an opcode ++ * plus a "next" pointer, possibly plus an operand. "Next" pointers of ++ * all nodes except BRANCH implement concatenation; a "next" pointer with ++ * a BRANCH on both ends of it is connecting two alternatives. (Here we ++ * have one of the subtle syntax dependencies: an individual BRANCH (as ++ * opposed to a collection of them) is never concatenated with anything ++ * because of operator precedence.) The operand of some types of node is ++ * a literal string; for others, it is a node leading into a sub-FSM. In ++ * particular, the operand of a BRANCH node is the first node of the branch. ++ * (NB this is *not* a tree structure: the tail of the branch connects ++ * to the thing following the set of BRANCHes.) The opcodes are: ++ */ ++ ++/* definition number opnd? meaning */ ++#define END 0 /* no End of program. */ ++#define BOL 1 /* no Match "" at beginning of line. */ ++#define EOL 2 /* no Match "" at end of line. */ ++#define ANY 3 /* no Match any one character. */ ++#define ANYOF 4 /* str Match any character in this string. */ ++#define ANYBUT 5 /* str Match any character not in this string. */ ++#define BRANCH 6 /* node Match this alternative, or the next... */ ++#define BACK 7 /* no Match "", "next" ptr points backward. */ ++#define EXACTLY 8 /* str Match this string. */ ++#define NOTHING 9 /* no Match empty string. */ ++#define STAR 10 /* node Match this (simple) thing 0 or more times. */ ++#define PLUS 11 /* node Match this (simple) thing 1 or more times. */ ++#define OPEN 20 /* no Mark this point in input as start of #n. */ ++ /* OPEN+1 is number 1, etc. */ ++#define CLOSE 30 /* no Analogous to OPEN. */ ++ ++/* ++ * Opcode notes: ++ * ++ * BRANCH The set of branches constituting a single choice are hooked ++ * together with their "next" pointers, since precedence prevents ++ * anything being concatenated to any individual branch. The ++ * "next" pointer of the last BRANCH in a choice points to the ++ * thing following the whole choice. This is also where the ++ * final "next" pointer of each individual branch points; each ++ * branch starts with the operand node of a BRANCH node. ++ * ++ * BACK Normal "next" pointers all implicitly point forward; BACK ++ * exists to make loop structures possible. ++ * ++ * STAR,PLUS '?', and complex '*' and '+', are implemented as circular ++ * BRANCH structures using BACK. Simple cases (one character ++ * per match) are implemented with STAR and PLUS for speed ++ * and to minimize recursive plunges. ++ * ++ * OPEN,CLOSE ...are numbered at compile time. ++ */ ++ ++/* ++ * A node is one char of opcode followed by two chars of "next" pointer. ++ * "Next" pointers are stored as two 8-bit pieces, high order first. The ++ * value is a positive offset from the opcode of the node containing it. ++ * An operand, if any, simply follows the node. (Note that much of the ++ * code generation knows about this implicit relationship.) ++ * ++ * Using two bytes for the "next" pointer is vast overkill for most things, ++ * but allows patterns to get big without disasters. ++ */ ++#define OP(p) (*(p)) ++#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377)) ++#define OPERAND(p) ((p) + 3) ++ ++/* ++ * See regmagic.h for one further detail of program structure. ++ */ ++ ++ ++/* ++ * Utility definitions. ++ */ ++#ifndef CHARBITS ++#define UCHARAT(p) ((int)*(unsigned char *)(p)) ++#else ++#define UCHARAT(p) ((int)*(p)&CHARBITS) ++#endif ++ ++#define FAIL(m) { regerror(m); return(NULL); } ++#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?') ++#define META "^$.[()|?+*\\" ++ ++/* ++ * Flags to be passed up and down. ++ */ ++#define HASWIDTH 01 /* Known never to match null string. */ ++#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */ ++#define SPSTART 04 /* Starts with * or +. */ ++#define WORST 0 /* Worst case. */ ++ ++/* ++ * Global work variables for regcomp(). ++ */ ++struct match_globals { ++char *reginput; /* String-input pointer. */ ++char *regbol; /* Beginning of input, for ^ check. */ ++char **regstartp; /* Pointer to startp array. */ ++char **regendp; /* Ditto for endp. */ ++char *regparse; /* Input-scan pointer. */ ++int regnpar; /* () count. */ ++char regdummy; ++char *regcode; /* Code-emit pointer; ®dummy = don't. */ ++long regsize; /* Code size. */ ++}; ++ ++/* ++ * Forward declarations for regcomp()'s friends. ++ */ ++#ifndef STATIC ++#define STATIC static ++#endif ++STATIC char *reg(struct match_globals *g, int paren,int *flagp); ++STATIC char *regbranch(struct match_globals *g, int *flagp); ++STATIC char *regpiece(struct match_globals *g, int *flagp); ++STATIC char *regatom(struct match_globals *g, int *flagp); ++STATIC char *regnode(struct match_globals *g, char op); ++STATIC char *regnext(struct match_globals *g, char *p); ++STATIC void regc(struct match_globals *g, char b); ++STATIC void reginsert(struct match_globals *g, char op, char *opnd); ++STATIC void regtail(struct match_globals *g, char *p, char *val); ++STATIC void regoptail(struct match_globals *g, char *p, char *val); ++ ++ ++__kernel_size_t my_strcspn(const char *s1,const char *s2) ++{ ++ char *scan1; ++ char *scan2; ++ int count; ++ ++ count = 0; ++ for (scan1 = (char *)s1; *scan1 != '\0'; scan1++) { ++ for (scan2 = (char *)s2; *scan2 != '\0';) /* ++ moved down. */ ++ if (*scan1 == *scan2++) ++ return(count); ++ count++; ++ } ++ return(count); ++} ++ ++/* ++ - regcomp - compile a regular expression into internal code ++ * ++ * We can't allocate space until we know how big the compiled form will be, ++ * but we can't compile it (and thus know how big it is) until we've got a ++ * place to put the code. So we cheat: we compile it twice, once with code ++ * generation turned off and size counting turned on, and once "for real". ++ * This also means that we don't allocate space until we are sure that the ++ * thing really will compile successfully, and we never have to move the ++ * code and thus invalidate pointers into it. (Note that it has to be in ++ * one piece because free() must be able to free it all.) ++ * ++ * Beware that the optimization-preparation code in here knows about some ++ * of the structure of the compiled regexp. ++ */ ++regexp * ++regcomp(char *exp,int *patternsize) ++{ ++ register regexp *r; ++ register char *scan; ++ register char *longest; ++ register int len; ++ int flags; ++ struct match_globals g; ++ ++ /* commented out by ethan ++ extern char *malloc(); ++ */ ++ ++ if (exp == NULL) ++ FAIL("NULL argument"); ++ ++ /* First pass: determine size, legality. */ ++ g.regparse = exp; ++ g.regnpar = 1; ++ g.regsize = 0L; ++ g.regcode = &g.regdummy; ++ regc(&g, MAGIC); ++ if (reg(&g, 0, &flags) == NULL) ++ return(NULL); ++ ++ /* Small enough for pointer-storage convention? */ ++ if (g.regsize >= 32767L) /* Probably could be 65535L. */ ++ FAIL("regexp too big"); ++ ++ /* Allocate space. */ ++ *patternsize=sizeof(regexp) + (unsigned)g.regsize; ++ r = (regexp *)malloc(sizeof(regexp) + (unsigned)g.regsize); ++ if (r == NULL) ++ FAIL("out of space"); ++ ++ /* Second pass: emit code. */ ++ g.regparse = exp; ++ g.regnpar = 1; ++ g.regcode = r->program; ++ regc(&g, MAGIC); ++ if (reg(&g, 0, &flags) == NULL) ++ return(NULL); ++ ++ /* Dig out information for optimizations. */ ++ r->regstart = '\0'; /* Worst-case defaults. */ ++ r->reganch = 0; ++ r->regmust = NULL; ++ r->regmlen = 0; ++ scan = r->program+1; /* First BRANCH. */ ++ if (OP(regnext(&g, scan)) == END) { /* Only one top-level choice. */ ++ scan = OPERAND(scan); ++ ++ /* Starting-point info. */ ++ if (OP(scan) == EXACTLY) ++ r->regstart = *OPERAND(scan); ++ else if (OP(scan) == BOL) ++ r->reganch++; ++ ++ /* ++ * If there's something expensive in the r.e., find the ++ * longest literal string that must appear and make it the ++ * regmust. Resolve ties in favor of later strings, since ++ * the regstart check works with the beginning of the r.e. ++ * and avoiding duplication strengthens checking. Not a ++ * strong reason, but sufficient in the absence of others. ++ */ ++ if (flags&SPSTART) { ++ longest = NULL; ++ len = 0; ++ for (; scan != NULL; scan = regnext(&g, scan)) ++ if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { ++ longest = OPERAND(scan); ++ len = strlen(OPERAND(scan)); ++ } ++ r->regmust = longest; ++ r->regmlen = len; ++ } ++ } ++ ++ return(r); ++} ++ ++/* ++ - reg - regular expression, i.e. main body or parenthesized thing ++ * ++ * Caller must absorb opening parenthesis. ++ * ++ * Combining parenthesis handling with the base level of regular expression ++ * is a trifle forced, but the need to tie the tails of the branches to what ++ * follows makes it hard to avoid. ++ */ ++static char * ++reg(struct match_globals *g, int paren, int *flagp /* Parenthesized? */ ) ++{ ++ register char *ret; ++ register char *br; ++ register char *ender; ++ register int parno = 0; /* 0 makes gcc happy */ ++ int flags; ++ ++ *flagp = HASWIDTH; /* Tentatively. */ ++ ++ /* Make an OPEN node, if parenthesized. */ ++ if (paren) { ++ if (g->regnpar >= NSUBEXP) ++ FAIL("too many ()"); ++ parno = g->regnpar; ++ g->regnpar++; ++ ret = regnode(g, OPEN+parno); ++ } else ++ ret = NULL; ++ ++ /* Pick up the branches, linking them together. */ ++ br = regbranch(g, &flags); ++ if (br == NULL) ++ return(NULL); ++ if (ret != NULL) ++ regtail(g, ret, br); /* OPEN -> first. */ ++ else ++ ret = br; ++ if (!(flags&HASWIDTH)) ++ *flagp &= ~HASWIDTH; ++ *flagp |= flags&SPSTART; ++ while (*g->regparse == '|') { ++ g->regparse++; ++ br = regbranch(g, &flags); ++ if (br == NULL) ++ return(NULL); ++ regtail(g, ret, br); /* BRANCH -> BRANCH. */ ++ if (!(flags&HASWIDTH)) ++ *flagp &= ~HASWIDTH; ++ *flagp |= flags&SPSTART; ++ } ++ ++ /* Make a closing node, and hook it on the end. */ ++ ender = regnode(g, (paren) ? CLOSE+parno : END); ++ regtail(g, ret, ender); ++ ++ /* Hook the tails of the branches to the closing node. */ ++ for (br = ret; br != NULL; br = regnext(g, br)) ++ regoptail(g, br, ender); ++ ++ /* Check for proper termination. */ ++ if (paren && *g->regparse++ != ')') { ++ FAIL("unmatched ()"); ++ } else if (!paren && *g->regparse != '\0') { ++ if (*g->regparse == ')') { ++ FAIL("unmatched ()"); ++ } else ++ FAIL("junk on end"); /* "Can't happen". */ ++ /* NOTREACHED */ ++ } ++ ++ return(ret); ++} ++ ++/* ++ - regbranch - one alternative of an | operator ++ * ++ * Implements the concatenation operator. ++ */ ++static char * ++regbranch(struct match_globals *g, int *flagp) ++{ ++ register char *ret; ++ register char *chain; ++ register char *latest; ++ int flags; ++ ++ *flagp = WORST; /* Tentatively. */ ++ ++ ret = regnode(g, BRANCH); ++ chain = NULL; ++ while (*g->regparse != '\0' && *g->regparse != '|' && *g->regparse != ')') { ++ latest = regpiece(g, &flags); ++ if (latest == NULL) ++ return(NULL); ++ *flagp |= flags&HASWIDTH; ++ if (chain == NULL) /* First piece. */ ++ *flagp |= flags&SPSTART; ++ else ++ regtail(g, chain, latest); ++ chain = latest; ++ } ++ if (chain == NULL) /* Loop ran zero times. */ ++ (void) regnode(g, NOTHING); ++ ++ return(ret); ++} ++ ++/* ++ - regpiece - something followed by possible [*+?] ++ * ++ * Note that the branching code sequences used for ? and the general cases ++ * of * and + are somewhat optimized: they use the same NOTHING node as ++ * both the endmarker for their branch list and the body of the last branch. ++ * It might seem that this node could be dispensed with entirely, but the ++ * endmarker role is not redundant. ++ */ ++static char * ++regpiece(struct match_globals *g, int *flagp) ++{ ++ register char *ret; ++ register char op; ++ register char *next; ++ int flags; ++ ++ ret = regatom(g, &flags); ++ if (ret == NULL) ++ return(NULL); ++ ++ op = *g->regparse; ++ if (!ISMULT(op)) { ++ *flagp = flags; ++ return(ret); ++ } ++ ++ if (!(flags&HASWIDTH) && op != '?') ++ FAIL("*+ operand could be empty"); ++ *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH); ++ ++ if (op == '*' && (flags&SIMPLE)) ++ reginsert(g, STAR, ret); ++ else if (op == '*') { ++ /* Emit x* as (x&|), where & means "self". */ ++ reginsert(g, BRANCH, ret); /* Either x */ ++ regoptail(g, ret, regnode(g, BACK)); /* and loop */ ++ regoptail(g, ret, ret); /* back */ ++ regtail(g, ret, regnode(g, BRANCH)); /* or */ ++ regtail(g, ret, regnode(g, NOTHING)); /* null. */ ++ } else if (op == '+' && (flags&SIMPLE)) ++ reginsert(g, PLUS, ret); ++ else if (op == '+') { ++ /* Emit x+ as x(&|), where & means "self". */ ++ next = regnode(g, BRANCH); /* Either */ ++ regtail(g, ret, next); ++ regtail(g, regnode(g, BACK), ret); /* loop back */ ++ regtail(g, next, regnode(g, BRANCH)); /* or */ ++ regtail(g, ret, regnode(g, NOTHING)); /* null. */ ++ } else if (op == '?') { ++ /* Emit x? as (x|) */ ++ reginsert(g, BRANCH, ret); /* Either x */ ++ regtail(g, ret, regnode(g, BRANCH)); /* or */ ++ next = regnode(g, NOTHING); /* null. */ ++ regtail(g, ret, next); ++ regoptail(g, ret, next); ++ } ++ g->regparse++; ++ if (ISMULT(*g->regparse)) ++ FAIL("nested *?+"); ++ ++ return(ret); ++} ++ ++/* ++ - regatom - the lowest level ++ * ++ * Optimization: gobbles an entire sequence of ordinary characters so that ++ * it can turn them into a single node, which is smaller to store and ++ * faster to run. Backslashed characters are exceptions, each becoming a ++ * separate node; the code is simpler that way and it's not worth fixing. ++ */ ++static char * ++regatom(struct match_globals *g, int *flagp) ++{ ++ register char *ret; ++ int flags; ++ ++ *flagp = WORST; /* Tentatively. */ ++ ++ switch (*g->regparse++) { ++ case '^': ++ ret = regnode(g, BOL); ++ break; ++ case '$': ++ ret = regnode(g, EOL); ++ break; ++ case '.': ++ ret = regnode(g, ANY); ++ *flagp |= HASWIDTH|SIMPLE; ++ break; ++ case '[': { ++ register int class; ++ register int classend; ++ ++ if (*g->regparse == '^') { /* Complement of range. */ ++ ret = regnode(g, ANYBUT); ++ g->regparse++; ++ } else ++ ret = regnode(g, ANYOF); ++ if (*g->regparse == ']' || *g->regparse == '-') ++ regc(g, *g->regparse++); ++ while (*g->regparse != '\0' && *g->regparse != ']') { ++ if (*g->regparse == '-') { ++ g->regparse++; ++ if (*g->regparse == ']' || *g->regparse == '\0') ++ regc(g, '-'); ++ else { ++ class = UCHARAT(g->regparse-2)+1; ++ classend = UCHARAT(g->regparse); ++ if (class > classend+1) ++ FAIL("invalid [] range"); ++ for (; class <= classend; class++) ++ regc(g, class); ++ g->regparse++; ++ } ++ } else ++ regc(g, *g->regparse++); ++ } ++ regc(g, '\0'); ++ if (*g->regparse != ']') ++ FAIL("unmatched []"); ++ g->regparse++; ++ *flagp |= HASWIDTH|SIMPLE; ++ } ++ break; ++ case '(': ++ ret = reg(g, 1, &flags); ++ if (ret == NULL) ++ return(NULL); ++ *flagp |= flags&(HASWIDTH|SPSTART); ++ break; ++ case '\0': ++ case '|': ++ case ')': ++ FAIL("internal urp"); /* Supposed to be caught earlier. */ ++ break; ++ case '?': ++ case '+': ++ case '*': ++ FAIL("?+* follows nothing"); ++ break; ++ case '\\': ++ if (*g->regparse == '\0') ++ FAIL("trailing \\"); ++ ret = regnode(g, EXACTLY); ++ regc(g, *g->regparse++); ++ regc(g, '\0'); ++ *flagp |= HASWIDTH|SIMPLE; ++ break; ++ default: { ++ register int len; ++ register char ender; ++ ++ g->regparse--; ++ len = my_strcspn((const char *)g->regparse, (const char *)META); ++ if (len <= 0) ++ FAIL("internal disaster"); ++ ender = *(g->regparse+len); ++ if (len > 1 && ISMULT(ender)) ++ len--; /* Back off clear of ?+* operand. */ ++ *flagp |= HASWIDTH; ++ if (len == 1) ++ *flagp |= SIMPLE; ++ ret = regnode(g, EXACTLY); ++ while (len > 0) { ++ regc(g, *g->regparse++); ++ len--; ++ } ++ regc(g, '\0'); ++ } ++ break; ++ } ++ ++ return(ret); ++} ++ ++/* ++ - regnode - emit a node ++ */ ++static char * /* Location. */ ++regnode(struct match_globals *g, char op) ++{ ++ register char *ret; ++ register char *ptr; ++ ++ ret = g->regcode; ++ if (ret == &g->regdummy) { ++ g->regsize += 3; ++ return(ret); ++ } ++ ++ ptr = ret; ++ *ptr++ = op; ++ *ptr++ = '\0'; /* Null "next" pointer. */ ++ *ptr++ = '\0'; ++ g->regcode = ptr; ++ ++ return(ret); ++} ++ ++/* ++ - regc - emit (if appropriate) a byte of code ++ */ ++static void ++regc(struct match_globals *g, char b) ++{ ++ if (g->regcode != &g->regdummy) ++ *g->regcode++ = b; ++ else ++ g->regsize++; ++} ++ ++/* ++ - reginsert - insert an operator in front of already-emitted operand ++ * ++ * Means relocating the operand. ++ */ ++static void ++reginsert(struct match_globals *g, char op, char* opnd) ++{ ++ register char *src; ++ register char *dst; ++ register char *place; ++ ++ if (g->regcode == &g->regdummy) { ++ g->regsize += 3; ++ return; ++ } ++ ++ src = g->regcode; ++ g->regcode += 3; ++ dst = g->regcode; ++ while (src > opnd) ++ *--dst = *--src; ++ ++ place = opnd; /* Op node, where operand used to be. */ ++ *place++ = op; ++ *place++ = '\0'; ++ *place++ = '\0'; ++} ++ ++/* ++ - regtail - set the next-pointer at the end of a node chain ++ */ ++static void ++regtail(struct match_globals *g, char *p, char *val) ++{ ++ register char *scan; ++ register char *temp; ++ register int offset; ++ ++ if (p == &g->regdummy) ++ return; ++ ++ /* Find last node. */ ++ scan = p; ++ for (;;) { ++ temp = regnext(g, scan); ++ if (temp == NULL) ++ break; ++ scan = temp; ++ } ++ ++ if (OP(scan) == BACK) ++ offset = scan - val; ++ else ++ offset = val - scan; ++ *(scan+1) = (offset>>8)&0377; ++ *(scan+2) = offset&0377; ++} ++ ++/* ++ - regoptail - regtail on operand of first argument; nop if operandless ++ */ ++static void ++regoptail(struct match_globals *g, char *p, char *val) ++{ ++ /* "Operandless" and "op != BRANCH" are synonymous in practice. */ ++ if (p == NULL || p == &g->regdummy || OP(p) != BRANCH) ++ return; ++ regtail(g, OPERAND(p), val); ++} ++ ++/* ++ * regexec and friends ++ */ ++ ++ ++/* ++ * Forwards. ++ */ ++STATIC int regtry(struct match_globals *g, regexp *prog, char *string); ++STATIC int regmatch(struct match_globals *g, char *prog); ++STATIC int regrepeat(struct match_globals *g, char *p); ++ ++#ifdef DEBUG ++int regnarrate = 0; ++void regdump(); ++STATIC char *regprop(char *op); ++#endif ++ ++/* ++ - regexec - match a regexp against a string ++ */ ++int ++regexec(regexp *prog, char *string) ++{ ++ register char *s; ++ struct match_globals g; ++ ++ /* Be paranoid... */ ++ if (prog == NULL || string == NULL) { ++ printk("<3>Regexp: NULL parameter\n"); ++ return(0); ++ } ++ ++ /* Check validity of program. */ ++ if (UCHARAT(prog->program) != MAGIC) { ++ printk("<3>Regexp: corrupted program\n"); ++ return(0); ++ } ++ ++ /* If there is a "must appear" string, look for it. */ ++ if (prog->regmust != NULL) { ++ s = string; ++ while ((s = strchr(s, prog->regmust[0])) != NULL) { ++ if (strncmp(s, prog->regmust, prog->regmlen) == 0) ++ break; /* Found it. */ ++ s++; ++ } ++ if (s == NULL) /* Not present. */ ++ return(0); ++ } ++ ++ /* Mark beginning of line for ^ . */ ++ g.regbol = string; ++ ++ /* Simplest case: anchored match need be tried only once. */ ++ if (prog->reganch) ++ return(regtry(&g, prog, string)); ++ ++ /* Messy cases: unanchored match. */ ++ s = string; ++ if (prog->regstart != '\0') ++ /* We know what char it must start with. */ ++ while ((s = strchr(s, prog->regstart)) != NULL) { ++ if (regtry(&g, prog, s)) ++ return(1); ++ s++; ++ } ++ else ++ /* We don't -- general case. */ ++ do { ++ if (regtry(&g, prog, s)) ++ return(1); ++ } while (*s++ != '\0'); ++ ++ /* Failure. */ ++ return(0); ++} ++ ++/* ++ - regtry - try match at specific point ++ */ ++static int /* 0 failure, 1 success */ ++regtry(struct match_globals *g, regexp *prog, char *string) ++{ ++ register int i; ++ register char **sp; ++ register char **ep; ++ ++ g->reginput = string; ++ g->regstartp = prog->startp; ++ g->regendp = prog->endp; ++ ++ sp = prog->startp; ++ ep = prog->endp; ++ for (i = NSUBEXP; i > 0; i--) { ++ *sp++ = NULL; ++ *ep++ = NULL; ++ } ++ if (regmatch(g, prog->program + 1)) { ++ prog->startp[0] = string; ++ prog->endp[0] = g->reginput; ++ return(1); ++ } else ++ return(0); ++} ++ ++/* ++ - regmatch - main matching routine ++ * ++ * Conceptually the strategy is simple: check to see whether the current ++ * node matches, call self recursively to see whether the rest matches, ++ * and then act accordingly. In practice we make some effort to avoid ++ * recursion, in particular by going through "ordinary" nodes (that don't ++ * need to know whether the rest of the match failed) by a loop instead of ++ * by recursion. ++ */ ++static int /* 0 failure, 1 success */ ++regmatch(struct match_globals *g, char *prog) ++{ ++ register char *scan = prog; /* Current node. */ ++ char *next; /* Next node. */ ++ ++#ifdef DEBUG ++ if (scan != NULL && regnarrate) ++ fprintf(stderr, "%s(\n", regprop(scan)); ++#endif ++ while (scan != NULL) { ++#ifdef DEBUG ++ if (regnarrate) ++ fprintf(stderr, "%s...\n", regprop(scan)); ++#endif ++ next = regnext(g, scan); ++ ++ switch (OP(scan)) { ++ case BOL: ++ if (g->reginput != g->regbol) ++ return(0); ++ break; ++ case EOL: ++ if (*g->reginput != '\0') ++ return(0); ++ break; ++ case ANY: ++ if (*g->reginput == '\0') ++ return(0); ++ g->reginput++; ++ break; ++ case EXACTLY: { ++ register int len; ++ register char *opnd; ++ ++ opnd = OPERAND(scan); ++ /* Inline the first character, for speed. */ ++ if (*opnd != *g->reginput) ++ return(0); ++ len = strlen(opnd); ++ if (len > 1 && strncmp(opnd, g->reginput, len) != 0) ++ return(0); ++ g->reginput += len; ++ } ++ break; ++ case ANYOF: ++ if (*g->reginput == '\0' || strchr(OPERAND(scan), *g->reginput) == NULL) ++ return(0); ++ g->reginput++; ++ break; ++ case ANYBUT: ++ if (*g->reginput == '\0' || strchr(OPERAND(scan), *g->reginput) != NULL) ++ return(0); ++ g->reginput++; ++ break; ++ case NOTHING: ++ case BACK: ++ break; ++ case OPEN+1: ++ case OPEN+2: ++ case OPEN+3: ++ case OPEN+4: ++ case OPEN+5: ++ case OPEN+6: ++ case OPEN+7: ++ case OPEN+8: ++ case OPEN+9: { ++ register int no; ++ register char *save; ++ ++ no = OP(scan) - OPEN; ++ save = g->reginput; ++ ++ if (regmatch(g, next)) { ++ /* ++ * Don't set startp if some later ++ * invocation of the same parentheses ++ * already has. ++ */ ++ if (g->regstartp[no] == NULL) ++ g->regstartp[no] = save; ++ return(1); ++ } else ++ return(0); ++ } ++ break; ++ case CLOSE+1: ++ case CLOSE+2: ++ case CLOSE+3: ++ case CLOSE+4: ++ case CLOSE+5: ++ case CLOSE+6: ++ case CLOSE+7: ++ case CLOSE+8: ++ case CLOSE+9: ++ { ++ register int no; ++ register char *save; ++ ++ no = OP(scan) - CLOSE; ++ save = g->reginput; ++ ++ if (regmatch(g, next)) { ++ /* ++ * Don't set endp if some later ++ * invocation of the same parentheses ++ * already has. ++ */ ++ if (g->regendp[no] == NULL) ++ g->regendp[no] = save; ++ return(1); ++ } else ++ return(0); ++ } ++ break; ++ case BRANCH: { ++ register char *save; ++ ++ if (OP(next) != BRANCH) /* No choice. */ ++ next = OPERAND(scan); /* Avoid recursion. */ ++ else { ++ do { ++ save = g->reginput; ++ if (regmatch(g, OPERAND(scan))) ++ return(1); ++ g->reginput = save; ++ scan = regnext(g, scan); ++ } while (scan != NULL && OP(scan) == BRANCH); ++ return(0); ++ /* NOTREACHED */ ++ } ++ } ++ break; ++ case STAR: ++ case PLUS: { ++ register char nextch; ++ register int no; ++ register char *save; ++ register int min; ++ ++ /* ++ * Lookahead to avoid useless match attempts ++ * when we know what character comes next. ++ */ ++ nextch = '\0'; ++ if (OP(next) == EXACTLY) ++ nextch = *OPERAND(next); ++ min = (OP(scan) == STAR) ? 0 : 1; ++ save = g->reginput; ++ no = regrepeat(g, OPERAND(scan)); ++ while (no >= min) { ++ /* If it could work, try it. */ ++ if (nextch == '\0' || *g->reginput == nextch) ++ if (regmatch(g, next)) ++ return(1); ++ /* Couldn't or didn't -- back up. */ ++ no--; ++ g->reginput = save + no; ++ } ++ return(0); ++ } ++ break; ++ case END: ++ return(1); /* Success! */ ++ break; ++ default: ++ printk("<3>Regexp: memory corruption\n"); ++ return(0); ++ break; ++ } ++ ++ scan = next; ++ } ++ ++ /* ++ * We get here only if there's trouble -- normally "case END" is ++ * the terminating point. ++ */ ++ printk("<3>Regexp: corrupted pointers\n"); ++ return(0); ++} ++ ++/* ++ - regrepeat - repeatedly match something simple, report how many ++ */ ++static int ++regrepeat(struct match_globals *g, char *p) ++{ ++ register int count = 0; ++ register char *scan; ++ register char *opnd; ++ ++ scan = g->reginput; ++ opnd = OPERAND(p); ++ switch (OP(p)) { ++ case ANY: ++ count = strlen(scan); ++ scan += count; ++ break; ++ case EXACTLY: ++ while (*opnd == *scan) { ++ count++; ++ scan++; ++ } ++ break; ++ case ANYOF: ++ while (*scan != '\0' && strchr(opnd, *scan) != NULL) { ++ count++; ++ scan++; ++ } ++ break; ++ case ANYBUT: ++ while (*scan != '\0' && strchr(opnd, *scan) == NULL) { ++ count++; ++ scan++; ++ } ++ break; ++ default: /* Oh dear. Called inappropriately. */ ++ printk("<3>Regexp: internal foulup\n"); ++ count = 0; /* Best compromise. */ ++ break; ++ } ++ g->reginput = scan; ++ ++ return(count); ++} ++ ++/* ++ - regnext - dig the "next" pointer out of a node ++ */ ++static char* ++regnext(struct match_globals *g, char *p) ++{ ++ register int offset; ++ ++ if (p == &g->regdummy) ++ return(NULL); ++ ++ offset = NEXT(p); ++ if (offset == 0) ++ return(NULL); ++ ++ if (OP(p) == BACK) ++ return(p-offset); ++ else ++ return(p+offset); ++} ++ ++#ifdef DEBUG ++ ++STATIC char *regprop(); ++ ++/* ++ - regdump - dump a regexp onto stdout in vaguely comprehensible form ++ */ ++void ++regdump(regexp *r) ++{ ++ register char *s; ++ register char op = EXACTLY; /* Arbitrary non-END op. */ ++ register char *next; ++ /* extern char *strchr(); */ ++ ++ ++ s = r->program + 1; ++ while (op != END) { /* While that wasn't END last time... */ ++ op = OP(s); ++ printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */ ++ next = regnext(s); ++ if (next == NULL) /* Next ptr. */ ++ printf("(0)"); ++ else ++ printf("(%d)", (s-r->program)+(next-s)); ++ s += 3; ++ if (op == ANYOF || op == ANYBUT || op == EXACTLY) { ++ /* Literal string, where present. */ ++ while (*s != '\0') { ++ putchar(*s); ++ s++; ++ } ++ s++; ++ } ++ putchar('\n'); ++ } ++ ++ /* Header fields of interest. */ ++ if (r->regstart != '\0') ++ printf("start `%c' ", r->regstart); ++ if (r->reganch) ++ printf("anchored "); ++ if (r->regmust != NULL) ++ printf("must have \"%s\"", r->regmust); ++ printf("\n"); ++} ++ ++/* ++ - regprop - printable representation of opcode ++ */ ++static char * ++regprop(char *op) ++{ ++#define BUFLEN 50 ++ register char *p; ++ static char buf[BUFLEN]; ++ ++ strcpy(buf, ":"); ++ ++ switch (OP(op)) { ++ case BOL: ++ p = "BOL"; ++ break; ++ case EOL: ++ p = "EOL"; ++ break; ++ case ANY: ++ p = "ANY"; ++ break; ++ case ANYOF: ++ p = "ANYOF"; ++ break; ++ case ANYBUT: ++ p = "ANYBUT"; ++ break; ++ case BRANCH: ++ p = "BRANCH"; ++ break; ++ case EXACTLY: ++ p = "EXACTLY"; ++ break; ++ case NOTHING: ++ p = "NOTHING"; ++ break; ++ case BACK: ++ p = "BACK"; ++ break; ++ case END: ++ p = "END"; ++ break; ++ case OPEN+1: ++ case OPEN+2: ++ case OPEN+3: ++ case OPEN+4: ++ case OPEN+5: ++ case OPEN+6: ++ case OPEN+7: ++ case OPEN+8: ++ case OPEN+9: ++ snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "OPEN%d", OP(op)-OPEN); ++ p = NULL; ++ break; ++ case CLOSE+1: ++ case CLOSE+2: ++ case CLOSE+3: ++ case CLOSE+4: ++ case CLOSE+5: ++ case CLOSE+6: ++ case CLOSE+7: ++ case CLOSE+8: ++ case CLOSE+9: ++ snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "CLOSE%d", OP(op)-CLOSE); ++ p = NULL; ++ break; ++ case STAR: ++ p = "STAR"; ++ break; ++ case PLUS: ++ p = "PLUS"; ++ break; ++ default: ++ printk("<3>Regexp: corrupted opcode\n"); ++ break; ++ } ++ if (p != NULL) ++ strncat(buf, p, BUFLEN-strlen(buf)); ++ return(buf); ++} ++#endif ++ ++ +--- /dev/null ++++ b/net/ipv4/netfilter/weburl_deps/regexp.h +@@ -0,0 +1,41 @@ ++/* ++ * Definitions etc. for regexp(3) routines. ++ * ++ * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof], ++ * not the System V one. ++ */ ++ ++#ifndef REGEXP_H ++#define REGEXP_H ++ ++ ++/* ++http://www.opensource.apple.com/darwinsource/10.3/expect-1/expect/expect.h , ++which contains a version of this library, says: ++ ++ * ++ * NSUBEXP must be at least 10, and no greater than 117 or the parser ++ * will not work properly. ++ * ++ ++However, it looks rather like this library is limited to 10. If you think ++otherwise, let us know. ++*/ ++ ++#define NSUBEXP 10 ++typedef struct regexp { ++ char *startp[NSUBEXP]; ++ char *endp[NSUBEXP]; ++ char regstart; /* Internal use only. */ ++ char reganch; /* Internal use only. */ ++ char *regmust; /* Internal use only. */ ++ int regmlen; /* Internal use only. */ ++ char program[1]; /* Unwarranted chumminess with compiler. */ ++} regexp; ++ ++regexp * regcomp(char *exp, int *patternsize); ++int regexec(regexp *prog, char *string); ++void regsub(regexp *prog, char *source, char *dest); ++void regerror(char *s); ++ ++#endif +--- /dev/null ++++ b/net/ipv4/netfilter/weburl_deps/regmagic.h +@@ -0,0 +1,5 @@ ++/* ++ * The first byte of the regexp internal "program" is actually this magic ++ * number; the start node begins in the second byte. ++ */ ++#define MAGIC 0234 +--- /dev/null ++++ b/net/ipv4/netfilter/weburl_deps/regsub.c +@@ -0,0 +1,95 @@ ++/* ++ * regsub ++ * @(#)regsub.c 1.3 of 2 April 86 ++ * ++ * Copyright (c) 1986 by University of Toronto. ++ * Written by Henry Spencer. Not derived from licensed software. ++ * ++ * Permission is granted to anyone to use this software for any ++ * purpose on any computer system, and to redistribute it freely, ++ * subject to the following restrictions: ++ * ++ * 1. The author is not responsible for the consequences of use of ++ * this software, no matter how awful, even if they arise ++ * from defects in it. ++ * ++ * 2. The origin of this software must not be misrepresented, either ++ * by explicit claim or by omission. ++ * ++ * 3. Altered versions must be plainly marked as such, and must not ++ * be misrepresented as being the original software. ++ * ++ * ++ * This code was modified by Ethan Sommer to work within the kernel ++ * (it now uses kmalloc etc..) ++ * ++ */ ++#include "regexp.h" ++#include "regmagic.h" ++#include ++ ++ ++#ifndef CHARBITS ++#define UCHARAT(p) ((int)*(unsigned char *)(p)) ++#else ++#define UCHARAT(p) ((int)*(p)&CHARBITS) ++#endif ++ ++#if 0 ++//void regerror(char * s) ++//{ ++// printk("regexp(3): %s", s); ++// /* NOTREACHED */ ++//} ++#endif ++ ++/* ++ - regsub - perform substitutions after a regexp match ++ */ ++void ++regsub(regexp * prog, char * source, char * dest) ++{ ++ register char *src; ++ register char *dst; ++ register char c; ++ register int no; ++ register int len; ++ ++ /* Not necessary and gcc doesn't like it -MLS */ ++ /*extern char *strncpy();*/ ++ ++ if (prog == NULL || source == NULL || dest == NULL) { ++ regerror("NULL parm to regsub"); ++ return; ++ } ++ if (UCHARAT(prog->program) != MAGIC) { ++ regerror("damaged regexp fed to regsub"); ++ return; ++ } ++ ++ src = source; ++ dst = dest; ++ while ((c = *src++) != '\0') { ++ if (c == '&') ++ no = 0; ++ else if (c == '\\' && '0' <= *src && *src <= '9') ++ no = *src++ - '0'; ++ else ++ no = -1; ++ ++ if (no < 0) { /* Ordinary character. */ ++ if (c == '\\' && (*src == '\\' || *src == '&')) ++ c = *src++; ++ *dst++ = c; ++ } else if (prog->startp[no] != NULL && prog->endp[no] != NULL) { ++ len = prog->endp[no] - prog->startp[no]; ++ (void) strncpy(dst, prog->startp[no], len); ++ dst += len; ++ if (len != 0 && *(dst-1) == '\0') { /* strncpy hit NUL. */ ++ regerror("damaged match string"); ++ return; ++ } ++ } ++ } ++ *dst++ = '\0'; ++} +--- /dev/null ++++ b/net/ipv4/netfilter/weburl_deps/tree_map.h +@@ -0,0 +1,1084 @@ ++/* ++ * Copyright © 2008 by Eric Bishop ++ * ++ * This work 'as-is' we provide. ++ * No warranty, express or implied. ++ * We've done our best, ++ * to debug and test. ++ * Liability for damages denied. ++ * ++ * Permission is granted hereby, ++ * to copy, share, and modify. ++ * Use as is fit, ++ * free or for profit. ++ * On this notice these rights rely. ++ * ++ * ++ * ++ * Note that unlike other portions of Gargoyle this code ++ * does not fall under the GPL, but the rather whimsical ++ * 'Poetic License' above. ++ * ++ * Basically, this library contains a bunch of utilities ++ * that I find useful. I'm sure other libraries exist ++ * that are just as good or better, but I like these tools ++ * because I personally wrote them, so I know their quirks. ++ * (i.e. I know where the bodies are buried). I want to ++ * make sure that I can re-use these utilities for whatever ++ * code I may want to write in the future be it ++ * proprietary or open-source, so I've put them under ++ * a very, very permissive license. ++ * ++ * If you find this code useful, use it. If not, don't. ++ * I really don't care. ++ * ++ */ ++ ++ ++#if __KERNEL__ ++ #define malloc(foo) kmalloc(foo,GFP_ATOMIC) ++ #define free(foo) kfree(foo) ++ #define printf(format,args...) printk(format,##args) ++ ++ /* kernel strdup */ ++ static inline char *kernel_strdup(const char *str); ++ static inline char *kernel_strdup(const char *str) ++ { ++ char *tmp; ++ long int s; ++ s=strlen(str) + 1; ++ tmp = kmalloc(s, GFP_ATOMIC); ++ if (tmp != NULL) ++ { ++ memcpy(tmp, str, s); ++ } ++ return tmp; ++ } ++ #define strdup kernel_strdup ++ ++#endif ++ ++ ++ ++/* tree_map structs / prototypes */ ++typedef struct long_tree_map_node ++{ ++ unsigned long key; ++ void* value; ++ ++ signed char balance; ++ struct long_tree_map_node* left; ++ struct long_tree_map_node* right; ++} long_map_node; ++ ++typedef struct ++{ ++ long_map_node* root; ++ unsigned long num_elements; ++ ++}long_map; ++ ++typedef struct ++{ ++ long_map lm; ++ unsigned char store_keys; ++ unsigned long num_elements; ++ ++}string_map; ++ ++ ++ ++/* long map functions */ ++long_map* initialize_long_map(void); ++void* get_long_map_element(long_map* map, unsigned long key); ++void* get_smallest_long_map_element(long_map* map, unsigned long* smallest_key); ++void* get_largest_long_map_element(long_map* map, unsigned long* largest_key); ++void* remove_smallest_long_map_element(long_map* map, unsigned long* smallest_key); ++void* remove_largest_long_map_element(long_map* map, unsigned long* largest_key); ++void* set_long_map_element(long_map* map, unsigned long key, void* value); ++void* remove_long_map_element(long_map* map, unsigned long key); ++unsigned long* get_sorted_long_map_keys(long_map* map, unsigned long* num_keys_returned); ++void** get_sorted_long_map_values(long_map* map, unsigned long* num_values_returned); ++void** destroy_long_map(long_map* map, int destruction_type, unsigned long* num_destroyed); ++void apply_to_every_long_map_value(long_map* map, void (*apply_func)(unsigned long key, void* value)); ++ ++/* string map functions */ ++string_map* initialize_string_map(unsigned char store_keys); ++void* get_string_map_element(string_map* map, const char* key); ++void* set_string_map_element(string_map* map, const char* key, void* value); ++void* remove_string_map_element(string_map* map, const char* key); ++char** get_string_map_keys(string_map* map, unsigned long* num_keys_returned); ++void** get_string_map_values(string_map* map, unsigned long* num_values_returned); ++void** destroy_string_map(string_map* map, int destruction_type, unsigned long* num_destroyed); ++void apply_to_every_string_map_value(string_map* map, void (*apply_func)(char* key, void* value)); ++ ++ ++/* ++ * three different ways to deal with values when data structure is destroyed ++ */ ++#define DESTROY_MODE_RETURN_VALUES 20 ++#define DESTROY_MODE_FREE_VALUES 21 ++#define DESTROY_MODE_IGNORE_VALUES 22 ++ ++ ++/* ++ * for convenience & backwards compatibility alias _string_map_ functions to ++ * _map_ functions since string map is used more often than long map ++ */ ++#define initialize_map initialize_string_map ++#define set_map_element set_string_map_element ++#define get_map_element get_string_map_element ++#define remove_map_element remove_string_map_element ++#define get_map_keys get_string_map_keys ++#define get_map_values get_string_map_values ++#define destroy_map destroy_string_map ++ ++ ++/* internal utility structures/ functions */ ++typedef struct stack_node_struct ++{ ++ long_map_node** node_ptr; ++ signed char direction; ++ struct stack_node_struct* previous; ++} stack_node; ++ ++static void free_stack(stack_node* stack); ++static void** destroy_long_map_values(long_map* map, int destruction_type, unsigned long* num_destroyed); ++static void apply_to_every_long_map_node(long_map_node* node, void (*apply_func)(unsigned long key, void* value)); ++static void apply_to_every_string_map_node(long_map_node* node, unsigned char has_key, void (*apply_func)(char* key, void* value)); ++static void get_sorted_node_keys(long_map_node* node, unsigned long* key_list, unsigned long* next_key_index, int depth); ++static void get_sorted_node_values(long_map_node* node, void** value_list, unsigned long* next_value_index, int depth); ++static signed char rebalance (long_map_node** n, signed char direction, signed char update_op); ++static void rotate_right (long_map_node** parent); ++static void rotate_left (long_map_node** parent); ++ ++/* internal for string map */ ++typedef struct ++{ ++ char* key; ++ void* value; ++} string_map_key_value; ++static unsigned long sdbm_string_hash(const char *key); ++ ++ ++ ++ ++/*************************************************** ++ * For testing only ++ ***************************************************/ ++/* ++void print_list(stack_node *l); ++ ++void print_list(stack_node *l) ++{ ++ if(l != NULL) ++ { ++ printf(" list key = %ld, dir=%d, \n", (*(l->node_ptr))->key, l->direction); ++ print_list(l->previous); ++ } ++} ++*/ ++/****************************************************** ++ * End testing Code ++ *******************************************************/ ++ ++ ++ ++ ++/*************************************************** ++ * string_map function definitions ++ ***************************************************/ ++ ++string_map* initialize_string_map(unsigned char store_keys) ++{ ++ string_map* map = (string_map*)malloc(sizeof(string_map)); ++ if(map != NULL) ++ { ++ map->store_keys = store_keys; ++ map->lm.root = NULL; ++ map->lm.num_elements = 0; ++ map->num_elements = map->lm.num_elements; ++ } ++ return map; ++} ++ ++void* get_string_map_element(string_map* map, const char* key) ++{ ++ unsigned long hashed_key = sdbm_string_hash(key); ++ void* return_value = get_long_map_element( &(map->lm), hashed_key); ++ if(return_value != NULL && map->store_keys) ++ { ++ string_map_key_value* r = (string_map_key_value*)return_value; ++ return_value = r->value; ++ } ++ map->num_elements = map->lm.num_elements; ++ return return_value; ++} ++ ++void* set_string_map_element(string_map* map, const char* key, void* value) ++{ ++ unsigned long hashed_key = sdbm_string_hash(key); ++ void* return_value = NULL; ++ if(map->store_keys) ++ { ++ string_map_key_value* kv = (string_map_key_value*)malloc(sizeof(string_map_key_value)); ++ if(kv == NULL) /* deal with malloc failure */ ++ { ++ return NULL; ++ } ++ kv->key = strdup(key); ++ if(kv->key == NULL) /* deal with malloc failure */ ++ { ++ free(kv); ++ return NULL; ++ } ++ kv->value = value; ++ return_value = set_long_map_element( &(map->lm), hashed_key, kv); ++ if(return_value != NULL) ++ { ++ string_map_key_value* r = (string_map_key_value*)return_value; ++ return_value = r->value; ++ free(r->key); ++ free(r); ++ } ++ } ++ else ++ { ++ return_value = set_long_map_element( &(map->lm), hashed_key, value); ++ } ++ map->num_elements = map->lm.num_elements; ++ return return_value; ++} ++ ++void* remove_string_map_element(string_map* map, const char* key) ++{ ++ unsigned long hashed_key = sdbm_string_hash(key); ++ void* return_value = remove_long_map_element( &(map->lm), hashed_key); ++ ++ if(return_value != NULL && map->store_keys) ++ { ++ string_map_key_value* r = (string_map_key_value*)return_value; ++ return_value = r->value; ++ free(r->key); ++ free(r); ++ } ++ map->num_elements = map->lm.num_elements; ++ return return_value; ++} ++ ++char** get_string_map_keys(string_map* map, unsigned long* num_keys_returned) ++{ ++ char** str_keys; ++ str_keys = (char**)malloc((map->num_elements+1)*sizeof(char*)); ++ if(str_keys == NULL) /* deal with malloc failure */ ++ { ++ return NULL; ++ } ++ str_keys[0] = NULL; ++ *num_keys_returned = 0; ++ if(map->store_keys && map->num_elements > 0) ++ { ++ unsigned long list_length; ++ void** long_values = get_sorted_long_map_values( &(map->lm), &list_length); ++ unsigned long key_index; ++ /*list_length will be 0 on malloc failure in get_sorted_long_map_values, so this code shouldn't seg fault if that happens */ ++ for(key_index = 0; key_index < list_length; key_index++) ++ { ++ str_keys[key_index] = strdup( ((string_map_key_value*)(long_values[key_index]))->key); ++ if(str_keys[key_index] == NULL) /* deal with malloc failure */ ++ { ++ //just return the incomplete list (hey, it's null terminated...) ++ free(long_values); ++ return str_keys; ++ } ++ *num_keys_returned = *num_keys_returned + 1; ++ } ++ str_keys[list_length] = NULL; ++ free(long_values); ++ } ++ return str_keys; ++} ++ ++ ++void** get_string_map_values(string_map* map, unsigned long* num_values_returned) ++{ ++ void** values = NULL; ++ if(map != NULL) ++ { ++ values = get_sorted_long_map_values ( &(map->lm), num_values_returned ); ++ } ++ return values; ++} ++ ++ ++void** destroy_string_map(string_map* map, int destruction_type, unsigned long* num_destroyed) ++{ ++ void** return_values = NULL; ++ if(map != NULL) ++ { ++ if(map->store_keys) ++ { ++ void** kvs = destroy_long_map_values( &(map->lm), DESTROY_MODE_RETURN_VALUES, num_destroyed ); ++ unsigned long kv_index = 0; ++ for(kv_index=0; kv_index < *num_destroyed; kv_index++) ++ { ++ string_map_key_value* kv = (string_map_key_value*)kvs[kv_index]; ++ void* value = kv->value; ++ ++ free(kv->key); ++ free(kv); ++ if(destruction_type == DESTROY_MODE_FREE_VALUES) ++ { ++ free(value); ++ } ++ if(destruction_type == DESTROY_MODE_RETURN_VALUES) ++ { ++ kvs[kv_index] = value; ++ } ++ } ++ if(destruction_type == DESTROY_MODE_RETURN_VALUES) ++ { ++ return_values = kvs; ++ } ++ else ++ { ++ free(kvs); ++ } ++ } ++ else ++ { ++ return_values = destroy_long_map_values( &(map->lm), destruction_type, num_destroyed ); ++ } ++ free(map); ++ } ++ return return_values; ++} ++ ++ ++ ++ ++/*************************************************** ++ * long_map function definitions ++ ***************************************************/ ++ ++long_map* initialize_long_map(void) ++{ ++ long_map* map = (long_map*)malloc(sizeof(long_map)); ++ if(map != NULL) /* test for malloc failure */ ++ { ++ map->root = NULL; ++ map->num_elements = 0; ++ } ++ return map; ++} ++ ++void* get_long_map_element(long_map* map, unsigned long key) ++{ ++ void* value = NULL; ++ ++ if(map->root != NULL) ++ { ++ long_map_node* parent_node = map->root; ++ long_map_node* next_node; ++ while( key != parent_node->key && (next_node = (long_map_node *)(key < parent_node->key ? parent_node->left : parent_node->right)) != NULL) ++ { ++ parent_node = next_node; ++ } ++ if(parent_node->key == key) ++ { ++ value = parent_node->value; ++ } ++ } ++ return value; ++} ++ ++void* get_smallest_long_map_element(long_map* map, unsigned long* smallest_key) ++{ ++ void* value = NULL; ++ if(map->root != NULL) ++ { ++ long_map_node* next_node = map->root; ++ while( next_node->left != NULL) ++ { ++ next_node = next_node->left; ++ } ++ value = next_node->value; ++ *smallest_key = next_node->key; ++ } ++ return value; ++} ++ ++void* get_largest_long_map_element(long_map* map, unsigned long* largest_key) ++{ ++ void* value = NULL; ++ if(map->root != NULL) ++ { ++ long_map_node* next_node = map->root; ++ while( next_node->right != NULL) ++ { ++ next_node = next_node->right; ++ } ++ value = next_node->value; ++ *largest_key = next_node->key; ++ } ++ return value; ++} ++ ++void* remove_smallest_long_map_element(long_map* map, unsigned long* smallest_key) ++{ ++ get_smallest_long_map_element(map, smallest_key); ++ return remove_long_map_element(map, *smallest_key); ++} ++ ++void* remove_largest_long_map_element(long_map* map, unsigned long* largest_key) ++{ ++ get_largest_long_map_element(map, largest_key); ++ return remove_long_map_element(map, *largest_key); ++} ++ ++ ++/* if replacement performed, returns replaced value, otherwise null */ ++void* set_long_map_element(long_map* map, unsigned long key, void* value) ++{ ++ stack_node* parent_list = NULL; ++ void* old_value = NULL; ++ int old_value_found = 0; ++ ++ long_map_node* parent_node; ++ long_map_node* next_node; ++ stack_node* next_parent; ++ stack_node* previous_parent; ++ signed char new_balance; ++ ++ ++ long_map_node* new_node = (long_map_node*)malloc(sizeof(long_map_node)); ++ if(new_node == NULL) ++ { ++ return NULL; ++ } ++ new_node->value = value; ++ new_node->key = key; ++ new_node->left = NULL; ++ new_node->right = NULL; ++ new_node->balance = 0; ++ ++ ++ ++ if(map->root == NULL) ++ { ++ map->root = new_node; ++ } ++ else ++ { ++ parent_node = map->root; ++ ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ free(new_node); ++ return NULL; /* won't insert but won't seg fault */ ++ } ++ next_parent->node_ptr = &(map->root); ++ next_parent->previous = parent_list; ++ parent_list = next_parent; ++ ++ while( key != parent_node->key && (next_node = (key < parent_node->key ? parent_node->left : parent_node->right) ) != NULL) ++ { ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ free(new_node); ++ return NULL; ++ } ++ next_parent->node_ptr = key < parent_node->key ? &(parent_node->left) : &(parent_node->right); ++ next_parent->previous = parent_list; ++ next_parent->previous->direction = key < parent_node->key ? -1 : 1; ++ parent_list = next_parent; ++ ++ parent_node = next_node; ++ } ++ ++ ++ if(key == parent_node->key) ++ { ++ old_value = parent_node->value; ++ old_value_found = 1; ++ parent_node->value = value; ++ free(new_node); ++ /* we merely replaced a node, no need to rebalance */ ++ } ++ else ++ { ++ if(key < parent_node->key) ++ { ++ parent_node->left = (void*)new_node; ++ parent_list->direction = -1; ++ } ++ else ++ { ++ parent_node->right = (void*)new_node; ++ parent_list->direction = 1; ++ } ++ ++ ++ /* we inserted a node, rebalance */ ++ previous_parent = parent_list; ++ new_balance = 1; /* initial value is not used, but must not be 0 for initial loop condition */ ++ ++ ++ while(previous_parent != NULL && new_balance != 0) ++ { ++ new_balance = rebalance(previous_parent->node_ptr, previous_parent->direction, 1); ++ previous_parent = previous_parent->previous; ++ } ++ } ++ } ++ ++ free_stack(parent_list); ++ ++ if(old_value_found == 0) ++ { ++ map->num_elements = map->num_elements + 1; ++ } ++ ++ return old_value; ++} ++ ++ ++void* remove_long_map_element(long_map* map, unsigned long key) ++{ ++ ++ void* value = NULL; ++ ++ long_map_node* root_node = map->root; ++ stack_node* parent_list = NULL; ++ ++ ++ long_map_node* remove_parent; ++ long_map_node* remove_node; ++ long_map_node* next_node; ++ ++ long_map_node* replacement; ++ long_map_node* replacement_parent; ++ long_map_node* replacement_next; ++ ++ stack_node* next_parent; ++ stack_node* previous_parent; ++ stack_node* replacement_stack_node; ++ ++ ++ signed char new_balance; ++ ++ ++ ++ if(root_node != NULL) ++ { ++ remove_parent = root_node; ++ remove_node = key < remove_parent->key ? remove_parent->left : remove_parent->right; ++ ++ if(remove_node != NULL && key != remove_parent->key) ++ { ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ return NULL; ++ } ++ next_parent->node_ptr = &(map->root); ++ next_parent->previous = parent_list; ++ parent_list = next_parent; ++ while( key != remove_node->key && (next_node = (key < remove_node->key ? remove_node->left : remove_node->right)) != NULL) ++ { ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ next_parent->node_ptr = key < remove_parent->key ? &(remove_parent->left) : &(remove_parent->right); ++ next_parent->previous = parent_list; ++ next_parent->previous->direction = key < remove_parent->key ? -1 : 1; ++ parent_list = next_parent; ++ ++ ++ remove_parent = remove_node; ++ remove_node = next_node; ++ } ++ parent_list->direction = key < remove_parent-> key ? -1 : 1; ++ } ++ else ++ { ++ remove_node = remove_parent; ++ } ++ ++ ++ if(key == remove_node->key) ++ { ++ ++ /* find replacement for node we are deleting */ ++ if( remove_node->right == NULL ) ++ { ++ replacement = remove_node->left; ++ } ++ else if( remove_node->right->left == NULL) ++ { ++ ++ replacement = remove_node->right; ++ replacement->left = remove_node->left; ++ replacement->balance = remove_node->balance; ++ ++ /* put pointer to replacement node into list for balance update */ ++ replacement_stack_node = (stack_node*)malloc(sizeof(stack_node)); ++ if(replacement_stack_node == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ replacement_stack_node->previous = parent_list; ++ replacement_stack_node->direction = 1; /* replacement is from right */ ++ if(remove_node == remove_parent) /* special case for root node */ ++ { ++ replacement_stack_node->node_ptr = &(map->root); ++ } ++ else ++ { ++ replacement_stack_node->node_ptr = key < remove_parent-> key ? &(remove_parent->left) : &(remove_parent->right); ++ } ++ parent_list = replacement_stack_node; ++ ++ } ++ else ++ { ++ /* put pointer to replacement node into list for balance update */ ++ replacement_stack_node = (stack_node*)malloc(sizeof(stack_node)); ++ if(replacement_stack_node == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ ++ replacement_stack_node->previous = parent_list; ++ replacement_stack_node->direction = 1; /* we always look for replacement on right */ ++ if(remove_node == remove_parent) /* special case for root node */ ++ { ++ replacement_stack_node->node_ptr = &(map->root); ++ } ++ else ++ { ++ replacement_stack_node->node_ptr = key < remove_parent-> key ? &(remove_parent->left) : &(remove_parent->right); ++ } ++ ++ parent_list = replacement_stack_node; ++ ++ ++ /* ++ * put pointer to replacement node->right into list for balance update ++ * this node will have to be updated with the proper pointer ++ * after we have identified the replacement ++ */ ++ replacement_stack_node = (stack_node*)malloc(sizeof(stack_node)); ++ if(replacement_stack_node == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ ++ replacement_stack_node->previous = parent_list; ++ replacement_stack_node->direction = -1; /* we always look for replacement to left of this node */ ++ parent_list = replacement_stack_node; ++ ++ /* find smallest node on right (large) side of tree */ ++ replacement_parent = remove_node->right; ++ replacement = replacement_parent->left; ++ ++ while((replacement_next = replacement->left) != NULL) ++ { ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ ++ next_parent->node_ptr = &(replacement_parent->left); ++ next_parent->previous = parent_list; ++ next_parent->direction = -1; /* we always go left */ ++ parent_list = next_parent; ++ ++ replacement_parent = replacement; ++ replacement = replacement_next; ++ ++ } ++ ++ replacement_parent->left = replacement->right; ++ ++ replacement->left = remove_node->left; ++ replacement->right = remove_node->right; ++ replacement->balance = remove_node->balance; ++ replacement_stack_node->node_ptr = &(replacement->right); ++ } ++ ++ /* insert replacement at proper location in tree */ ++ if(remove_node == remove_parent) ++ { ++ map->root = replacement; ++ } ++ else ++ { ++ remove_parent->left = remove_node == remove_parent->left ? replacement : remove_parent->left; ++ remove_parent->right = remove_node == remove_parent->right ? replacement : remove_parent->right; ++ } ++ ++ ++ /* rebalance tree */ ++ previous_parent = parent_list; ++ new_balance = 0; ++ while(previous_parent != NULL && new_balance == 0) ++ { ++ new_balance = rebalance(previous_parent->node_ptr, previous_parent->direction, -1); ++ previous_parent = previous_parent->previous; ++ } ++ ++ ++ ++ ++ /* ++ * since we found a value to remove, decrease number of elements in map ++ * set return value to the deleted node's value and free the node ++ */ ++ map->num_elements = map->num_elements - 1; ++ value = remove_node->value; ++ free(remove_node); ++ } ++ } ++ ++ free_stack(parent_list); ++ ++ return value; ++} ++ ++ ++/* note: returned keys are dynamically allocated, you need to free them! */ ++unsigned long* get_sorted_long_map_keys(long_map* map, unsigned long* num_keys_returned) ++{ ++ unsigned long* key_list = (unsigned long*)malloc((map->num_elements)*sizeof(unsigned long)); ++ unsigned long next_key_index; ++ if(key_list == NULL) ++ { ++ *num_keys_returned = 0; ++ return NULL; ++ } ++ next_key_index = 0; ++ get_sorted_node_keys(map->root, key_list, &next_key_index, 0); ++ ++ *num_keys_returned = map->num_elements; ++ ++ return key_list; ++} ++ ++ ++void** get_sorted_long_map_values(long_map* map, unsigned long* num_values_returned) ++{ ++ void** value_list = (void**)malloc((map->num_elements+1)*sizeof(void*)); ++ unsigned long next_value_index; ++ ++ if(value_list == NULL) ++ { ++ *num_values_returned = 0; ++ return NULL; ++ } ++ next_value_index = 0; ++ get_sorted_node_values(map->root, value_list, &next_value_index, 0); ++ value_list[map->num_elements] = NULL; /* since we're dealing with pointers make list null terminated */ ++ ++ *num_values_returned = map->num_elements; ++ return value_list; ++ ++} ++ ++ ++ ++void** destroy_long_map(long_map* map, int destruction_type, unsigned long* num_destroyed) ++{ ++ void** return_values = destroy_long_map_values(map, destruction_type, num_destroyed); ++ free(map); ++ return return_values; ++} ++ ++ ++ ++void apply_to_every_long_map_value(long_map* map, void (*apply_func)(unsigned long key, void* value)) ++{ ++ apply_to_every_long_map_node(map->root, apply_func); ++} ++void apply_to_every_string_map_value(string_map* map, void (*apply_func)(char* key, void* value)) ++{ ++ apply_to_every_string_map_node( (map->lm).root, map->store_keys, apply_func); ++} ++ ++ ++/*************************************************** ++ * internal utility function definitions ++ ***************************************************/ ++static void free_stack(stack_node* stack) ++{ ++ while(stack != NULL) ++ { ++ stack_node* prev_node = stack; ++ stack = prev_node->previous; ++ free(prev_node); ++ } ++ ++} ++ ++static void** destroy_long_map_values(long_map* map, int destruction_type, unsigned long* num_destroyed) ++{ ++ void** return_values = NULL; ++ unsigned long return_index = 0; ++ ++ *num_destroyed = 0; ++ ++ if(destruction_type == DESTROY_MODE_RETURN_VALUES) ++ { ++ return_values = (void**)malloc((map->num_elements+1)*sizeof(void*)); ++ if(return_values == NULL) /* deal with malloc failure */ ++ { ++ destruction_type = DESTROY_MODE_IGNORE_VALUES; /* could cause memory leak, but there's no other way to be sure we won't seg fault */ ++ } ++ else ++ { ++ return_values[map->num_elements] = NULL; ++ } ++ } ++ while(map->num_elements > 0) ++ { ++ unsigned long smallest_key; ++ void* removed_value = remove_smallest_long_map_element(map, &smallest_key); ++ if(destruction_type == DESTROY_MODE_RETURN_VALUES) ++ { ++ return_values[return_index] = removed_value; ++ } ++ if(destruction_type == DESTROY_MODE_FREE_VALUES) ++ { ++ free(removed_value); ++ } ++ return_index++; ++ *num_destroyed = *num_destroyed + 1; ++ } ++ return return_values; ++} ++ ++static void apply_to_every_long_map_node(long_map_node* node, void (*apply_func)(unsigned long key, void* value)) ++{ ++ if(node != NULL) ++ { ++ apply_to_every_long_map_node(node->left, apply_func); ++ ++ apply_func(node->key, node->value); ++ ++ apply_to_every_long_map_node(node->right, apply_func); ++ } ++} ++static void apply_to_every_string_map_node(long_map_node* node, unsigned char has_key, void (*apply_func)(char* key, void* value)) ++{ ++ if(node != NULL) ++ { ++ apply_to_every_string_map_node(node->left, has_key, apply_func); ++ ++ if(has_key) ++ { ++ string_map_key_value* kv = (string_map_key_value*)(node->value); ++ apply_func(kv->key, kv->value); ++ } ++ else ++ { ++ apply_func(NULL, node->value); ++ } ++ apply_to_every_string_map_node(node->right, has_key, apply_func); ++ } ++} ++ ++ ++ ++static void get_sorted_node_keys(long_map_node* node, unsigned long* key_list, unsigned long* next_key_index, int depth) ++{ ++ if(node != NULL) ++ { ++ get_sorted_node_keys(node->left, key_list, next_key_index, depth+1); ++ ++ key_list[ *next_key_index ] = node->key; ++ (*next_key_index)++; ++ ++ get_sorted_node_keys(node->right, key_list, next_key_index, depth+1); ++ } ++} ++ ++static void get_sorted_node_values(long_map_node* node, void** value_list, unsigned long* next_value_index, int depth) ++{ ++ if(node != NULL) ++ { ++ get_sorted_node_values(node->left, value_list, next_value_index, depth+1); ++ ++ value_list[ *next_value_index ] = node->value; ++ (*next_value_index)++; ++ ++ get_sorted_node_values(node->right, value_list, next_value_index, depth+1); ++ } ++} ++ ++ ++ ++/* ++ * direction = -1 indicates left subtree updated, direction = 1 for right subtree ++ * update_op = -1 indicates delete node, update_op = 1 for insert node ++ */ ++static signed char rebalance (long_map_node** n, signed char direction, signed char update_op) ++{ ++ /* ++ printf( "original: key = %ld, balance = %d, update_op=%d, direction=%d\n", (*n)->key, (*n)->balance, update_op, direction); ++ */ ++ ++ (*n)->balance = (*n)->balance + (update_op*direction); ++ ++ if( (*n)->balance < -1) ++ { ++ if((*n)->left->balance < 0) ++ { ++ rotate_right(n); ++ (*n)->right->balance = 0; ++ (*n)->balance = 0; ++ } ++ else if((*n)->left->balance == 0) ++ { ++ rotate_right(n); ++ (*n)->right->balance = -1; ++ (*n)->balance = 1; ++ } ++ else if((*n)->left->balance > 0) ++ { ++ rotate_left( &((*n)->left) ); ++ rotate_right(n); ++ /* ++ if( (*n)->balance < 0 ) ++ { ++ (*n)->left->balance = 0; ++ (*n)->right->balance = 1; ++ } ++ else if( (*n)->balance == 0 ) ++ { ++ (*n)->left->balance = 0; ++ (*n)->right->balance = 0; ++ } ++ else if( (*n)->balance > 0 ) ++ { ++ (*n)->left->balance = -1; ++ (*n)->right->balance = 0; ++ } ++ */ ++ (*n)->left->balance = (*n)->balance > 0 ? -1 : 0; ++ (*n)->right->balance = (*n)->balance < 0 ? 1 : 0; ++ (*n)->balance = 0; ++ } ++ } ++ if( (*n)->balance > 1) ++ { ++ if((*n)->right->balance > 0) ++ { ++ rotate_left(n); ++ (*n)->left->balance = 0; ++ (*n)->balance = 0; ++ } ++ else if ((*n)->right->balance == 0) ++ { ++ rotate_left(n); ++ (*n)->left->balance = 1; ++ (*n)->balance = -1; ++ } ++ else if((*n)->right->balance < 0) ++ { ++ rotate_right( &((*n)->right) ); ++ rotate_left(n); ++ /* ++ if( (*n)->balance < 0 ) ++ { ++ (*n)->left->balance = 0; ++ (*n)->right->balance = 1; ++ } ++ else if( (*n)->balance == 0 ) ++ { ++ (*n)->left->balance = 0; ++ (*n)->right->balance = 0; ++ } ++ else if( (*n)->balance > 0 ) ++ { ++ (*n)->left->balance = -1; ++ (*n)->right->balance = 0; ++ } ++ */ ++ (*n)->left->balance = (*n)->balance > 0 ? -1 : 0; ++ (*n)->right->balance = (*n)->balance < 0 ? 1 : 0; ++ (*n)->balance = 0; ++ } ++ } ++ ++ /* ++ printf( "key = %ld, balance = %d\n", (*n)->key, (*n)->balance); ++ */ ++ ++ return (*n)->balance; ++} ++ ++ ++static void rotate_right (long_map_node** parent) ++{ ++ long_map_node* old_parent = *parent; ++ long_map_node* pivot = old_parent->left; ++ old_parent->left = pivot->right; ++ pivot->right = old_parent; ++ ++ *parent = pivot; ++} ++ ++static void rotate_left (long_map_node** parent) ++{ ++ long_map_node* old_parent = *parent; ++ long_map_node* pivot = old_parent->right; ++ old_parent->right = pivot->left; ++ pivot->left = old_parent; ++ ++ *parent = pivot; ++} ++ ++ ++ ++/*************************************************************************** ++ * This algorithm was created for the sdbm database library (a public-domain ++ * reimplementation of ndbm) and seems to work relatively well in ++ * scrambling bits ++ * ++ * ++ * This code was derived from code found at: ++ * http://www.cse.yorku.ca/~oz/hash.html ++ ***************************************************************************/ ++static unsigned long sdbm_string_hash(const char *key) ++{ ++ unsigned long hashed_key = 0; ++ ++ int index = 0; ++ unsigned int nextch; ++ while(key[index] != '\0') ++ { ++ nextch = key[index]; ++ hashed_key = nextch + (hashed_key << 6) + (hashed_key << 16) - hashed_key; ++ index++; ++ } ++ return hashed_key; ++} ++ ++ diff --git a/target/linux/generic/pending-4.19/608-add-kernel-gargoyle-netfilter-match-modules.patch b/target/linux/generic/pending-4.19/608-add-kernel-gargoyle-netfilter-match-modules.patch new file mode 100644 index 0000000000..7fb6078ac8 --- /dev/null +++ b/target/linux/generic/pending-4.19/608-add-kernel-gargoyle-netfilter-match-modules.patch @@ -0,0 +1,10864 @@ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ipt_bandwidth.h +@@ -0,0 +1,106 @@ ++/* bandwidth -- An iptables extension for bandwidth monitoring/control ++ * Can be used to efficiently monitor bandwidth and/or implement bandwidth quotas ++ * Can be queried using the iptbwctl userspace library ++ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) ++ * ++ * ++ * Copyright © 2009 by Eric Bishop ++ * ++ * This file is free software: you may copy, redistribute and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation, either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This file is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#ifndef _IPT_BANDWIDTH_H ++#define _IPT_BANDWIDTH_H ++ ++/*flags -- first three don't map to parameters the rest do */ ++#define BANDWIDTH_INITIALIZED 1 ++#define BANDWIDTH_REQUIRES_SUBNET 2 ++#define BANDWIDTH_SUBNET 4 ++#define BANDWIDTH_CMP 8 ++#define BANDWIDTH_CURRENT 16 ++#define BANDWIDTH_RESET_INTERVAL 32 ++#define BANDWIDTH_RESET_TIME 64 ++#define BANDWIDTH_LAST_BACKUP 128 ++ ++ ++/* parameter defs that don't map to flag bits */ ++#define BANDWIDTH_TYPE 70 ++#define BANDWIDTH_ID 71 ++#define BANDWIDTH_GT 72 ++#define BANDWIDTH_LT 73 ++#define BANDWIDTH_MONITOR 74 ++#define BANDWIDTH_CHECK 75 ++#define BANDWIDTH_CHECK_NOSWAP 76 ++#define BANDWIDTH_CHECK_SWAP 77 ++#define BANDWIDTH_NUM_INTERVALS 78 ++ ++/* possible reset intervals */ ++#define BANDWIDTH_MINUTE 80 ++#define BANDWIDTH_HOUR 81 ++#define BANDWIDTH_DAY 82 ++#define BANDWIDTH_WEEK 83 ++#define BANDWIDTH_MONTH 84 ++#define BANDWIDTH_NEVER 85 ++ ++/* possible monitoring types */ ++#define BANDWIDTH_COMBINED 90 ++#define BANDWIDTH_INDIVIDUAL_SRC 91 ++#define BANDWIDTH_INDIVIDUAL_DST 92 ++#define BANDWIDTH_INDIVIDUAL_LOCAL 93 ++#define BANDWIDTH_INDIVIDUAL_REMOTE 94 ++ ++ ++ ++/* socket id parameters (for userspace i/o) */ ++#define BANDWIDTH_SET 2048 ++#define BANDWIDTH_GET 2049 ++ ++/* max id length */ ++#define BANDWIDTH_MAX_ID_LENGTH 50 ++ ++/* 4 bytes for total number of entries, 100 entries of 12 bytes each, + 1 byte indicating whether all have been dumped */ ++#define BANDWIDTH_QUERY_LENGTH 1205 ++#define BANDWIDTH_ENTRY_LENGTH 12 ++ ++ ++struct ipt_bandwidth_info ++{ ++ char id[BANDWIDTH_MAX_ID_LENGTH]; ++ unsigned char type; ++ unsigned char check_type; ++ uint32_t local_subnet; ++ uint32_t local_subnet_mask; ++ ++ unsigned char cmp; ++ unsigned char reset_is_constant_interval; ++ time_t reset_interval; //specific fixed type (see above) or interval length in seconds ++ time_t reset_time; //seconds from start of month/week/day/hour/minute to do reset, or start point of interval if it is a constant interval ++ uint64_t bandwidth_cutoff; ++ uint64_t current_bandwidth; ++ time_t next_reset; ++ time_t previous_reset; ++ time_t last_backup_time; ++ ++ uint32_t num_intervals_to_save; ++ ++ ++ unsigned long hashed_id; ++ void* iam; ++ uint64_t* combined_bw; ++ struct ipt_bandwidth_info* non_const_self; ++ unsigned long* ref_count; ++ ++ ++}; ++#endif /*_IPT_BANDWIDTH_H*/ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ipt_timerange.h +@@ -0,0 +1,43 @@ ++/* timerange -- An iptables extension to match multiple timeranges within a week ++ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) ++ * ++ * ++ * Copyright © 2009 by Eric Bishop ++ * ++ * This file is free software: you may copy, redistribute and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation, either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This file is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++ ++ ++ ++#ifndef _IPT_TIMERANGE_H ++#define _IPT_TIMERANGE_H ++ ++ ++#define RANGE_LENGTH 51 ++ ++#define HOURS 1 ++#define WEEKDAYS 2 ++#define DAYS_HOURS (HOURS+WEEKDAYS) ++#define WEEKLY_RANGE 4 ++ ++ ++struct ipt_timerange_info ++{ ++ long ranges[RANGE_LENGTH]; ++ char days[7]; ++ char type; ++ unsigned char invert; ++}; ++#endif /*_IPT_TIMERANGE_H*/ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ipt_webmon.h +@@ -0,0 +1,63 @@ ++/* webmon -- A netfilter module to match URLs in HTTP requests ++ * This module can match using string match or regular expressions ++ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) ++ * ++ * ++ * Copyright © 2008-2010 by Eric Bishop ++ * ++ * This file is free software: you may copy, redistribute and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation, either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This file is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++ ++ ++ ++#ifndef _IPT_WEBMON_H ++#define _IPT_WEBMON_H ++ ++ ++#define WEBMON_MAX_IPS 256 ++#define WEBMON_MAX_IP_RANGES 16 ++ ++#define WEBMON_EXCLUDE 1 ++#define WEBMON_INCLUDE 2 ++ ++#define WEBMON_MAXDOMAIN 4 ++#define WEBMON_MAXSEARCH 8 ++ ++#define WEBMON_DOMAIN 16 ++#define WEBMON_SEARCH 32 ++ ++ ++#define WEBMON_SET 3064 ++ ++struct ipt_webmon_ip_range ++{ ++ uint32_t start; ++ uint32_t end; ++}; ++ ++struct ipt_webmon_info ++{ ++ uint32_t max_domains; ++ uint32_t max_searches; ++ uint32_t exclude_ips[WEBMON_MAX_IPS]; ++ struct ipt_webmon_ip_range exclude_ranges[WEBMON_MAX_IP_RANGES]; ++ uint32_t num_exclude_ips; ++ uint32_t num_exclude_ranges; ++ unsigned char exclude_type; ++ uint32_t* ref_count; ++ ++}; ++ ++#endif /*_IPT_WEBMON_H*/ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ipt_weburl.h +@@ -0,0 +1,45 @@ ++/* weburl -- A netfilter module to match URLs in HTTP requests ++ * This module can match using string match or regular expressions ++ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) ++ * ++ * ++ * Copyright © 2008 by Eric Bishop ++ * ++ * This file is free software: you may copy, redistribute and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation, either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This file is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++ ++ ++ ++#ifndef _IPT_WEBURL_H ++#define _IPT_WEBURL_H ++ ++ ++#define MAX_TEST_STR 1024 ++ ++#define WEBURL_CONTAINS_TYPE 1 ++#define WEBURL_REGEX_TYPE 2 ++#define WEBURL_EXACT_TYPE 3 ++#define WEBURL_ALL_PART 4 ++#define WEBURL_DOMAIN_PART 5 ++#define WEBURL_PATH_PART 6 ++ ++struct ipt_weburl_info ++{ ++ char test_str[MAX_TEST_STR]; ++ unsigned char match_type; ++ unsigned char match_part; ++ unsigned char invert; ++}; ++#endif /*_IPT_WEBURL_H*/ +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -417,5 +417,25 @@ config IP_NF_ARP_MANGLE + + endif # IP_NF_ARPTABLES + ++config IP_NF_MATCH_BANDWIDTH ++ tristate "bandwidth match support" ++ depends on IP_NF_IPTABLES ++ help ++ This option enables bandwidth match support. ++config IP_NF_MATCH_TIMERANGE ++ tristate "timerange match support" ++ depends on IP_NF_IPTABLES ++ help ++ This option enables timerange match support. ++config IP_NF_MATCH_WEBMON ++ tristate "webmon match support" ++ depends on IP_NF_IPTABLES ++ help ++ This option enables webmon match support. ++config IP_NF_MATCH_WEBURL ++ tristate "weburl match support" ++ depends on IP_NF_IPTABLES ++ help ++ This option enables weburl match support. + endmenu + +--- a/net/ipv4/netfilter/Makefile ++++ b/net/ipv4/netfilter/Makefile +@@ -55,6 +55,10 @@ obj-$(CONFIG_IP_NF_RAW) += iptable_raw.o + obj-$(CONFIG_IP_NF_SECURITY) += iptable_security.o + + # matches ++obj-$(CONFIG_IP_NF_MATCH_BANDWIDTH) += ipt_bandwidth.o ++obj-$(CONFIG_IP_NF_MATCH_TIMERANGE) += ipt_timerange.o ++obj-$(CONFIG_IP_NF_MATCH_WEBMON) += ipt_webmon.o ++obj-$(CONFIG_IP_NF_MATCH_WEBURL) += ipt_weburl.o + obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o + obj-$(CONFIG_IP_NF_MATCH_RPFILTER) += ipt_rpfilter.o + +--- /dev/null ++++ b/net/ipv4/netfilter/bandwidth_deps/tree_map.h +@@ -0,0 +1,1093 @@ ++/* ++ * Copyright © 2008 by Eric Bishop ++ * ++ * This work 'as-is' we provide. ++ * No warranty, express or implied. ++ * We've done our best, ++ * to debug and test. ++ * Liability for damages denied. ++ * ++ * Permission is granted hereby, ++ * to copy, share, and modify. ++ * Use as is fit, ++ * free or for profit. ++ * On this notice these rights rely. ++ * ++ * ++ * ++ * Note that unlike other portions of Gargoyle this code ++ * does not fall under the GPL, but the rather whimsical ++ * 'Poetic License' above. ++ * ++ * Basically, this library contains a bunch of utilities ++ * that I find useful. I'm sure other libraries exist ++ * that are just as good or better, but I like these tools ++ * because I personally wrote them, so I know their quirks. ++ * (i.e. I know where the bodies are buried). I want to ++ * make sure that I can re-use these utilities for whatever ++ * code I may want to write in the future be it ++ * proprietary or open-source, so I've put them under ++ * a very, very permissive license. ++ * ++ * If you find this code useful, use it. If not, don't. ++ * I really don't care. ++ * ++ */ ++ ++ ++#if __KERNEL__ ++ #define malloc(foo) kmalloc(foo,GFP_ATOMIC) ++ #define free(foo) kfree(foo) ++ #define printf(format,args...) printk(format,##args) ++ ++ /* kernel strdup */ ++ static inline char *kernel_strdup(const char *str); ++ static inline char *kernel_strdup(const char *str) ++ { ++ char *tmp; ++ long int s; ++ s=strlen(str) + 1; ++ tmp = kmalloc(s, GFP_ATOMIC); ++ if (tmp != NULL) ++ { ++ memcpy(tmp, str, s); ++ } ++ return tmp; ++ } ++ #define strdup kernel_strdup ++ ++#endif ++ ++ ++ ++/* tree_map structs / prototypes */ ++typedef struct long_tree_map_node ++{ ++ unsigned long key; ++ void* value; ++ ++ signed char balance; ++ struct long_tree_map_node* left; ++ struct long_tree_map_node* right; ++} long_map_node; ++ ++typedef struct ++{ ++ long_map_node* root; ++ unsigned long num_elements; ++ ++}long_map; ++ ++typedef struct ++{ ++ long_map lm; ++ unsigned char store_keys; ++ unsigned long num_elements; ++ ++}string_map; ++ ++ ++ ++/* long map functions */ ++long_map* initialize_long_map(void); ++void* get_long_map_element(long_map* map, unsigned long key); ++void* get_smallest_long_map_element(long_map* map, unsigned long* smallest_key); ++void* get_largest_long_map_element(long_map* map, unsigned long* largest_key); ++void* remove_smallest_long_map_element(long_map* map, unsigned long* smallest_key); ++void* remove_largest_long_map_element(long_map* map, unsigned long* largest_key); ++void* set_long_map_element(long_map* map, unsigned long key, void* value); ++void* remove_long_map_element(long_map* map, unsigned long key); ++unsigned long* get_sorted_long_map_keys(long_map* map, unsigned long* num_keys_returned); ++void** get_sorted_long_map_values(long_map* map, unsigned long* num_values_returned); ++void** destroy_long_map(long_map* map, int destruction_type, unsigned long* num_destroyed); ++void apply_to_every_long_map_value(long_map* map, void (*apply_func)(unsigned long key, void* value)); ++ ++/* string map functions */ ++string_map* initialize_string_map(unsigned char store_keys); ++void* get_string_map_element(string_map* map, const char* key); ++void* get_string_map_element_with_hashed_key(string_map* map, unsigned long hashed_key); ++void* set_string_map_element(string_map* map, const char* key, void* value); ++void* remove_string_map_element(string_map* map, const char* key); ++char** get_string_map_keys(string_map* map, unsigned long* num_keys_returned); ++void** get_string_map_values(string_map* map, unsigned long* num_values_returned); ++void** destroy_string_map(string_map* map, int destruction_type, unsigned long* num_destroyed); ++void apply_to_every_string_map_value(string_map* map, void (*apply_func)(char* key, void* value)); ++ ++ ++/* ++ * three different ways to deal with values when data structure is destroyed ++ */ ++#define DESTROY_MODE_RETURN_VALUES 20 ++#define DESTROY_MODE_FREE_VALUES 21 ++#define DESTROY_MODE_IGNORE_VALUES 22 ++ ++ ++/* ++ * for convenience & backwards compatibility alias _string_map_ functions to ++ * _map_ functions since string map is used more often than long map ++ */ ++#define initialize_map initialize_string_map ++#define set_map_element set_string_map_element ++#define get_map_element get_string_map_element ++#define remove_map_element remove_string_map_element ++#define get_map_keys get_string_map_keys ++#define get_map_values get_string_map_values ++#define destroy_map destroy_string_map ++ ++ ++/* internal utility structures/ functions */ ++typedef struct stack_node_struct ++{ ++ long_map_node** node_ptr; ++ signed char direction; ++ struct stack_node_struct* previous; ++} stack_node; ++ ++static void free_stack(stack_node* stack); ++static void** destroy_long_map_values(long_map* map, int destruction_type, unsigned long* num_destroyed); ++static void apply_to_every_long_map_node(long_map_node* node, void (*apply_func)(unsigned long key, void* value)); ++static void apply_to_every_string_map_node(long_map_node* node, unsigned char has_key, void (*apply_func)(char* key, void* value)); ++static void get_sorted_node_keys(long_map_node* node, unsigned long* key_list, unsigned long* next_key_index, int depth); ++static void get_sorted_node_values(long_map_node* node, void** value_list, unsigned long* next_value_index, int depth); ++static signed char rebalance (long_map_node** n, signed char direction, signed char update_op); ++static void rotate_right (long_map_node** parent); ++static void rotate_left (long_map_node** parent); ++ ++/* internal for string map */ ++typedef struct ++{ ++ char* key; ++ void* value; ++} string_map_key_value; ++static unsigned long sdbm_string_hash(const char *key); ++ ++ ++ ++ ++/*************************************************** ++ * For testing only ++ ***************************************************/ ++/* ++void print_list(stack_node *l); ++ ++void print_list(stack_node *l) ++{ ++ if(l != NULL) ++ { ++ printf(" list key = %ld, dir=%d, \n", (*(l->node_ptr))->key, l->direction); ++ print_list(l->previous); ++ } ++} ++*/ ++/****************************************************** ++ * End testing Code ++ *******************************************************/ ++ ++ ++ ++ ++/*************************************************** ++ * string_map function definitions ++ ***************************************************/ ++ ++string_map* initialize_string_map(unsigned char store_keys) ++{ ++ string_map* map = (string_map*)malloc(sizeof(string_map)); ++ if(map != NULL) ++ { ++ map->store_keys = store_keys; ++ map->lm.root = NULL; ++ map->lm.num_elements = 0; ++ map->num_elements = map->lm.num_elements; ++ } ++ return map; ++} ++ ++void* get_string_map_element(string_map* map, const char* key) ++{ ++ unsigned long hashed_key = sdbm_string_hash(key); ++ ++ return get_string_map_element_with_hashed_key(map, hashed_key); ++} ++ ++void* get_string_map_element_with_hashed_key(string_map* map, unsigned long hashed_key) ++{ ++ void* return_value; ++ /* printk("doing lookup for key = %lu\n", hashed_key); */ ++ return_value = get_long_map_element( &(map->lm), hashed_key); ++ if(return_value != NULL && map->store_keys) ++ { ++ string_map_key_value* r = (string_map_key_value*)return_value; ++ return_value = r->value; ++ } ++ map->num_elements = map->lm.num_elements; ++ return return_value; ++} ++ ++void* set_string_map_element(string_map* map, const char* key, void* value) ++{ ++ unsigned long hashed_key = sdbm_string_hash(key); ++ void* return_value = NULL; ++ if(map->store_keys) ++ { ++ string_map_key_value* kv = (string_map_key_value*)malloc(sizeof(string_map_key_value)); ++ if(kv == NULL) /* deal with malloc failure */ ++ { ++ return NULL; ++ } ++ kv->key = strdup(key); ++ if(kv->key == NULL) /* deal with malloc failure */ ++ { ++ free(kv); ++ return NULL; ++ } ++ kv->value = value; ++ return_value = set_long_map_element( &(map->lm), hashed_key, kv); ++ if(return_value != NULL) ++ { ++ string_map_key_value* r = (string_map_key_value*)return_value; ++ return_value = r->value; ++ free(r->key); ++ free(r); ++ } ++ } ++ else ++ { ++ return_value = set_long_map_element( &(map->lm), hashed_key, value); ++ } ++ map->num_elements = map->lm.num_elements; ++ return return_value; ++} ++ ++void* remove_string_map_element(string_map* map, const char* key) ++{ ++ unsigned long hashed_key = sdbm_string_hash(key); ++ void* return_value = remove_long_map_element( &(map->lm), hashed_key); ++ ++ if(return_value != NULL && map->store_keys) ++ { ++ string_map_key_value* r = (string_map_key_value*)return_value; ++ return_value = r->value; ++ free(r->key); ++ free(r); ++ } ++ map->num_elements = map->lm.num_elements; ++ return return_value; ++} ++ ++char** get_string_map_keys(string_map* map, unsigned long* num_keys_returned) ++{ ++ char** str_keys; ++ str_keys = (char**)malloc((map->num_elements+1)*sizeof(char*)); ++ if(str_keys == NULL) /* deal with malloc failure */ ++ { ++ return NULL; ++ } ++ str_keys[0] = NULL; ++ *num_keys_returned = 0; ++ if(map->store_keys && map->num_elements > 0) ++ { ++ unsigned long list_length; ++ void** long_values = get_sorted_long_map_values( &(map->lm), &list_length); ++ unsigned long key_index; ++ /*list_length will be 0 on malloc failure in get_sorted_long_map_values, so this code shouldn't seg fault if that happens */ ++ for(key_index = 0; key_index < list_length; key_index++) ++ { ++ str_keys[key_index] = strdup( ((string_map_key_value*)(long_values[key_index]))->key); ++ if(str_keys[key_index] == NULL) /* deal with malloc failure */ ++ { ++ //just return the incomplete list (hey, it's null terminated...) ++ free(long_values); ++ return str_keys; ++ } ++ *num_keys_returned = *num_keys_returned + 1; ++ } ++ str_keys[list_length] = NULL; ++ free(long_values); ++ } ++ return str_keys; ++} ++ ++ ++void** get_string_map_values(string_map* map, unsigned long* num_values_returned) ++{ ++ void** values = NULL; ++ if(map != NULL) ++ { ++ values = get_sorted_long_map_values ( &(map->lm), num_values_returned ); ++ } ++ return values; ++} ++ ++ ++void** destroy_string_map(string_map* map, int destruction_type, unsigned long* num_destroyed) ++{ ++ void** return_values = NULL; ++ if(map != NULL) ++ { ++ if(map->store_keys) ++ { ++ void** kvs = destroy_long_map_values( &(map->lm), DESTROY_MODE_RETURN_VALUES, num_destroyed ); ++ unsigned long kv_index = 0; ++ for(kv_index=0; kv_index < *num_destroyed; kv_index++) ++ { ++ string_map_key_value* kv = (string_map_key_value*)kvs[kv_index]; ++ void* value = kv->value; ++ ++ free(kv->key); ++ free(kv); ++ if(destruction_type == DESTROY_MODE_FREE_VALUES) ++ { ++ free(value); ++ } ++ if(destruction_type == DESTROY_MODE_RETURN_VALUES) ++ { ++ kvs[kv_index] = value; ++ } ++ } ++ if(destruction_type == DESTROY_MODE_RETURN_VALUES) ++ { ++ return_values = kvs; ++ } ++ else ++ { ++ free(kvs); ++ } ++ } ++ else ++ { ++ return_values = destroy_long_map_values( &(map->lm), destruction_type, num_destroyed ); ++ } ++ free(map); ++ } ++ return return_values; ++} ++ ++ ++ ++ ++/*************************************************** ++ * long_map function definitions ++ ***************************************************/ ++ ++long_map* initialize_long_map(void) ++{ ++ long_map* map = (long_map*)malloc(sizeof(long_map)); ++ if(map != NULL) /* test for malloc failure */ ++ { ++ map->root = NULL; ++ map->num_elements = 0; ++ } ++ return map; ++} ++ ++void* get_long_map_element(long_map* map, unsigned long key) ++{ ++ void* value = NULL; ++ ++ if(map->root != NULL) ++ { ++ long_map_node* parent_node = map->root; ++ long_map_node* next_node; ++ while( key != parent_node->key && (next_node = (long_map_node *)(key < parent_node->key ? parent_node->left : parent_node->right)) != NULL) ++ { ++ parent_node = next_node; ++ } ++ if(parent_node->key == key) ++ { ++ value = parent_node->value; ++ } ++ } ++ return value; ++} ++ ++void* get_smallest_long_map_element(long_map* map, unsigned long* smallest_key) ++{ ++ void* value = NULL; ++ if(map->root != NULL) ++ { ++ long_map_node* next_node = map->root; ++ while( next_node->left != NULL) ++ { ++ next_node = next_node->left; ++ } ++ value = next_node->value; ++ *smallest_key = next_node->key; ++ } ++ return value; ++} ++ ++void* get_largest_long_map_element(long_map* map, unsigned long* largest_key) ++{ ++ void* value = NULL; ++ if(map->root != NULL) ++ { ++ long_map_node* next_node = map->root; ++ while( next_node->right != NULL) ++ { ++ next_node = next_node->right; ++ } ++ value = next_node->value; ++ *largest_key = next_node->key; ++ } ++ return value; ++} ++ ++void* remove_smallest_long_map_element(long_map* map, unsigned long* smallest_key) ++{ ++ get_smallest_long_map_element(map, smallest_key); ++ return remove_long_map_element(map, *smallest_key); ++} ++ ++void* remove_largest_long_map_element(long_map* map, unsigned long* largest_key) ++{ ++ get_largest_long_map_element(map, largest_key); ++ return remove_long_map_element(map, *largest_key); ++} ++ ++ ++/* if replacement performed, returns replaced value, otherwise null */ ++void* set_long_map_element(long_map* map, unsigned long key, void* value) ++{ ++ stack_node* parent_list = NULL; ++ void* old_value = NULL; ++ int old_value_found = 0; ++ ++ long_map_node* parent_node; ++ long_map_node* next_node; ++ stack_node* next_parent; ++ stack_node* previous_parent; ++ signed char new_balance; ++ ++ ++ long_map_node* new_node = (long_map_node*)malloc(sizeof(long_map_node)); ++ if(new_node == NULL) ++ { ++ return NULL; ++ } ++ new_node->value = value; ++ new_node->key = key; ++ new_node->left = NULL; ++ new_node->right = NULL; ++ new_node->balance = 0; ++ ++ ++ ++ if(map->root == NULL) ++ { ++ map->root = new_node; ++ } ++ else ++ { ++ parent_node = map->root; ++ ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ free(new_node); ++ return NULL; /* won't insert but won't seg fault */ ++ } ++ next_parent->node_ptr = &(map->root); ++ next_parent->previous = parent_list; ++ parent_list = next_parent; ++ ++ while( key != parent_node->key && (next_node = (key < parent_node->key ? parent_node->left : parent_node->right) ) != NULL) ++ { ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ free(new_node); ++ return NULL; ++ } ++ next_parent->node_ptr = key < parent_node->key ? &(parent_node->left) : &(parent_node->right); ++ next_parent->previous = parent_list; ++ next_parent->previous->direction = key < parent_node->key ? -1 : 1; ++ parent_list = next_parent; ++ ++ parent_node = next_node; ++ } ++ ++ ++ if(key == parent_node->key) ++ { ++ old_value = parent_node->value; ++ old_value_found = 1; ++ parent_node->value = value; ++ free(new_node); ++ /* we merely replaced a node, no need to rebalance */ ++ } ++ else ++ { ++ if(key < parent_node->key) ++ { ++ parent_node->left = (void*)new_node; ++ parent_list->direction = -1; ++ } ++ else ++ { ++ parent_node->right = (void*)new_node; ++ parent_list->direction = 1; ++ } ++ ++ ++ /* we inserted a node, rebalance */ ++ previous_parent = parent_list; ++ new_balance = 1; /* initial value is not used, but must not be 0 for initial loop condition */ ++ ++ ++ while(previous_parent != NULL && new_balance != 0) ++ { ++ new_balance = rebalance(previous_parent->node_ptr, previous_parent->direction, 1); ++ previous_parent = previous_parent->previous; ++ } ++ } ++ } ++ ++ free_stack(parent_list); ++ ++ if(old_value_found == 0) ++ { ++ map->num_elements = map->num_elements + 1; ++ } ++ ++ return old_value; ++} ++ ++ ++void* remove_long_map_element(long_map* map, unsigned long key) ++{ ++ ++ void* value = NULL; ++ ++ long_map_node* root_node = map->root; ++ stack_node* parent_list = NULL; ++ ++ ++ long_map_node* remove_parent; ++ long_map_node* remove_node; ++ long_map_node* next_node; ++ ++ long_map_node* replacement; ++ long_map_node* replacement_parent; ++ long_map_node* replacement_next; ++ ++ stack_node* next_parent; ++ stack_node* previous_parent; ++ stack_node* replacement_stack_node; ++ ++ ++ signed char new_balance; ++ ++ ++ ++ if(root_node != NULL) ++ { ++ remove_parent = root_node; ++ remove_node = key < remove_parent->key ? remove_parent->left : remove_parent->right; ++ ++ if(remove_node != NULL && key != remove_parent->key) ++ { ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ return NULL; ++ } ++ next_parent->node_ptr = &(map->root); ++ next_parent->previous = parent_list; ++ parent_list = next_parent; ++ while( key != remove_node->key && (next_node = (key < remove_node->key ? remove_node->left : remove_node->right)) != NULL) ++ { ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ next_parent->node_ptr = key < remove_parent->key ? &(remove_parent->left) : &(remove_parent->right); ++ next_parent->previous = parent_list; ++ next_parent->previous->direction = key < remove_parent->key ? -1 : 1; ++ parent_list = next_parent; ++ ++ ++ remove_parent = remove_node; ++ remove_node = next_node; ++ } ++ parent_list->direction = key < remove_parent-> key ? -1 : 1; ++ } ++ else ++ { ++ remove_node = remove_parent; ++ } ++ ++ ++ if(key == remove_node->key) ++ { ++ ++ /* find replacement for node we are deleting */ ++ if( remove_node->right == NULL ) ++ { ++ replacement = remove_node->left; ++ } ++ else if( remove_node->right->left == NULL) ++ { ++ ++ replacement = remove_node->right; ++ replacement->left = remove_node->left; ++ replacement->balance = remove_node->balance; ++ ++ /* put pointer to replacement node into list for balance update */ ++ replacement_stack_node = (stack_node*)malloc(sizeof(stack_node)); ++ if(replacement_stack_node == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ replacement_stack_node->previous = parent_list; ++ replacement_stack_node->direction = 1; /* replacement is from right */ ++ if(remove_node == remove_parent) /* special case for root node */ ++ { ++ replacement_stack_node->node_ptr = &(map->root); ++ } ++ else ++ { ++ replacement_stack_node->node_ptr = key < remove_parent-> key ? &(remove_parent->left) : &(remove_parent->right); ++ } ++ parent_list = replacement_stack_node; ++ ++ } ++ else ++ { ++ /* put pointer to replacement node into list for balance update */ ++ replacement_stack_node = (stack_node*)malloc(sizeof(stack_node)); ++ if(replacement_stack_node == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ ++ replacement_stack_node->previous = parent_list; ++ replacement_stack_node->direction = 1; /* we always look for replacement on right */ ++ if(remove_node == remove_parent) /* special case for root node */ ++ { ++ replacement_stack_node->node_ptr = &(map->root); ++ } ++ else ++ { ++ replacement_stack_node->node_ptr = key < remove_parent-> key ? &(remove_parent->left) : &(remove_parent->right); ++ } ++ ++ parent_list = replacement_stack_node; ++ ++ ++ /* ++ * put pointer to replacement node->right into list for balance update ++ * this node will have to be updated with the proper pointer ++ * after we have identified the replacement ++ */ ++ replacement_stack_node = (stack_node*)malloc(sizeof(stack_node)); ++ if(replacement_stack_node == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ ++ replacement_stack_node->previous = parent_list; ++ replacement_stack_node->direction = -1; /* we always look for replacement to left of this node */ ++ parent_list = replacement_stack_node; ++ ++ /* find smallest node on right (large) side of tree */ ++ replacement_parent = remove_node->right; ++ replacement = replacement_parent->left; ++ ++ while((replacement_next = replacement->left) != NULL) ++ { ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ ++ next_parent->node_ptr = &(replacement_parent->left); ++ next_parent->previous = parent_list; ++ next_parent->direction = -1; /* we always go left */ ++ parent_list = next_parent; ++ ++ replacement_parent = replacement; ++ replacement = replacement_next; ++ ++ } ++ ++ replacement_parent->left = replacement->right; ++ ++ replacement->left = remove_node->left; ++ replacement->right = remove_node->right; ++ replacement->balance = remove_node->balance; ++ replacement_stack_node->node_ptr = &(replacement->right); ++ } ++ ++ /* insert replacement at proper location in tree */ ++ if(remove_node == remove_parent) ++ { ++ map->root = replacement; ++ } ++ else ++ { ++ remove_parent->left = remove_node == remove_parent->left ? replacement : remove_parent->left; ++ remove_parent->right = remove_node == remove_parent->right ? replacement : remove_parent->right; ++ } ++ ++ ++ /* rebalance tree */ ++ previous_parent = parent_list; ++ new_balance = 0; ++ while(previous_parent != NULL && new_balance == 0) ++ { ++ new_balance = rebalance(previous_parent->node_ptr, previous_parent->direction, -1); ++ previous_parent = previous_parent->previous; ++ } ++ ++ ++ ++ ++ /* ++ * since we found a value to remove, decrease number of elements in map ++ * set return value to the deleted node's value and free the node ++ */ ++ map->num_elements = map->num_elements - 1; ++ value = remove_node->value; ++ free(remove_node); ++ } ++ } ++ ++ free_stack(parent_list); ++ ++ return value; ++} ++ ++ ++/* note: returned keys are dynamically allocated, you need to free them! */ ++unsigned long* get_sorted_long_map_keys(long_map* map, unsigned long* num_keys_returned) ++{ ++ unsigned long* key_list = (unsigned long*)malloc((map->num_elements)*sizeof(unsigned long)); ++ unsigned long next_key_index; ++ if(key_list == NULL) ++ { ++ *num_keys_returned = 0; ++ return NULL; ++ } ++ next_key_index = 0; ++ get_sorted_node_keys(map->root, key_list, &next_key_index, 0); ++ ++ *num_keys_returned = map->num_elements; ++ ++ return key_list; ++} ++ ++ ++void** get_sorted_long_map_values(long_map* map, unsigned long* num_values_returned) ++{ ++ void** value_list = (void**)malloc((map->num_elements+1)*sizeof(void*)); ++ unsigned long next_value_index; ++ ++ if(value_list == NULL) ++ { ++ *num_values_returned = 0; ++ return NULL; ++ } ++ next_value_index = 0; ++ get_sorted_node_values(map->root, value_list, &next_value_index, 0); ++ value_list[map->num_elements] = NULL; /* since we're dealing with pointers make list null terminated */ ++ ++ *num_values_returned = map->num_elements; ++ return value_list; ++ ++} ++ ++ ++ ++void** destroy_long_map(long_map* map, int destruction_type, unsigned long* num_destroyed) ++{ ++ void** return_values = destroy_long_map_values(map, destruction_type, num_destroyed); ++ free(map); ++ return return_values; ++} ++ ++ ++ ++void apply_to_every_long_map_value(long_map* map, void (*apply_func)(unsigned long key, void* value)) ++{ ++ apply_to_every_long_map_node(map->root, apply_func); ++} ++void apply_to_every_string_map_value(string_map* map, void (*apply_func)(char* key, void* value)) ++{ ++ apply_to_every_string_map_node( (map->lm).root, map->store_keys, apply_func); ++} ++ ++ ++/*************************************************** ++ * internal utility function definitions ++ ***************************************************/ ++static void free_stack(stack_node* stack) ++{ ++ while(stack != NULL) ++ { ++ stack_node* prev_node = stack; ++ stack = prev_node->previous; ++ free(prev_node); ++ } ++ ++} ++ ++static void** destroy_long_map_values(long_map* map, int destruction_type, unsigned long* num_destroyed) ++{ ++ void** return_values = NULL; ++ unsigned long return_index = 0; ++ ++ *num_destroyed = 0; ++ ++ if(destruction_type == DESTROY_MODE_RETURN_VALUES) ++ { ++ return_values = (void**)malloc((map->num_elements+1)*sizeof(void*)); ++ if(return_values == NULL) /* deal with malloc failure */ ++ { ++ destruction_type = DESTROY_MODE_IGNORE_VALUES; /* could cause memory leak, but there's no other way to be sure we won't seg fault */ ++ } ++ else ++ { ++ return_values[map->num_elements] = NULL; ++ } ++ } ++ while(map->num_elements > 0) ++ { ++ unsigned long smallest_key; ++ void* removed_value = remove_smallest_long_map_element(map, &smallest_key); ++ if(destruction_type == DESTROY_MODE_RETURN_VALUES) ++ { ++ return_values[return_index] = removed_value; ++ } ++ if(destruction_type == DESTROY_MODE_FREE_VALUES) ++ { ++ free(removed_value); ++ } ++ return_index++; ++ *num_destroyed = *num_destroyed + 1; ++ } ++ return return_values; ++} ++ ++static void apply_to_every_long_map_node(long_map_node* node, void (*apply_func)(unsigned long key, void* value)) ++{ ++ if(node != NULL) ++ { ++ apply_to_every_long_map_node(node->left, apply_func); ++ ++ apply_func(node->key, node->value); ++ ++ apply_to_every_long_map_node(node->right, apply_func); ++ } ++} ++static void apply_to_every_string_map_node(long_map_node* node, unsigned char has_key, void (*apply_func)(char* key, void* value)) ++{ ++ if(node != NULL) ++ { ++ apply_to_every_string_map_node(node->left, has_key, apply_func); ++ ++ if(has_key) ++ { ++ string_map_key_value* kv = (string_map_key_value*)(node->value); ++ apply_func(kv->key, kv->value); ++ } ++ else ++ { ++ apply_func(NULL, node->value); ++ } ++ apply_to_every_string_map_node(node->right, has_key, apply_func); ++ } ++} ++ ++ ++ ++static void get_sorted_node_keys(long_map_node* node, unsigned long* key_list, unsigned long* next_key_index, int depth) ++{ ++ if(node != NULL) ++ { ++ get_sorted_node_keys(node->left, key_list, next_key_index, depth+1); ++ ++ key_list[ *next_key_index ] = node->key; ++ (*next_key_index)++; ++ ++ get_sorted_node_keys(node->right, key_list, next_key_index, depth+1); ++ } ++} ++ ++static void get_sorted_node_values(long_map_node* node, void** value_list, unsigned long* next_value_index, int depth) ++{ ++ if(node != NULL) ++ { ++ get_sorted_node_values(node->left, value_list, next_value_index, depth+1); ++ ++ value_list[ *next_value_index ] = node->value; ++ (*next_value_index)++; ++ ++ get_sorted_node_values(node->right, value_list, next_value_index, depth+1); ++ } ++} ++ ++ ++ ++/* ++ * direction = -1 indicates left subtree updated, direction = 1 for right subtree ++ * update_op = -1 indicates delete node, update_op = 1 for insert node ++ */ ++static signed char rebalance (long_map_node** n, signed char direction, signed char update_op) ++{ ++ /* ++ printf( "original: key = %ld, balance = %d, update_op=%d, direction=%d\n", (*n)->key, (*n)->balance, update_op, direction); ++ */ ++ ++ (*n)->balance = (*n)->balance + (update_op*direction); ++ ++ if( (*n)->balance < -1) ++ { ++ if((*n)->left->balance < 0) ++ { ++ rotate_right(n); ++ (*n)->right->balance = 0; ++ (*n)->balance = 0; ++ } ++ else if((*n)->left->balance == 0) ++ { ++ rotate_right(n); ++ (*n)->right->balance = -1; ++ (*n)->balance = 1; ++ } ++ else if((*n)->left->balance > 0) ++ { ++ rotate_left( &((*n)->left) ); ++ rotate_right(n); ++ /* ++ if( (*n)->balance < 0 ) ++ { ++ (*n)->left->balance = 0; ++ (*n)->right->balance = 1; ++ } ++ else if( (*n)->balance == 0 ) ++ { ++ (*n)->left->balance = 0; ++ (*n)->right->balance = 0; ++ } ++ else if( (*n)->balance > 0 ) ++ { ++ (*n)->left->balance = -1; ++ (*n)->right->balance = 0; ++ } ++ */ ++ (*n)->left->balance = (*n)->balance > 0 ? -1 : 0; ++ (*n)->right->balance = (*n)->balance < 0 ? 1 : 0; ++ (*n)->balance = 0; ++ } ++ } ++ if( (*n)->balance > 1) ++ { ++ if((*n)->right->balance > 0) ++ { ++ rotate_left(n); ++ (*n)->left->balance = 0; ++ (*n)->balance = 0; ++ } ++ else if ((*n)->right->balance == 0) ++ { ++ rotate_left(n); ++ (*n)->left->balance = 1; ++ (*n)->balance = -1; ++ } ++ else if((*n)->right->balance < 0) ++ { ++ rotate_right( &((*n)->right) ); ++ rotate_left(n); ++ /* ++ if( (*n)->balance < 0 ) ++ { ++ (*n)->left->balance = 0; ++ (*n)->right->balance = 1; ++ } ++ else if( (*n)->balance == 0 ) ++ { ++ (*n)->left->balance = 0; ++ (*n)->right->balance = 0; ++ } ++ else if( (*n)->balance > 0 ) ++ { ++ (*n)->left->balance = -1; ++ (*n)->right->balance = 0; ++ } ++ */ ++ (*n)->left->balance = (*n)->balance > 0 ? -1 : 0; ++ (*n)->right->balance = (*n)->balance < 0 ? 1 : 0; ++ (*n)->balance = 0; ++ } ++ } ++ ++ /* ++ printf( "key = %ld, balance = %d\n", (*n)->key, (*n)->balance); ++ */ ++ ++ return (*n)->balance; ++} ++ ++ ++static void rotate_right (long_map_node** parent) ++{ ++ long_map_node* old_parent = *parent; ++ long_map_node* pivot = old_parent->left; ++ old_parent->left = pivot->right; ++ pivot->right = old_parent; ++ ++ *parent = pivot; ++} ++ ++static void rotate_left (long_map_node** parent) ++{ ++ long_map_node* old_parent = *parent; ++ long_map_node* pivot = old_parent->right; ++ old_parent->right = pivot->left; ++ pivot->left = old_parent; ++ ++ *parent = pivot; ++} ++ ++ ++ ++/*************************************************************************** ++ * This algorithm was created for the sdbm database library (a public-domain ++ * reimplementation of ndbm) and seems to work relatively well in ++ * scrambling bits ++ * ++ * ++ * This code was derived from code found at: ++ * http://www.cse.yorku.ca/~oz/hash.html ++ ***************************************************************************/ ++static unsigned long sdbm_string_hash(const char *key) ++{ ++ unsigned long hashed_key = 0; ++ ++ int index = 0; ++ unsigned int nextch; ++ while(key[index] != '\0') ++ { ++ nextch = key[index]; ++ hashed_key = nextch + (hashed_key << 6) + (hashed_key << 16) - hashed_key; ++ index++; ++ } ++ return hashed_key; ++} ++ ++ +--- /dev/null ++++ b/net/ipv4/netfilter/ipt_bandwidth.c +@@ -0,0 +1,2501 @@ ++/* bandwidth -- An iptables extension for bandwidth monitoring/control ++ * Can be used to efficiently monitor bandwidth and/or implement bandwidth quotas ++ * Can be queried using the iptbwctl userspace library ++ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) ++ * ++ * ++ * Copyright © 2009-2011 by Eric Bishop ++ * ++ * This file is free software: you may copy, redistribute and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation, either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This file is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++ ++ ++#include "bandwidth_deps/tree_map.h" ++#include ++#include ++ ++ ++#include ++#include ++ ++ ++/* #define BANDWIDTH_DEBUG 1 */ ++ ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Eric Bishop"); ++MODULE_DESCRIPTION("Match bandwidth used, designed for use with Gargoyle web interface (www.gargoyle-router.com)"); ++ ++/* ++ * WARNING: accessing the sys_tz variable takes FOREVER, and kills performance ++ * keep a local variable that gets updated from the extern variable ++ */ ++extern struct timezone sys_tz; ++static int local_minutes_west; ++static int local_seconds_west; ++static time_t last_local_mw_update; ++ ++ ++static spinlock_t bandwidth_lock = __SPIN_LOCK_UNLOCKED(bandwidth_lock); ++DEFINE_SEMAPHORE(userspace_lock); ++ ++static string_map* id_map = NULL; ++ ++ ++typedef struct info_and_maps_struct ++{ ++ struct ipt_bandwidth_info* info; ++ long_map* ip_map; ++ long_map* ip_history_map; ++}info_and_maps; ++ ++typedef struct history_struct ++{ ++ time_t first_start; ++ time_t first_end; ++ time_t last_end; /* also beginning of current time frame */ ++ uint32_t max_nodes; ++ uint32_t num_nodes; ++ uint32_t non_zero_nodes; ++ uint32_t current_index; ++ uint64_t* history_data; ++} bw_history; ++ ++ ++ ++static unsigned char set_in_progress = 0; ++static char set_id[BANDWIDTH_MAX_ID_LENGTH] = ""; ++ ++/* ++ * function prototypes ++ * ++ * (prototypes only provided for ++ * functions not part of iptables API) ++ * ++*/ ++ ++ ++static void adjust_ip_for_backwards_time_shift(unsigned long key, void* value); ++static void adjust_id_for_backwards_time_shift(char* key, void* value); ++static void check_for_backwards_time_shift(time_t now); ++ ++ ++static void shift_timezone_of_ip(unsigned long key, void* value); ++static void shift_timezone_of_id(char* key, void* value); ++static void check_for_timezone_shift(time_t now, int already_locked); ++ ++ ++ ++static bw_history* initialize_history(uint32_t max_nodes); ++static unsigned char update_history(bw_history* history, time_t interval_start, time_t interval_end, struct ipt_bandwidth_info* info); ++ ++ ++ ++static void do_reset(unsigned long key, void* value); ++static void set_bandwidth_to_zero(unsigned long key, void* value); ++static void handle_interval_reset(info_and_maps* iam, time_t now); ++ ++static uint64_t pow64(uint64_t base, uint64_t pow); ++static uint64_t get_bw_record_max(void); /* called by init to set global variable */ ++ ++static inline int is_leap(unsigned int y); ++static time_t get_next_reset_time(struct ipt_bandwidth_info *info, time_t now, time_t previous_reset); ++static time_t get_nominal_previous_reset_time(struct ipt_bandwidth_info *info, time_t current_next_reset); ++ ++static uint64_t* initialize_map_entries_for_ip(info_and_maps* iam, unsigned long ip, uint64_t initial_bandwidth); ++ ++ ++ ++ ++static time_t backwards_check = 0; ++static time_t backwards_adjust_current_time = 0; ++static time_t backwards_adjust_info_previous_reset = 0; ++static time_t backwards_adjust_ips_zeroed = 0; ++static info_and_maps* backwards_adjust_iam = NULL; ++ ++/* ++static char print_out_buf[25000]; ++static void print_to_buf(char* outdat); ++static void reset_buf(void); ++static void do_print_buf(void); ++ ++static void print_to_buf(char* outdat) ++{ ++ int buf_len = strlen(print_out_buf); ++ sprintf(print_out_buf+buf_len, "\t%s\n", outdat); ++} ++static void reset_buf(void) ++{ ++ print_out_buf[0] = '\n'; ++ print_out_buf[1] = '\0'; ++} ++static void do_print_buf(void) ++{ ++ char* start = print_out_buf; ++ char* next = strchr(start, '\n'); ++ while(next != NULL) ++ { ++ *next = '\0'; ++ printk("%s\n", start); ++ start = next+1; ++ next = strchr(start, '\n'); ++ } ++ printk("%s\n", start); ++ ++ reset_buf(); ++} ++*/ ++ ++static void adjust_ip_for_backwards_time_shift(unsigned long key, void* value) ++{ ++ bw_history* old_history = (bw_history*)value; ++ ++ if(old_history->num_nodes == 1) ++ { ++ if(backwards_adjust_info_previous_reset > backwards_adjust_current_time) ++ { ++ if(backwards_adjust_ips_zeroed == 0) ++ { ++ apply_to_every_long_map_value(backwards_adjust_iam->ip_map, set_bandwidth_to_zero); ++ backwards_adjust_iam->info->next_reset = get_next_reset_time(backwards_adjust_iam->info, backwards_adjust_current_time, backwards_adjust_current_time); ++ backwards_adjust_iam->info->previous_reset = backwards_adjust_current_time; ++ backwards_adjust_iam->info->current_bandwidth = 0; ++ backwards_adjust_ips_zeroed = 1; ++ } ++ } ++ return; ++ } ++ else if(old_history->last_end < backwards_adjust_current_time) ++ { ++ return; ++ } ++ else ++ { ++ ++ /* ++ * reconstruct new history without newest nodes, to represent data as it was ++ * last time the current time was set to the interval to which we just jumped back ++ */ ++ uint32_t next_old_index; ++ time_t old_next_start = old_history->first_start == 0 ? backwards_adjust_info_previous_reset : old_history->first_start; /* first time point in old history */ ++ bw_history* new_history = initialize_history(old_history->max_nodes); ++ if(new_history == NULL) ++ { ++ printk("ipt_bandwidth: warning, kmalloc failure!\n"); ++ return; ++ } ++ ++ ++ ++ /* oldest index in old history -- we iterate forward through old history using this index */ ++ next_old_index = old_history->num_nodes == old_history->max_nodes ? (old_history->current_index+1) % old_history->max_nodes : 0; ++ ++ ++ /* if first time point is after current time, just completely re-initialize history, otherwise set first time point to old first time point */ ++ (new_history->history_data)[ new_history->current_index ] = old_next_start < backwards_adjust_current_time ? (old_history->history_data)[next_old_index] : 0; ++ backwards_adjust_iam->info->previous_reset = old_next_start < backwards_adjust_current_time ? old_next_start : backwards_adjust_current_time; ++ ++ ++ /* iterate through old history, rebuilding in new history*/ ++ while( old_next_start < backwards_adjust_current_time ) ++ { ++ time_t old_next_end = get_next_reset_time(backwards_adjust_iam->info, old_next_start, old_next_start); /* 2nd param = last reset, 3rd param = current time */ ++ if( old_next_end < backwards_adjust_current_time) ++ { ++ update_history(new_history, old_next_start, old_next_end, backwards_adjust_iam->info); ++ next_old_index++; ++ (new_history->history_data)[ new_history->current_index ] = (old_history->history_data)[next_old_index]; ++ } ++ backwards_adjust_iam->info->previous_reset = old_next_start; /*update previous_reset variable in bw_info as we iterate */ ++ old_next_start = old_next_end; ++ } ++ ++ /* update next_reset variable from previous_reset variable which we've already set */ ++ backwards_adjust_iam->info->next_reset = get_next_reset_time(backwards_adjust_iam->info, backwards_adjust_iam->info->previous_reset, backwards_adjust_iam->info->previous_reset); ++ ++ ++ ++ /* set old_history to be new_history */ ++ kfree(old_history->history_data); ++ old_history->history_data = new_history->history_data; ++ old_history->first_start = new_history->first_start; ++ old_history->first_end = new_history->first_end; ++ old_history->last_end = new_history->last_end; ++ old_history->num_nodes = new_history->num_nodes; ++ old_history->non_zero_nodes = new_history->non_zero_nodes; ++ old_history->current_index = new_history->current_index; ++ set_long_map_element(backwards_adjust_iam->ip_map, key, (void*)(old_history->history_data + old_history->current_index) ); ++ if(key == 0) ++ { ++ backwards_adjust_iam->info->combined_bw = (uint64_t*)(old_history->history_data + old_history->current_index); ++ } ++ ++ /* ++ * free new history (which was just temporary) ++ * note that we don't need to free history_data from new_history ++ * we freed the history_data from old history, and set that to the history_data from new_history ++ * so, this cleanup has already been handled ++ */ ++ kfree(new_history); ++ ++ } ++} ++static void adjust_id_for_backwards_time_shift(char* key, void* value) ++{ ++ info_and_maps* iam = (info_and_maps*)value; ++ if(iam == NULL) ++ { ++ return; ++ } ++ if(iam->info == NULL) ++ { ++ return; ++ } ++ ++ backwards_adjust_iam = iam; ++ if( (iam->info->reset_is_constant_interval == 0 && iam->info->reset_interval == BANDWIDTH_NEVER) || iam->info->cmp == BANDWIDTH_CHECK ) ++ { ++ return; ++ } ++ if(iam->ip_history_map != NULL) ++ { ++ backwards_adjust_info_previous_reset = iam->info->previous_reset; ++ backwards_adjust_ips_zeroed = 0; ++ apply_to_every_long_map_value(iam->ip_history_map, adjust_ip_for_backwards_time_shift); ++ } ++ else ++ { ++ time_t next_reset_after_adjustment = get_next_reset_time(iam->info, backwards_adjust_current_time, backwards_adjust_current_time); ++ if(next_reset_after_adjustment < iam->info->next_reset) ++ { ++ iam->info->previous_reset = backwards_adjust_current_time; ++ iam->info->next_reset = next_reset_after_adjustment; ++ } ++ } ++ backwards_adjust_iam = NULL; ++} ++static void check_for_backwards_time_shift(time_t now) ++{ ++ spin_lock_bh(&bandwidth_lock); ++ if(now < backwards_check && backwards_check != 0) ++ { ++ printk("ipt_bandwidth: backwards time shift detected, adjusting\n"); ++ ++ /* adjust */ ++ down(&userspace_lock); ++ ++ /* This function is always called with absolute time, not time adjusted for timezone. Correct that before adjusting. */ ++ backwards_adjust_current_time = now - local_seconds_west; ++ apply_to_every_string_map_value(id_map, adjust_id_for_backwards_time_shift); ++ up(&userspace_lock); ++ } ++ backwards_check = now; ++ spin_unlock_bh(&bandwidth_lock); ++} ++ ++ ++ ++static int old_minutes_west; ++static time_t shift_timezone_current_time; ++static time_t shift_timezone_info_previous_reset; ++static info_and_maps* shift_timezone_iam = NULL; ++static void shift_timezone_of_ip(unsigned long key, void* value) ++{ ++ #ifdef BANDWIDTH_DEBUG ++ unsigned long* ip = &key; ++ printk("shifting ip = %u.%u.%u.%u\n", *((char*)ip), *(((char*)ip)+1), *(((char*)ip)+2), *(((char*)ip)+3) ); ++ #endif ++ ++ ++ bw_history* history = (bw_history*)value; ++ int32_t timezone_adj = (old_minutes_west-local_minutes_west)*60; ++ #ifdef BANDWIDTH_DEBUG ++ printk(" before jump:\n"); ++ printk(" current time = %ld\n", shift_timezone_current_time); ++ printk(" first_start = %ld\n", history->first_start); ++ printk(" first_end = %ld\n", history->first_end); ++ printk(" last_end = %ld\n", history->last_end); ++ printk("\n"); ++ #endif ++ ++ /* given time after shift, calculate next and previous reset times */ ++ time_t next_reset = get_next_reset_time(shift_timezone_iam->info, shift_timezone_current_time, 0); ++ time_t previous_reset = get_nominal_previous_reset_time(shift_timezone_iam->info, next_reset); ++ shift_timezone_iam->info->next_reset = next_reset; ++ ++ /*if we're resetting on a constant interval, we can just adjust -- no need to worry about relationship to constant boundaries, e.g. end of day */ ++ if(shift_timezone_iam->info->reset_is_constant_interval) ++ { ++ shift_timezone_iam->info->previous_reset = previous_reset; ++ if(history->num_nodes > 1) ++ { ++ history->first_start = history->first_start + timezone_adj; ++ history->first_end = history->first_end + timezone_adj; ++ history->last_end = history->last_end + timezone_adj; ++ } ++ } ++ else ++ { ++ ++ ++ /* next reset will be the newly computed next_reset. */ ++ int node_index=history->num_nodes - 1; ++ if(node_index > 0) ++ { ++ /* based on new, shifted time, iterate back over all nodes in history */ ++ shift_timezone_iam->info->previous_reset = previous_reset ; ++ history->last_end = previous_reset; ++ ++ while(node_index > 1) ++ { ++ previous_reset = get_nominal_previous_reset_time(shift_timezone_iam->info, previous_reset); ++ node_index--; ++ } ++ history->first_end = previous_reset; ++ ++ previous_reset = get_nominal_previous_reset_time(shift_timezone_iam->info, previous_reset); ++ history->first_start = previous_reset > history->first_start + timezone_adj ? previous_reset : history->first_start + timezone_adj; ++ } ++ else ++ { ++ /* ++ * history hasn't really been initialized -- there's only one, current time point. ++ * we only know what's in the current accumulator in info. Just adjust previous reset time and make sure it's valid ++ */ ++ shift_timezone_iam->info->previous_reset = previous_reset > shift_timezone_info_previous_reset + timezone_adj ? previous_reset : shift_timezone_info_previous_reset + timezone_adj; ++ } ++ } ++ ++ ++ #ifdef BANDWIDTH_DEBUG ++ printk("\n"); ++ printk(" after jump:\n"); ++ printk(" first_start = %ld\n", history->first_start); ++ printk(" first_end = %ld\n", history->first_end); ++ printk(" last_end = %ld\n", history->last_end); ++ printk("\n\n"); ++ #endif ++ ++} ++static void shift_timezone_of_id(char* key, void* value) ++{ ++ info_and_maps* iam = (info_and_maps*)value; ++ int history_found = 0; ++ if(iam == NULL) ++ { ++ return; ++ } ++ if(iam->info == NULL) ++ { ++ return; ++ } ++ ++ #ifdef BANDWIDTH_DEBUG ++ printk("shifting id %s\n", key); ++ #endif ++ ++ shift_timezone_iam = iam; ++ if( (iam->info->reset_is_constant_interval == 0 && iam->info->reset_interval == BANDWIDTH_NEVER) || iam->info->cmp == BANDWIDTH_CHECK ) ++ { ++ return; ++ } ++ ++ if(iam->ip_history_map != NULL) ++ { ++ if(iam->ip_history_map->num_elements > 0) ++ { ++ history_found = 1; ++ shift_timezone_info_previous_reset = iam->info->previous_reset; ++ apply_to_every_long_map_value(iam->ip_history_map, shift_timezone_of_ip); ++ } ++ } ++ if(history_found == 0) ++ { ++ iam->info->previous_reset = iam->info->previous_reset + ((old_minutes_west - local_minutes_west )*60); ++ if(iam->info->previous_reset > shift_timezone_current_time) ++ { ++ iam->info->next_reset = get_next_reset_time(iam->info, shift_timezone_current_time, shift_timezone_current_time); ++ iam->info->previous_reset = shift_timezone_current_time; ++ } ++ else ++ { ++ iam->info->next_reset = get_next_reset_time(iam->info, shift_timezone_current_time, iam->info->previous_reset); ++ while (iam->info->next_reset < shift_timezone_current_time) ++ { ++ iam->info->previous_reset = iam->info->next_reset; ++ iam->info->next_reset = get_next_reset_time(iam->info, iam->info->previous_reset, iam->info->previous_reset); ++ } ++ } ++ } ++ shift_timezone_iam = NULL; ++} ++ ++static void check_for_timezone_shift(time_t now, int already_locked) ++{ ++ ++ if(already_locked == 0) { spin_lock_bh(&bandwidth_lock); } ++ if(now != last_local_mw_update ) /* make sure nothing changed while waiting for lock */ ++ { ++ local_minutes_west = sys_tz.tz_minuteswest; ++ local_seconds_west = 60*local_minutes_west; ++ last_local_mw_update = now; ++ if(local_seconds_west > last_local_mw_update) ++ { ++ /* we can't let adjusted time be < 0 -- pretend timezone is still UTC */ ++ local_minutes_west = 0; ++ local_seconds_west = 0; ++ } ++ ++ if(local_minutes_west != old_minutes_west) ++ { ++ int adj_minutes = old_minutes_west-local_minutes_west; ++ adj_minutes = adj_minutes < 0 ? adj_minutes*-1 : adj_minutes; ++ ++ if(already_locked == 0) { down(&userspace_lock); } ++ ++ printk("ipt_bandwidth: timezone shift of %d minutes detected, adjusting\n", adj_minutes); ++ printk(" old minutes west=%d, new minutes west=%d\n", old_minutes_west, local_minutes_west); ++ ++ /* this function is always called with absolute time, not time adjusted for timezone. Correct that before adjusting */ ++ shift_timezone_current_time = now - local_seconds_west; ++ apply_to_every_string_map_value(id_map, shift_timezone_of_id); ++ ++ old_minutes_west = local_minutes_west; ++ ++ ++ if(already_locked == 0) { up(&userspace_lock); } ++ } ++ } ++ if(already_locked == 0) { spin_unlock_bh(&bandwidth_lock); } ++} ++ ++ ++ ++static bw_history* initialize_history(uint32_t max_nodes) ++{ ++ bw_history* new_history = (bw_history*)kmalloc(sizeof(bw_history), GFP_ATOMIC); ++ if(new_history != NULL) ++ { ++ new_history->history_data = (uint64_t*)kmalloc((1+max_nodes)*sizeof(uint64_t), GFP_ATOMIC); /*number to save +1 for current */ ++ if(new_history->history_data == NULL) /* deal with malloc failure */ ++ { ++ kfree(new_history); ++ new_history = NULL; ++ } ++ else ++ { ++ new_history->first_start = 0; ++ new_history->first_end = 0; ++ new_history->last_end = 0; ++ new_history->max_nodes = max_nodes+1; /*number to save +1 for current */ ++ new_history->num_nodes = 1; ++ new_history->non_zero_nodes = 0; /* counts non_zero nodes other than current, so initialize to 0 */ ++ new_history->current_index = 0; ++ memset(new_history->history_data, 0, max_nodes*sizeof(uint64_t)); ++ } ++ } ++ return new_history; /* in case of malloc failure new_history will be NULL, this should be safe */ ++} ++ ++/* returns 1 if there are non-zero nodes in history, 0 if history is empty (all zero) */ ++static unsigned char update_history(bw_history* history, time_t interval_start, time_t interval_end, struct ipt_bandwidth_info* info) ++{ ++ unsigned char history_is_nonzero = 0; ++ if(history != NULL) /* should never be null, but let's be sure */ ++ { ++ ++ /* adjust number of non-zero nodes */ ++ if(history->num_nodes == history->max_nodes) ++ { ++ uint32_t first_index = (history->current_index+1) % history->max_nodes; ++ if( (history->history_data)[first_index] > 0) ++ { ++ history->non_zero_nodes = history->non_zero_nodes -1; ++ } ++ } ++ if( (history->history_data)[history->current_index] > 0 ) ++ { ++ history->non_zero_nodes = history->non_zero_nodes + 1; ++ } ++ history_is_nonzero = history->non_zero_nodes > 0 ? 1 : 0; ++ ++ ++ /* update interval start/end */ ++ if(history->first_start == 0) ++ { ++ history->first_start = interval_start; ++ history->first_end = interval_end; ++ } ++ if(history->num_nodes >= history->max_nodes) ++ { ++ history->first_start = history->first_end; ++ history->first_end = get_next_reset_time(info, history->first_start, history->first_start); ++ } ++ history->last_end = interval_end; ++ ++ ++ history->num_nodes = history->num_nodes < history->max_nodes ? history->num_nodes+1 : history->max_nodes; ++ history->current_index = (history->current_index+1) % history->max_nodes; ++ (history->history_data)[history->current_index] = 0; ++ ++ #ifdef BANDWIDTH_DEBUG ++ printk("after update history->num_nodes = %d\n", history->num_nodes); ++ printk("after update history->current_index = %d\n", history->current_index); ++ #endif ++ } ++ return history_is_nonzero; ++} ++ ++ ++static struct ipt_bandwidth_info* do_reset_info = NULL; ++static long_map* do_reset_ip_map = NULL; ++static long_map* do_reset_delete_ips = NULL; ++static time_t do_reset_interval_start = 0; ++static time_t do_reset_interval_end = 0; ++static void do_reset(unsigned long key, void* value) ++{ ++ bw_history* history = (bw_history*)value; ++ if(history != NULL && do_reset_info != NULL) /* should never be null.. but let's be sure */ ++ { ++ unsigned char history_contains_data = update_history(history, do_reset_interval_start, do_reset_interval_end, do_reset_info); ++ if(history_contains_data == 0 || do_reset_ip_map == NULL) ++ { ++ //schedule data for ip to be deleted (can't delete history while we're traversing history tree data structure!) ++ if(do_reset_delete_ips != NULL) /* should never be null.. but let's be sure */ ++ { ++ set_long_map_element(do_reset_delete_ips, key, (void*)(history->history_data + history->current_index)); ++ } ++ } ++ else ++ { ++ set_long_map_element(do_reset_ip_map, key, (void*)(history->history_data + history->current_index) ); ++ } ++ } ++} ++ ++long_map* clear_ip_map = NULL; ++long_map* clear_ip_history_map = NULL; ++static void clear_ips(unsigned long key, void* value) ++{ ++ if(clear_ip_history_map != NULL && clear_ip_map != NULL) ++ { ++ bw_history* history; ++ ++ #ifdef BANDWIDTH_DEBUG ++ unsigned long* ip = &key; ++ printk("clearing ip = %u.%u.%u.%u\n", *((char*)ip), *(((char*)ip)+1), *(((char*)ip)+2), *(((char*)ip)+3) ); ++ #endif ++ ++ remove_long_map_element(clear_ip_map, key); ++ history = (bw_history*)remove_long_map_element(clear_ip_history_map, key); ++ if(history != NULL) ++ { ++ kfree(history->history_data); ++ kfree(history); ++ } ++ } ++} ++ ++static void set_bandwidth_to_zero(unsigned long key, void* value) ++{ ++ *((uint64_t*)value) = 0; ++} ++ ++ ++long_map* reset_histories_ip_map = NULL; ++static void reset_histories(unsigned long key, void* value) ++{ ++ bw_history* bh = (bw_history*)value; ++ bh->first_start = 0; ++ bh->first_end = 0; ++ bh->last_end = 0; ++ bh->num_nodes = 1; ++ bh->non_zero_nodes = 1; ++ bh->current_index = 0; ++ (bh->history_data)[0] = 0; ++ if(reset_histories_ip_map != NULL) ++ { ++ set_long_map_element(reset_histories_ip_map, key, bh->history_data); ++ } ++} ++ ++ ++static void handle_interval_reset(info_and_maps* iam, time_t now) ++{ ++ struct ipt_bandwidth_info* info; ++ ++ #ifdef BANDWIDTH_DEBUG ++ printk("now, handling interval reset\n"); ++ #endif ++ if(iam == NULL) ++ { ++ #ifdef BANDWIDTH_DEBUG ++ printk("error: doing reset, iam is null \n"); ++ #endif ++ return; ++ } ++ if(iam->ip_map == NULL) ++ { ++ #ifdef BANDWIDTH_DEBUG ++ printk("error: doing reset, ip_map is null\n"); ++ #endif ++ return; ++ } ++ if(iam->info == NULL) ++ { ++ #ifdef BANDWIDTH_DEBUG ++ printk("error: doing reset, info is null\n"); ++ #endif ++ ++ return; ++ } ++ ++ info = iam->info; ++ if(info->num_intervals_to_save == 0) ++ { ++ #ifdef BANDWIDTH_DEBUG ++ printk("doing reset for case where no intervals are saved\n"); ++ #endif ++ ++ if(info->next_reset <= now) ++ { ++ info->next_reset = get_next_reset_time(info, info->previous_reset, info->previous_reset); ++ if(info->next_reset <= now) ++ { ++ info->next_reset = get_next_reset_time(info, now, info->previous_reset); ++ } ++ } ++ apply_to_every_long_map_value(iam->ip_map, set_bandwidth_to_zero); ++ } ++ else ++ { ++ unsigned long num_updates; ++ #ifdef BANDWIDTH_DEBUG ++ printk("doing reset for case where at least one interval is saved\n"); ++ #endif ++ ++ ++ if(iam->ip_history_map == NULL) ++ { ++ #ifdef BANDWIDTH_DEBUG ++ printk("error: doing reset, history_map is null when num_intervals_to_save > 0\n"); ++ #endif ++ return; ++ } ++ ++ do_reset_info = info; ++ do_reset_ip_map = iam->ip_map; ++ clear_ip_map = iam->ip_map; ++ clear_ip_history_map = iam->ip_history_map; ++ ++ ++ /* ++ * at most update as many times as we have intervals to save -- prevents ++ * rediculously long loop if interval length is 2 seconds and time was ++ * reset to 5 years in the future ++ */ ++ num_updates = 0; ++ while(info->next_reset <= now && num_updates < info->num_intervals_to_save) ++ { ++ do_reset_delete_ips = initialize_long_map(); ++ /* ++ * don't check for malloc failure here -- we ++ * include tests for whether do_reset_delete_ips ++ * is null below (reset should still be able to procede) ++ */ ++ ++ do_reset_interval_start = info->previous_reset; ++ do_reset_interval_end = info->next_reset; ++ ++ apply_to_every_long_map_value(iam->ip_history_map, do_reset); ++ ++ ++ info->previous_reset = info->next_reset; ++ info->next_reset = get_next_reset_time(info, info->previous_reset, info->previous_reset); ++ ++ /* free all data for ips whose entire histories contain only zeros to conserve space */ ++ if(do_reset_delete_ips != NULL) ++ { ++ unsigned long num_destroyed; ++ ++ /* only clear ips if this is the last iteration of this update */ ++ if(info->next_reset >= now) ++ { ++ /* ++ * no need to reset iam->info->combined_bw if it gets deleted here. ++ * below, at end of function it will get set to NULL if it gets wiped ++ */ ++ ++ apply_to_every_long_map_value(do_reset_delete_ips, clear_ips); ++ } ++ ++ /* but clear do_reset_delete_ips no matter what, values are just pointers to history data so we can ignore them */ ++ destroy_long_map(do_reset_delete_ips, DESTROY_MODE_IGNORE_VALUES, &num_destroyed); ++ do_reset_delete_ips = NULL; ++ } ++ num_updates++; ++ } ++ do_reset_info = NULL; ++ do_reset_ip_map = NULL; ++ clear_ip_map = NULL; ++ clear_ip_history_map = NULL; ++ ++ do_reset_interval_start = 0; ++ do_reset_interval_end = 0; ++ ++ /* ++ * test if we've cycled past all existing data -- if so wipe all existing histories ++ * and set previous reset time to now, and compute next reset time from ++ * current time ++ */ ++ if(info->next_reset <= now) ++ { ++ reset_histories_ip_map = iam->ip_map; ++ apply_to_every_long_map_value(iam->ip_history_map, reset_histories); ++ reset_histories_ip_map = NULL; ++ ++ info->previous_reset = now; ++ info->next_reset = get_next_reset_time(info, now, info->previous_reset); ++ } ++ } ++ info->combined_bw = (uint64_t*)get_long_map_element(iam->ip_map, 0); ++ info->current_bandwidth = 0; ++} ++ ++/* ++ * set max bandwidth to be max possible using 63 of the ++ * 64 bits in our record. In some systems uint64_t is treated ++ * like signed, so to prevent errors, use only 63 bits ++ */ ++static uint64_t pow64(uint64_t base, uint64_t pow) ++{ ++ uint64_t val = 1; ++ if(pow > 0) ++ { ++ val = base*pow64(base, pow-1); ++ } ++ return val; ++} ++static uint64_t get_bw_record_max(void) /* called by init to set global variable */ ++{ ++ return (pow64(2,62)) + (pow64(2,62)-1); ++} ++static uint64_t bandwidth_record_max; ++ ++ ++#define ADD_UP_TO_MAX(original,add,is_check) (bandwidth_record_max - original > add && is_check== 0) ? original+add : (is_check ? original : bandwidth_record_max); ++ ++ ++/* ++ * Shamelessly yoinked from xt_time.c ++ * "That is so amazingly amazing, I think I'd like to steal it." ++ * -- Zaphod Beeblebrox ++ */ ++ ++static const u_int16_t days_since_year[] = { ++ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, ++}; ++ ++static const u_int16_t days_since_leapyear[] = { ++ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, ++}; ++ ++/* ++ * Since time progresses forward, it is best to organize this array in reverse, ++ * to minimize lookup time. These are days since epoch since start of each year, ++ * going back to 1970 ++ */ ++#define DSE_FIRST 2039 ++static const u_int16_t days_since_epoch_for_each_year_start[] = { ++ /* 2039 - 2030 */ ++ 25202, 24837, 24472, 24106, 23741, 23376, 23011, 22645, 22280, 21915, ++ /* 2029 - 2020 */ ++ 21550, 21184, 20819, 20454, 20089, 19723, 19358, 18993, 18628, 18262, ++ /* 2019 - 2010 */ ++ 17897, 17532, 17167, 16801, 16436, 16071, 15706, 15340, 14975, 14610, ++ /* 2009 - 2000 */ ++ 14245, 13879, 13514, 13149, 12784, 12418, 12053, 11688, 11323, 10957, ++ /* 1999 - 1990 */ ++ 10592, 10227, 9862, 9496, 9131, 8766, 8401, 8035, 7670, 7305, ++ /* 1989 - 1980 */ ++ 6940, 6574, 6209, 5844, 5479, 5113, 4748, 4383, 4018, 3652, ++ /* 1979 - 1970 */ ++ 3287, 2922, 2557, 2191, 1826, 1461, 1096, 730, 365, 0, ++}; ++ ++static inline int is_leap(unsigned int y) ++{ ++ return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0); ++} ++ ++/* end of code yoinked from xt_time */ ++ ++ ++static time_t get_nominal_previous_reset_time(struct ipt_bandwidth_info *info, time_t current_next_reset) ++{ ++ time_t previous_reset = current_next_reset; ++ if(info->reset_is_constant_interval == 0) ++ { ++ /* skip backwards in halves of interval after next, until */ ++ time_t next = get_next_reset_time(info, current_next_reset, 0); ++ time_t half_interval = (next-current_next_reset)/2; ++ time_t half_count, tmp; ++ half_interval = half_interval == 0 ? 1 : half_interval; /* must be at least one second, otherwise we loop forever*/ ++ ++ half_count = 1; ++ tmp = get_next_reset_time(info, (current_next_reset-(half_count*half_interval)),0); ++ while(previous_reset >= current_next_reset) ++ { ++ previous_reset = tmp; ++ half_count++; ++ tmp = get_next_reset_time(info, (current_next_reset-(half_count*half_interval)),0); ++ } ++ } ++ else ++ { ++ previous_reset = current_next_reset - info->reset_interval; ++ } ++ return previous_reset; ++} ++ ++ ++static time_t get_next_reset_time(struct ipt_bandwidth_info *info, time_t now, time_t previous_reset) ++{ ++ //first calculate when next reset would be if reset_time is 0 (which it may be) ++ time_t next_reset = 0; ++ if(info->reset_is_constant_interval == 0) ++ { ++ if(info->reset_interval == BANDWIDTH_MINUTE) ++ { ++ next_reset = ( (long)(now/60) + 1)*60; ++ if(info->reset_time > 0) ++ { ++ time_t alt_reset = next_reset + info->reset_time - 60; ++ next_reset = alt_reset > now ? alt_reset : next_reset+info->reset_time; ++ } ++ } ++ else if(info->reset_interval == BANDWIDTH_HOUR) ++ { ++ next_reset = ( (long)(now/(60*60)) + 1)*60*60; ++ if(info->reset_time > 0) ++ { ++ time_t alt_reset = next_reset + info->reset_time - (60*60); ++ next_reset = alt_reset > now ? alt_reset : next_reset+info->reset_time; ++ } ++ } ++ else if(info->reset_interval == BANDWIDTH_DAY) ++ { ++ next_reset = ( (long)(now/(60*60*24)) + 1)*60*60*24; ++ if(info->reset_time > 0) ++ { ++ time_t alt_reset = next_reset + info->reset_time - (60*60*24); ++ next_reset = alt_reset > now ? alt_reset : next_reset+info->reset_time; ++ } ++ } ++ else if(info->reset_interval == BANDWIDTH_WEEK) ++ { ++ long days_since_epoch = now/(60*60*24); ++ long current_weekday = (4 + days_since_epoch ) % 7 ; ++ next_reset = (days_since_epoch + (7-current_weekday) )*(60*60*24); ++ if(info->reset_time > 0) ++ { ++ time_t alt_reset = next_reset + info->reset_time - (60*60*24*7); ++ next_reset = alt_reset > now ? alt_reset : next_reset+info->reset_time; ++ } ++ } ++ else if(info->reset_interval == BANDWIDTH_MONTH) ++ { ++ /* yeah, most of this is yoinked from xt_time too */ ++ int year; ++ int year_index; ++ int year_day; ++ int month; ++ long days_since_epoch = now/(60*60*24); ++ uint16_t* month_start_days; ++ time_t alt_reset; ++ ++ for (year_index = 0, year = DSE_FIRST; days_since_epoch_for_each_year_start[year_index] > days_since_epoch; year_index++) ++ { ++ year--; ++ } ++ year_day = days_since_epoch - days_since_epoch_for_each_year_start[year_index]; ++ if (is_leap(year)) ++ { ++ month_start_days = (u_int16_t*)days_since_leapyear; ++ } ++ else ++ { ++ month_start_days = (u_int16_t*)days_since_year; ++ } ++ for (month = 11 ; month > 0 && month_start_days[month] > year_day; month--){} ++ ++ /* end majority of yoinkage */ ++ ++ alt_reset = (days_since_epoch_for_each_year_start[year_index] + month_start_days[month])*(60*60*24) + info->reset_time; ++ if(alt_reset > now) ++ { ++ next_reset = alt_reset; ++ } ++ else if(month == 11) ++ { ++ next_reset = days_since_epoch_for_each_year_start[year_index-1]*(60*60*24) + info->reset_time; ++ } ++ else ++ { ++ next_reset = (days_since_epoch_for_each_year_start[year_index] + month_start_days[month+1])*(60*60*24) + info->reset_time; ++ } ++ } ++ } ++ else ++ { ++ if(info->reset_time > 0 && previous_reset > 0 && previous_reset <= now) ++ { ++ unsigned long adj_reset_time = info->reset_time; ++ unsigned long tz_secs = 60 * local_minutes_west; ++ if(adj_reset_time < tz_secs) ++ { ++ unsigned long interval_multiple = 1+(tz_secs/info->reset_interval); ++ adj_reset_time = adj_reset_time + (interval_multiple*info->reset_interval); ++ } ++ adj_reset_time = adj_reset_time - tz_secs; ++ ++ if(info->reset_time > now) ++ { ++ unsigned long whole_intervals = ((info->reset_time - now)/info->reset_interval) + 1; /* add one to make sure integer gets rounded UP (since we're subtracting) */ ++ next_reset = info->reset_time - (whole_intervals*info->reset_interval); ++ while(next_reset <= now) ++ { ++ next_reset = next_reset + info->reset_interval; ++ } ++ ++ } ++ else /* info->reset_time <= now */ ++ { ++ unsigned long whole_intervals = (now-info->reset_time)/info->reset_interval; /* integer gets rounded down */ ++ next_reset = info->reset_time + (whole_intervals*info->reset_interval); ++ while(next_reset <= now) ++ { ++ next_reset = next_reset + info->reset_interval; ++ } ++ } ++ } ++ else if(previous_reset > 0) ++ { ++ next_reset = previous_reset; ++ if(next_reset <= now) /* check just to be sure, if this is not true VERY BAD THINGS will happen */ ++ { ++ unsigned long whole_intervals = (now-next_reset)/info->reset_interval; /* integer gets rounded down */ ++ next_reset = next_reset + (whole_intervals*info->reset_interval); ++ while(next_reset <= now) ++ { ++ next_reset = next_reset + info->reset_interval; ++ } ++ } ++ } ++ else ++ { ++ next_reset = now + info->reset_interval; ++ } ++ } ++ ++ return next_reset; ++} ++ ++ ++ ++static uint64_t* initialize_map_entries_for_ip(info_and_maps* iam, unsigned long ip, uint64_t initial_bandwidth) ++{ ++ #ifdef BANDWIDTH_DEBUG ++ printk("initializing entry for ip, bw=%lld\n", initial_bandwidth); ++ #endif ++ ++ #ifdef BANDWIDTH_DEBUG ++ if(iam == NULL){ printk("error in initialization: iam is null!\n"); } ++ #endif ++ ++ ++ uint64_t* new_bw = NULL; ++ if(iam != NULL) /* should never happen, but let's be certain */ ++ { ++ struct ipt_bandwidth_info *info = iam->info; ++ long_map* ip_map = iam->ip_map; ++ long_map* ip_history_map = iam->ip_history_map; ++ ++ #ifdef BANDWIDTH_DEBUG ++ if(info == NULL){ printk("error in initialization: info is null!\n"); } ++ if(ip_map == NULL){ printk("error in initialization: ip_map is null!\n"); } ++ #endif ++ ++ ++ if(info != NULL && ip_map != NULL) /* again... should never happen but let's be sure */ ++ { ++ if(info->num_intervals_to_save == 0 || ip_history_map == NULL) ++ { ++ #ifdef BANDWIDTH_DEBUG ++ printk(" initializing entry for ip without history\n"); ++ #endif ++ new_bw = (uint64_t*)kmalloc(sizeof(uint64_t), GFP_ATOMIC); ++ } ++ else ++ { ++ #ifdef BANDWIDTH_DEBUG ++ printk(" initializing entry for ip with history\n"); ++ #endif ++ ++ bw_history *new_history = initialize_history(info->num_intervals_to_save); ++ if(new_history != NULL) /* check for kmalloc failure */ ++ { ++ bw_history* old_history; ++ #ifdef BANDWIDTH_DEBUG ++ printk(" malloc succeeded, new history is non-null\n"); ++ #endif ++ ++ new_bw = (uint64_t*)(new_history->history_data + new_history->current_index); ++ old_history = set_long_map_element(ip_history_map, ip, (void*)new_history); ++ if(old_history != NULL) ++ { ++ #ifdef BANDWIDTH_DEBUG ++ printk(" after initialization old_history not null! (something is FUBAR)\n"); ++ #endif ++ kfree(old_history->history_data); ++ kfree(old_history); ++ } ++ ++ #ifdef BANDWIDTH_DEBUG ++ ++ #endif ++ } ++ } ++ if(new_bw != NULL) /* check for kmalloc failure */ ++ { ++ uint64_t* old_bw; ++ *new_bw = initial_bandwidth; ++ old_bw = set_long_map_element(ip_map, ip, (void*)new_bw ); ++ ++ /* only free old_bw if num_intervals_to_save is zero -- otherwise it already got freed above when we wiped the old history */ ++ if(old_bw != NULL && info->num_intervals_to_save == 0) ++ { ++ free(old_bw); ++ } ++ ++ if(ip == 0) ++ { ++ info->combined_bw = new_bw; ++ } ++ ++ #ifdef BANDWIDTH_DEBUG ++ uint64_t *test = (uint64_t*)get_long_map_element(ip_map, ip); ++ if(test == NULL) ++ { ++ printk(" after initialization bw is null!\n"); ++ } ++ else ++ { ++ printk(" after initialization bw is %lld\n", *new_bw); ++ printk(" after initialization test is %lld\n", *test); ++ } ++ #endif ++ } ++ } ++ } ++ ++ return new_bw; ++} ++ ++ ++static bool match(const struct sk_buff *skb, struct xt_action_param *par) ++{ ++ ++ struct ipt_bandwidth_info *info = ((const struct ipt_bandwidth_info*)(par->matchinfo))->non_const_self; ++ ++ time_t now; ++ int match_found; ++ ++ ++ unsigned char is_check = info->cmp == BANDWIDTH_CHECK ? 1 : 0; ++ unsigned char do_src_dst_swap = 0; ++ info_and_maps* iam = NULL; ++ long_map* ip_map = NULL; ++ ++ uint64_t* bws[2] = {NULL, NULL}; ++ ++ /* if we're currently setting this id, ignore new data until set is complete */ ++ if(set_in_progress == 1) ++ { ++ if(strcmp(info->id, set_id) == 0) ++ { ++ return 0; ++ } ++ } ++ ++ ++ ++ ++ /* ++ * BEFORE we lock, check for timezone shift ++ * this will almost always be be very,very quick, ++ * but in the event there IS a shift this ++ * function will lock both kernel update spinlock ++ * and userspace i/o semaphore, and do a lot of ++ * number crunching so we shouldn't ++ * already be locked. ++ */ ++ now = get_seconds(); ++ ++ ++ if(now != last_local_mw_update ) ++ { ++ check_for_timezone_shift(now, 0); ++ check_for_backwards_time_shift(now); ++ } ++ now = now - local_seconds_west; /* Adjust for local timezone */ ++ ++ spin_lock_bh(&bandwidth_lock); ++ ++ if(is_check) ++ { ++ info_and_maps* check_iam; ++ do_src_dst_swap = info->check_type == BANDWIDTH_CHECK_SWAP ? 1 : 0; ++ check_iam = (info_and_maps*)get_string_map_element_with_hashed_key(id_map, info->hashed_id); ++ if(check_iam == NULL) ++ { ++ spin_unlock_bh(&bandwidth_lock); ++ return 0; ++ } ++ info = check_iam->info; ++ } ++ ++ ++ ++ ++ if(info->reset_interval != BANDWIDTH_NEVER) ++ { ++ if(info->next_reset < now) ++ { ++ //do reset ++ //iam = (info_and_maps*)get_string_map_element_with_hashed_key(id_map, info->hashed_id); ++ iam = (info_and_maps*)info->iam; ++ if(iam != NULL) /* should never be null, but let's be sure */ ++ { ++ handle_interval_reset(iam, now); ++ ip_map = iam->ip_map; ++ } ++ else ++ { ++ /* even in case of malloc failure or weird error we can update these params */ ++ info->current_bandwidth = 0; ++ info->next_reset = get_next_reset_time(info, now, info->previous_reset); ++ } ++ } ++ } ++ ++ if(info->type == BANDWIDTH_COMBINED) ++ { ++ if(iam == NULL) ++ { ++ //iam = (info_and_maps*)get_string_map_element_with_hashed_key(id_map, info->hashed_id); ++ iam = (info_and_maps*)info->iam; ++ if(iam != NULL) ++ { ++ ip_map = iam->ip_map; ++ } ++ } ++ if(ip_map != NULL) /* if this ip_map != NULL iam can never be NULL, so we don't need to check this */ ++ { ++ ++ if(info->combined_bw == NULL) ++ { ++ bws[0] = initialize_map_entries_for_ip(iam, 0, skb->len); ++ } ++ else ++ { ++ bws[0] = info->combined_bw; ++ *(bws[0]) = ADD_UP_TO_MAX(*(bws[0]), (uint64_t)skb->len, is_check); ++ } ++ } ++ else ++ { ++ #ifdef BANDWIDTH_DEBUG ++ printk("error: ip_map is null in match!\n"); ++ #endif ++ } ++ info->current_bandwidth = ADD_UP_TO_MAX(info->current_bandwidth, (uint64_t)skb->len, is_check); ++ } ++ else ++ { ++ uint32_t bw_ip, bw_ip_index; ++ uint32_t bw_ips[2] = {0, 0}; ++ struct iphdr* iph = (struct iphdr*)(skb_network_header(skb)); ++ if(info->type == BANDWIDTH_INDIVIDUAL_SRC) ++ { ++ //src ip ++ bw_ips[0] = iph->saddr; ++ if(do_src_dst_swap) ++ { ++ bw_ips[0] = iph->daddr; ++ } ++ } ++ else if (info->type == BANDWIDTH_INDIVIDUAL_DST) ++ { ++ //dst ip ++ bw_ips[0] = iph->daddr; ++ if(do_src_dst_swap) ++ { ++ bw_ips[0] = iph->saddr; ++ } ++ } ++ else if(info->type == BANDWIDTH_INDIVIDUAL_LOCAL || info->type == BANDWIDTH_INDIVIDUAL_REMOTE) ++ { ++ //remote or local ip -- need to test both src && dst ++ uint32_t src_ip = iph->saddr; ++ uint32_t dst_ip = iph->daddr; ++ if(info->type == BANDWIDTH_INDIVIDUAL_LOCAL) ++ { ++ bw_ips[0] = ((info->local_subnet_mask & src_ip) == info->local_subnet) ? src_ip : 0; ++ bw_ips[1] = ((info->local_subnet_mask & dst_ip) == info->local_subnet) ? dst_ip : 0; ++ } ++ else if(info->type == BANDWIDTH_INDIVIDUAL_REMOTE) ++ { ++ bw_ips[0] = ((info->local_subnet_mask & src_ip) != info->local_subnet ) ? src_ip : 0; ++ bw_ips[1] = ((info->local_subnet_mask & dst_ip) != info->local_subnet ) ? dst_ip : 0; ++ } ++ } ++ ++ if(ip_map == NULL) ++ { ++ //iam = (info_and_maps*)get_string_map_element_with_hashed_key(id_map, info->hashed_id); ++ iam = (info_and_maps*)info->iam; ++ if(iam != NULL) ++ { ++ ip_map = iam->ip_map; ++ } ++ } ++ if(!is_check && info->cmp == BANDWIDTH_MONITOR) ++ { ++ uint64_t* combined_oldval = info->combined_bw; ++ if(combined_oldval == NULL) ++ { ++ combined_oldval = initialize_map_entries_for_ip(iam, 0, (uint64_t)skb->len); ++ } ++ else ++ { ++ *combined_oldval = ADD_UP_TO_MAX(*combined_oldval, (uint64_t)skb->len, is_check); ++ } ++ } ++ bw_ip_index = bw_ips[0] == 0 ? 1 : 0; ++ bw_ip = bw_ips[bw_ip_index]; ++ if(bw_ip != 0 && ip_map != NULL) ++ { ++ uint64_t* oldval = get_long_map_element(ip_map, (unsigned long)bw_ip); ++ if(oldval == NULL) ++ { ++ if(!is_check) ++ { ++ /* may return NULL on malloc failure but that's ok */ ++ oldval = initialize_map_entries_for_ip(iam, (unsigned long)bw_ip, (uint64_t)skb->len); ++ } ++ } ++ else ++ { ++ *oldval = ADD_UP_TO_MAX(*oldval, (uint64_t)skb->len, is_check); ++ } ++ ++ /* this is fine, setting bws[bw_ip_index] to NULL on check for undefined value or kmalloc failure won't crash anything */ ++ bws[bw_ip_index] = oldval; ++ } ++ ++ } ++ ++ ++ match_found = 0; ++ if(info->cmp != BANDWIDTH_MONITOR) ++ { ++ if(info->cmp == BANDWIDTH_GT) ++ { ++ match_found = bws[0] != NULL ? ( *(bws[0]) > info->bandwidth_cutoff ? 1 : match_found ) : match_found; ++ match_found = bws[1] != NULL ? ( *(bws[1]) > info->bandwidth_cutoff ? 1 : match_found ) : match_found; ++ match_found = info->current_bandwidth > info->bandwidth_cutoff ? 1 : match_found; ++ } ++ else if(info->cmp == BANDWIDTH_LT) ++ { ++ match_found = bws[0] != NULL ? ( *(bws[0]) < info->bandwidth_cutoff ? 1 : match_found ) : match_found; ++ match_found = bws[1] != NULL ? ( *(bws[1]) < info->bandwidth_cutoff ? 1 : match_found ) : match_found; ++ match_found = info->current_bandwidth < info->bandwidth_cutoff ? 1 : match_found; ++ } ++ } ++ ++ ++ spin_unlock_bh(&bandwidth_lock); ++ ++ ++ ++ ++ ++ return match_found; ++} ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++/********************** ++ * Get functions ++ *********************/ ++ ++#define MAX_IP_STR_LENGTH 16 ++ ++#define ERROR_NONE 0 ++#define ERROR_NO_ID 1 ++#define ERROR_BUFFER_TOO_SHORT 2 ++#define ERROR_NO_HISTORY 3 ++#define ERROR_UNKNOWN 4 ++typedef struct get_req_struct ++{ ++ uint32_t ip; ++ uint32_t next_ip_index; ++ unsigned char return_history; ++ char id[BANDWIDTH_MAX_ID_LENGTH]; ++} get_request; ++ ++static unsigned long* output_ip_list = NULL; ++static unsigned long output_ip_list_length = 0; ++ ++static char add_ip_block( uint32_t ip, ++ unsigned char full_history_requested, ++ info_and_maps* iam, ++ unsigned char* output_buffer, ++ uint32_t* current_output_index, ++ uint32_t buffer_length ++ ); ++static void parse_get_request(unsigned char* request_buffer, get_request* parsed_request); ++static int handle_get_failure(int ret_value, int unlock_user_sem, int unlock_bandwidth_spin, unsigned char error_code, unsigned char* out_buffer, unsigned char* free_buffer ); ++ ++ ++/* ++ * returns whether we succeeded in adding ip block, 0= success, ++ * otherwise error code of problem that we found ++ */ ++static char add_ip_block( uint32_t ip, ++ unsigned char full_history_requested, ++ info_and_maps* iam, ++ unsigned char* output_buffer, ++ uint32_t* current_output_index, ++ uint32_t output_buffer_length ++ ) ++{ ++ #ifdef BANDWIDTH_DEBUG ++ uint32_t *ipp = &ip; ++ printk("doing output for ip = %u.%u.%u.%u\n", *((unsigned char*)ipp), *(((unsigned char*)ipp)+1), *(((unsigned char*)ipp)+2), *(((unsigned char*)ipp)+3) ); ++ #endif ++ ++ if(full_history_requested) ++ { ++ bw_history* history = NULL; ++ if(iam->info->num_intervals_to_save > 0 && iam->ip_history_map != NULL) ++ { ++ history = (bw_history*)get_long_map_element(iam->ip_history_map, ip); ++ } ++ if(history == NULL) ++ { ++ #ifdef BANDWIDTH_DEBUG ++ printk(" no history map for ip, dumping latest value in history format\n" ); ++ #endif ++ ++ ++ uint32_t block_length = (2*4) + (3*8); ++ uint64_t *bw; ++ ++ if(*current_output_index + block_length > output_buffer_length) ++ { ++ return ERROR_BUFFER_TOO_SHORT; ++ } ++ *( (uint32_t*)(output_buffer + *current_output_index) ) = ip; ++ *current_output_index = *current_output_index + 4; ++ ++ *( (uint32_t*)(output_buffer + *current_output_index) ) = 1; ++ *current_output_index = *current_output_index + 4; ++ ++ *( (uint64_t*)(output_buffer + *current_output_index) ) = (uint64_t)iam->info->previous_reset + (60 * local_minutes_west); ++ *current_output_index = *current_output_index + 8; ++ ++ *( (uint64_t*)(output_buffer + *current_output_index) ) = (uint64_t)iam->info->previous_reset + (60 * local_minutes_west); ++ *current_output_index = *current_output_index + 8; ++ ++ *( (uint64_t*)(output_buffer + *current_output_index) ) = (uint64_t)iam->info->previous_reset + (60 * local_minutes_west); ++ *current_output_index = *current_output_index + 8; ++ ++ bw = (uint64_t*)get_long_map_element(iam->ip_map, ip); ++ if(bw == NULL) ++ { ++ *( (uint64_t*)(output_buffer + *current_output_index) ) = 0; ++ } ++ else ++ { ++ *( (uint64_t*)(output_buffer + *current_output_index) ) = *bw; ++ } ++ *current_output_index = *current_output_index + 8; ++ ++ } ++ else ++ { ++ uint32_t block_length = (2*4) + (3*8) + (8*history->num_nodes); ++ uint64_t last_reset; ++ uint32_t node_num; ++ uint32_t next_index; ++ ++ if(*current_output_index + block_length > output_buffer_length) ++ { ++ return ERROR_BUFFER_TOO_SHORT; ++ } ++ ++ *( (uint32_t*)(output_buffer + *current_output_index) ) = ip; ++ *current_output_index = *current_output_index + 4; ++ ++ *( (uint32_t*)(output_buffer + *current_output_index) )= history->num_nodes; ++ *current_output_index = *current_output_index + 4; ++ ++ ++ ++ /* need to return times in regular UTC not the UTC - minutes west, which is useful for processing */ ++ last_reset = (uint64_t)iam->info->previous_reset + (60 * local_minutes_west); ++ *( (uint64_t*)(output_buffer + *current_output_index) ) = history->first_start > 0 ? (uint64_t)history->first_start + (60 * local_minutes_west) : last_reset; ++ #ifdef BANDWIDTH_DEBUG ++ printk(" dumping first start = %lld\n", *( (uint64_t*)(output_buffer + *current_output_index) ) ); ++ #endif ++ *current_output_index = *current_output_index + 8; ++ ++ ++ ++ *( (uint64_t*)(output_buffer + *current_output_index) ) = history->first_end > 0 ? (uint64_t)history->first_end + (60 * local_minutes_west) : last_reset; ++ #ifdef BANDWIDTH_DEBUG ++ printk(" dumping first end = %lld\n", *( (uint64_t*)(output_buffer + *current_output_index) ) ); ++ #endif ++ *current_output_index = *current_output_index + 8; ++ ++ ++ ++ *( (uint64_t*)(output_buffer + *current_output_index) ) = history->last_end > 0 ? (uint64_t)history->last_end + (60 * local_minutes_west) : last_reset; ++ #ifdef BANDWIDTH_DEBUG ++ printk(" dumping last end = %lld\n", *( (uint64_t*)(output_buffer + *current_output_index) ) ); ++ #endif ++ *current_output_index = *current_output_index + 8; ++ ++ ++ ++ node_num = 0; ++ next_index = history->num_nodes == history->max_nodes ? history->current_index+1 : 0; ++ next_index = next_index >= history->max_nodes ? 0 : next_index; ++ for(node_num=0; node_num < history->num_nodes; node_num++) ++ { ++ *( (uint64_t*)(output_buffer + *current_output_index) ) = (history->history_data)[ next_index ]; ++ *current_output_index = *current_output_index + 8; ++ next_index = (next_index + 1) % history->max_nodes; ++ } ++ } ++ } ++ else ++ { ++ uint64_t *bw; ++ if(*current_output_index + 8 > output_buffer_length) ++ { ++ return ERROR_BUFFER_TOO_SHORT; ++ } ++ ++ *( (uint32_t*)(output_buffer + *current_output_index) ) = ip; ++ *current_output_index = *current_output_index + 4; ++ ++ ++ bw = (uint64_t*)get_long_map_element(iam->ip_map, ip); ++ if(bw == NULL) ++ { ++ *( (uint64_t*)(output_buffer + *current_output_index) ) = 0; ++ } ++ else ++ { ++ *( (uint64_t*)(output_buffer + *current_output_index) ) = *bw; ++ } ++ *current_output_index = *current_output_index + 8; ++ } ++ return ERROR_NONE; ++} ++ ++ ++ ++/* ++ * convenience method for cleaning crap up after failed malloc or other ++ * error that we can't recover from in get function ++ */ ++static int handle_get_failure(int ret_value, int unlock_user_sem, int unlock_bandwidth_spin, unsigned char error_code, unsigned char* out_buffer, unsigned char* free_buffer ) ++{ ++ copy_to_user(out_buffer, &error_code, 1); ++ if( free_buffer != NULL ) { kfree(free_buffer); } ++ if(unlock_bandwidth_spin) { spin_unlock_bh(&bandwidth_lock); } ++ if(unlock_user_sem) { up(&userspace_lock); } ++ return ret_value; ++} ++ ++/* ++ * request structure: ++ * bytes 1:4 is ip (uint32_t) ++ * bytes 4:8 is the next ip index (uint32_t) ++ * byte 9 is whether to return full history or just current usage (unsigned char) ++ * bytes 10:10+MAX_ID_LENGTH are the id (a string) ++ */ ++static void parse_get_request(unsigned char* request_buffer, get_request* parsed_request) ++{ ++ uint32_t* ip = (uint32_t*)(request_buffer+0); ++ uint32_t* next_ip_index = (uint32_t*)(request_buffer+4); ++ unsigned char* return_history = (unsigned char*)(request_buffer+8); ++ ++ ++ ++ parsed_request->ip = *ip; ++ parsed_request->next_ip_index = *next_ip_index; ++ parsed_request->return_history = *return_history; ++ memcpy(parsed_request->id, request_buffer+9, BANDWIDTH_MAX_ID_LENGTH); ++ (parsed_request->id)[BANDWIDTH_MAX_ID_LENGTH-1] = '\0'; /* make sure id is null terminated no matter what */ ++ ++ #ifdef BANDWIDTH_DEBUG ++ printk("ip = %u.%u.%u.%u\n", *((char*)ip), *(((char*)ip)+1), *(((char*)ip)+2), *(((char*)ip)+3) ); ++ printk("next ip index = %d\n", *next_ip_index); ++ printk("return_history = %d\n", *return_history); ++ #endif ++} ++ ++ ++static int ipt_bandwidth_get_ctl(struct sock *sk, int cmd, void *user, int *len) ++{ ++ /* check for timezone shift & adjust if necessary */ ++ char* buffer; ++ get_request query; ++ info_and_maps* iam; ++ ++ unsigned char* error; ++ uint32_t* total_ips; ++ uint32_t* start_index; ++ uint32_t* num_ips_in_response; ++ uint64_t* reset_interval; ++ uint64_t* reset_time; ++ unsigned char* reset_is_constant_interval; ++ uint32_t current_output_index; ++ time_t now = get_seconds(); ++ check_for_timezone_shift(now, 0); ++ check_for_backwards_time_shift(now); ++ now = now - local_seconds_west; /* Adjust for local timezone */ ++ ++ ++ down(&userspace_lock); ++ ++ ++ /* first check that query buffer is big enough to hold the info needed to parse the query */ ++ if(*len < BANDWIDTH_MAX_ID_LENGTH + 9) ++ { ++ ++ return handle_get_failure(0, 1, 0, ERROR_BUFFER_TOO_SHORT, user, NULL); ++ } ++ ++ ++ ++ /* copy the query from userspace to kernel space & parse */ ++ buffer = kmalloc(*len, GFP_ATOMIC); ++ if(buffer == NULL) /* check for malloc failure */ ++ { ++ return handle_get_failure(0, 1, 0, ERROR_UNKNOWN, user, NULL); ++ } ++ copy_from_user(buffer, user, *len); ++ parse_get_request(buffer, &query); ++ ++ ++ ++ ++ ++ ++ /* ++ * retrieve data for this id and verify all variables are properly defined, just to be sure ++ * this is a kernel module -- it pays to be paranoid! ++ */ ++ spin_lock_bh(&bandwidth_lock); ++ ++ iam = (info_and_maps*)get_string_map_element(id_map, query.id); ++ ++ if(iam == NULL) ++ { ++ return handle_get_failure(0, 1, 1, ERROR_NO_ID, user, buffer); ++ } ++ if(iam->info == NULL || iam->ip_map == NULL) ++ { ++ return handle_get_failure(0, 1, 1, ERROR_NO_ID, user, buffer); ++ } ++ if(iam->info->num_intervals_to_save > 0 && iam->ip_history_map == NULL) ++ { ++ return handle_get_failure(0, 1, 1, ERROR_NO_ID, user, buffer); ++ } ++ ++ /* allocate ip list if this is first query */ ++ if(query.next_ip_index == 0 && query.ip == 0) ++ { ++ if(output_ip_list != NULL) ++ { ++ kfree(output_ip_list); ++ } ++ if(iam->info->type == BANDWIDTH_COMBINED) ++ { ++ output_ip_list_length = 1; ++ output_ip_list = (unsigned long*)kmalloc(sizeof(unsigned long), GFP_ATOMIC); ++ if(output_ip_list != NULL) { *output_ip_list = 0; } ++ } ++ else ++ { ++ output_ip_list = get_sorted_long_map_keys(iam->ip_map, &output_ip_list_length); ++ } ++ ++ if(output_ip_list == NULL) ++ { ++ return handle_get_failure(0, 1, 1, ERROR_UNKNOWN, user, buffer); ++ } ++ } ++ ++ /* if this is not first query do a sanity check -- make sure it's within bounds of allocated ip list */ ++ if(query.next_ip_index > 0 && (output_ip_list == NULL || query.next_ip_index > output_ip_list_length)) ++ { ++ return handle_get_failure(0, 1, 1, ERROR_UNKNOWN, user, buffer); ++ } ++ ++ ++ ++ ++ /* ++ // values only reset when a packet hits a rule, so ++ // reset may have expired without data being reset. ++ // So, test if we need to reset values to zero ++ */ ++ if(iam->info->reset_interval != BANDWIDTH_NEVER) ++ { ++ if(iam->info->next_reset < now) ++ { ++ //do reset ++ handle_interval_reset(iam, now); ++ } ++ } ++ ++ ++ ++ /* compute response & store it in buffer ++ * ++ * format of response: ++ * byte 1 : error code (0 for ok) ++ * bytes 2-5 : total_num_ips found in query (further gets may be necessary to retrieve them) ++ * bytes 6-9 : start_index, index (in a list of total_num_ips) of first ip in response ++ * bytes 10-13 : num_ips_in_response, number of ips in this response ++ * bytes 14-21 : reset_interval (helps deal with DST shifts in userspace) ++ * bytes 22-29 : reset_time (helps deal with DST shifts in userspace) ++ * byte 30 : reset_is_constant_interval (helps deal with DST shifts in userspace) ++ * remaining bytes contain blocks of ip data ++ * format is dependent on whether history was queried ++ * ++ * if history was NOT queried we have ++ * bytes 1-4 : ip ++ * bytes 5-12 : bandwidth ++ * ++ * if history WAS queried we have ++ * (note we are using 64 bit integers for time here ++ * even though time_t is 32 bits on most 32 bit systems ++ * just to be on the safe side) ++ * bytes 1-4 : ip ++ * bytes 4-8 : history_length number of history values (including current) ++ * bytes 9-16 : first start ++ * bytes 17-24 : first end ++ * bytes 25-32 : recent end ++ * 33 onward : list of 64 bit integers of length history_length ++ * ++ */ ++ error = buffer; ++ total_ips = (uint32_t*)(buffer+1); ++ start_index = (uint32_t*)(buffer+5); ++ num_ips_in_response = (uint32_t*)(buffer+9); ++ reset_interval = (uint64_t*)(buffer+13); ++ reset_time = (uint64_t*)(buffer+21); ++ reset_is_constant_interval = (char*)(buffer+29); ++ ++ *reset_interval = (uint64_t)iam->info->reset_interval; ++ *reset_time = (uint64_t)iam->info->reset_time; ++ *reset_is_constant_interval = iam->info->reset_is_constant_interval; ++ ++ current_output_index = 30; ++ if(query.ip != 0) ++ { ++ *error = add_ip_block( query.ip, ++ query.return_history, ++ iam, ++ buffer, ++ ¤t_output_index, ++ *len ++ ); ++ ++ *total_ips = *error == 0; ++ *start_index = 0; ++ *num_ips_in_response = *error == 0 ? 1 : 0; ++ } ++ else ++ { ++ uint32_t next_index = query.next_ip_index; ++ *error = ERROR_NONE; ++ *total_ips = output_ip_list_length; ++ *start_index = next_index; ++ *num_ips_in_response = 0; ++ while(*error == ERROR_NONE && next_index < output_ip_list_length) ++ { ++ uint32_t next_ip = output_ip_list[next_index]; ++ *error = add_ip_block( next_ip, ++ query.return_history, ++ iam, ++ buffer, ++ ¤t_output_index, ++ *len ++ ); ++ if(*error == ERROR_NONE) ++ { ++ *num_ips_in_response = *num_ips_in_response + 1; ++ next_index++; ++ } ++ } ++ if(*error == ERROR_BUFFER_TOO_SHORT && *num_ips_in_response > 0) ++ { ++ *error = ERROR_NONE; ++ } ++ if(next_index == output_ip_list_length) ++ { ++ kfree(output_ip_list); ++ output_ip_list = NULL; ++ output_ip_list_length = 0; ++ } ++ } ++ ++ spin_unlock_bh(&bandwidth_lock); ++ ++ copy_to_user(user, buffer, *len); ++ kfree(buffer); ++ ++ ++ ++ up(&userspace_lock); ++ ++ ++ return 0; ++} ++ ++ ++ ++ ++ ++/******************** ++ * Set functions ++ ********************/ ++ ++typedef struct set_header_struct ++{ ++ uint32_t total_ips; ++ uint32_t next_ip_index; ++ uint32_t num_ips_in_buffer; ++ unsigned char history_included; ++ unsigned char zero_unset_ips; ++ time_t last_backup; ++ char id[BANDWIDTH_MAX_ID_LENGTH]; ++} set_header; ++ ++static int handle_set_failure(int ret_value, int unlock_user_sem, int unlock_bandwidth_spin, unsigned char* free_buffer ); ++static void parse_set_header(unsigned char* input_buffer, set_header* header); ++static void set_single_ip_data(unsigned char history_included, info_and_maps* iam, unsigned char* buffer, uint32_t* buffer_index, time_t now); ++ ++static int handle_set_failure(int ret_value, int unlock_user_sem, int unlock_bandwidth_spin, unsigned char* free_buffer ) ++{ ++ if( free_buffer != NULL ) { kfree(free_buffer); } ++ set_in_progress = 0; ++ if(unlock_bandwidth_spin) { spin_unlock_bh(&bandwidth_lock); } ++ if(unlock_user_sem) { up(&userspace_lock); } ++ return ret_value; ++} ++ ++static void parse_set_header(unsigned char* input_buffer, set_header* header) ++{ ++ /* ++ * set header structure: ++ * bytes 1-4 : total_ips being set in this and subsequent requests ++ * bytes 5-8 : next_ip_index, first ip being set in this set command ++ * bytes 9-12 : num_ips_in_buffer, the number of ips in this set request ++ * byte 13 : history_included (whether history data is included, or just current data) ++ * byte 14 : zero_unset_ips, whether to zero all ips not included in this and subsequent requests ++ * bytes 15-22 : last_backup time (64 bit) ++ * bytes 23-23+BANDWIDTH_MAX_ID_LENGTH : id ++ * bytes 23+ : ip data ++ */ ++ ++ uint32_t* total_ips = (uint32_t*)(input_buffer+0); ++ uint32_t* next_ip_index = (uint32_t*)(input_buffer+4); ++ uint32_t* num_ips_in_buffer = (uint32_t*)(input_buffer+8); ++ unsigned char* history_included = (unsigned char*)(input_buffer+12); ++ unsigned char* zero_unset_ips = (unsigned char*)(input_buffer+13); ++ uint64_t* last_backup = (uint64_t*)(input_buffer+14); ++ ++ ++ header->total_ips = *total_ips; ++ header->next_ip_index = *next_ip_index; ++ header->num_ips_in_buffer = *num_ips_in_buffer; ++ header->history_included = *history_included; ++ header->zero_unset_ips = *zero_unset_ips; ++ header->last_backup = (time_t)*last_backup; ++ memcpy(header->id, input_buffer+22, BANDWIDTH_MAX_ID_LENGTH); ++ (header->id)[BANDWIDTH_MAX_ID_LENGTH-1] = '\0'; /* make sure id is null terminated no matter what */ ++ ++ #ifdef BANDWIDTH_DEBUG ++ printk("parsed set header:\n"); ++ printk(" total_ips = %d\n", header->total_ips); ++ printk(" next_ip_index = %d\n", header->next_ip_index); ++ printk(" num_ips_in_buffer = %d\n", header->num_ips_in_buffer); ++ printk(" zero_unset_ips = %d\n", header->zero_unset_ips); ++ printk(" last_backup = %ld\n", header->last_backup); ++ printk(" id = %s\n", header->id); ++ #endif ++} ++static void set_single_ip_data(unsigned char history_included, info_and_maps* iam, unsigned char* buffer, uint32_t* buffer_index, time_t now) ++{ ++ /* ++ * note that times stored within the module are adjusted so they are equal to seconds ++ * since unix epoch that corrosponds to the UTC wall-clock time (timezone offset 0) ++ * that is equal to the wall-clock time in the current time-zone. Incoming values must ++ * be adjusted similarly ++ */ ++ uint32_t ip = *( (uint32_t*)(buffer + *buffer_index) ); ++ ++ #ifdef BANDWIDTH_DEBUG ++ uint32_t* ipp = &ip; ++ printk("doing set for ip = %u.%u.%u.%u\n", *((unsigned char*)ipp), *(((unsigned char*)ipp)+1), *(((unsigned char*)ipp)+2), *(((unsigned char*)ipp)+3) ); ++ printk("ip index = %d\n", *buffer_index); ++ #endif ++ ++ if(history_included) ++ { ++ uint32_t num_history_nodes = *( (uint32_t*)(buffer + *buffer_index+4)); ++ if(iam->info->num_intervals_to_save > 0 && iam->ip_history_map != NULL) ++ { ++ time_t first_start = (time_t) *( (uint64_t*)(buffer + *buffer_index+8)); ++ /* time_t first_end = (time_t) *( (uint64_t*)(buffer + *buffer_index+16)); //not used */ ++ /* time_t last_end = (time_t) *( (uint64_t*)(buffer + *buffer_index+24)); //not used */ ++ time_t next_start; ++ time_t next_end; ++ uint32_t node_index; ++ uint32_t zero_count; ++ bw_history* history; ++ ++ ++ #ifdef BANDWIDTH_DEBUG ++ printk("setting history with first start = %ld, now = %ld\n", first_start, now); ++ #endif ++ ++ ++ *buffer_index = *buffer_index + (2*4) + (3*8); ++ ++ /* adjust for timezone */ ++ next_start = first_start - (60 * local_minutes_west); ++ next_end = get_next_reset_time(iam->info, next_start, next_start); ++ node_index=0; ++ zero_count=0; ++ history = NULL; ++ while(next_start < now) ++ { ++ uint64_t next_bw = 0; ++ if(node_index < num_history_nodes) ++ { ++ next_bw = *( (uint64_t*)(buffer + *buffer_index)); ++ *buffer_index = *buffer_index + 8; ++ } ++ zero_count = next_bw == 0 ? zero_count+1 : 0; ++ ++ if(node_index == 0 || history == NULL) ++ { ++ initialize_map_entries_for_ip(iam, ip, next_bw); ++ history = get_long_map_element(iam->ip_history_map, (unsigned long)ip); ++ } ++ else if(next_end < now) /* if this is most recent node, don't do update since last node is current bandwidth */ ++ { ++ update_history(history, next_start, next_end, iam->info); ++ (history->history_data)[ history->current_index ] = next_bw; ++ if(zero_count < history->max_nodes +2) ++ { ++ next_start = next_end; ++ next_end = get_next_reset_time(iam->info, next_start, next_start); ++ } ++ else ++ { ++ /* do history reset */ ++ history->first_start = 0; ++ history->first_end = 0; ++ history->last_end = 0; ++ history->num_nodes = 1; ++ history->non_zero_nodes = 1; ++ history->current_index = 0; ++ (history->history_data)[0] = 0; ++ ++ next_start = now; ++ next_end = get_next_reset_time(iam->info, now, next_start); ++ } ++ } ++ else /* if this is most recent node, we still need to exit loop*/ ++ { ++ break; ++ } ++ node_index++; ++ } ++ while(node_index < num_history_nodes) ++ { ++ *buffer_index = *buffer_index + 8; ++ node_index++; ++ } ++ if(history != NULL) ++ { ++ set_long_map_element(iam->ip_map, ip, (history->history_data + history->current_index) ); ++ iam->info->previous_reset = next_start; ++ iam->info->next_reset = next_end; ++ if(ip == 0) ++ { ++ iam->info->current_bandwidth = (history->history_data)[history->current_index]; ++ } ++ } ++ } ++ else ++ { ++ uint64_t bw; ++ *buffer_index = *buffer_index + (2*4) + (3*8) + ((num_history_nodes-1)*8); ++ bw = *( (uint64_t*)(buffer + *buffer_index)); ++ initialize_map_entries_for_ip(iam, ip, bw); /* automatically frees existing values if they exist */ ++ *buffer_index = *buffer_index + 8; ++ if(ip == 0) ++ { ++ iam->info->current_bandwidth = bw; ++ } ++ } ++ ++ } ++ else ++ { ++ uint64_t bw = *( (uint64_t*)(buffer + *buffer_index+4) ); ++ #ifdef BANDWIDTH_DEBUG ++ printk(" setting bw to %lld\n", bw ); ++ #endif ++ ++ ++ initialize_map_entries_for_ip(iam, ip, bw); /* automatically frees existing values if they exist */ ++ *buffer_index = *buffer_index + 12; ++ ++ if(ip == 0) ++ { ++ iam->info->current_bandwidth = bw; ++ } ++ } ++ ++ ++} ++ ++static int ipt_bandwidth_set_ctl(struct sock *sk, int cmd, void *user, u_int32_t len) ++{ ++ /* check for timezone shift & adjust if necessary */ ++ char* buffer; ++ set_header header; ++ info_and_maps* iam; ++ uint32_t buffer_index; ++ uint32_t next_ip_index; ++ time_t now = get_seconds(); ++ check_for_timezone_shift(now, 0); ++ check_for_backwards_time_shift(now); ++ now = now - local_seconds_west; /* Adjust for local timezone */ ++ ++ ++ /* just return right away if user buffer is too short to contain even the header */ ++ if(len < (3*4) + 2 + 8 + BANDWIDTH_MAX_ID_LENGTH) ++ { ++ #ifdef BANDWIDTH_DEBUG ++ printk("set error: buffer not large enough!\n"); ++ #endif ++ return 0; ++ } ++ ++ down(&userspace_lock); ++ set_in_progress = 1; ++ ++ buffer = kmalloc(len, GFP_ATOMIC); ++ if(buffer == NULL) /* check for malloc failure */ ++ { ++ return handle_set_failure(0, 1, 0, NULL); ++ } ++ copy_from_user(buffer, user, len); ++ parse_set_header(buffer, &header); ++ ++ ++ ++ ++ /* ++ * retrieve data for this id and verify all variables are properly defined, just to be sure ++ * this is a kernel module -- it pays to be paranoid! ++ */ ++ spin_lock_bh(&bandwidth_lock); ++ ++ ++ iam = (info_and_maps*)get_string_map_element(id_map, header.id); ++ if(iam == NULL) ++ { ++ return handle_set_failure(0, 1, 1, buffer); ++ } ++ if(iam->info == NULL || iam->ip_map == NULL) ++ { ++ return handle_set_failure(0, 1, 1, buffer); ++ } ++ if(iam->info->num_intervals_to_save > 0 && iam->ip_history_map == NULL) ++ { ++ return handle_set_failure(0, 1, 1, buffer); ++ } ++ ++ /* ++ * during set unconditionally set combined_bw to NULL ++ * if combined data (ip=0) exists after set exits cleanly, we will restore it ++ */ ++ iam->info->combined_bw = NULL; ++ ++ //if zero_unset_ips == 1 && next_ip_index == 0 ++ //then clear data for all ips for this id ++ if(header.zero_unset_ips && header.next_ip_index == 0) ++ { ++ //clear data ++ if(iam->info->num_intervals_to_save > 0) ++ { ++ while(iam->ip_map->num_elements > 0) ++ { ++ unsigned long key; ++ remove_smallest_long_map_element(iam->ip_map, &key); ++ /* ignore return value -- it's actually malloced in history, not here */ ++ } ++ while(iam->ip_history_map->num_elements > 0) ++ { ++ unsigned long key; ++ bw_history* history = remove_smallest_long_map_element(iam->ip_history_map, &key); ++ kfree(history->history_data); ++ kfree(history); ++ } ++ } ++ else ++ { ++ while(iam->ip_map->num_elements > 0) ++ { ++ unsigned long key; ++ uint64_t *bw = remove_smallest_long_map_element(iam->ip_map, &key); ++ kfree(bw); ++ } ++ } ++ } ++ ++ /* ++ * last_backup parameter is only relevant for case where we are not setting history ++ * and when we don't have a constant interval length or a specified reset_time (since in this case start time gets reset when rule is inserted and there is therefore no constant end) ++ * If num_intervals_to_save =0 and is_constant_interval=0, check it. If it's nonzero (0=ignore) and invalid, return. ++ */ ++ if(header.last_backup > 0 && iam->info->num_intervals_to_save == 0 && (iam->info->reset_is_constant_interval == 0 || iam->info->reset_time != 0) ) ++ { ++ time_t adjusted_last_backup_time = header.last_backup - (60 * local_minutes_west); ++ time_t next_reset_of_last_backup = get_next_reset_time(iam->info, adjusted_last_backup_time, adjusted_last_backup_time); ++ if(next_reset_of_last_backup != iam->info->next_reset) ++ { ++ return handle_set_failure(0, 1, 1, buffer); ++ } ++ } ++ ++ ++ /* ++ * iterate over each ip block in buffer, ++ * loading data into necessary kerenel-space data structures ++ */ ++ buffer_index = (3*4) + 1 + 1 + 8 + BANDWIDTH_MAX_ID_LENGTH; ++ next_ip_index = header.next_ip_index; ++ ++ while(next_ip_index < header.num_ips_in_buffer) ++ { ++ set_single_ip_data(header.history_included, iam, buffer, &buffer_index, now); ++ next_ip_index++; ++ } ++ ++ if (next_ip_index == header.total_ips) ++ { ++ set_in_progress = 0; ++ } ++ ++ /* set combined_bw */ ++ iam->info->combined_bw = (uint64_t*)get_long_map_element(iam->ip_map, 0); ++ ++ kfree(buffer); ++ spin_unlock_bh(&bandwidth_lock); ++ up(&userspace_lock); ++ return 0; ++} ++static int checkentry(const struct xt_mtchk_param *par) ++{ ++ ++ ++ struct ipt_bandwidth_info *info = (struct ipt_bandwidth_info*)(par->matchinfo); ++ ++ ++ ++ #ifdef BANDWIDTH_DEBUG ++ printk("checkentry called\n"); ++ #endif ++ ++ ++ ++ ++ ++ if(info->ref_count == NULL) /* first instance, we're inserting rule */ ++ { ++ struct ipt_bandwidth_info *master_info = (struct ipt_bandwidth_info*)kmalloc(sizeof(struct ipt_bandwidth_info), GFP_ATOMIC); ++ info->ref_count = (unsigned long*)kmalloc(sizeof(unsigned long), GFP_ATOMIC); ++ ++ if(info->ref_count == NULL) /* deal with kmalloc failure */ ++ { ++ printk("ipt_bandwidth: kmalloc failure in checkentry!\n"); ++ return 0; ++ } ++ *(info->ref_count) = 1; ++ info->non_const_self = master_info; ++ info->hashed_id = sdbm_string_hash(info->id); ++ info->iam = NULL; ++ info->combined_bw = NULL; ++ ++ memcpy(master_info->id, info->id, BANDWIDTH_MAX_ID_LENGTH); ++ master_info->type = info->type; ++ master_info->check_type = info->check_type; ++ master_info->local_subnet = info->local_subnet; ++ master_info->local_subnet_mask = info->local_subnet_mask; ++ master_info->cmp = info->cmp; ++ master_info->reset_is_constant_interval = info->reset_is_constant_interval; ++ master_info->reset_interval = info->reset_interval; ++ master_info->reset_time = info->reset_time; ++ master_info->bandwidth_cutoff = info->bandwidth_cutoff; ++ master_info->current_bandwidth = info->current_bandwidth; ++ master_info->next_reset = info->next_reset; ++ master_info->previous_reset = info->previous_reset; ++ master_info->last_backup_time = info->last_backup_time; ++ master_info->num_intervals_to_save = info->num_intervals_to_save; ++ ++ master_info->hashed_id = info->hashed_id; ++ master_info->iam = info->iam; ++ master_info->combined_bw = info->combined_bw; ++ master_info->non_const_self = info->non_const_self; ++ master_info->ref_count = info->ref_count; ++ ++ #ifdef BANDWIDTH_DEBUG ++ printk(" after increment, ref count = %ld\n", *(info->ref_count) ); ++ #endif ++ ++ if(info->cmp != BANDWIDTH_CHECK) ++ { ++ info_and_maps *iam; ++ ++ down(&userspace_lock); ++ spin_lock_bh(&bandwidth_lock); ++ ++ ++ ++ iam = (info_and_maps*)get_string_map_element(id_map, info->id); ++ if(iam != NULL) ++ { ++ printk("ipt_bandwidth: error, \"%s\" is a duplicate id\n", info->id); ++ spin_unlock_bh(&bandwidth_lock); ++ up(&userspace_lock); ++ return 0; ++ } ++ ++ if(info->reset_interval != BANDWIDTH_NEVER) ++ { ++ time_t now = get_seconds(); ++ if(now != last_local_mw_update ) ++ { ++ check_for_timezone_shift(now, 1); ++ } ++ ++ ++ now = now - (60 * local_minutes_west); /* Adjust for local timezone */ ++ info->previous_reset = now; ++ master_info->previous_reset = now; ++ if(info->next_reset == 0) ++ { ++ info->next_reset = get_next_reset_time(info, now, now); ++ master_info->next_reset = info->next_reset; ++ /* ++ * if we specify last backup time, check that next reset is consistent, ++ * otherwise reset current_bandwidth to 0 ++ * ++ * only applies to combined type -- otherwise we need to handle setting bandwidth ++ * through userspace library ++ */ ++ if(info->last_backup_time != 0 && info->type == BANDWIDTH_COMBINED) ++ { ++ time_t adjusted_last_backup_time = info->last_backup_time - (60 * local_minutes_west); ++ time_t next_reset_of_last_backup = get_next_reset_time(info, adjusted_last_backup_time, adjusted_last_backup_time); ++ if(next_reset_of_last_backup != info->next_reset) ++ { ++ info->current_bandwidth = 0; ++ master_info->current_bandwidth = 0; ++ } ++ info->last_backup_time = 0; ++ master_info->last_backup_time = 0; ++ } ++ } ++ } ++ ++ iam = (info_and_maps*)kmalloc( sizeof(info_and_maps), GFP_ATOMIC); ++ if(iam == NULL) /* handle kmalloc failure */ ++ { ++ printk("ipt_bandwidth: kmalloc failure in checkentry!\n"); ++ spin_unlock_bh(&bandwidth_lock); ++ up(&userspace_lock); ++ return 0; ++ } ++ iam->ip_map = initialize_long_map(); ++ if(iam->ip_map == NULL) /* handle kmalloc failure */ ++ { ++ printk("ipt_bandwidth: kmalloc failure in checkentry!\n"); ++ spin_unlock_bh(&bandwidth_lock); ++ up(&userspace_lock); ++ return 0; ++ } ++ iam->ip_history_map = NULL; ++ if(info->num_intervals_to_save > 0) ++ { ++ iam->ip_history_map = initialize_long_map(); ++ if(iam->ip_history_map == NULL) /* handle kmalloc failure */ ++ { ++ printk("ipt_bandwidth: kmalloc failure in checkentry!\n"); ++ spin_unlock_bh(&bandwidth_lock); ++ up(&userspace_lock); ++ return 0; ++ } ++ } ++ ++ ++ iam->info = master_info; ++ set_string_map_element(id_map, info->id, iam); ++ ++ info->iam = (void*)iam; ++ master_info->iam = (void*)iam; ++ ++ ++ spin_unlock_bh(&bandwidth_lock); ++ up(&userspace_lock); ++ } ++ } ++ ++ else ++ { ++ /* info->non_const_self = info; */ ++ ++ ++ *(info->ref_count) = *(info->ref_count) + 1; ++ #ifdef BANDWIDTH_DEBUG ++ printk(" after increment, ref count = %ld\n", *(info->ref_count) ); ++ #endif ++ ++ ++ /* ++ if(info->cmp != BANDWIDTH_CHECK) ++ { ++ info_and_maps* iam; ++ down(&userspace_lock); ++ spin_lock_bh(&bandwidth_lock); ++ iam = (info_and_maps*)get_string_map_element(id_map, info->id); ++ if(iam != NULL) ++ { ++ iam->info = info; ++ } ++ spin_unlock_bh(&bandwidth_lock); ++ up(&userspace_lock); ++ } ++ */ ++ } ++ ++ #ifdef BANDWIDTH_DEBUG ++ printk("checkentry complete\n"); ++ #endif ++ return 0; ++} ++ ++static void destroy(const struct xt_mtdtor_param *par) ++{ ++ ++ struct ipt_bandwidth_info *info = (struct ipt_bandwidth_info*)(par->matchinfo); ++ ++ #ifdef BANDWIDTH_DEBUG ++ printk("destroy called\n"); ++ #endif ++ ++ *(info->ref_count) = *(info->ref_count) - 1; ++ ++ #ifdef BANDWIDTH_DEBUG ++ printk(" after decrement refcount = %ld\n", *(info->ref_count)); ++ #endif ++ ++ if(*(info->ref_count) == 0) ++ { ++ info_and_maps* iam; ++ down(&userspace_lock); ++ spin_lock_bh(&bandwidth_lock); ++ ++ info->combined_bw = NULL; ++ iam = (info_and_maps*)remove_string_map_element(id_map, info->id); ++ if(iam != NULL && info->cmp != BANDWIDTH_CHECK) ++ { ++ unsigned long num_destroyed; ++ if(iam->ip_map != NULL && iam->ip_history_map != NULL) ++ { ++ unsigned long history_index = 0; ++ bw_history** histories_to_free; ++ ++ destroy_long_map(iam->ip_map, DESTROY_MODE_IGNORE_VALUES, &num_destroyed); ++ ++ histories_to_free = (bw_history**)destroy_long_map(iam->ip_history_map, DESTROY_MODE_RETURN_VALUES, &num_destroyed); ++ ++ /* num_destroyed will be 0 if histories_to_free is null after malloc failure, so this is safe */ ++ for(history_index = 0; history_index < num_destroyed; history_index++) ++ { ++ bw_history* h = histories_to_free[history_index]; ++ if(h != NULL) ++ { ++ kfree(h->history_data); ++ kfree(h); ++ } ++ } ++ ++ } ++ else if(iam->ip_map != NULL) ++ { ++ destroy_long_map(iam->ip_map, DESTROY_MODE_FREE_VALUES, &num_destroyed); ++ } ++ kfree(iam); ++ /* info portion of iam gets taken care of automatically */ ++ } ++ kfree(info->ref_count); ++ kfree(info->non_const_self); ++ ++ spin_unlock_bh(&bandwidth_lock); ++ up(&userspace_lock); ++ } ++ ++ #ifdef BANDWIDTH_DEBUG ++ printk("destroy complete\n"); ++ #endif ++} ++ ++static struct nf_sockopt_ops ipt_bandwidth_sockopts = ++{ ++ .pf = PF_INET, ++ .set_optmin = BANDWIDTH_SET, ++ .set_optmax = BANDWIDTH_SET+1, ++ .set = ipt_bandwidth_set_ctl, ++ .get_optmin = BANDWIDTH_GET, ++ .get_optmax = BANDWIDTH_GET+1, ++ .get = ipt_bandwidth_get_ctl ++}; ++ ++ ++static struct xt_match bandwidth_match __read_mostly = ++{ ++ .name = "bandwidth", ++ .match = match, ++ .family = AF_INET, ++ .matchsize = sizeof(struct ipt_bandwidth_info), ++ .checkentry = checkentry, ++ .destroy = destroy, ++ .me = THIS_MODULE, ++}; ++ ++static int __init init(void) ++{ ++ /* Register setsockopt */ ++ if (nf_register_sockopt(&ipt_bandwidth_sockopts) < 0) ++ { ++ printk("ipt_bandwidth: Can't register sockopts. Aborting\n"); ++ } ++ bandwidth_record_max = get_bw_record_max(); ++ local_minutes_west = old_minutes_west = sys_tz.tz_minuteswest; ++ local_seconds_west = local_minutes_west*60; ++ last_local_mw_update = get_seconds(); ++ if(local_seconds_west > last_local_mw_update) ++ { ++ /* we can't let adjusted time be < 0 -- pretend timezone is still UTC */ ++ local_minutes_west = 0; ++ local_seconds_west = 0; ++ } ++ ++ id_map = initialize_string_map(0); ++ if(id_map == NULL) /* deal with kmalloc failure */ ++ { ++ printk("id map is null, returning -1\n"); ++ return -1; ++ } ++ ++ ++ return xt_register_match(&bandwidth_match); ++} ++ ++static void __exit fini(void) ++{ ++ down(&userspace_lock); ++ spin_lock_bh(&bandwidth_lock); ++ if(id_map != NULL) ++ { ++ unsigned long num_returned; ++ info_and_maps **iams = (info_and_maps**)destroy_string_map(id_map, DESTROY_MODE_RETURN_VALUES, &num_returned); ++ int iam_index; ++ for(iam_index=0; iam_index < num_returned; iam_index++) ++ { ++ info_and_maps* iam = iams[iam_index]; ++ long_map* ip_map = iam->ip_map; ++ unsigned long num_destroyed; ++ destroy_long_map(ip_map, DESTROY_MODE_FREE_VALUES, &num_destroyed); ++ kfree(iam); ++ /* info portion of iam gets taken care of automatically */ ++ } ++ } ++ nf_unregister_sockopt(&ipt_bandwidth_sockopts); ++ xt_unregister_match(&bandwidth_match); ++ spin_unlock_bh(&bandwidth_lock); ++ up(&userspace_lock); ++ ++} ++ ++module_init(init); ++module_exit(fini); ++ + +--- /dev/null ++++ b/net/ipv4/netfilter/ipt_bandwidth.mod.c +@@ -0,0 +1,20 @@ ++#include ++#include ++#include ++ ++MODULE_INFO(vermagic, VERMAGIC_STRING); ++ ++struct module __this_module ++__attribute__((section(".gnu.linkonce.this_module"))) = { ++ .name = KBUILD_MODNAME, ++ .init = init_module, ++#ifdef CONFIG_MODULE_UNLOAD ++ .exit = cleanup_module, ++#endif ++}; ++ ++static const char __module_depends[] ++__attribute_used__ ++__attribute__((section(".modinfo"))) = ++"depends="; ++ +--- /dev/null ++++ b/net/ipv4/netfilter/ipt_timerange.c +@@ -0,0 +1,142 @@ ++/* timerange -- An iptables extension to match multiple timeranges within a week ++ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) ++ * ++ * ++ * Copyright © 2009-2010 by Eric Bishop ++ * ++ * This file is free software: you may copy, redistribute and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation, either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This file is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++ ++ ++#include ++ ++#include ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Eric Bishop"); ++MODULE_DESCRIPTION("Match time ranges, designed for use with Gargoyle web interface (www.gargoyle-router.com)"); ++ ++ ++extern struct timezone sys_tz; ++ ++ ++static bool match(const struct sk_buff *skb, struct xt_action_param *par) ++{ ++ const struct ipt_timerange_info *info = (const struct ipt_timerange_info*)(par->matchinfo); ++ ++ ++ time_t stamp_time; ++ int weekday; ++ int seconds_since_midnight; ++ int test_index; ++ int match_found; ++ ++ struct timeval test_time; ++ ++ do_gettimeofday(&test_time); ++ stamp_time = test_time.tv_sec; ++ stamp_time = stamp_time - (60 * sys_tz.tz_minuteswest); /* Adjust for local timezone */ ++ seconds_since_midnight = stamp_time % 86400; /* 86400 seconds per day */ ++ weekday = (4 + (stamp_time/86400)) % 7; /* 1970-01-01 (time=0) was a Thursday (4). */ ++ ++ /* ++ printk("time=%d, since midnight = %d, day=%d, minuteswest=%d\n", stamp_time, seconds_since_midnight, weekday, sys_tz.tz_minuteswest); ++ */ ++ ++ match_found = 0; ++ if(info->type == HOURS) ++ { ++ for(test_index=0; info->ranges[test_index] != -1 && match_found == 0 && seconds_since_midnight >= info->ranges[test_index]; test_index=test_index+2) ++ { ++ match_found = seconds_since_midnight >= info->ranges[test_index] && seconds_since_midnight <= info->ranges[test_index+1] ? 1 : match_found; ++ } ++ } ++ else if(info->type == WEEKDAYS) ++ { ++ match_found = info->days[weekday]; ++ } ++ else if(info->type == DAYS_HOURS) ++ { ++ match_found = info->days[weekday]; ++ if(match_found == 1) ++ { ++ match_found = 0; ++ for(test_index=0; info->ranges[test_index] != -1 && match_found == 0 && seconds_since_midnight >= info->ranges[test_index]; test_index=test_index+2) ++ { ++ match_found = seconds_since_midnight >= info->ranges[test_index] && seconds_since_midnight <= info->ranges[test_index+1] ? 1 : match_found; ++ } ++ } ++ } ++ else if(info->type == WEEKLY_RANGE) ++ { ++ time_t seconds_since_sunday_midnight = seconds_since_midnight + (weekday*86400); ++ for(test_index=0; info->ranges[test_index] != -1 && match_found == 0 && seconds_since_sunday_midnight >= info->ranges[test_index]; test_index=test_index+2) ++ { ++ match_found = seconds_since_sunday_midnight >= info->ranges[test_index] && seconds_since_sunday_midnight <= info->ranges[test_index+1] ? 1 : match_found; ++ } ++ ++ } ++ ++ match_found = info->invert == 0 ? match_found : !match_found; ++ return match_found; ++} ++ ++ ++static int checkentry(const struct xt_mtchk_param *par) ++{ ++ return 0; ++} ++ ++ ++static struct xt_match timerange_match __read_mostly = ++{ ++ .name = "timerange", ++ .match = match, ++ .family = AF_INET, ++ .matchsize = sizeof(struct ipt_timerange_info), ++ .checkentry = checkentry, ++ .me = THIS_MODULE, ++}; ++ ++static int __init init(void) ++{ ++ return xt_register_match(&timerange_match); ++} ++ ++static void __exit fini(void) ++{ ++ xt_unregister_match(&timerange_match); ++} ++ ++module_init(init); ++module_exit(fini); ++ + +--- /dev/null ++++ b/net/ipv4/netfilter/ipt_timerange.mod.c +@@ -0,0 +1,20 @@ ++#include ++#include ++#include ++ ++MODULE_INFO(vermagic, VERMAGIC_STRING); ++ ++struct module __this_module ++__attribute__((section(".gnu.linkonce.this_module"))) = { ++ .name = KBUILD_MODNAME, ++ .init = init_module, ++#ifdef CONFIG_MODULE_UNLOAD ++ .exit = cleanup_module, ++#endif ++}; ++ ++static const char __module_depends[] ++__attribute_used__ ++__attribute__((section(".modinfo"))) = ++"depends="; ++ +--- /dev/null ++++ b/net/ipv4/netfilter/ipt_webmon.c +@@ -0,0 +1,1319 @@ ++/* webmon -- A netfilter module to match URLs in HTTP(S) requests ++ * This module can match using string match or regular expressions ++ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) ++ * ++ * ++ * Copyright © 2008-2011 by Eric Bishop ++ * ++ * This file is free software: you may copy, redistribute and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation, either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This file is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "webmon_deps/tree_map.h" ++ ++ ++#include ++ ++ ++#include ++#include ++ ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Eric Bishop"); ++MODULE_DESCRIPTION("Monitor URL in HTTP(S) Requests, designed for use with Gargoyle web interface (www.gargoyle-router.com)"); ++ ++#define NIPQUAD(addr) \ ++ ((unsigned char *)&addr)[0], \ ++ ((unsigned char *)&addr)[1], \ ++ ((unsigned char *)&addr)[2], \ ++ ((unsigned char *)&addr)[3] ++#define STRIP "%u.%u.%u.%u" ++ ++typedef struct qn ++{ ++ uint32_t src_ip; ++ char* value; ++ struct timeval time; ++ struct qn* next; ++ struct qn* previous; ++} queue_node; ++ ++typedef struct ++{ ++ queue_node* first; ++ queue_node* last; ++ int length; ++} queue; ++ ++static string_map* domain_map = NULL; ++static queue* recent_domains = NULL; ++ ++static string_map* search_map = NULL; ++static queue* recent_searches = NULL; ++ ++ ++static int max_domain_queue_length = 5; ++static int max_search_queue_length = 5; ++ ++static spinlock_t webmon_lock = __SPIN_LOCK_UNLOCKED(webmon_lock);; ++ ++ ++static void update_queue_node_time(queue_node* update_node, queue* full_queue) ++{ ++ struct timeval t; ++ do_gettimeofday(&t); ++ update_node->time = t; ++ ++ /* move to front of queue if not already at front of queue */ ++ if(update_node->previous != NULL) ++ { ++ queue_node* p = update_node->previous; ++ queue_node* n = update_node->next; ++ p->next = n; ++ if(n != NULL) ++ { ++ n->previous = p; ++ } ++ else ++ { ++ full_queue->last = p; ++ } ++ update_node->previous = NULL; ++ update_node->next = full_queue->first; ++ full_queue->first->previous = update_node; ++ full_queue->first = update_node; ++ } ++} ++ ++void add_queue_node(uint32_t src_ip, char* value, queue* full_queue, string_map* queue_index, char* queue_index_key, uint32_t max_queue_length ) ++{ ++ ++ queue_node *new_node = (queue_node*)kmalloc(sizeof(queue_node), GFP_ATOMIC); ++ char* dyn_value = kernel_strdup(value); ++ struct timeval t; ++ ++ ++ if(new_node == NULL || dyn_value == NULL) ++ { ++ if(dyn_value) { kfree(dyn_value); } ++ if(new_node) { kfree(new_node); }; ++ ++ return; ++ } ++ set_map_element(queue_index, queue_index_key, (void*)new_node); ++ ++ ++ do_gettimeofday(&t); ++ new_node->time = t; ++ new_node->src_ip = src_ip; ++ new_node->value = dyn_value; ++ new_node->previous = NULL; ++ ++ new_node->next = full_queue->first; ++ if(full_queue->first != NULL) ++ { ++ full_queue->first->previous = new_node; ++ } ++ full_queue->first = new_node; ++ full_queue->last = (full_queue->last == NULL) ? new_node : full_queue->last ; ++ full_queue->length = full_queue->length + 1; ++ ++ if( full_queue->length > max_queue_length ) ++ { ++ queue_node *old_node = full_queue->last; ++ full_queue->last = old_node->previous; ++ full_queue->last->next = NULL; ++ full_queue->first = old_node->previous == NULL ? NULL : full_queue->first; /*shouldn't be needed, but just in case...*/ ++ full_queue->length = full_queue->length - 1; ++ ++ sprintf(queue_index_key, STRIP"@%s", NIPQUAD(old_node->src_ip), old_node->value); ++ remove_map_element(queue_index, queue_index_key); ++ ++ kfree(old_node->value); ++ kfree(old_node); ++ } ++ ++ /* ++ queue_node* n = full_queue->first; ++ while(n != NULL) ++ { ++ printf("%ld\t%s\t%s\t%s\n", (unsigned long)n->time, n->src_ip, n->dst_ip, n->domain); ++ n = (queue_node*)n->next; ++ } ++ printf("\n\n"); ++ */ ++} ++ ++void destroy_queue(queue* q) ++{ ++ queue_node *last_node = q->last; ++ while(last_node != NULL) ++ { ++ queue_node *previous_node = last_node->previous; ++ free(last_node->value); ++ free(last_node); ++ last_node = previous_node; ++ } ++ free(q); ++} ++ ++ ++int strnicmp(const char * cs,const char * ct,size_t count) ++{ ++ register signed char __res = 0; ++ ++ while (count) ++ { ++ if ((__res = toupper( *cs ) - toupper( *ct++ ) ) != 0 || !*cs++) ++ { ++ break; ++ } ++ count--; ++ } ++ return __res; ++} ++ ++char *strnistr(const char *s, const char *find, size_t slen) ++{ ++ char c, sc; ++ size_t len; ++ ++ ++ if ((c = *find++) != '\0') ++ { ++ len = strlen(find); ++ do ++ { ++ do ++ { ++ if (slen < 1 || (sc = *s) == '\0') ++ { ++ return (NULL); ++ } ++ --slen; ++ ++s; ++ } ++ while ( toupper(sc) != toupper(c)); ++ ++ if (len > slen) ++ { ++ return (NULL); ++ } ++ } ++ while (strnicmp(s, find, len) != 0); ++ ++ s--; ++ } ++ return ((char *)s); ++} ++ ++/* NOTE: This is not quite real edit distance -- all differences are assumed to be in one contiguous block ++ * If differences are not in a contiguous block computed edit distance will be greater than real edit distance. ++ * Edit distance computed here is an upper bound on real edit distance. ++ */ ++int within_edit_distance(char *s1, char *s2, int max_edit) ++{ ++ int ret = 0; ++ if(s1 != NULL && s2 != NULL) ++ { ++ int edit1 = strlen(s1); ++ int edit2 = strlen(s2); ++ char* s1sp = s1; ++ char* s2sp = s2; ++ char* s1ep = s1 + (edit1-1); ++ char* s2ep = s2 + (edit2-1); ++ while(*s1sp != '\0' && *s2sp != '\0' && *s1sp == *s2sp) ++ { ++ s1sp++; ++ s2sp++; ++ edit1--; ++ edit2--; ++ } ++ ++ /* if either is zero we got to the end of one of the strings */ ++ while(s1ep > s1sp && s2ep > s2sp && *s1ep == *s2ep) ++ { ++ s1ep--; ++ s2ep--; ++ edit1--; ++ edit2--; ++ } ++ ret = edit1 <= max_edit && edit2 <= max_edit ? 1 : 0; ++ } ++ return ret; ++} ++ ++ ++/* ++ * line is the line to be parsed -- it is not modified in any way ++ * max_pieces indicates number of pieces to return, if negative this is determined dynamically ++ * include_remainder_at_max indicates whether the last piece, when max pieces are reached, ++ * should be what it would normally be (0) or the entire remainder of the line (1) ++ * if max_pieces < 0 this parameter is ignored ++ * ++ * ++ * returns all non-separator pieces in a line ++ * result is dynamically allocated, MUST be freed after call-- even if ++ * line is empty (you still get a valid char** pointer to to a NULL char*) ++ */ ++char** split_on_separators(char* line, char* separators, int num_separators, int max_pieces, int include_remainder_at_max, unsigned long *num_pieces) ++{ ++ char** split; ++ ++ *num_pieces = 0; ++ if(line != NULL) ++ { ++ int split_index; ++ int non_separator_found; ++ char* dup_line; ++ char* start; ++ ++ if(max_pieces < 0) ++ { ++ /* count number of separator characters in line -- this count + 1 is an upperbound on number of pieces */ ++ int separator_count = 0; ++ int line_index; ++ for(line_index = 0; line[line_index] != '\0'; line_index++) ++ { ++ int sep_index; ++ int found = 0; ++ for(sep_index =0; found == 0 && sep_index < num_separators; sep_index++) ++ { ++ found = separators[sep_index] == line[line_index] ? 1 : 0; ++ } ++ separator_count = separator_count+ found; ++ } ++ max_pieces = separator_count + 1; ++ } ++ split = (char**)malloc((1+max_pieces)*sizeof(char*)); ++ split_index = 0; ++ split[split_index] = NULL; ++ ++ ++ dup_line = strdup(line); ++ start = dup_line; ++ non_separator_found = 0; ++ while(non_separator_found == 0) ++ { ++ int matches = 0; ++ int sep_index; ++ for(sep_index =0; sep_index < num_separators; sep_index++) ++ { ++ matches = matches == 1 || separators[sep_index] == start[0] ? 1 : 0; ++ } ++ non_separator_found = matches==0 || start[0] == '\0' ? 1 : 0; ++ if(non_separator_found == 0) ++ { ++ start++; ++ } ++ } ++ ++ while(start[0] != '\0' && split_index < max_pieces) ++ { ++ /* find first separator index */ ++ int first_separator_index = 0; ++ int separator_found = 0; ++ while( separator_found == 0 ) ++ { ++ int sep_index; ++ for(sep_index =0; separator_found == 0 && sep_index < num_separators; sep_index++) ++ { ++ separator_found = separators[sep_index] == start[first_separator_index] || start[first_separator_index] == '\0' ? 1 : 0; ++ } ++ if(separator_found == 0) ++ { ++ first_separator_index++; ++ } ++ } ++ ++ /* copy next piece to split array */ ++ if(first_separator_index > 0) ++ { ++ char* next_piece = NULL; ++ if(split_index +1 < max_pieces || include_remainder_at_max <= 0) ++ { ++ next_piece = (char*)malloc((first_separator_index+1)*sizeof(char)); ++ memcpy(next_piece, start, first_separator_index); ++ next_piece[first_separator_index] = '\0'; ++ } ++ else ++ { ++ next_piece = strdup(start); ++ } ++ split[split_index] = next_piece; ++ split[split_index+1] = NULL; ++ split_index++; ++ } ++ ++ ++ /* find next non-separator index, indicating start of next piece */ ++ start = start+ first_separator_index; ++ non_separator_found = 0; ++ while(non_separator_found == 0) ++ { ++ int matches = 0; ++ int sep_index; ++ for(sep_index =0; sep_index < num_separators; sep_index++) ++ { ++ matches = matches == 1 || separators[sep_index] == start[0] ? 1 : 0; ++ } ++ non_separator_found = matches==0 || start[0] == '\0' ? 1 : 0; ++ if(non_separator_found == 0) ++ { ++ start++; ++ } ++ } ++ } ++ free(dup_line); ++ *num_pieces = split_index; ++ } ++ else ++ { ++ split = (char**)malloc((1)*sizeof(char*)); ++ split[0] = NULL; ++ } ++ return split; ++} ++ ++ ++ ++static void extract_url(const unsigned char* packet_data, int packet_length, char* domain, char* path) ++{ ++ ++ int path_start_index; ++ int path_end_index; ++ int last_header_index; ++ char last_two_buf[2]; ++ int end_found; ++ char* domain_match; ++ char* start_ptr; ++ ++ domain[0] = '\0'; ++ path[0] = '\0'; ++ ++ ++ /* get path portion of URL */ ++ start_ptr = strnistr((char*)packet_data, " ", packet_length); ++ if(start_ptr == NULL) ++ { ++ return; ++ } ++ ++ path_start_index = (int)(start_ptr - (char*)packet_data); ++ start_ptr = strnistr((char*)(packet_data+path_start_index), " ", packet_length-(path_start_index+2)); ++ if(start_ptr == NULL) ++ { ++ return; ++ } ++ ++ while( packet_data[path_start_index] == ' ') ++ { ++ path_start_index++; ++ } ++ path_end_index= (int)(strstr( (char*)(packet_data+path_start_index), " ") - (char*)packet_data); ++ if(path_end_index > 0) ++ { ++ int path_length = path_end_index-path_start_index; ++ path_length = path_length < 625 ? path_length : 624; /* prevent overflow */ ++ memcpy(path, packet_data+path_start_index, path_length); ++ path[ path_length] = '\0'; ++ } ++ else ++ { ++ return; ++ } ++ ++ /* get header length */ ++ last_header_index = 2; ++ memcpy(last_two_buf,(char*)packet_data, 2); ++ end_found = 0; ++ while(end_found == 0 && last_header_index < packet_length) ++ { ++ char next = (char)packet_data[last_header_index]; ++ if(next == '\n') ++ { ++ end_found = last_two_buf[1] == '\n' || (last_two_buf[0] == '\n' && last_two_buf[1] == '\r') ? 1 : 0; ++ } ++ if(end_found == 0) ++ { ++ last_two_buf[0] = last_two_buf[1]; ++ last_two_buf[1] = next; ++ last_header_index++; ++ } ++ } ++ ++ /* get domain portion of URL */ ++ domain_match = strnistr( (char*)packet_data, "Host:", last_header_index); ++ if(domain_match != NULL) ++ { ++ int domain_end_index; ++ domain_match = domain_match + 5; /* character after "Host:" */ ++ while(domain_match[0] == ' ' && ( (char*)domain_match - (char*)packet_data) < last_header_index) ++ { ++ domain_match = domain_match+1; ++ } ++ ++ domain_end_index = 0; ++ while( domain_match[domain_end_index] != '\n' && ++ domain_match[domain_end_index] != '\r' && ++ domain_match[domain_end_index] != ' ' && ++ domain_match[domain_end_index] != ':' && ++ ((char*)domain_match - (char*)packet_data)+domain_end_index < last_header_index ++ ) ++ { ++ domain_end_index++; ++ } ++ domain_end_index = domain_end_index < 625 ? domain_end_index : 624; /* prevent overflow */ ++ memcpy(domain, domain_match, domain_end_index); ++ domain[domain_end_index] = '\0'; ++ ++ for(domain_end_index=0; domain[domain_end_index] != '\0'; domain_end_index++) ++ { ++ domain[domain_end_index] = (char)tolower(domain[domain_end_index]); ++ } ++ } ++} ++ ++static void extract_url_https(const unsigned char* packet_data, int packet_length, char* domain) ++{ ++ //TLSv1.2 Record Layer - All calculations based on this ++ //We want to abuse the SNI (Server Name Indication) extension to harvest likely URLs ++ //Content Type = 0x16 (22) is a "Handshake", HandShake Type 0x01 (1) is a "Client Hello" ++ int x, packet_limit; ++ unsigned short cslen, ext_type, ext_len, maxextlen; ++ unsigned char conttype, hndshktype, sidlen, cmplen; ++ unsigned char* packet_ptr; ++ ++ domain[0] = '\0'; ++ packet_ptr = packet_data; ++ ++ if (packet_length < 43) ++ { ++ /*printk("Packet less than 43 bytes, exiting\n");*/ ++ return; ++ } ++ conttype = packet_data[0]; ++ hndshktype = packet_data[5]; ++ sidlen = packet_data[43]; ++ /*printk("conttype=%d, hndshktype=%d, sidlen=%d ",conttype,hndshktype,sidlen);*/ ++ if(conttype != 22) ++ { ++ /*printk("conttype not 22, exiting\n");*/ ++ return; ++ } ++ if(hndshktype != 1) ++ { ++ /*printk("hndshktype not 1, exiting\n");*/ ++ return; //We aren't in a Client Hello ++ } ++ ++ packet_ptr = packet_data + 1 + 43 + sidlen; //Skip to Cipher Suites Length ++ cslen = ntohs(*(unsigned short*)packet_ptr); //Length of Cipher Suites (2 byte) ++ packet_ptr = packet_ptr + 2 + cslen; //Skip to Compression Methods ++ cmplen = *packet_ptr; //Length of Compression Methods (1 byte) ++ packet_ptr = packet_ptr + 1 + cmplen; //Skip to Extensions Length **IMPORTANT** ++ maxextlen = ntohs(*(unsigned short*)packet_ptr); //Length of extensions (2 byte) ++ packet_ptr = packet_ptr + 2; //Skip to beginning of first extension and start looping ++ ext_type = 1; ++ /*printk("cslen=%d, cmplen=%d, maxextlen=%d, pktlen=%d,ptrpos=%d\n",cslen,cmplen,maxextlen,packet_length,packet_ptr - packet_data);*/ ++ //Limit the pointer bounds to the smaller of either the extensions length or the packet length ++ packet_limit = ((packet_ptr - packet_data) + maxextlen) < packet_length ? ((packet_ptr - packet_data) + maxextlen) : packet_length; ++ ++ //Extension Type and Extension Length are both 2 byte. SNI Extension is "0" ++ while(((packet_ptr - packet_data) < packet_limit) && (ext_type != 0)) ++ { ++ ext_type = ntohs(*(unsigned short*)packet_ptr); ++ packet_ptr = packet_ptr + 2; ++ ext_len = ntohs(*(unsigned short*)packet_ptr); ++ packet_ptr = packet_ptr + 2; ++ /*printk("ext_type=%d, ext_len=%d\n",ext_type,ext_len);*/ ++ if(ext_type == 0) ++ { ++ unsigned short snilen; ++ /*printk("FOUND SNI EXT\n");*/ ++ packet_ptr = packet_ptr + 3; //Skip to length of SNI ++ snilen = ntohs(*(unsigned short*)packet_ptr); ++ /*printk("snilen=%d\n",snilen);*/ ++ packet_ptr = packet_ptr + 2; //Skip to beginning of SNI ++ if((((packet_ptr - packet_data) + snilen) < packet_limit) && (snilen > 0)) ++ { ++ /*printk("FOUND SNI\n");*/ ++ snilen = snilen < 625 ? snilen : 624; // prevent overflow ++ memcpy(domain, packet_ptr, snilen); ++ domain[snilen] = '\0'; ++ for(x=0; domain[x] != '\0'; x++) ++ { ++ domain[x] = (char)tolower(domain[x]); ++ } ++ /*printk("sni=%s\n",domain);*/ ++ } ++ } ++ else ++ { ++ packet_ptr = packet_ptr + ext_len; ++ } ++ } ++} ++ ++#ifdef CONFIG_PROC_FS ++ ++static void *webmon_proc_start(struct seq_file *seq, loff_t *loff_pos) ++{ ++ static unsigned long counter = 0; ++ ++ /* beginning a new sequence ? */ ++ if ( *loff_pos == 0 ) ++ { ++ /* yes => return a non null value to begin the sequence */ ++ return &counter; ++ } ++ else ++ { ++ /* no => it's the end of the sequence, return end to stop reading */ ++ *loff_pos = 0; ++ return NULL; ++ } ++} ++ ++static void *webmon_proc_next(struct seq_file *seq, void *v, loff_t *pos) ++{ ++ return NULL; ++} ++ ++ ++static void webmon_proc_stop(struct seq_file *seq, void *v) ++{ ++ //don't need to do anything ++} ++ ++ ++static int webmon_proc_domain_show(struct seq_file *s, void *v) ++{ ++ queue_node* next_node; ++ spin_lock_bh(&webmon_lock); ++ ++ next_node = recent_domains->last; ++ while(next_node != NULL) ++ { ++ seq_printf(s, "%ld\t"STRIP"\t%s\n", (unsigned long)(next_node->time).tv_sec, NIPQUAD(next_node->src_ip), next_node->value); ++ next_node = (queue_node*)next_node->previous; ++ } ++ spin_unlock_bh(&webmon_lock); ++ ++ return 0; ++} ++ ++static int webmon_proc_search_show(struct seq_file *s, void *v) ++{ ++ queue_node* next_node; ++ spin_lock_bh(&webmon_lock); ++ ++ next_node = recent_searches->last; ++ while(next_node != NULL) ++ { ++ seq_printf(s, "%ld\t"STRIP"\t%s\n", (unsigned long)(next_node->time).tv_sec, NIPQUAD(next_node->src_ip), next_node->value); ++ next_node = (queue_node*)next_node->previous; ++ } ++ spin_unlock_bh(&webmon_lock); ++ ++ return 0; ++} ++ ++ ++static struct seq_operations webmon_proc_domain_sops = { ++ .start = webmon_proc_start, ++ .next = webmon_proc_next, ++ .stop = webmon_proc_stop, ++ .show = webmon_proc_domain_show ++}; ++ ++static struct seq_operations webmon_proc_search_sops = { ++ .start = webmon_proc_start, ++ .next = webmon_proc_next, ++ .stop = webmon_proc_stop, ++ .show = webmon_proc_search_show ++}; ++ ++ ++static int webmon_proc_domain_open(struct inode *inode, struct file* file) ++{ ++ return seq_open(file, &webmon_proc_domain_sops); ++} ++static int webmon_proc_search_open(struct inode *inode, struct file* file) ++{ ++ return seq_open(file, &webmon_proc_search_sops); ++} ++ ++ ++ ++static struct file_operations webmon_proc_domain_fops = { ++ .owner = THIS_MODULE, ++ .open = webmon_proc_domain_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = seq_release ++}; ++static struct file_operations webmon_proc_search_fops = { ++ .owner = THIS_MODULE, ++ .open = webmon_proc_search_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = seq_release ++}; ++ ++ ++#endif ++ ++ ++ ++ ++ ++ ++static int ipt_webmon_set_ctl(struct sock *sk, int cmd, void *user, u_int32_t len) ++{ ++ ++ char* buffer = kmalloc(len, GFP_ATOMIC); ++ if(buffer == NULL) /* check for malloc failure */ ++ { ++ return 0; ++ } ++ spin_lock_bh(&webmon_lock); ++ copy_from_user(buffer, user, len); ++ ++ if(len > 1 + sizeof(uint32_t)) ++ { ++ unsigned char type = buffer[0]; ++ uint32_t max_queue_length = *((uint32_t*)(buffer+1)); ++ char* data = buffer+1+sizeof(uint32_t); ++ char newline_terminator[] = { '\n', '\r' }; ++ char whitespace_chars[] = { '\t', ' ' }; ++ ++ if(type == WEBMON_DOMAIN || type == WEBMON_SEARCH ) ++ { ++ unsigned long num_destroyed; ++ ++ ++ /* destroy and re-initialize queue and map */ ++ if(type == WEBMON_DOMAIN ) ++ { ++ destroy_map(domain_map, DESTROY_MODE_IGNORE_VALUES, &num_destroyed); ++ destroy_queue(recent_domains); ++ recent_domains = (queue*)malloc(sizeof(queue)); ++ recent_domains->first = NULL; ++ recent_domains->last = NULL; ++ recent_domains->length = 0; ++ domain_map = initialize_map(0); ++ ++ max_domain_queue_length = max_queue_length; ++ } ++ else if(type == WEBMON_SEARCH) ++ { ++ destroy_map(search_map, DESTROY_MODE_IGNORE_VALUES, &num_destroyed); ++ destroy_queue(recent_searches); ++ recent_searches = (queue*)malloc(sizeof(queue)); ++ recent_searches->first = NULL; ++ recent_searches->last = NULL; ++ recent_searches->length = 0; ++ search_map = initialize_map(0); ++ ++ max_search_queue_length = max_queue_length; ++ } ++ ++ if(data[0] != '\0') ++ { ++ unsigned long num_lines; ++ unsigned long line_index; ++ char** lines = split_on_separators(data, newline_terminator, 2, -1, 0, &num_lines); ++ for(line_index=0; line_index < num_lines; line_index++) ++ { ++ char* line = lines[line_index]; ++ unsigned long num_pieces; ++ char** split = split_on_separators(line, whitespace_chars, 2, -1, 0, &num_pieces); ++ ++ //check that there are 3 pieces (time, src_ip, value) ++ int length; ++ for(length=0; split[length] != NULL ; length++){} ++ if(length == 3) ++ { ++ time_t time; ++ int parsed_ip[4]; ++ int valid_ip = sscanf(split[1], "%d.%d.%d.%d", parsed_ip, parsed_ip+1, parsed_ip+2, parsed_ip+3); ++ if(valid_ip == 4) ++ { ++ valid_ip = parsed_ip[0] <= 255 && parsed_ip[1] <= 255 && parsed_ip[2] <= 255 && parsed_ip[3] <= 255 ? valid_ip : 0; ++ } ++ if(sscanf(split[0], "%ld", &time) > 0 && valid_ip == 4) ++ { ++ char* value = split[2]; ++ char value_key[700]; ++ uint32_t ip = (parsed_ip[0]<<24) + (parsed_ip[1]<<16) + (parsed_ip[2]<<8) + (parsed_ip[3]) ; ++ ip = htonl(ip); ++ sprintf(value_key, STRIP"@%s", NIPQUAD(ip), value); ++ if(type == WEBMON_DOMAIN) ++ { ++ add_queue_node(ip, value, recent_domains, domain_map, value_key, max_domain_queue_length ); ++ (recent_domains->first->time).tv_sec = time; ++ } ++ else if(type == WEBMON_SEARCH) ++ { ++ add_queue_node(ip, value, recent_searches, search_map, value_key, max_search_queue_length ); ++ (recent_searches->first->time).tv_sec = time; ++ } ++ } ++ } ++ ++ for(length=0; split[length] != NULL ; length++) ++ { ++ free(split[length]); ++ } ++ free(split); ++ free(line); ++ } ++ free(lines); ++ } ++ } ++ } ++ kfree(buffer); ++ spin_unlock_bh(&webmon_lock); ++ ++ ++ return 1; ++} ++static struct nf_sockopt_ops ipt_webmon_sockopts = ++{ ++ .pf = PF_INET, ++ .set_optmin = WEBMON_SET, ++ .set_optmax = WEBMON_SET+1, ++ .set = ipt_webmon_set_ctl, ++}; ++ ++ ++ ++ ++static bool match(const struct sk_buff *skb, struct xt_action_param *par) ++{ ++ ++ const struct ipt_webmon_info *info = (const struct ipt_webmon_info*)(par->matchinfo); ++ ++ ++ struct iphdr* iph; ++ ++ /* linearize skb if necessary */ ++ struct sk_buff *linear_skb; ++ int skb_copied; ++ if(skb_is_nonlinear(skb)) ++ { ++ linear_skb = skb_copy(skb, GFP_ATOMIC); ++ skb_copied = 1; ++ } ++ else ++ { ++ linear_skb = (struct sk_buff*)skb; ++ skb_copied = 0; ++ } ++ ++ ++ ++ /* ignore packets that are not TCP */ ++ iph = (struct iphdr*)(skb_network_header(skb)); ++ if(iph->protocol == IPPROTO_TCP) ++ { ++ /* get payload */ ++ struct tcphdr* tcp_hdr = (struct tcphdr*)( ((unsigned char*)iph) + (iph->ihl*4) ); ++ unsigned short payload_offset = (tcp_hdr->doff*4) + (iph->ihl*4); ++ unsigned char* payload = ((unsigned char*)iph) + payload_offset; ++ unsigned short payload_length = ntohs(iph->tot_len) - payload_offset; ++ ++ ++ ++ /* if payload length <= 10 bytes don't bother doing a check, otherwise check for match */ ++ if(payload_length > 10) ++ { ++ /* are we dealing with a web page request */ ++ if(strnicmp((char*)payload, "GET ", 4) == 0 || strnicmp( (char*)payload, "POST ", 5) == 0 || strnicmp((char*)payload, "HEAD ", 5) == 0) ++ { ++ char domain[650]; ++ char path[650]; ++ char domain_key[700]; ++ unsigned char save = info->exclude_type == WEBMON_EXCLUDE ? 1 : 0; ++ uint32_t ip_index; ++ ++ ++ for(ip_index = 0; ip_index < info->num_exclude_ips; ip_index++) ++ { ++ if( (info->exclude_ips)[ip_index] == iph->saddr ) ++ { ++ save = info->exclude_type == WEBMON_EXCLUDE ? 0 : 1; ++ } ++ } ++ for(ip_index=0; ip_index < info->num_exclude_ranges; ip_index++) ++ { ++ struct ipt_webmon_ip_range r = (info->exclude_ranges)[ip_index]; ++ if( (unsigned long)ntohl( r.start) <= (unsigned long)ntohl(iph->saddr) && (unsigned long)ntohl(r.end) >= (unsigned long)ntohl(iph->saddr) ) ++ { ++ save = info->exclude_type == WEBMON_EXCLUDE ? 0 : 1; ++ } ++ } ++ ++ ++ if(save) ++ { ++ extract_url(payload, payload_length, domain, path); ++ ++ ++ sprintf(domain_key, STRIP"@%s", NIPQUAD(iph->saddr), domain); ++ ++ if(strlen(domain) > 0) ++ { ++ char *search_part = NULL; ++ spin_lock_bh(&webmon_lock); ++ ++ ++ ++ if(get_string_map_element(domain_map, domain_key)) ++ { ++ //update time ++ update_queue_node_time( (queue_node*)get_map_element(domain_map, domain_key), recent_domains ); ++ } ++ else ++ { ++ //add ++ add_queue_node(iph->saddr, domain, recent_domains, domain_map, domain_key, max_domain_queue_length ); ++ } ++ ++ ++ /* printk("domain,path=\"%s\", \"%s\"\n", domain, path); */ ++ ++ if(strnistr(domain, "google.", 625) != NULL) ++ { ++ search_part = strstr(path, "&q="); ++ search_part = search_part == NULL ? strstr(path, "#q=") : search_part; ++ search_part = search_part == NULL ? strstr(path, "?q=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+3; ++ } ++ else if(strstr(domain, "bing.") != NULL) ++ { ++ search_part = strstr(path, "?q="); ++ search_part = search_part == NULL ? strstr(path, "&q=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+3; ++ } ++ else if(strstr(domain, "yahoo.") != NULL) ++ { ++ search_part = strstr(path, "?p="); ++ search_part = search_part == NULL ? strstr(path, "&p=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+3; ++ } ++ else if(strstr(domain, "lycos.") != NULL) ++ { ++ search_part = strstr(path, "&query="); ++ search_part = search_part == NULL ? strstr(path, "?query=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+7; ++ } ++ else if(strstr(domain, "altavista.") != NULL) ++ { ++ search_part = strstr(path, "&q="); ++ search_part = search_part == NULL ? strstr(path, "?q=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+3; ++ } ++ else if(strstr(domain, "duckduckgo.") != NULL) ++ { ++ search_part = strstr(path, "?q="); ++ search_part = search_part == NULL ? strstr(path, "&q=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+3; ++ } ++ else if(strstr(domain, "baidu.") != NULL) ++ { ++ search_part = strstr(path, "?wd="); ++ search_part = search_part == NULL ? strstr(path, "&wd=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+4; ++ } ++ else if(strstr(domain, "search.") != NULL) ++ { ++ search_part = strstr(path, "?q="); ++ search_part = search_part == NULL ? strstr(path, "&q=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+3; ++ } ++ else if(strstr(domain, "aol.") != NULL) ++ { ++ search_part = strstr(path, "&q="); ++ search_part = search_part == NULL ? strstr(path, "?q=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+3; ++ } ++ else if(strstr(domain, "ask.") != NULL) ++ { ++ search_part = strstr(path, "?q="); ++ search_part = search_part == NULL ? strstr(path, "&q=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+3; ++ } ++ else if(strstr(domain, "yandex.") != NULL) ++ { ++ search_part = strstr(path, "?text="); ++ search_part = search_part == NULL ? strstr(path, "&text=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+6; ++ } ++ else if(strstr(domain, "naver.") != NULL) ++ { ++ search_part = strstr(path, "&query="); ++ search_part = search_part == NULL ? strstr(path, "?query=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+7; ++ } ++ else if(strstr(domain, "daum.") != NULL) ++ { ++ search_part = strstr(path, "&q="); ++ search_part = search_part == NULL ? strstr(path, "?q=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+3; ++ } ++ else if(strstr(domain, "cuil.") != NULL) ++ { ++ search_part = strstr(path, "?q="); ++ search_part = search_part == NULL ? strstr(path, "&q=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+3; ++ } ++ else if(strstr(domain, "kosmix.") != NULL) ++ { ++ search_part = strstr(path, "/topic/"); ++ search_part = search_part == NULL ? search_part : search_part+7; ++ } ++ else if(strstr(domain, "yebol.") != NULL) ++ { ++ search_part = strstr(path, "?key="); ++ search_part = search_part == NULL ? strstr(path, "&key=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+5; ++ } ++ else if(strstr(domain, "sogou.") != NULL) ++ { ++ search_part = strstr(path, "&query="); ++ search_part = search_part == NULL ? strstr(path, "?query=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+7; ++ } ++ else if(strstr(domain, "youdao.") != NULL) ++ { ++ search_part = strstr(path, "?q="); ++ search_part = search_part == NULL ? strstr(path, "&q=") : search_part; ++ search_part = search_part == NULL ? search_part : search_part+3; ++ } ++ else if(strstr(domain, "metacrawler.") != NULL) ++ { ++ search_part = strstr(path, "/ws/results/Web/"); ++ search_part = search_part == NULL ? search_part : search_part+16; ++ } ++ else if(strstr(domain, "webcrawler.") != NULL) ++ { ++ search_part = strstr(path, "/ws/results/Web/"); ++ search_part = search_part == NULL ? search_part : search_part+16; ++ } ++ else if(strstr(domain, "thepiratebay.") != NULL) ++ { ++ search_part = strstr(path, "/search/"); ++ search_part = search_part == NULL ? search_part : search_part+8; ++ } ++ ++ ++ if(search_part != NULL) ++ { ++ int spi, si; ++ char search_key[700]; ++ char search[650]; ++ queue_node *recent_node = recent_searches->first; ++ ++ /*unescape, replacing whitespace with + */ ++ si = 0; ++ for(spi=0; search_part[spi] != '\0' && search_part[spi] != '&' && search_part[spi] != '/'; spi++) ++ { ++ int parsed_hex = 0; ++ if( search_part[spi] == '%') ++ { ++ if(search_part[spi+1] != '\0' && search_part[spi+1] != '&' && search_part[spi+1] != '/') ++ { ++ if(search_part[spi+2] != '\0' && search_part[spi+2] != '&' && search_part[spi+2] != '/') ++ { ++ char enc[3]; ++ int hex; ++ enc[0] = search_part[spi+1]; ++ enc[1] = search_part[spi+2]; ++ enc[2] = '\0'; ++ if(sscanf(enc, "%x", &hex) > 0) ++ { ++ parsed_hex = 1; ++ search[si] = hex == ' ' || hex == '\t' || hex == '\r' || hex == '\n' ? '+' : (char)hex; ++ spi = spi+2; ++ } ++ } ++ } ++ } ++ if(parsed_hex == 0) ++ { ++ search[si] = search_part[spi]; ++ } ++ si++; ++ } ++ search[si] = '\0'; ++ ++ ++ ++ sprintf(search_key, STRIP"@%s", NIPQUAD(iph->saddr), search); ++ ++ ++ /* Often times search engines will initiate a search as you type it in, but these intermediate queries aren't the real search query ++ * So, if the most recent query is a substring of the current one, discard it in favor of this one ++ */ ++ if(recent_node != NULL) ++ { ++ if(recent_node->src_ip == iph->saddr) ++ { ++ struct timeval t; ++ do_gettimeofday(&t); ++ if( (recent_node->time).tv_sec + 1 >= t.tv_sec || ((recent_node->time).tv_sec + 5 >= t.tv_sec && within_edit_distance(search, recent_node->value, 2))) ++ { ++ char recent_key[700]; ++ ++ sprintf(recent_key, STRIP"@%s", NIPQUAD(recent_node->src_ip), recent_node->value); ++ remove_map_element(search_map, recent_key); ++ ++ recent_searches->first = recent_node->next; ++ recent_searches->last = recent_searches->first == NULL ? NULL : recent_searches->last; ++ if(recent_searches->first != NULL) ++ { ++ recent_searches->first->previous = NULL; ++ } ++ recent_searches->length = recent_searches->length - 1 ; ++ free(recent_node->value); ++ free(recent_node); ++ } ++ } ++ } ++ ++ ++ ++ if(get_string_map_element(search_map, search_key)) ++ { ++ //update time ++ update_queue_node_time( (queue_node*)get_map_element(search_map, search_key), recent_searches ); ++ } ++ else ++ { ++ //add ++ add_queue_node(iph->saddr, search, recent_searches, search_map, search_key, max_search_queue_length ); ++ } ++ } ++ spin_unlock_bh(&webmon_lock); ++ } ++ } ++ } ++ else if ((unsigned short)ntohs(tcp_hdr->dest) == 443) // broad assumption that traffic on 443 is HTTPS. make effort to return fast as soon as we know we are wrong to not slow down processing ++ { ++ char domain[650]; ++ char domain_key[700]; ++ unsigned char save = info->exclude_type == WEBMON_EXCLUDE ? 1 : 0; ++ uint32_t ip_index; ++ ++ for(ip_index = 0; ip_index < info->num_exclude_ips; ip_index++) ++ { ++ if( (info->exclude_ips)[ip_index] == iph->saddr ) ++ { ++ save = info->exclude_type == WEBMON_EXCLUDE ? 0 : 1; ++ } ++ } ++ for(ip_index=0; ip_index < info->num_exclude_ranges; ip_index++) ++ { ++ struct ipt_webmon_ip_range r = (info->exclude_ranges)[ip_index]; ++ if( (unsigned long)ntohl( r.start) <= (unsigned long)ntohl(iph->saddr) && (unsigned long)ntohl(r.end) >= (unsigned long)ntohl(iph->saddr) ) ++ { ++ save = info->exclude_type == WEBMON_EXCLUDE ? 0 : 1; ++ } ++ } ++ ++ ++ if(save) ++ { ++ extract_url_https(payload, payload_length, domain); ++ ++ sprintf(domain_key, STRIP"@%s", NIPQUAD(iph->saddr), domain); ++ ++ if(strlen(domain) > 0) ++ { ++ spin_lock_bh(&webmon_lock); ++ ++ if(get_string_map_element(domain_map, domain_key)) ++ { ++ //update time ++ update_queue_node_time( (queue_node*)get_map_element(domain_map, domain_key), recent_domains ); ++ } ++ else ++ { ++ //add ++ add_queue_node(iph->saddr, domain, recent_domains, domain_map, domain_key, max_domain_queue_length ); ++ } ++ ++ spin_unlock_bh(&webmon_lock); ++ } ++ } ++ } ++ } ++ } ++ ++ /* free skb if we made a copy to linearize it */ ++ if(skb_copied == 1) ++ { ++ kfree_skb(linear_skb); ++ } ++ ++ ++ /* printk("returning %d from webmon\n\n\n", test); */ ++ return 0; ++} ++ ++ ++ ++static int checkentry(const struct xt_mtchk_param *par) ++{ ++ ++ struct ipt_webmon_info *info = (struct ipt_webmon_info*)(par->matchinfo); ++ ++ ++ spin_lock_bh(&webmon_lock); ++ if(info->ref_count == NULL) /* first instance, we're inserting rule */ ++ { ++ info->ref_count = (uint32_t*)kmalloc(sizeof(uint32_t), GFP_ATOMIC); ++ if(info->ref_count == NULL) /* deal with kmalloc failure */ ++ { ++ printk("ipt_webmon: kmalloc failure in checkentry!\n"); ++ return 0; ++ } ++ *(info->ref_count) = 1; ++ ++ ++ max_search_queue_length = info->max_searches; ++ max_domain_queue_length = info->max_domains; ++ ++ ++ } ++ else ++ { ++ *(info->ref_count) = *(info->ref_count) + 1; ++ } ++ spin_unlock_bh(&webmon_lock); ++ ++ return 0; ++} ++ ++static void destroy( const struct xt_mtdtor_param *par ) ++{ ++ struct ipt_webmon_info *info = (struct ipt_webmon_info*)(par->matchinfo); ++ ++ spin_lock_bh(&webmon_lock); ++ *(info->ref_count) = *(info->ref_count) - 1; ++ if(*(info->ref_count) == 0) ++ { ++ kfree(info->ref_count); ++ } ++ spin_unlock_bh(&webmon_lock); ++ ++} ++ ++static struct xt_match webmon_match __read_mostly = ++{ ++ ++ .name = "webmon", ++ .match = match, ++ .family = AF_INET, ++ .matchsize = sizeof(struct ipt_webmon_info), ++ .checkentry = checkentry, ++ .destroy = destroy, ++ .me = THIS_MODULE, ++}; ++ ++static int __init init(void) ++{ ++ ++ #ifdef CONFIG_PROC_FS ++ struct proc_dir_entry *proc_webmon_recent_domains; ++ struct proc_dir_entry *proc_webmon_recent_searches; ++ #endif ++ ++ spin_lock_bh(&webmon_lock); ++ ++ recent_domains = (queue*)malloc(sizeof(queue)); ++ recent_domains->first = NULL; ++ recent_domains->last = NULL; ++ recent_domains->length = 0; ++ domain_map = initialize_string_map(0); ++ ++ recent_searches = (queue*)malloc(sizeof(queue)); ++ recent_searches->first = NULL; ++ recent_searches->last = NULL; ++ recent_searches->length = 0; ++ search_map = initialize_string_map(0); ++ ++ ++ ++ #ifdef CONFIG_PROC_FS ++ proc_create("webmon_recent_domains", 0, NULL, &webmon_proc_domain_fops); ++ proc_create("webmon_recent_searches", 0, NULL, &webmon_proc_search_fops); ++ #endif ++ ++ if (nf_register_sockopt(&ipt_webmon_sockopts) < 0) ++ { ++ printk("ipt_webmon: Can't register sockopts. Aborting\n"); ++ spin_unlock_bh(&webmon_lock); ++ return -1; ++ } ++ spin_unlock_bh(&webmon_lock); ++ ++ return xt_register_match(&webmon_match); ++} ++ ++static void __exit fini(void) ++{ ++ ++ unsigned long num_destroyed; ++ ++ spin_lock_bh(&webmon_lock); ++ ++ ++ #ifdef CONFIG_PROC_FS ++ remove_proc_entry("webmon_recent_domains", NULL); ++ remove_proc_entry("webmon_recent_searches", NULL); ++ #endif ++ nf_unregister_sockopt(&ipt_webmon_sockopts); ++ xt_unregister_match(&webmon_match); ++ destroy_map(domain_map, DESTROY_MODE_IGNORE_VALUES, &num_destroyed); ++ destroy_map(search_map, DESTROY_MODE_IGNORE_VALUES, &num_destroyed); ++ destroy_queue(recent_domains); ++ destroy_queue(recent_searches); ++ ++ spin_unlock_bh(&webmon_lock); ++ ++ ++} ++ ++module_init(init); ++module_exit(fini); + +--- /dev/null ++++ b/net/ipv4/netfilter/ipt_weburl.c +@@ -0,0 +1,537 @@ ++/* weburl -- A netfilter module to match URLs in HTTP(S) requests ++ * This module can match using string match or regular expressions ++ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) ++ * ++ * ++ * Copyright © 2008-2010 by Eric Bishop ++ * ++ * This file is free software: you may copy, redistribute and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation, either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This file is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "weburl_deps/regexp.c" ++#include "weburl_deps/tree_map.h" ++ ++ ++#include ++ ++ ++#include ++ ++ ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Eric Bishop"); ++MODULE_DESCRIPTION("Match URL in HTTP(S) requests, designed for use with Gargoyle web interface (www.gargoyle-router.com)"); ++ ++string_map* compiled_map = NULL; ++ ++int strnicmp(const char * cs,const char * ct,size_t count) ++{ ++ register signed char __res = 0; ++ ++ while (count) ++ { ++ if ((__res = toupper( *cs ) - toupper( *ct++ ) ) != 0 || !*cs++) ++ { ++ break; ++ } ++ count--; ++ } ++ return __res; ++} ++ ++char *strnistr(const char *s, const char *find, size_t slen) ++{ ++ char c, sc; ++ size_t len; ++ ++ ++ if ((c = *find++) != '\0') ++ { ++ len = strlen(find); ++ do ++ { ++ do ++ { ++ if (slen < 1 || (sc = *s) == '\0') ++ { ++ return (NULL); ++ } ++ --slen; ++ ++s; ++ } ++ while ( toupper(sc) != toupper(c)); ++ ++ if (len > slen) ++ { ++ return (NULL); ++ } ++ } ++ while (strnicmp(s, find, len) != 0); ++ ++ s--; ++ } ++ return ((char *)s); ++} ++ ++ ++int do_match_test(unsigned char match_type, const char* reference, char* query) ++{ ++ int matches = 0; ++ struct regexp* r; ++ switch(match_type) ++ { ++ case WEBURL_CONTAINS_TYPE: ++ matches = (strstr(query, reference) != NULL); ++ break; ++ case WEBURL_REGEX_TYPE: ++ ++ if(compiled_map == NULL) ++ { ++ compiled_map = initialize_map(0); ++ if(compiled_map == NULL) /* test for malloc failure */ ++ { ++ return 0; ++ } ++ } ++ r = (struct regexp*)get_map_element(compiled_map, reference); ++ if(r == NULL) ++ { ++ int rlen = strlen(reference); ++ r= regcomp((char*)reference, &rlen); ++ if(r == NULL) /* test for malloc failure */ ++ { ++ return 0; ++ } ++ set_map_element(compiled_map, reference, (void*)r); ++ } ++ matches = regexec(r, query); ++ break; ++ case WEBURL_EXACT_TYPE: ++ matches = (strstr(query, reference) != NULL) && strlen(query) == strlen(reference); ++ break; ++ } ++ return matches; ++} ++ ++int http_match(const struct ipt_weburl_info* info, const unsigned char* packet_data, int packet_length) ++{ ++ int test = 0; ++ ++ /* printk("found a http web page request\n"); */ ++ char path[625] = ""; ++ char host[625] = ""; ++ int path_start_index; ++ int path_end_index; ++ int last_header_index; ++ char last_two_buf[2]; ++ int end_found; ++ char* host_match; ++ char* test_prefixes[6]; ++ int prefix_index; ++ ++ /* get path portion of URL */ ++ path_start_index = (int)(strstr((char*)packet_data, " ") - (char*)packet_data); ++ while( packet_data[path_start_index] == ' ') ++ { ++ path_start_index++; ++ } ++ path_end_index= (int)(strstr( (char*)(packet_data+path_start_index), " ") - (char*)packet_data); ++ if(path_end_index > 0) ++ { ++ int path_length = path_end_index-path_start_index; ++ path_length = path_length < 625 ? path_length : 624; /* prevent overflow */ ++ memcpy(path, packet_data+path_start_index, path_length); ++ path[ path_length] = '\0'; ++ } ++ ++ /* get header length */ ++ last_header_index = 2; ++ memcpy(last_two_buf,(char*)packet_data, 2); ++ end_found = 0; ++ while(end_found == 0 && last_header_index < packet_length) ++ { ++ char next = (char)packet_data[last_header_index]; ++ if(next == '\n') ++ { ++ end_found = last_two_buf[1] == '\n' || (last_two_buf[0] == '\n' && last_two_buf[1] == '\r') ? 1 : 0; ++ } ++ if(end_found == 0) ++ { ++ last_two_buf[0] = last_two_buf[1]; ++ last_two_buf[1] = next; ++ last_header_index++; ++ } ++ } ++ ++ /* get host portion of URL */ ++ host_match = strnistr( (char*)packet_data, "Host:", last_header_index); ++ if(host_match != NULL) ++ { ++ int host_end_index; ++ host_match = host_match + 5; /* character after "Host:" */ ++ while(host_match[0] == ' ') ++ { ++ host_match = host_match+1; ++ } ++ ++ host_end_index = 0; ++ while( host_match[host_end_index] != '\n' && ++ host_match[host_end_index] != '\r' && ++ host_match[host_end_index] != ' ' && ++ host_match[host_end_index] != ':' && ++ ((char*)host_match - (char*)packet_data)+host_end_index < last_header_index ++ ) ++ { ++ host_end_index++; ++ } ++ memcpy(host, host_match, host_end_index); ++ host_end_index = host_end_index < 625 ? host_end_index : 624; /* prevent overflow */ ++ host[host_end_index] = '\0'; ++ ++ ++ } ++ ++ /* printk("host = \"%s\", path =\"%s\"\n", host, path); */ ++ ++ ++ switch(info->match_part) ++ { ++ case WEBURL_DOMAIN_PART: ++ test = do_match_test(info->match_type, info->test_str, host); ++ if(!test && strstr(host, "www.") == host) ++ { ++ test = do_match_test(info->match_type, info->test_str, ((char*)host+4) ); ++ } ++ break; ++ case WEBURL_PATH_PART: ++ test = do_match_test(info->match_type, info->test_str, path); ++ if( !test && path[0] == '/' ) ++ { ++ test = do_match_test(info->match_type, info->test_str, ((char*)path+1) ); ++ } ++ break; ++ case WEBURL_ALL_PART: ++ ++ test_prefixes[0] = "http://"; ++ test_prefixes[1] = ""; ++ test_prefixes[2] = NULL; ++ ++ ++ for(prefix_index=0; test_prefixes[prefix_index] != NULL && test == 0; prefix_index++) ++ { ++ char test_url[1250]; ++ test_url[0] = '\0'; ++ strcat(test_url, test_prefixes[prefix_index]); ++ strcat(test_url, host); ++ if(strcmp(path, "/") != 0) ++ { ++ strcat(test_url, path); ++ } ++ test = do_match_test(info->match_type, info->test_str, test_url); ++ if(!test && strcmp(path, "/") == 0) ++ { ++ strcat(test_url, path); ++ test = do_match_test(info->match_type, info->test_str, test_url); ++ } ++ ++ /* printk("test_url = \"%s\", test=%d\n", test_url, test); */ ++ } ++ if(!test && strstr(host, "www.") == host) ++ { ++ char* www_host = ((char*)host+4); ++ for(prefix_index=0; test_prefixes[prefix_index] != NULL && test == 0; prefix_index++) ++ { ++ char test_url[1250]; ++ test_url[0] = '\0'; ++ strcat(test_url, test_prefixes[prefix_index]); ++ strcat(test_url, www_host); ++ if(strcmp(path, "/") != 0) ++ { ++ strcat(test_url, path); ++ } ++ test = do_match_test(info->match_type, info->test_str, test_url); ++ if(!test && strcmp(path, "/") == 0) ++ { ++ strcat(test_url, path); ++ test = do_match_test(info->match_type, info->test_str, test_url); ++ } ++ ++ /* printk("test_url = \"%s\", test=%d\n", test_url, test); */ ++ } ++ } ++ break; ++ ++ } ++ ++ ++ /* ++ * If invert flag is set, return true if it didn't match ++ */ ++ test = info->invert ? !test : test; ++ ++ return test; ++} ++ ++int https_match(const struct ipt_weburl_info* info, const unsigned char* packet_data, int packet_length) ++{ ++ int test = 0; ++ ++ /* printk("found a https web page request\n"); */ ++ char host[625] = ""; ++ char* test_prefixes[6]; ++ int prefix_index, x, packet_limit; ++ unsigned short cslen, ext_type, ext_len, maxextlen; ++ unsigned char conttype, hndshktype, sidlen, cmplen; ++ unsigned char* packet_ptr; ++ ++ host[0] = '\0'; ++ packet_ptr = packet_data; ++ ++ if (packet_length < 43) ++ { ++ /*printk("Packet less than 43 bytes, exiting\n");*/ ++ return test; ++ } ++ conttype = packet_data[0]; ++ hndshktype = packet_data[5]; ++ sidlen = packet_data[43]; ++ /*printk("conttype=%d, hndshktype=%d, sidlen=%d ",conttype,hndshktype,sidlen);*/ ++ if(conttype != 22) ++ { ++ /*printk("conttype not 22, exiting\n");*/ ++ return test; ++ } ++ if(hndshktype != 1) ++ { ++ /*printk("hndshktype not 1, exiting\n");*/ ++ return test; //We aren't in a Client Hello ++ } ++ ++ packet_ptr = packet_data + 1 + 43 + sidlen; //Skip to Cipher Suites Length ++ cslen = ntohs(*(unsigned short*)packet_ptr); //Length of Cipher Suites (2 byte) ++ packet_ptr = packet_ptr + 2 + cslen; //Skip to Compression Methods ++ cmplen = *packet_ptr; //Length of Compression Methods (1 byte) ++ packet_ptr = packet_ptr + 1 + cmplen; //Skip to Extensions Length **IMPORTANT** ++ maxextlen = ntohs(*(unsigned short*)packet_ptr); //Length of extensions (2 byte) ++ packet_ptr = packet_ptr + 2; //Skip to beginning of first extension and start looping ++ ext_type = 1; ++ /*printk("cslen=%d, cmplen=%d, maxextlen=%d, pktlen=%d,ptrpos=%d\n",cslen,cmplen,maxextlen,packet_length,packet_ptr - packet_data);*/ ++ //Limit the pointer bounds to the smaller of either the extensions length or the packet length ++ packet_limit = ((packet_ptr - packet_data) + maxextlen) < packet_length ? ((packet_ptr - packet_data) + maxextlen) : packet_length; ++ ++ //Extension Type and Extension Length are both 2 byte. SNI Extension is "0" ++ while(((packet_ptr - packet_data) < packet_limit) && (ext_type != 0)) ++ { ++ ext_type = ntohs(*(unsigned short*)packet_ptr); ++ packet_ptr = packet_ptr + 2; ++ ext_len = ntohs(*(unsigned short*)packet_ptr); ++ packet_ptr = packet_ptr + 2; ++ /*printk("ext_type=%d, ext_len=%d\n",ext_type,ext_len);*/ ++ if(ext_type == 0) ++ { ++ unsigned short snilen; ++ /*printk("FOUND SNI EXT\n");*/ ++ packet_ptr = packet_ptr + 3; //Skip to length of SNI ++ snilen = ntohs(*(unsigned short*)packet_ptr); ++ /*printk("snilen=%d\n",snilen);*/ ++ packet_ptr = packet_ptr + 2; //Skip to beginning of SNI ++ if((((packet_ptr - packet_data) + snilen) < packet_limit) && (snilen > 0)) ++ { ++ /*printk("FOUND SNI\n");*/ ++ snilen = snilen < 625 ? snilen : 624; // prevent overflow ++ memcpy(host, packet_ptr, snilen); ++ host[snilen] = '\0'; ++ for(x=0; host[x] != '\0'; x++) ++ { ++ host[x] = (char)tolower(host[x]); ++ } ++ /*printk("sni=%s\n",host);*/ ++ } ++ } ++ else ++ { ++ packet_ptr = packet_ptr + ext_len; ++ } ++ } ++ ++ /* printk("host = \"%s\"\n", host); */ ++ ++ switch(info->match_part) ++ { ++ case WEBURL_DOMAIN_PART: ++ test = do_match_test(info->match_type, info->test_str, host); ++ if(!test && strstr(host, "www.") == host) ++ { ++ test = do_match_test(info->match_type, info->test_str, ((char*)host+4) ); ++ } ++ break; ++ case WEBURL_PATH_PART: ++ test = 0; //we will never have a Path for HTTPS ++ break; ++ case WEBURL_ALL_PART: ++ test_prefixes[0] = "https://"; ++ test_prefixes[1] = ""; ++ test_prefixes[2] = NULL; ++ ++ for(prefix_index=0; test_prefixes[prefix_index] != NULL && test == 0; prefix_index++) ++ { ++ char test_url[1250]; ++ test_url[0] = '\0'; ++ strcat(test_url, test_prefixes[prefix_index]); ++ strcat(test_url, host); ++ ++ test = do_match_test(info->match_type, info->test_str, test_url); ++ ++ /* printk("test_url = \"%s\", test=%d\n", test_url, test); */ ++ } ++ if(!test && strstr(host, "www.") == host) ++ { ++ char* www_host = ((char*)host+4); ++ for(prefix_index=0; test_prefixes[prefix_index] != NULL && test == 0; prefix_index++) ++ { ++ char test_url[1250]; ++ test_url[0] = '\0'; ++ strcat(test_url, test_prefixes[prefix_index]); ++ strcat(test_url, www_host); ++ ++ test = do_match_test(info->match_type, info->test_str, test_url); ++ ++ /* printk("test_url = \"%s\", test=%d\n", test_url, test); */ ++ } ++ } ++ break; ++ } ++ ++ /* ++ * If invert flag is set, return true if it didn't match ++ */ ++ test = info->invert ? !test : test; ++ ++ return test; ++} ++ ++ ++static bool match(const struct sk_buff *skb, struct xt_action_param *par) ++{ ++ ++ const struct ipt_weburl_info *info = (const struct ipt_weburl_info*)(par->matchinfo); ++ ++ ++ int test = 0; ++ struct iphdr* iph; ++ ++ /* linearize skb if necessary */ ++ struct sk_buff *linear_skb; ++ int skb_copied; ++ if(skb_is_nonlinear(skb)) ++ { ++ linear_skb = skb_copy(skb, GFP_ATOMIC); ++ skb_copied = 1; ++ } ++ else ++ { ++ linear_skb = (struct sk_buff*)skb; ++ skb_copied = 0; ++ } ++ ++ ++ ++ /* ignore packets that are not TCP */ ++ iph = (struct iphdr*)(skb_network_header(skb)); ++ if(iph->protocol == IPPROTO_TCP) ++ { ++ /* get payload */ ++ struct tcphdr* tcp_hdr = (struct tcphdr*)( ((unsigned char*)iph) + (iph->ihl*4) ); ++ unsigned short payload_offset = (tcp_hdr->doff*4) + (iph->ihl*4); ++ unsigned char* payload = ((unsigned char*)iph) + payload_offset; ++ unsigned short payload_length = ntohs(iph->tot_len) - payload_offset; ++ ++ ++ ++ /* if payload length <= 10 bytes don't bother doing a check, otherwise check for match */ ++ if(payload_length > 10) ++ { ++ if(strnicmp((char*)payload, "GET ", 4) == 0 || strnicmp( (char*)payload, "POST ", 5) == 0 || strnicmp((char*)payload, "HEAD ", 5) == 0) ++ { ++ test = http_match(info, payload, payload_length); ++ } ++ else if ((unsigned short)ntohs(tcp_hdr->dest) == 443) ++ { ++ test = https_match(info, payload, payload_length); ++ } ++ } ++ } ++ ++ /* free skb if we made a copy to linearize it */ ++ if(skb_copied == 1) ++ { ++ kfree_skb(linear_skb); ++ } ++ ++ ++ /* printk("returning %d from weburl\n\n\n", test); */ ++ return test; ++} ++ ++ ++static int checkentry(const struct xt_mtchk_param *par) ++{ ++ return 0; ++} ++ ++ ++static struct xt_match weburl_match __read_mostly = ++{ ++ .name = "weburl", ++ .match = match, ++ .family = AF_INET, ++ .matchsize = sizeof(struct ipt_weburl_info), ++ .checkentry = checkentry, ++ .me = THIS_MODULE, ++}; ++ ++static int __init init(void) ++{ ++ compiled_map = NULL; ++ return xt_register_match(&weburl_match); ++ ++} ++ ++static void __exit fini(void) ++{ ++ xt_unregister_match(&weburl_match); ++ if(compiled_map != NULL) ++ { ++ unsigned long num_destroyed; ++ destroy_map(compiled_map, DESTROY_MODE_FREE_VALUES, &num_destroyed); ++ } ++} ++ ++module_init(init); ++module_exit(fini); + +--- /dev/null ++++ b/net/ipv4/netfilter/ipt_weburl.mod.c +@@ -0,0 +1,20 @@ ++#include ++#include ++#include ++ ++MODULE_INFO(vermagic, VERMAGIC_STRING); ++ ++struct module __this_module ++__attribute__((section(".gnu.linkonce.this_module"))) = { ++ .name = KBUILD_MODNAME, ++ .init = init_module, ++#ifdef CONFIG_MODULE_UNLOAD ++ .exit = cleanup_module, ++#endif ++}; ++ ++static const char __module_depends[] ++__attribute_used__ ++__attribute__((section(".modinfo"))) = ++"depends="; ++ +--- /dev/null ++++ b/net/ipv4/netfilter/regexp/regexp.c +@@ -0,0 +1,1197 @@ ++/* ++ * regcomp and regexec -- regsub and regerror are elsewhere ++ * @(#)regexp.c 1.3 of 18 April 87 ++ * ++ * Copyright (c) 1986 by University of Toronto. ++ * Written by Henry Spencer. Not derived from licensed software. ++ * ++ * Permission is granted to anyone to use this software for any ++ * purpose on any computer system, and to redistribute it freely, ++ * subject to the following restrictions: ++ * ++ * 1. The author is not responsible for the consequences of use of ++ * this software, no matter how awful, even if they arise ++ * from defects in it. ++ * ++ * 2. The origin of this software must not be misrepresented, either ++ * by explicit claim or by omission. ++ * ++ * 3. Altered versions must be plainly marked as such, and must not ++ * be misrepresented as being the original software. ++ * ++ * Beware that some of this code is subtly aware of the way operator ++ * precedence is structured in regular expressions. Serious changes in ++ * regular-expression syntax might require a total rethink. ++ * ++ * This code was modified by Ethan Sommer to work within the kernel ++ * (it now uses kmalloc etc..) ++ * ++ * Modified slightly by Matthew Strait to use more modern C. ++ */ ++ ++#include "regexp.h" ++#include "regmagic.h" ++ ++/* added by ethan and matt. Lets it work in both kernel and user space. ++(So iptables can use it, for instance.) Yea, it goes both ways... */ ++#if __KERNEL__ ++ #define malloc(foo) kmalloc(foo,GFP_ATOMIC) ++#else ++ #define printk(format,args...) printf(format,##args) ++#endif ++ ++void regerror(char * s) ++{ ++ printk("<3>Regexp: %s\n", s); ++ /* NOTREACHED */ ++} ++ ++/* ++ * The "internal use only" fields in regexp.h are present to pass info from ++ * compile to execute that permits the execute phase to run lots faster on ++ * simple cases. They are: ++ * ++ * regstart char that must begin a match; '\0' if none obvious ++ * reganch is the match anchored (at beginning-of-line only)? ++ * regmust string (pointer into program) that match must include, or NULL ++ * regmlen length of regmust string ++ * ++ * Regstart and reganch permit very fast decisions on suitable starting points ++ * for a match, cutting down the work a lot. Regmust permits fast rejection ++ * of lines that cannot possibly match. The regmust tests are costly enough ++ * that regcomp() supplies a regmust only if the r.e. contains something ++ * potentially expensive (at present, the only such thing detected is * or + ++ * at the start of the r.e., which can involve a lot of backup). Regmlen is ++ * supplied because the test in regexec() needs it and regcomp() is computing ++ * it anyway. ++ */ ++ ++/* ++ * Structure for regexp "program". This is essentially a linear encoding ++ * of a nondeterministic finite-state machine (aka syntax charts or ++ * "railroad normal form" in parsing technology). Each node is an opcode ++ * plus a "next" pointer, possibly plus an operand. "Next" pointers of ++ * all nodes except BRANCH implement concatenation; a "next" pointer with ++ * a BRANCH on both ends of it is connecting two alternatives. (Here we ++ * have one of the subtle syntax dependencies: an individual BRANCH (as ++ * opposed to a collection of them) is never concatenated with anything ++ * because of operator precedence.) The operand of some types of node is ++ * a literal string; for others, it is a node leading into a sub-FSM. In ++ * particular, the operand of a BRANCH node is the first node of the branch. ++ * (NB this is *not* a tree structure: the tail of the branch connects ++ * to the thing following the set of BRANCHes.) The opcodes are: ++ */ ++ ++/* definition number opnd? meaning */ ++#define END 0 /* no End of program. */ ++#define BOL 1 /* no Match "" at beginning of line. */ ++#define EOL 2 /* no Match "" at end of line. */ ++#define ANY 3 /* no Match any one character. */ ++#define ANYOF 4 /* str Match any character in this string. */ ++#define ANYBUT 5 /* str Match any character not in this string. */ ++#define BRANCH 6 /* node Match this alternative, or the next... */ ++#define BACK 7 /* no Match "", "next" ptr points backward. */ ++#define EXACTLY 8 /* str Match this string. */ ++#define NOTHING 9 /* no Match empty string. */ ++#define STAR 10 /* node Match this (simple) thing 0 or more times. */ ++#define PLUS 11 /* node Match this (simple) thing 1 or more times. */ ++#define OPEN 20 /* no Mark this point in input as start of #n. */ ++ /* OPEN+1 is number 1, etc. */ ++#define CLOSE 30 /* no Analogous to OPEN. */ ++ ++/* ++ * Opcode notes: ++ * ++ * BRANCH The set of branches constituting a single choice are hooked ++ * together with their "next" pointers, since precedence prevents ++ * anything being concatenated to any individual branch. The ++ * "next" pointer of the last BRANCH in a choice points to the ++ * thing following the whole choice. This is also where the ++ * final "next" pointer of each individual branch points; each ++ * branch starts with the operand node of a BRANCH node. ++ * ++ * BACK Normal "next" pointers all implicitly point forward; BACK ++ * exists to make loop structures possible. ++ * ++ * STAR,PLUS '?', and complex '*' and '+', are implemented as circular ++ * BRANCH structures using BACK. Simple cases (one character ++ * per match) are implemented with STAR and PLUS for speed ++ * and to minimize recursive plunges. ++ * ++ * OPEN,CLOSE ...are numbered at compile time. ++ */ ++ ++/* ++ * A node is one char of opcode followed by two chars of "next" pointer. ++ * "Next" pointers are stored as two 8-bit pieces, high order first. The ++ * value is a positive offset from the opcode of the node containing it. ++ * An operand, if any, simply follows the node. (Note that much of the ++ * code generation knows about this implicit relationship.) ++ * ++ * Using two bytes for the "next" pointer is vast overkill for most things, ++ * but allows patterns to get big without disasters. ++ */ ++#define OP(p) (*(p)) ++#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377)) ++#define OPERAND(p) ((p) + 3) ++ ++/* ++ * See regmagic.h for one further detail of program structure. ++ */ ++ ++ ++/* ++ * Utility definitions. ++ */ ++#ifndef CHARBITS ++#define UCHARAT(p) ((int)*(unsigned char *)(p)) ++#else ++#define UCHARAT(p) ((int)*(p)&CHARBITS) ++#endif ++ ++#define FAIL(m) { regerror(m); return(NULL); } ++#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?') ++#define META "^$.[()|?+*\\" ++ ++/* ++ * Flags to be passed up and down. ++ */ ++#define HASWIDTH 01 /* Known never to match null string. */ ++#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */ ++#define SPSTART 04 /* Starts with * or +. */ ++#define WORST 0 /* Worst case. */ ++ ++/* ++ * Global work variables for regcomp(). ++ */ ++struct match_globals { ++char *reginput; /* String-input pointer. */ ++char *regbol; /* Beginning of input, for ^ check. */ ++char **regstartp; /* Pointer to startp array. */ ++char **regendp; /* Ditto for endp. */ ++char *regparse; /* Input-scan pointer. */ ++int regnpar; /* () count. */ ++char regdummy; ++char *regcode; /* Code-emit pointer; ®dummy = don't. */ ++long regsize; /* Code size. */ ++}; ++ ++/* ++ * Forward declarations for regcomp()'s friends. ++ */ ++#ifndef STATIC ++#define STATIC static ++#endif ++STATIC char *reg(struct match_globals *g, int paren,int *flagp); ++STATIC char *regbranch(struct match_globals *g, int *flagp); ++STATIC char *regpiece(struct match_globals *g, int *flagp); ++STATIC char *regatom(struct match_globals *g, int *flagp); ++STATIC char *regnode(struct match_globals *g, char op); ++STATIC char *regnext(struct match_globals *g, char *p); ++STATIC void regc(struct match_globals *g, char b); ++STATIC void reginsert(struct match_globals *g, char op, char *opnd); ++STATIC void regtail(struct match_globals *g, char *p, char *val); ++STATIC void regoptail(struct match_globals *g, char *p, char *val); ++ ++ ++__kernel_size_t my_strcspn(const char *s1,const char *s2) ++{ ++ char *scan1; ++ char *scan2; ++ int count; ++ ++ count = 0; ++ for (scan1 = (char *)s1; *scan1 != '\0'; scan1++) { ++ for (scan2 = (char *)s2; *scan2 != '\0';) /* ++ moved down. */ ++ if (*scan1 == *scan2++) ++ return(count); ++ count++; ++ } ++ return(count); ++} ++ ++/* ++ - regcomp - compile a regular expression into internal code ++ * ++ * We can't allocate space until we know how big the compiled form will be, ++ * but we can't compile it (and thus know how big it is) until we've got a ++ * place to put the code. So we cheat: we compile it twice, once with code ++ * generation turned off and size counting turned on, and once "for real". ++ * This also means that we don't allocate space until we are sure that the ++ * thing really will compile successfully, and we never have to move the ++ * code and thus invalidate pointers into it. (Note that it has to be in ++ * one piece because free() must be able to free it all.) ++ * ++ * Beware that the optimization-preparation code in here knows about some ++ * of the structure of the compiled regexp. ++ */ ++regexp * ++regcomp(char *exp,int *patternsize) ++{ ++ register regexp *r; ++ register char *scan; ++ register char *longest; ++ register int len; ++ int flags; ++ struct match_globals g; ++ ++ /* commented out by ethan ++ extern char *malloc(); ++ */ ++ ++ if (exp == NULL) ++ FAIL("NULL argument"); ++ ++ /* First pass: determine size, legality. */ ++ g.regparse = exp; ++ g.regnpar = 1; ++ g.regsize = 0L; ++ g.regcode = &g.regdummy; ++ regc(&g, MAGIC); ++ if (reg(&g, 0, &flags) == NULL) ++ return(NULL); ++ ++ /* Small enough for pointer-storage convention? */ ++ if (g.regsize >= 32767L) /* Probably could be 65535L. */ ++ FAIL("regexp too big"); ++ ++ /* Allocate space. */ ++ *patternsize=sizeof(regexp) + (unsigned)g.regsize; ++ r = (regexp *)malloc(sizeof(regexp) + (unsigned)g.regsize); ++ if (r == NULL) ++ FAIL("out of space"); ++ ++ /* Second pass: emit code. */ ++ g.regparse = exp; ++ g.regnpar = 1; ++ g.regcode = r->program; ++ regc(&g, MAGIC); ++ if (reg(&g, 0, &flags) == NULL) ++ return(NULL); ++ ++ /* Dig out information for optimizations. */ ++ r->regstart = '\0'; /* Worst-case defaults. */ ++ r->reganch = 0; ++ r->regmust = NULL; ++ r->regmlen = 0; ++ scan = r->program+1; /* First BRANCH. */ ++ if (OP(regnext(&g, scan)) == END) { /* Only one top-level choice. */ ++ scan = OPERAND(scan); ++ ++ /* Starting-point info. */ ++ if (OP(scan) == EXACTLY) ++ r->regstart = *OPERAND(scan); ++ else if (OP(scan) == BOL) ++ r->reganch++; ++ ++ /* ++ * If there's something expensive in the r.e., find the ++ * longest literal string that must appear and make it the ++ * regmust. Resolve ties in favor of later strings, since ++ * the regstart check works with the beginning of the r.e. ++ * and avoiding duplication strengthens checking. Not a ++ * strong reason, but sufficient in the absence of others. ++ */ ++ if (flags&SPSTART) { ++ longest = NULL; ++ len = 0; ++ for (; scan != NULL; scan = regnext(&g, scan)) ++ if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { ++ longest = OPERAND(scan); ++ len = strlen(OPERAND(scan)); ++ } ++ r->regmust = longest; ++ r->regmlen = len; ++ } ++ } ++ ++ return(r); ++} ++ ++/* ++ - reg - regular expression, i.e. main body or parenthesized thing ++ * ++ * Caller must absorb opening parenthesis. ++ * ++ * Combining parenthesis handling with the base level of regular expression ++ * is a trifle forced, but the need to tie the tails of the branches to what ++ * follows makes it hard to avoid. ++ */ ++static char * ++reg(struct match_globals *g, int paren, int *flagp /* Parenthesized? */ ) ++{ ++ register char *ret; ++ register char *br; ++ register char *ender; ++ register int parno = 0; /* 0 makes gcc happy */ ++ int flags; ++ ++ *flagp = HASWIDTH; /* Tentatively. */ ++ ++ /* Make an OPEN node, if parenthesized. */ ++ if (paren) { ++ if (g->regnpar >= NSUBEXP) ++ FAIL("too many ()"); ++ parno = g->regnpar; ++ g->regnpar++; ++ ret = regnode(g, OPEN+parno); ++ } else ++ ret = NULL; ++ ++ /* Pick up the branches, linking them together. */ ++ br = regbranch(g, &flags); ++ if (br == NULL) ++ return(NULL); ++ if (ret != NULL) ++ regtail(g, ret, br); /* OPEN -> first. */ ++ else ++ ret = br; ++ if (!(flags&HASWIDTH)) ++ *flagp &= ~HASWIDTH; ++ *flagp |= flags&SPSTART; ++ while (*g->regparse == '|') { ++ g->regparse++; ++ br = regbranch(g, &flags); ++ if (br == NULL) ++ return(NULL); ++ regtail(g, ret, br); /* BRANCH -> BRANCH. */ ++ if (!(flags&HASWIDTH)) ++ *flagp &= ~HASWIDTH; ++ *flagp |= flags&SPSTART; ++ } ++ ++ /* Make a closing node, and hook it on the end. */ ++ ender = regnode(g, (paren) ? CLOSE+parno : END); ++ regtail(g, ret, ender); ++ ++ /* Hook the tails of the branches to the closing node. */ ++ for (br = ret; br != NULL; br = regnext(g, br)) ++ regoptail(g, br, ender); ++ ++ /* Check for proper termination. */ ++ if (paren && *g->regparse++ != ')') { ++ FAIL("unmatched ()"); ++ } else if (!paren && *g->regparse != '\0') { ++ if (*g->regparse == ')') { ++ FAIL("unmatched ()"); ++ } else ++ FAIL("junk on end"); /* "Can't happen". */ ++ /* NOTREACHED */ ++ } ++ ++ return(ret); ++} ++ ++/* ++ - regbranch - one alternative of an | operator ++ * ++ * Implements the concatenation operator. ++ */ ++static char * ++regbranch(struct match_globals *g, int *flagp) ++{ ++ register char *ret; ++ register char *chain; ++ register char *latest; ++ int flags; ++ ++ *flagp = WORST; /* Tentatively. */ ++ ++ ret = regnode(g, BRANCH); ++ chain = NULL; ++ while (*g->regparse != '\0' && *g->regparse != '|' && *g->regparse != ')') { ++ latest = regpiece(g, &flags); ++ if (latest == NULL) ++ return(NULL); ++ *flagp |= flags&HASWIDTH; ++ if (chain == NULL) /* First piece. */ ++ *flagp |= flags&SPSTART; ++ else ++ regtail(g, chain, latest); ++ chain = latest; ++ } ++ if (chain == NULL) /* Loop ran zero times. */ ++ (void) regnode(g, NOTHING); ++ ++ return(ret); ++} ++ ++/* ++ - regpiece - something followed by possible [*+?] ++ * ++ * Note that the branching code sequences used for ? and the general cases ++ * of * and + are somewhat optimized: they use the same NOTHING node as ++ * both the endmarker for their branch list and the body of the last branch. ++ * It might seem that this node could be dispensed with entirely, but the ++ * endmarker role is not redundant. ++ */ ++static char * ++regpiece(struct match_globals *g, int *flagp) ++{ ++ register char *ret; ++ register char op; ++ register char *next; ++ int flags; ++ ++ ret = regatom(g, &flags); ++ if (ret == NULL) ++ return(NULL); ++ ++ op = *g->regparse; ++ if (!ISMULT(op)) { ++ *flagp = flags; ++ return(ret); ++ } ++ ++ if (!(flags&HASWIDTH) && op != '?') ++ FAIL("*+ operand could be empty"); ++ *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH); ++ ++ if (op == '*' && (flags&SIMPLE)) ++ reginsert(g, STAR, ret); ++ else if (op == '*') { ++ /* Emit x* as (x&|), where & means "self". */ ++ reginsert(g, BRANCH, ret); /* Either x */ ++ regoptail(g, ret, regnode(g, BACK)); /* and loop */ ++ regoptail(g, ret, ret); /* back */ ++ regtail(g, ret, regnode(g, BRANCH)); /* or */ ++ regtail(g, ret, regnode(g, NOTHING)); /* null. */ ++ } else if (op == '+' && (flags&SIMPLE)) ++ reginsert(g, PLUS, ret); ++ else if (op == '+') { ++ /* Emit x+ as x(&|), where & means "self". */ ++ next = regnode(g, BRANCH); /* Either */ ++ regtail(g, ret, next); ++ regtail(g, regnode(g, BACK), ret); /* loop back */ ++ regtail(g, next, regnode(g, BRANCH)); /* or */ ++ regtail(g, ret, regnode(g, NOTHING)); /* null. */ ++ } else if (op == '?') { ++ /* Emit x? as (x|) */ ++ reginsert(g, BRANCH, ret); /* Either x */ ++ regtail(g, ret, regnode(g, BRANCH)); /* or */ ++ next = regnode(g, NOTHING); /* null. */ ++ regtail(g, ret, next); ++ regoptail(g, ret, next); ++ } ++ g->regparse++; ++ if (ISMULT(*g->regparse)) ++ FAIL("nested *?+"); ++ ++ return(ret); ++} ++ ++/* ++ - regatom - the lowest level ++ * ++ * Optimization: gobbles an entire sequence of ordinary characters so that ++ * it can turn them into a single node, which is smaller to store and ++ * faster to run. Backslashed characters are exceptions, each becoming a ++ * separate node; the code is simpler that way and it's not worth fixing. ++ */ ++static char * ++regatom(struct match_globals *g, int *flagp) ++{ ++ register char *ret; ++ int flags; ++ ++ *flagp = WORST; /* Tentatively. */ ++ ++ switch (*g->regparse++) { ++ case '^': ++ ret = regnode(g, BOL); ++ break; ++ case '$': ++ ret = regnode(g, EOL); ++ break; ++ case '.': ++ ret = regnode(g, ANY); ++ *flagp |= HASWIDTH|SIMPLE; ++ break; ++ case '[': { ++ register int class; ++ register int classend; ++ ++ if (*g->regparse == '^') { /* Complement of range. */ ++ ret = regnode(g, ANYBUT); ++ g->regparse++; ++ } else ++ ret = regnode(g, ANYOF); ++ if (*g->regparse == ']' || *g->regparse == '-') ++ regc(g, *g->regparse++); ++ while (*g->regparse != '\0' && *g->regparse != ']') { ++ if (*g->regparse == '-') { ++ g->regparse++; ++ if (*g->regparse == ']' || *g->regparse == '\0') ++ regc(g, '-'); ++ else { ++ class = UCHARAT(g->regparse-2)+1; ++ classend = UCHARAT(g->regparse); ++ if (class > classend+1) ++ FAIL("invalid [] range"); ++ for (; class <= classend; class++) ++ regc(g, class); ++ g->regparse++; ++ } ++ } else ++ regc(g, *g->regparse++); ++ } ++ regc(g, '\0'); ++ if (*g->regparse != ']') ++ FAIL("unmatched []"); ++ g->regparse++; ++ *flagp |= HASWIDTH|SIMPLE; ++ } ++ break; ++ case '(': ++ ret = reg(g, 1, &flags); ++ if (ret == NULL) ++ return(NULL); ++ *flagp |= flags&(HASWIDTH|SPSTART); ++ break; ++ case '\0': ++ case '|': ++ case ')': ++ FAIL("internal urp"); /* Supposed to be caught earlier. */ ++ break; ++ case '?': ++ case '+': ++ case '*': ++ FAIL("?+* follows nothing"); ++ break; ++ case '\\': ++ if (*g->regparse == '\0') ++ FAIL("trailing \\"); ++ ret = regnode(g, EXACTLY); ++ regc(g, *g->regparse++); ++ regc(g, '\0'); ++ *flagp |= HASWIDTH|SIMPLE; ++ break; ++ default: { ++ register int len; ++ register char ender; ++ ++ g->regparse--; ++ len = my_strcspn((const char *)g->regparse, (const char *)META); ++ if (len <= 0) ++ FAIL("internal disaster"); ++ ender = *(g->regparse+len); ++ if (len > 1 && ISMULT(ender)) ++ len--; /* Back off clear of ?+* operand. */ ++ *flagp |= HASWIDTH; ++ if (len == 1) ++ *flagp |= SIMPLE; ++ ret = regnode(g, EXACTLY); ++ while (len > 0) { ++ regc(g, *g->regparse++); ++ len--; ++ } ++ regc(g, '\0'); ++ } ++ break; ++ } ++ ++ return(ret); ++} ++ ++/* ++ - regnode - emit a node ++ */ ++static char * /* Location. */ ++regnode(struct match_globals *g, char op) ++{ ++ register char *ret; ++ register char *ptr; ++ ++ ret = g->regcode; ++ if (ret == &g->regdummy) { ++ g->regsize += 3; ++ return(ret); ++ } ++ ++ ptr = ret; ++ *ptr++ = op; ++ *ptr++ = '\0'; /* Null "next" pointer. */ ++ *ptr++ = '\0'; ++ g->regcode = ptr; ++ ++ return(ret); ++} ++ ++/* ++ - regc - emit (if appropriate) a byte of code ++ */ ++static void ++regc(struct match_globals *g, char b) ++{ ++ if (g->regcode != &g->regdummy) ++ *g->regcode++ = b; ++ else ++ g->regsize++; ++} ++ ++/* ++ - reginsert - insert an operator in front of already-emitted operand ++ * ++ * Means relocating the operand. ++ */ ++static void ++reginsert(struct match_globals *g, char op, char* opnd) ++{ ++ register char *src; ++ register char *dst; ++ register char *place; ++ ++ if (g->regcode == &g->regdummy) { ++ g->regsize += 3; ++ return; ++ } ++ ++ src = g->regcode; ++ g->regcode += 3; ++ dst = g->regcode; ++ while (src > opnd) ++ *--dst = *--src; ++ ++ place = opnd; /* Op node, where operand used to be. */ ++ *place++ = op; ++ *place++ = '\0'; ++ *place++ = '\0'; ++} ++ ++/* ++ - regtail - set the next-pointer at the end of a node chain ++ */ ++static void ++regtail(struct match_globals *g, char *p, char *val) ++{ ++ register char *scan; ++ register char *temp; ++ register int offset; ++ ++ if (p == &g->regdummy) ++ return; ++ ++ /* Find last node. */ ++ scan = p; ++ for (;;) { ++ temp = regnext(g, scan); ++ if (temp == NULL) ++ break; ++ scan = temp; ++ } ++ ++ if (OP(scan) == BACK) ++ offset = scan - val; ++ else ++ offset = val - scan; ++ *(scan+1) = (offset>>8)&0377; ++ *(scan+2) = offset&0377; ++} ++ ++/* ++ - regoptail - regtail on operand of first argument; nop if operandless ++ */ ++static void ++regoptail(struct match_globals *g, char *p, char *val) ++{ ++ /* "Operandless" and "op != BRANCH" are synonymous in practice. */ ++ if (p == NULL || p == &g->regdummy || OP(p) != BRANCH) ++ return; ++ regtail(g, OPERAND(p), val); ++} ++ ++/* ++ * regexec and friends ++ */ ++ ++ ++/* ++ * Forwards. ++ */ ++STATIC int regtry(struct match_globals *g, regexp *prog, char *string); ++STATIC int regmatch(struct match_globals *g, char *prog); ++STATIC int regrepeat(struct match_globals *g, char *p); ++ ++#ifdef DEBUG ++int regnarrate = 0; ++void regdump(); ++STATIC char *regprop(char *op); ++#endif ++ ++/* ++ - regexec - match a regexp against a string ++ */ ++int ++regexec(regexp *prog, char *string) ++{ ++ register char *s; ++ struct match_globals g; ++ ++ /* Be paranoid... */ ++ if (prog == NULL || string == NULL) { ++ printk("<3>Regexp: NULL parameter\n"); ++ return(0); ++ } ++ ++ /* Check validity of program. */ ++ if (UCHARAT(prog->program) != MAGIC) { ++ printk("<3>Regexp: corrupted program\n"); ++ return(0); ++ } ++ ++ /* If there is a "must appear" string, look for it. */ ++ if (prog->regmust != NULL) { ++ s = string; ++ while ((s = strchr(s, prog->regmust[0])) != NULL) { ++ if (strncmp(s, prog->regmust, prog->regmlen) == 0) ++ break; /* Found it. */ ++ s++; ++ } ++ if (s == NULL) /* Not present. */ ++ return(0); ++ } ++ ++ /* Mark beginning of line for ^ . */ ++ g.regbol = string; ++ ++ /* Simplest case: anchored match need be tried only once. */ ++ if (prog->reganch) ++ return(regtry(&g, prog, string)); ++ ++ /* Messy cases: unanchored match. */ ++ s = string; ++ if (prog->regstart != '\0') ++ /* We know what char it must start with. */ ++ while ((s = strchr(s, prog->regstart)) != NULL) { ++ if (regtry(&g, prog, s)) ++ return(1); ++ s++; ++ } ++ else ++ /* We don't -- general case. */ ++ do { ++ if (regtry(&g, prog, s)) ++ return(1); ++ } while (*s++ != '\0'); ++ ++ /* Failure. */ ++ return(0); ++} ++ ++/* ++ - regtry - try match at specific point ++ */ ++static int /* 0 failure, 1 success */ ++regtry(struct match_globals *g, regexp *prog, char *string) ++{ ++ register int i; ++ register char **sp; ++ register char **ep; ++ ++ g->reginput = string; ++ g->regstartp = prog->startp; ++ g->regendp = prog->endp; ++ ++ sp = prog->startp; ++ ep = prog->endp; ++ for (i = NSUBEXP; i > 0; i--) { ++ *sp++ = NULL; ++ *ep++ = NULL; ++ } ++ if (regmatch(g, prog->program + 1)) { ++ prog->startp[0] = string; ++ prog->endp[0] = g->reginput; ++ return(1); ++ } else ++ return(0); ++} ++ ++/* ++ - regmatch - main matching routine ++ * ++ * Conceptually the strategy is simple: check to see whether the current ++ * node matches, call self recursively to see whether the rest matches, ++ * and then act accordingly. In practice we make some effort to avoid ++ * recursion, in particular by going through "ordinary" nodes (that don't ++ * need to know whether the rest of the match failed) by a loop instead of ++ * by recursion. ++ */ ++static int /* 0 failure, 1 success */ ++regmatch(struct match_globals *g, char *prog) ++{ ++ register char *scan = prog; /* Current node. */ ++ char *next; /* Next node. */ ++ ++#ifdef DEBUG ++ if (scan != NULL && regnarrate) ++ fprintf(stderr, "%s(\n", regprop(scan)); ++#endif ++ while (scan != NULL) { ++#ifdef DEBUG ++ if (regnarrate) ++ fprintf(stderr, "%s...\n", regprop(scan)); ++#endif ++ next = regnext(g, scan); ++ ++ switch (OP(scan)) { ++ case BOL: ++ if (g->reginput != g->regbol) ++ return(0); ++ break; ++ case EOL: ++ if (*g->reginput != '\0') ++ return(0); ++ break; ++ case ANY: ++ if (*g->reginput == '\0') ++ return(0); ++ g->reginput++; ++ break; ++ case EXACTLY: { ++ register int len; ++ register char *opnd; ++ ++ opnd = OPERAND(scan); ++ /* Inline the first character, for speed. */ ++ if (*opnd != *g->reginput) ++ return(0); ++ len = strlen(opnd); ++ if (len > 1 && strncmp(opnd, g->reginput, len) != 0) ++ return(0); ++ g->reginput += len; ++ } ++ break; ++ case ANYOF: ++ if (*g->reginput == '\0' || strchr(OPERAND(scan), *g->reginput) == NULL) ++ return(0); ++ g->reginput++; ++ break; ++ case ANYBUT: ++ if (*g->reginput == '\0' || strchr(OPERAND(scan), *g->reginput) != NULL) ++ return(0); ++ g->reginput++; ++ break; ++ case NOTHING: ++ case BACK: ++ break; ++ case OPEN+1: ++ case OPEN+2: ++ case OPEN+3: ++ case OPEN+4: ++ case OPEN+5: ++ case OPEN+6: ++ case OPEN+7: ++ case OPEN+8: ++ case OPEN+9: { ++ register int no; ++ register char *save; ++ ++ no = OP(scan) - OPEN; ++ save = g->reginput; ++ ++ if (regmatch(g, next)) { ++ /* ++ * Don't set startp if some later ++ * invocation of the same parentheses ++ * already has. ++ */ ++ if (g->regstartp[no] == NULL) ++ g->regstartp[no] = save; ++ return(1); ++ } else ++ return(0); ++ } ++ break; ++ case CLOSE+1: ++ case CLOSE+2: ++ case CLOSE+3: ++ case CLOSE+4: ++ case CLOSE+5: ++ case CLOSE+6: ++ case CLOSE+7: ++ case CLOSE+8: ++ case CLOSE+9: ++ { ++ register int no; ++ register char *save; ++ ++ no = OP(scan) - CLOSE; ++ save = g->reginput; ++ ++ if (regmatch(g, next)) { ++ /* ++ * Don't set endp if some later ++ * invocation of the same parentheses ++ * already has. ++ */ ++ if (g->regendp[no] == NULL) ++ g->regendp[no] = save; ++ return(1); ++ } else ++ return(0); ++ } ++ break; ++ case BRANCH: { ++ register char *save; ++ ++ if (OP(next) != BRANCH) /* No choice. */ ++ next = OPERAND(scan); /* Avoid recursion. */ ++ else { ++ do { ++ save = g->reginput; ++ if (regmatch(g, OPERAND(scan))) ++ return(1); ++ g->reginput = save; ++ scan = regnext(g, scan); ++ } while (scan != NULL && OP(scan) == BRANCH); ++ return(0); ++ /* NOTREACHED */ ++ } ++ } ++ break; ++ case STAR: ++ case PLUS: { ++ register char nextch; ++ register int no; ++ register char *save; ++ register int min; ++ ++ /* ++ * Lookahead to avoid useless match attempts ++ * when we know what character comes next. ++ */ ++ nextch = '\0'; ++ if (OP(next) == EXACTLY) ++ nextch = *OPERAND(next); ++ min = (OP(scan) == STAR) ? 0 : 1; ++ save = g->reginput; ++ no = regrepeat(g, OPERAND(scan)); ++ while (no >= min) { ++ /* If it could work, try it. */ ++ if (nextch == '\0' || *g->reginput == nextch) ++ if (regmatch(g, next)) ++ return(1); ++ /* Couldn't or didn't -- back up. */ ++ no--; ++ g->reginput = save + no; ++ } ++ return(0); ++ } ++ break; ++ case END: ++ return(1); /* Success! */ ++ break; ++ default: ++ printk("<3>Regexp: memory corruption\n"); ++ return(0); ++ break; ++ } ++ ++ scan = next; ++ } ++ ++ /* ++ * We get here only if there's trouble -- normally "case END" is ++ * the terminating point. ++ */ ++ printk("<3>Regexp: corrupted pointers\n"); ++ return(0); ++} ++ ++/* ++ - regrepeat - repeatedly match something simple, report how many ++ */ ++static int ++regrepeat(struct match_globals *g, char *p) ++{ ++ register int count = 0; ++ register char *scan; ++ register char *opnd; ++ ++ scan = g->reginput; ++ opnd = OPERAND(p); ++ switch (OP(p)) { ++ case ANY: ++ count = strlen(scan); ++ scan += count; ++ break; ++ case EXACTLY: ++ while (*opnd == *scan) { ++ count++; ++ scan++; ++ } ++ break; ++ case ANYOF: ++ while (*scan != '\0' && strchr(opnd, *scan) != NULL) { ++ count++; ++ scan++; ++ } ++ break; ++ case ANYBUT: ++ while (*scan != '\0' && strchr(opnd, *scan) == NULL) { ++ count++; ++ scan++; ++ } ++ break; ++ default: /* Oh dear. Called inappropriately. */ ++ printk("<3>Regexp: internal foulup\n"); ++ count = 0; /* Best compromise. */ ++ break; ++ } ++ g->reginput = scan; ++ ++ return(count); ++} ++ ++/* ++ - regnext - dig the "next" pointer out of a node ++ */ ++static char* ++regnext(struct match_globals *g, char *p) ++{ ++ register int offset; ++ ++ if (p == &g->regdummy) ++ return(NULL); ++ ++ offset = NEXT(p); ++ if (offset == 0) ++ return(NULL); ++ ++ if (OP(p) == BACK) ++ return(p-offset); ++ else ++ return(p+offset); ++} ++ ++#ifdef DEBUG ++ ++STATIC char *regprop(); ++ ++/* ++ - regdump - dump a regexp onto stdout in vaguely comprehensible form ++ */ ++void ++regdump(regexp *r) ++{ ++ register char *s; ++ register char op = EXACTLY; /* Arbitrary non-END op. */ ++ register char *next; ++ /* extern char *strchr(); */ ++ ++ ++ s = r->program + 1; ++ while (op != END) { /* While that wasn't END last time... */ ++ op = OP(s); ++ printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */ ++ next = regnext(s); ++ if (next == NULL) /* Next ptr. */ ++ printf("(0)"); ++ else ++ printf("(%d)", (s-r->program)+(next-s)); ++ s += 3; ++ if (op == ANYOF || op == ANYBUT || op == EXACTLY) { ++ /* Literal string, where present. */ ++ while (*s != '\0') { ++ putchar(*s); ++ s++; ++ } ++ s++; ++ } ++ putchar('\n'); ++ } ++ ++ /* Header fields of interest. */ ++ if (r->regstart != '\0') ++ printf("start `%c' ", r->regstart); ++ if (r->reganch) ++ printf("anchored "); ++ if (r->regmust != NULL) ++ printf("must have \"%s\"", r->regmust); ++ printf("\n"); ++} ++ ++/* ++ - regprop - printable representation of opcode ++ */ ++static char * ++regprop(char *op) ++{ ++#define BUFLEN 50 ++ register char *p; ++ static char buf[BUFLEN]; ++ ++ strcpy(buf, ":"); ++ ++ switch (OP(op)) { ++ case BOL: ++ p = "BOL"; ++ break; ++ case EOL: ++ p = "EOL"; ++ break; ++ case ANY: ++ p = "ANY"; ++ break; ++ case ANYOF: ++ p = "ANYOF"; ++ break; ++ case ANYBUT: ++ p = "ANYBUT"; ++ break; ++ case BRANCH: ++ p = "BRANCH"; ++ break; ++ case EXACTLY: ++ p = "EXACTLY"; ++ break; ++ case NOTHING: ++ p = "NOTHING"; ++ break; ++ case BACK: ++ p = "BACK"; ++ break; ++ case END: ++ p = "END"; ++ break; ++ case OPEN+1: ++ case OPEN+2: ++ case OPEN+3: ++ case OPEN+4: ++ case OPEN+5: ++ case OPEN+6: ++ case OPEN+7: ++ case OPEN+8: ++ case OPEN+9: ++ snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "OPEN%d", OP(op)-OPEN); ++ p = NULL; ++ break; ++ case CLOSE+1: ++ case CLOSE+2: ++ case CLOSE+3: ++ case CLOSE+4: ++ case CLOSE+5: ++ case CLOSE+6: ++ case CLOSE+7: ++ case CLOSE+8: ++ case CLOSE+9: ++ snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "CLOSE%d", OP(op)-CLOSE); ++ p = NULL; ++ break; ++ case STAR: ++ p = "STAR"; ++ break; ++ case PLUS: ++ p = "PLUS"; ++ break; ++ default: ++ printk("<3>Regexp: corrupted opcode\n"); ++ break; ++ } ++ if (p != NULL) ++ strncat(buf, p, BUFLEN-strlen(buf)); ++ return(buf); ++} ++#endif ++ ++ +--- /dev/null ++++ b/net/ipv4/netfilter/regexp/regexp.h +@@ -0,0 +1,41 @@ ++/* ++ * Definitions etc. for regexp(3) routines. ++ * ++ * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof], ++ * not the System V one. ++ */ ++ ++#ifndef REGEXP_H ++#define REGEXP_H ++ ++ ++/* ++http://www.opensource.apple.com/darwinsource/10.3/expect-1/expect/expect.h , ++which contains a version of this library, says: ++ ++ * ++ * NSUBEXP must be at least 10, and no greater than 117 or the parser ++ * will not work properly. ++ * ++ ++However, it looks rather like this library is limited to 10. If you think ++otherwise, let us know. ++*/ ++ ++#define NSUBEXP 10 ++typedef struct regexp { ++ char *startp[NSUBEXP]; ++ char *endp[NSUBEXP]; ++ char regstart; /* Internal use only. */ ++ char reganch; /* Internal use only. */ ++ char *regmust; /* Internal use only. */ ++ int regmlen; /* Internal use only. */ ++ char program[1]; /* Unwarranted chumminess with compiler. */ ++} regexp; ++ ++regexp * regcomp(char *exp, int *patternsize); ++int regexec(regexp *prog, char *string); ++void regsub(regexp *prog, char *source, char *dest); ++void regerror(char *s); ++ ++#endif +--- /dev/null ++++ b/net/ipv4/netfilter/regexp/regmagic.h +@@ -0,0 +1,5 @@ ++/* ++ * The first byte of the regexp internal "program" is actually this magic ++ * number; the start node begins in the second byte. ++ */ ++#define MAGIC 0234 +--- /dev/null ++++ b/net/ipv4/netfilter/regexp/regsub.c +@@ -0,0 +1,95 @@ ++/* ++ * regsub ++ * @(#)regsub.c 1.3 of 2 April 86 ++ * ++ * Copyright (c) 1986 by University of Toronto. ++ * Written by Henry Spencer. Not derived from licensed software. ++ * ++ * Permission is granted to anyone to use this software for any ++ * purpose on any computer system, and to redistribute it freely, ++ * subject to the following restrictions: ++ * ++ * 1. The author is not responsible for the consequences of use of ++ * this software, no matter how awful, even if they arise ++ * from defects in it. ++ * ++ * 2. The origin of this software must not be misrepresented, either ++ * by explicit claim or by omission. ++ * ++ * 3. Altered versions must be plainly marked as such, and must not ++ * be misrepresented as being the original software. ++ * ++ * ++ * This code was modified by Ethan Sommer to work within the kernel ++ * (it now uses kmalloc etc..) ++ * ++ */ ++#include "regexp.h" ++#include "regmagic.h" ++#include ++ ++ ++#ifndef CHARBITS ++#define UCHARAT(p) ((int)*(unsigned char *)(p)) ++#else ++#define UCHARAT(p) ((int)*(p)&CHARBITS) ++#endif ++ ++#if 0 ++//void regerror(char * s) ++//{ ++// printk("regexp(3): %s", s); ++// /* NOTREACHED */ ++//} ++#endif ++ ++/* ++ - regsub - perform substitutions after a regexp match ++ */ ++void ++regsub(regexp * prog, char * source, char * dest) ++{ ++ register char *src; ++ register char *dst; ++ register char c; ++ register int no; ++ register int len; ++ ++ /* Not necessary and gcc doesn't like it -MLS */ ++ /*extern char *strncpy();*/ ++ ++ if (prog == NULL || source == NULL || dest == NULL) { ++ regerror("NULL parm to regsub"); ++ return; ++ } ++ if (UCHARAT(prog->program) != MAGIC) { ++ regerror("damaged regexp fed to regsub"); ++ return; ++ } ++ ++ src = source; ++ dst = dest; ++ while ((c = *src++) != '\0') { ++ if (c == '&') ++ no = 0; ++ else if (c == '\\' && '0' <= *src && *src <= '9') ++ no = *src++ - '0'; ++ else ++ no = -1; ++ ++ if (no < 0) { /* Ordinary character. */ ++ if (c == '\\' && (*src == '\\' || *src == '&')) ++ c = *src++; ++ *dst++ = c; ++ } else if (prog->startp[no] != NULL && prog->endp[no] != NULL) { ++ len = prog->endp[no] - prog->startp[no]; ++ (void) strncpy(dst, prog->startp[no], len); ++ dst += len; ++ if (len != 0 && *(dst-1) == '\0') { /* strncpy hit NUL. */ ++ regerror("damaged match string"); ++ return; ++ } ++ } ++ } ++ *dst++ = '\0'; ++} +--- /dev/null ++++ b/net/ipv4/netfilter/webmon_deps/tree_map.h +@@ -0,0 +1,1084 @@ ++/* ++ * Copyright © 2008 by Eric Bishop ++ * ++ * This work 'as-is' we provide. ++ * No warranty, express or implied. ++ * We've done our best, ++ * to debug and test. ++ * Liability for damages denied. ++ * ++ * Permission is granted hereby, ++ * to copy, share, and modify. ++ * Use as is fit, ++ * free or for profit. ++ * On this notice these rights rely. ++ * ++ * ++ * ++ * Note that unlike other portions of Gargoyle this code ++ * does not fall under the GPL, but the rather whimsical ++ * 'Poetic License' above. ++ * ++ * Basically, this library contains a bunch of utilities ++ * that I find useful. I'm sure other libraries exist ++ * that are just as good or better, but I like these tools ++ * because I personally wrote them, so I know their quirks. ++ * (i.e. I know where the bodies are buried). I want to ++ * make sure that I can re-use these utilities for whatever ++ * code I may want to write in the future be it ++ * proprietary or open-source, so I've put them under ++ * a very, very permissive license. ++ * ++ * If you find this code useful, use it. If not, don't. ++ * I really don't care. ++ * ++ */ ++ ++ ++#if __KERNEL__ ++ #define malloc(foo) kmalloc(foo,GFP_ATOMIC) ++ #define free(foo) kfree(foo) ++ #define printf(format,args...) printk(format,##args) ++ ++ /* kernel strdup */ ++ static inline char *kernel_strdup(const char *str); ++ static inline char *kernel_strdup(const char *str) ++ { ++ char *tmp; ++ long int s; ++ s=strlen(str) + 1; ++ tmp = kmalloc(s, GFP_ATOMIC); ++ if (tmp != NULL) ++ { ++ memcpy(tmp, str, s); ++ } ++ return tmp; ++ } ++ #define strdup kernel_strdup ++ ++#endif ++ ++ ++ ++/* tree_map structs / prototypes */ ++typedef struct long_tree_map_node ++{ ++ unsigned long key; ++ void* value; ++ ++ signed char balance; ++ struct long_tree_map_node* left; ++ struct long_tree_map_node* right; ++} long_map_node; ++ ++typedef struct ++{ ++ long_map_node* root; ++ unsigned long num_elements; ++ ++}long_map; ++ ++typedef struct ++{ ++ long_map lm; ++ unsigned char store_keys; ++ unsigned long num_elements; ++ ++}string_map; ++ ++ ++ ++/* long map functions */ ++long_map* initialize_long_map(void); ++void* get_long_map_element(long_map* map, unsigned long key); ++void* get_smallest_long_map_element(long_map* map, unsigned long* smallest_key); ++void* get_largest_long_map_element(long_map* map, unsigned long* largest_key); ++void* remove_smallest_long_map_element(long_map* map, unsigned long* smallest_key); ++void* remove_largest_long_map_element(long_map* map, unsigned long* largest_key); ++void* set_long_map_element(long_map* map, unsigned long key, void* value); ++void* remove_long_map_element(long_map* map, unsigned long key); ++unsigned long* get_sorted_long_map_keys(long_map* map, unsigned long* num_keys_returned); ++void** get_sorted_long_map_values(long_map* map, unsigned long* num_values_returned); ++void** destroy_long_map(long_map* map, int destruction_type, unsigned long* num_destroyed); ++void apply_to_every_long_map_value(long_map* map, void (*apply_func)(unsigned long key, void* value)); ++ ++/* string map functions */ ++string_map* initialize_string_map(unsigned char store_keys); ++void* get_string_map_element(string_map* map, const char* key); ++void* set_string_map_element(string_map* map, const char* key, void* value); ++void* remove_string_map_element(string_map* map, const char* key); ++char** get_string_map_keys(string_map* map, unsigned long* num_keys_returned); ++void** get_string_map_values(string_map* map, unsigned long* num_values_returned); ++void** destroy_string_map(string_map* map, int destruction_type, unsigned long* num_destroyed); ++void apply_to_every_string_map_value(string_map* map, void (*apply_func)(char* key, void* value)); ++ ++ ++/* ++ * three different ways to deal with values when data structure is destroyed ++ */ ++#define DESTROY_MODE_RETURN_VALUES 20 ++#define DESTROY_MODE_FREE_VALUES 21 ++#define DESTROY_MODE_IGNORE_VALUES 22 ++ ++ ++/* ++ * for convenience & backwards compatibility alias _string_map_ functions to ++ * _map_ functions since string map is used more often than long map ++ */ ++#define initialize_map initialize_string_map ++#define set_map_element set_string_map_element ++#define get_map_element get_string_map_element ++#define remove_map_element remove_string_map_element ++#define get_map_keys get_string_map_keys ++#define get_map_values get_string_map_values ++#define destroy_map destroy_string_map ++ ++ ++/* internal utility structures/ functions */ ++typedef struct stack_node_struct ++{ ++ long_map_node** node_ptr; ++ signed char direction; ++ struct stack_node_struct* previous; ++} stack_node; ++ ++static void free_stack(stack_node* stack); ++static void** destroy_long_map_values(long_map* map, int destruction_type, unsigned long* num_destroyed); ++static void apply_to_every_long_map_node(long_map_node* node, void (*apply_func)(unsigned long key, void* value)); ++static void apply_to_every_string_map_node(long_map_node* node, unsigned char has_key, void (*apply_func)(char* key, void* value)); ++static void get_sorted_node_keys(long_map_node* node, unsigned long* key_list, unsigned long* next_key_index, int depth); ++static void get_sorted_node_values(long_map_node* node, void** value_list, unsigned long* next_value_index, int depth); ++static signed char rebalance (long_map_node** n, signed char direction, signed char update_op); ++static void rotate_right (long_map_node** parent); ++static void rotate_left (long_map_node** parent); ++ ++/* internal for string map */ ++typedef struct ++{ ++ char* key; ++ void* value; ++} string_map_key_value; ++static unsigned long sdbm_string_hash(const char *key); ++ ++ ++ ++ ++/*************************************************** ++ * For testing only ++ ***************************************************/ ++/* ++void print_list(stack_node *l); ++ ++void print_list(stack_node *l) ++{ ++ if(l != NULL) ++ { ++ printf(" list key = %ld, dir=%d, \n", (*(l->node_ptr))->key, l->direction); ++ print_list(l->previous); ++ } ++} ++*/ ++/****************************************************** ++ * End testing Code ++ *******************************************************/ ++ ++ ++ ++ ++/*************************************************** ++ * string_map function definitions ++ ***************************************************/ ++ ++string_map* initialize_string_map(unsigned char store_keys) ++{ ++ string_map* map = (string_map*)malloc(sizeof(string_map)); ++ if(map != NULL) ++ { ++ map->store_keys = store_keys; ++ map->lm.root = NULL; ++ map->lm.num_elements = 0; ++ map->num_elements = map->lm.num_elements; ++ } ++ return map; ++} ++ ++void* get_string_map_element(string_map* map, const char* key) ++{ ++ unsigned long hashed_key = sdbm_string_hash(key); ++ void* return_value = get_long_map_element( &(map->lm), hashed_key); ++ if(return_value != NULL && map->store_keys) ++ { ++ string_map_key_value* r = (string_map_key_value*)return_value; ++ return_value = r->value; ++ } ++ map->num_elements = map->lm.num_elements; ++ return return_value; ++} ++ ++void* set_string_map_element(string_map* map, const char* key, void* value) ++{ ++ unsigned long hashed_key = sdbm_string_hash(key); ++ void* return_value = NULL; ++ if(map->store_keys) ++ { ++ string_map_key_value* kv = (string_map_key_value*)malloc(sizeof(string_map_key_value)); ++ if(kv == NULL) /* deal with malloc failure */ ++ { ++ return NULL; ++ } ++ kv->key = strdup(key); ++ if(kv->key == NULL) /* deal with malloc failure */ ++ { ++ free(kv); ++ return NULL; ++ } ++ kv->value = value; ++ return_value = set_long_map_element( &(map->lm), hashed_key, kv); ++ if(return_value != NULL) ++ { ++ string_map_key_value* r = (string_map_key_value*)return_value; ++ return_value = r->value; ++ free(r->key); ++ free(r); ++ } ++ } ++ else ++ { ++ return_value = set_long_map_element( &(map->lm), hashed_key, value); ++ } ++ map->num_elements = map->lm.num_elements; ++ return return_value; ++} ++ ++void* remove_string_map_element(string_map* map, const char* key) ++{ ++ unsigned long hashed_key = sdbm_string_hash(key); ++ void* return_value = remove_long_map_element( &(map->lm), hashed_key); ++ ++ if(return_value != NULL && map->store_keys) ++ { ++ string_map_key_value* r = (string_map_key_value*)return_value; ++ return_value = r->value; ++ free(r->key); ++ free(r); ++ } ++ map->num_elements = map->lm.num_elements; ++ return return_value; ++} ++ ++char** get_string_map_keys(string_map* map, unsigned long* num_keys_returned) ++{ ++ char** str_keys; ++ str_keys = (char**)malloc((map->num_elements+1)*sizeof(char*)); ++ if(str_keys == NULL) /* deal with malloc failure */ ++ { ++ return NULL; ++ } ++ str_keys[0] = NULL; ++ *num_keys_returned = 0; ++ if(map->store_keys && map->num_elements > 0) ++ { ++ unsigned long list_length; ++ void** long_values = get_sorted_long_map_values( &(map->lm), &list_length); ++ unsigned long key_index; ++ /*list_length will be 0 on malloc failure in get_sorted_long_map_values, so this code shouldn't seg fault if that happens */ ++ for(key_index = 0; key_index < list_length; key_index++) ++ { ++ str_keys[key_index] = strdup( ((string_map_key_value*)(long_values[key_index]))->key); ++ if(str_keys[key_index] == NULL) /* deal with malloc failure */ ++ { ++ //just return the incomplete list (hey, it's null terminated...) ++ free(long_values); ++ return str_keys; ++ } ++ *num_keys_returned = *num_keys_returned + 1; ++ } ++ str_keys[list_length] = NULL; ++ free(long_values); ++ } ++ return str_keys; ++} ++ ++ ++void** get_string_map_values(string_map* map, unsigned long* num_values_returned) ++{ ++ void** values = NULL; ++ if(map != NULL) ++ { ++ values = get_sorted_long_map_values ( &(map->lm), num_values_returned ); ++ } ++ return values; ++} ++ ++ ++void** destroy_string_map(string_map* map, int destruction_type, unsigned long* num_destroyed) ++{ ++ void** return_values = NULL; ++ if(map != NULL) ++ { ++ if(map->store_keys) ++ { ++ void** kvs = destroy_long_map_values( &(map->lm), DESTROY_MODE_RETURN_VALUES, num_destroyed ); ++ unsigned long kv_index = 0; ++ for(kv_index=0; kv_index < *num_destroyed; kv_index++) ++ { ++ string_map_key_value* kv = (string_map_key_value*)kvs[kv_index]; ++ void* value = kv->value; ++ ++ free(kv->key); ++ free(kv); ++ if(destruction_type == DESTROY_MODE_FREE_VALUES) ++ { ++ free(value); ++ } ++ if(destruction_type == DESTROY_MODE_RETURN_VALUES) ++ { ++ kvs[kv_index] = value; ++ } ++ } ++ if(destruction_type == DESTROY_MODE_RETURN_VALUES) ++ { ++ return_values = kvs; ++ } ++ else ++ { ++ free(kvs); ++ } ++ } ++ else ++ { ++ return_values = destroy_long_map_values( &(map->lm), destruction_type, num_destroyed ); ++ } ++ free(map); ++ } ++ return return_values; ++} ++ ++ ++ ++ ++/*************************************************** ++ * long_map function definitions ++ ***************************************************/ ++ ++long_map* initialize_long_map(void) ++{ ++ long_map* map = (long_map*)malloc(sizeof(long_map)); ++ if(map != NULL) /* test for malloc failure */ ++ { ++ map->root = NULL; ++ map->num_elements = 0; ++ } ++ return map; ++} ++ ++void* get_long_map_element(long_map* map, unsigned long key) ++{ ++ void* value = NULL; ++ ++ if(map->root != NULL) ++ { ++ long_map_node* parent_node = map->root; ++ long_map_node* next_node; ++ while( key != parent_node->key && (next_node = (long_map_node *)(key < parent_node->key ? parent_node->left : parent_node->right)) != NULL) ++ { ++ parent_node = next_node; ++ } ++ if(parent_node->key == key) ++ { ++ value = parent_node->value; ++ } ++ } ++ return value; ++} ++ ++void* get_smallest_long_map_element(long_map* map, unsigned long* smallest_key) ++{ ++ void* value = NULL; ++ if(map->root != NULL) ++ { ++ long_map_node* next_node = map->root; ++ while( next_node->left != NULL) ++ { ++ next_node = next_node->left; ++ } ++ value = next_node->value; ++ *smallest_key = next_node->key; ++ } ++ return value; ++} ++ ++void* get_largest_long_map_element(long_map* map, unsigned long* largest_key) ++{ ++ void* value = NULL; ++ if(map->root != NULL) ++ { ++ long_map_node* next_node = map->root; ++ while( next_node->right != NULL) ++ { ++ next_node = next_node->right; ++ } ++ value = next_node->value; ++ *largest_key = next_node->key; ++ } ++ return value; ++} ++ ++void* remove_smallest_long_map_element(long_map* map, unsigned long* smallest_key) ++{ ++ get_smallest_long_map_element(map, smallest_key); ++ return remove_long_map_element(map, *smallest_key); ++} ++ ++void* remove_largest_long_map_element(long_map* map, unsigned long* largest_key) ++{ ++ get_largest_long_map_element(map, largest_key); ++ return remove_long_map_element(map, *largest_key); ++} ++ ++ ++/* if replacement performed, returns replaced value, otherwise null */ ++void* set_long_map_element(long_map* map, unsigned long key, void* value) ++{ ++ stack_node* parent_list = NULL; ++ void* old_value = NULL; ++ int old_value_found = 0; ++ ++ long_map_node* parent_node; ++ long_map_node* next_node; ++ stack_node* next_parent; ++ stack_node* previous_parent; ++ signed char new_balance; ++ ++ ++ long_map_node* new_node = (long_map_node*)malloc(sizeof(long_map_node)); ++ if(new_node == NULL) ++ { ++ return NULL; ++ } ++ new_node->value = value; ++ new_node->key = key; ++ new_node->left = NULL; ++ new_node->right = NULL; ++ new_node->balance = 0; ++ ++ ++ ++ if(map->root == NULL) ++ { ++ map->root = new_node; ++ } ++ else ++ { ++ parent_node = map->root; ++ ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ free(new_node); ++ return NULL; /* won't insert but won't seg fault */ ++ } ++ next_parent->node_ptr = &(map->root); ++ next_parent->previous = parent_list; ++ parent_list = next_parent; ++ ++ while( key != parent_node->key && (next_node = (key < parent_node->key ? parent_node->left : parent_node->right) ) != NULL) ++ { ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ free(new_node); ++ return NULL; ++ } ++ next_parent->node_ptr = key < parent_node->key ? &(parent_node->left) : &(parent_node->right); ++ next_parent->previous = parent_list; ++ next_parent->previous->direction = key < parent_node->key ? -1 : 1; ++ parent_list = next_parent; ++ ++ parent_node = next_node; ++ } ++ ++ ++ if(key == parent_node->key) ++ { ++ old_value = parent_node->value; ++ old_value_found = 1; ++ parent_node->value = value; ++ free(new_node); ++ /* we merely replaced a node, no need to rebalance */ ++ } ++ else ++ { ++ if(key < parent_node->key) ++ { ++ parent_node->left = (void*)new_node; ++ parent_list->direction = -1; ++ } ++ else ++ { ++ parent_node->right = (void*)new_node; ++ parent_list->direction = 1; ++ } ++ ++ ++ /* we inserted a node, rebalance */ ++ previous_parent = parent_list; ++ new_balance = 1; /* initial value is not used, but must not be 0 for initial loop condition */ ++ ++ ++ while(previous_parent != NULL && new_balance != 0) ++ { ++ new_balance = rebalance(previous_parent->node_ptr, previous_parent->direction, 1); ++ previous_parent = previous_parent->previous; ++ } ++ } ++ } ++ ++ free_stack(parent_list); ++ ++ if(old_value_found == 0) ++ { ++ map->num_elements = map->num_elements + 1; ++ } ++ ++ return old_value; ++} ++ ++ ++void* remove_long_map_element(long_map* map, unsigned long key) ++{ ++ ++ void* value = NULL; ++ ++ long_map_node* root_node = map->root; ++ stack_node* parent_list = NULL; ++ ++ ++ long_map_node* remove_parent; ++ long_map_node* remove_node; ++ long_map_node* next_node; ++ ++ long_map_node* replacement; ++ long_map_node* replacement_parent; ++ long_map_node* replacement_next; ++ ++ stack_node* next_parent; ++ stack_node* previous_parent; ++ stack_node* replacement_stack_node; ++ ++ ++ signed char new_balance; ++ ++ ++ ++ if(root_node != NULL) ++ { ++ remove_parent = root_node; ++ remove_node = key < remove_parent->key ? remove_parent->left : remove_parent->right; ++ ++ if(remove_node != NULL && key != remove_parent->key) ++ { ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ return NULL; ++ } ++ next_parent->node_ptr = &(map->root); ++ next_parent->previous = parent_list; ++ parent_list = next_parent; ++ while( key != remove_node->key && (next_node = (key < remove_node->key ? remove_node->left : remove_node->right)) != NULL) ++ { ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ next_parent->node_ptr = key < remove_parent->key ? &(remove_parent->left) : &(remove_parent->right); ++ next_parent->previous = parent_list; ++ next_parent->previous->direction = key < remove_parent->key ? -1 : 1; ++ parent_list = next_parent; ++ ++ ++ remove_parent = remove_node; ++ remove_node = next_node; ++ } ++ parent_list->direction = key < remove_parent-> key ? -1 : 1; ++ } ++ else ++ { ++ remove_node = remove_parent; ++ } ++ ++ ++ if(key == remove_node->key) ++ { ++ ++ /* find replacement for node we are deleting */ ++ if( remove_node->right == NULL ) ++ { ++ replacement = remove_node->left; ++ } ++ else if( remove_node->right->left == NULL) ++ { ++ ++ replacement = remove_node->right; ++ replacement->left = remove_node->left; ++ replacement->balance = remove_node->balance; ++ ++ /* put pointer to replacement node into list for balance update */ ++ replacement_stack_node = (stack_node*)malloc(sizeof(stack_node)); ++ if(replacement_stack_node == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ replacement_stack_node->previous = parent_list; ++ replacement_stack_node->direction = 1; /* replacement is from right */ ++ if(remove_node == remove_parent) /* special case for root node */ ++ { ++ replacement_stack_node->node_ptr = &(map->root); ++ } ++ else ++ { ++ replacement_stack_node->node_ptr = key < remove_parent-> key ? &(remove_parent->left) : &(remove_parent->right); ++ } ++ parent_list = replacement_stack_node; ++ ++ } ++ else ++ { ++ /* put pointer to replacement node into list for balance update */ ++ replacement_stack_node = (stack_node*)malloc(sizeof(stack_node)); ++ if(replacement_stack_node == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ ++ replacement_stack_node->previous = parent_list; ++ replacement_stack_node->direction = 1; /* we always look for replacement on right */ ++ if(remove_node == remove_parent) /* special case for root node */ ++ { ++ replacement_stack_node->node_ptr = &(map->root); ++ } ++ else ++ { ++ replacement_stack_node->node_ptr = key < remove_parent-> key ? &(remove_parent->left) : &(remove_parent->right); ++ } ++ ++ parent_list = replacement_stack_node; ++ ++ ++ /* ++ * put pointer to replacement node->right into list for balance update ++ * this node will have to be updated with the proper pointer ++ * after we have identified the replacement ++ */ ++ replacement_stack_node = (stack_node*)malloc(sizeof(stack_node)); ++ if(replacement_stack_node == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ ++ replacement_stack_node->previous = parent_list; ++ replacement_stack_node->direction = -1; /* we always look for replacement to left of this node */ ++ parent_list = replacement_stack_node; ++ ++ /* find smallest node on right (large) side of tree */ ++ replacement_parent = remove_node->right; ++ replacement = replacement_parent->left; ++ ++ while((replacement_next = replacement->left) != NULL) ++ { ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ ++ next_parent->node_ptr = &(replacement_parent->left); ++ next_parent->previous = parent_list; ++ next_parent->direction = -1; /* we always go left */ ++ parent_list = next_parent; ++ ++ replacement_parent = replacement; ++ replacement = replacement_next; ++ ++ } ++ ++ replacement_parent->left = replacement->right; ++ ++ replacement->left = remove_node->left; ++ replacement->right = remove_node->right; ++ replacement->balance = remove_node->balance; ++ replacement_stack_node->node_ptr = &(replacement->right); ++ } ++ ++ /* insert replacement at proper location in tree */ ++ if(remove_node == remove_parent) ++ { ++ map->root = replacement; ++ } ++ else ++ { ++ remove_parent->left = remove_node == remove_parent->left ? replacement : remove_parent->left; ++ remove_parent->right = remove_node == remove_parent->right ? replacement : remove_parent->right; ++ } ++ ++ ++ /* rebalance tree */ ++ previous_parent = parent_list; ++ new_balance = 0; ++ while(previous_parent != NULL && new_balance == 0) ++ { ++ new_balance = rebalance(previous_parent->node_ptr, previous_parent->direction, -1); ++ previous_parent = previous_parent->previous; ++ } ++ ++ ++ ++ ++ /* ++ * since we found a value to remove, decrease number of elements in map ++ * set return value to the deleted node's value and free the node ++ */ ++ map->num_elements = map->num_elements - 1; ++ value = remove_node->value; ++ free(remove_node); ++ } ++ } ++ ++ free_stack(parent_list); ++ ++ return value; ++} ++ ++ ++/* note: returned keys are dynamically allocated, you need to free them! */ ++unsigned long* get_sorted_long_map_keys(long_map* map, unsigned long* num_keys_returned) ++{ ++ unsigned long* key_list = (unsigned long*)malloc((map->num_elements)*sizeof(unsigned long)); ++ unsigned long next_key_index; ++ if(key_list == NULL) ++ { ++ *num_keys_returned = 0; ++ return NULL; ++ } ++ next_key_index = 0; ++ get_sorted_node_keys(map->root, key_list, &next_key_index, 0); ++ ++ *num_keys_returned = map->num_elements; ++ ++ return key_list; ++} ++ ++ ++void** get_sorted_long_map_values(long_map* map, unsigned long* num_values_returned) ++{ ++ void** value_list = (void**)malloc((map->num_elements+1)*sizeof(void*)); ++ unsigned long next_value_index; ++ ++ if(value_list == NULL) ++ { ++ *num_values_returned = 0; ++ return NULL; ++ } ++ next_value_index = 0; ++ get_sorted_node_values(map->root, value_list, &next_value_index, 0); ++ value_list[map->num_elements] = NULL; /* since we're dealing with pointers make list null terminated */ ++ ++ *num_values_returned = map->num_elements; ++ return value_list; ++ ++} ++ ++ ++ ++void** destroy_long_map(long_map* map, int destruction_type, unsigned long* num_destroyed) ++{ ++ void** return_values = destroy_long_map_values(map, destruction_type, num_destroyed); ++ free(map); ++ return return_values; ++} ++ ++ ++ ++void apply_to_every_long_map_value(long_map* map, void (*apply_func)(unsigned long key, void* value)) ++{ ++ apply_to_every_long_map_node(map->root, apply_func); ++} ++void apply_to_every_string_map_value(string_map* map, void (*apply_func)(char* key, void* value)) ++{ ++ apply_to_every_string_map_node( (map->lm).root, map->store_keys, apply_func); ++} ++ ++ ++/*************************************************** ++ * internal utility function definitions ++ ***************************************************/ ++static void free_stack(stack_node* stack) ++{ ++ while(stack != NULL) ++ { ++ stack_node* prev_node = stack; ++ stack = prev_node->previous; ++ free(prev_node); ++ } ++ ++} ++ ++static void** destroy_long_map_values(long_map* map, int destruction_type, unsigned long* num_destroyed) ++{ ++ void** return_values = NULL; ++ unsigned long return_index = 0; ++ ++ *num_destroyed = 0; ++ ++ if(destruction_type == DESTROY_MODE_RETURN_VALUES) ++ { ++ return_values = (void**)malloc((map->num_elements+1)*sizeof(void*)); ++ if(return_values == NULL) /* deal with malloc failure */ ++ { ++ destruction_type = DESTROY_MODE_IGNORE_VALUES; /* could cause memory leak, but there's no other way to be sure we won't seg fault */ ++ } ++ else ++ { ++ return_values[map->num_elements] = NULL; ++ } ++ } ++ while(map->num_elements > 0) ++ { ++ unsigned long smallest_key; ++ void* removed_value = remove_smallest_long_map_element(map, &smallest_key); ++ if(destruction_type == DESTROY_MODE_RETURN_VALUES) ++ { ++ return_values[return_index] = removed_value; ++ } ++ if(destruction_type == DESTROY_MODE_FREE_VALUES) ++ { ++ free(removed_value); ++ } ++ return_index++; ++ *num_destroyed = *num_destroyed + 1; ++ } ++ return return_values; ++} ++ ++static void apply_to_every_long_map_node(long_map_node* node, void (*apply_func)(unsigned long key, void* value)) ++{ ++ if(node != NULL) ++ { ++ apply_to_every_long_map_node(node->left, apply_func); ++ ++ apply_func(node->key, node->value); ++ ++ apply_to_every_long_map_node(node->right, apply_func); ++ } ++} ++static void apply_to_every_string_map_node(long_map_node* node, unsigned char has_key, void (*apply_func)(char* key, void* value)) ++{ ++ if(node != NULL) ++ { ++ apply_to_every_string_map_node(node->left, has_key, apply_func); ++ ++ if(has_key) ++ { ++ string_map_key_value* kv = (string_map_key_value*)(node->value); ++ apply_func(kv->key, kv->value); ++ } ++ else ++ { ++ apply_func(NULL, node->value); ++ } ++ apply_to_every_string_map_node(node->right, has_key, apply_func); ++ } ++} ++ ++ ++ ++static void get_sorted_node_keys(long_map_node* node, unsigned long* key_list, unsigned long* next_key_index, int depth) ++{ ++ if(node != NULL) ++ { ++ get_sorted_node_keys(node->left, key_list, next_key_index, depth+1); ++ ++ key_list[ *next_key_index ] = node->key; ++ (*next_key_index)++; ++ ++ get_sorted_node_keys(node->right, key_list, next_key_index, depth+1); ++ } ++} ++ ++static void get_sorted_node_values(long_map_node* node, void** value_list, unsigned long* next_value_index, int depth) ++{ ++ if(node != NULL) ++ { ++ get_sorted_node_values(node->left, value_list, next_value_index, depth+1); ++ ++ value_list[ *next_value_index ] = node->value; ++ (*next_value_index)++; ++ ++ get_sorted_node_values(node->right, value_list, next_value_index, depth+1); ++ } ++} ++ ++ ++ ++/* ++ * direction = -1 indicates left subtree updated, direction = 1 for right subtree ++ * update_op = -1 indicates delete node, update_op = 1 for insert node ++ */ ++static signed char rebalance (long_map_node** n, signed char direction, signed char update_op) ++{ ++ /* ++ printf( "original: key = %ld, balance = %d, update_op=%d, direction=%d\n", (*n)->key, (*n)->balance, update_op, direction); ++ */ ++ ++ (*n)->balance = (*n)->balance + (update_op*direction); ++ ++ if( (*n)->balance < -1) ++ { ++ if((*n)->left->balance < 0) ++ { ++ rotate_right(n); ++ (*n)->right->balance = 0; ++ (*n)->balance = 0; ++ } ++ else if((*n)->left->balance == 0) ++ { ++ rotate_right(n); ++ (*n)->right->balance = -1; ++ (*n)->balance = 1; ++ } ++ else if((*n)->left->balance > 0) ++ { ++ rotate_left( &((*n)->left) ); ++ rotate_right(n); ++ /* ++ if( (*n)->balance < 0 ) ++ { ++ (*n)->left->balance = 0; ++ (*n)->right->balance = 1; ++ } ++ else if( (*n)->balance == 0 ) ++ { ++ (*n)->left->balance = 0; ++ (*n)->right->balance = 0; ++ } ++ else if( (*n)->balance > 0 ) ++ { ++ (*n)->left->balance = -1; ++ (*n)->right->balance = 0; ++ } ++ */ ++ (*n)->left->balance = (*n)->balance > 0 ? -1 : 0; ++ (*n)->right->balance = (*n)->balance < 0 ? 1 : 0; ++ (*n)->balance = 0; ++ } ++ } ++ if( (*n)->balance > 1) ++ { ++ if((*n)->right->balance > 0) ++ { ++ rotate_left(n); ++ (*n)->left->balance = 0; ++ (*n)->balance = 0; ++ } ++ else if ((*n)->right->balance == 0) ++ { ++ rotate_left(n); ++ (*n)->left->balance = 1; ++ (*n)->balance = -1; ++ } ++ else if((*n)->right->balance < 0) ++ { ++ rotate_right( &((*n)->right) ); ++ rotate_left(n); ++ /* ++ if( (*n)->balance < 0 ) ++ { ++ (*n)->left->balance = 0; ++ (*n)->right->balance = 1; ++ } ++ else if( (*n)->balance == 0 ) ++ { ++ (*n)->left->balance = 0; ++ (*n)->right->balance = 0; ++ } ++ else if( (*n)->balance > 0 ) ++ { ++ (*n)->left->balance = -1; ++ (*n)->right->balance = 0; ++ } ++ */ ++ (*n)->left->balance = (*n)->balance > 0 ? -1 : 0; ++ (*n)->right->balance = (*n)->balance < 0 ? 1 : 0; ++ (*n)->balance = 0; ++ } ++ } ++ ++ /* ++ printf( "key = %ld, balance = %d\n", (*n)->key, (*n)->balance); ++ */ ++ ++ return (*n)->balance; ++} ++ ++ ++static void rotate_right (long_map_node** parent) ++{ ++ long_map_node* old_parent = *parent; ++ long_map_node* pivot = old_parent->left; ++ old_parent->left = pivot->right; ++ pivot->right = old_parent; ++ ++ *parent = pivot; ++} ++ ++static void rotate_left (long_map_node** parent) ++{ ++ long_map_node* old_parent = *parent; ++ long_map_node* pivot = old_parent->right; ++ old_parent->right = pivot->left; ++ pivot->left = old_parent; ++ ++ *parent = pivot; ++} ++ ++ ++ ++/*************************************************************************** ++ * This algorithm was created for the sdbm database library (a public-domain ++ * reimplementation of ndbm) and seems to work relatively well in ++ * scrambling bits ++ * ++ * ++ * This code was derived from code found at: ++ * http://www.cse.yorku.ca/~oz/hash.html ++ ***************************************************************************/ ++static unsigned long sdbm_string_hash(const char *key) ++{ ++ unsigned long hashed_key = 0; ++ ++ int index = 0; ++ unsigned int nextch; ++ while(key[index] != '\0') ++ { ++ nextch = key[index]; ++ hashed_key = nextch + (hashed_key << 6) + (hashed_key << 16) - hashed_key; ++ index++; ++ } ++ return hashed_key; ++} ++ ++ +--- /dev/null ++++ b/net/ipv4/netfilter/weburl_deps/regexp.c +@@ -0,0 +1,1197 @@ ++/* ++ * regcomp and regexec -- regsub and regerror are elsewhere ++ * @(#)regexp.c 1.3 of 18 April 87 ++ * ++ * Copyright (c) 1986 by University of Toronto. ++ * Written by Henry Spencer. Not derived from licensed software. ++ * ++ * Permission is granted to anyone to use this software for any ++ * purpose on any computer system, and to redistribute it freely, ++ * subject to the following restrictions: ++ * ++ * 1. The author is not responsible for the consequences of use of ++ * this software, no matter how awful, even if they arise ++ * from defects in it. ++ * ++ * 2. The origin of this software must not be misrepresented, either ++ * by explicit claim or by omission. ++ * ++ * 3. Altered versions must be plainly marked as such, and must not ++ * be misrepresented as being the original software. ++ * ++ * Beware that some of this code is subtly aware of the way operator ++ * precedence is structured in regular expressions. Serious changes in ++ * regular-expression syntax might require a total rethink. ++ * ++ * This code was modified by Ethan Sommer to work within the kernel ++ * (it now uses kmalloc etc..) ++ * ++ * Modified slightly by Matthew Strait to use more modern C. ++ */ ++ ++#include "regexp.h" ++#include "regmagic.h" ++ ++/* added by ethan and matt. Lets it work in both kernel and user space. ++(So iptables can use it, for instance.) Yea, it goes both ways... */ ++#if __KERNEL__ ++ #define malloc(foo) kmalloc(foo,GFP_ATOMIC) ++#else ++ #define printk(format,args...) printf(format,##args) ++#endif ++ ++void regerror(char * s) ++{ ++ printk("<3>Regexp: %s\n", s); ++ /* NOTREACHED */ ++} ++ ++/* ++ * The "internal use only" fields in regexp.h are present to pass info from ++ * compile to execute that permits the execute phase to run lots faster on ++ * simple cases. They are: ++ * ++ * regstart char that must begin a match; '\0' if none obvious ++ * reganch is the match anchored (at beginning-of-line only)? ++ * regmust string (pointer into program) that match must include, or NULL ++ * regmlen length of regmust string ++ * ++ * Regstart and reganch permit very fast decisions on suitable starting points ++ * for a match, cutting down the work a lot. Regmust permits fast rejection ++ * of lines that cannot possibly match. The regmust tests are costly enough ++ * that regcomp() supplies a regmust only if the r.e. contains something ++ * potentially expensive (at present, the only such thing detected is * or + ++ * at the start of the r.e., which can involve a lot of backup). Regmlen is ++ * supplied because the test in regexec() needs it and regcomp() is computing ++ * it anyway. ++ */ ++ ++/* ++ * Structure for regexp "program". This is essentially a linear encoding ++ * of a nondeterministic finite-state machine (aka syntax charts or ++ * "railroad normal form" in parsing technology). Each node is an opcode ++ * plus a "next" pointer, possibly plus an operand. "Next" pointers of ++ * all nodes except BRANCH implement concatenation; a "next" pointer with ++ * a BRANCH on both ends of it is connecting two alternatives. (Here we ++ * have one of the subtle syntax dependencies: an individual BRANCH (as ++ * opposed to a collection of them) is never concatenated with anything ++ * because of operator precedence.) The operand of some types of node is ++ * a literal string; for others, it is a node leading into a sub-FSM. In ++ * particular, the operand of a BRANCH node is the first node of the branch. ++ * (NB this is *not* a tree structure: the tail of the branch connects ++ * to the thing following the set of BRANCHes.) The opcodes are: ++ */ ++ ++/* definition number opnd? meaning */ ++#define END 0 /* no End of program. */ ++#define BOL 1 /* no Match "" at beginning of line. */ ++#define EOL 2 /* no Match "" at end of line. */ ++#define ANY 3 /* no Match any one character. */ ++#define ANYOF 4 /* str Match any character in this string. */ ++#define ANYBUT 5 /* str Match any character not in this string. */ ++#define BRANCH 6 /* node Match this alternative, or the next... */ ++#define BACK 7 /* no Match "", "next" ptr points backward. */ ++#define EXACTLY 8 /* str Match this string. */ ++#define NOTHING 9 /* no Match empty string. */ ++#define STAR 10 /* node Match this (simple) thing 0 or more times. */ ++#define PLUS 11 /* node Match this (simple) thing 1 or more times. */ ++#define OPEN 20 /* no Mark this point in input as start of #n. */ ++ /* OPEN+1 is number 1, etc. */ ++#define CLOSE 30 /* no Analogous to OPEN. */ ++ ++/* ++ * Opcode notes: ++ * ++ * BRANCH The set of branches constituting a single choice are hooked ++ * together with their "next" pointers, since precedence prevents ++ * anything being concatenated to any individual branch. The ++ * "next" pointer of the last BRANCH in a choice points to the ++ * thing following the whole choice. This is also where the ++ * final "next" pointer of each individual branch points; each ++ * branch starts with the operand node of a BRANCH node. ++ * ++ * BACK Normal "next" pointers all implicitly point forward; BACK ++ * exists to make loop structures possible. ++ * ++ * STAR,PLUS '?', and complex '*' and '+', are implemented as circular ++ * BRANCH structures using BACK. Simple cases (one character ++ * per match) are implemented with STAR and PLUS for speed ++ * and to minimize recursive plunges. ++ * ++ * OPEN,CLOSE ...are numbered at compile time. ++ */ ++ ++/* ++ * A node is one char of opcode followed by two chars of "next" pointer. ++ * "Next" pointers are stored as two 8-bit pieces, high order first. The ++ * value is a positive offset from the opcode of the node containing it. ++ * An operand, if any, simply follows the node. (Note that much of the ++ * code generation knows about this implicit relationship.) ++ * ++ * Using two bytes for the "next" pointer is vast overkill for most things, ++ * but allows patterns to get big without disasters. ++ */ ++#define OP(p) (*(p)) ++#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377)) ++#define OPERAND(p) ((p) + 3) ++ ++/* ++ * See regmagic.h for one further detail of program structure. ++ */ ++ ++ ++/* ++ * Utility definitions. ++ */ ++#ifndef CHARBITS ++#define UCHARAT(p) ((int)*(unsigned char *)(p)) ++#else ++#define UCHARAT(p) ((int)*(p)&CHARBITS) ++#endif ++ ++#define FAIL(m) { regerror(m); return(NULL); } ++#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?') ++#define META "^$.[()|?+*\\" ++ ++/* ++ * Flags to be passed up and down. ++ */ ++#define HASWIDTH 01 /* Known never to match null string. */ ++#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */ ++#define SPSTART 04 /* Starts with * or +. */ ++#define WORST 0 /* Worst case. */ ++ ++/* ++ * Global work variables for regcomp(). ++ */ ++struct match_globals { ++char *reginput; /* String-input pointer. */ ++char *regbol; /* Beginning of input, for ^ check. */ ++char **regstartp; /* Pointer to startp array. */ ++char **regendp; /* Ditto for endp. */ ++char *regparse; /* Input-scan pointer. */ ++int regnpar; /* () count. */ ++char regdummy; ++char *regcode; /* Code-emit pointer; ®dummy = don't. */ ++long regsize; /* Code size. */ ++}; ++ ++/* ++ * Forward declarations for regcomp()'s friends. ++ */ ++#ifndef STATIC ++#define STATIC static ++#endif ++STATIC char *reg(struct match_globals *g, int paren,int *flagp); ++STATIC char *regbranch(struct match_globals *g, int *flagp); ++STATIC char *regpiece(struct match_globals *g, int *flagp); ++STATIC char *regatom(struct match_globals *g, int *flagp); ++STATIC char *regnode(struct match_globals *g, char op); ++STATIC char *regnext(struct match_globals *g, char *p); ++STATIC void regc(struct match_globals *g, char b); ++STATIC void reginsert(struct match_globals *g, char op, char *opnd); ++STATIC void regtail(struct match_globals *g, char *p, char *val); ++STATIC void regoptail(struct match_globals *g, char *p, char *val); ++ ++ ++__kernel_size_t my_strcspn(const char *s1,const char *s2) ++{ ++ char *scan1; ++ char *scan2; ++ int count; ++ ++ count = 0; ++ for (scan1 = (char *)s1; *scan1 != '\0'; scan1++) { ++ for (scan2 = (char *)s2; *scan2 != '\0';) /* ++ moved down. */ ++ if (*scan1 == *scan2++) ++ return(count); ++ count++; ++ } ++ return(count); ++} ++ ++/* ++ - regcomp - compile a regular expression into internal code ++ * ++ * We can't allocate space until we know how big the compiled form will be, ++ * but we can't compile it (and thus know how big it is) until we've got a ++ * place to put the code. So we cheat: we compile it twice, once with code ++ * generation turned off and size counting turned on, and once "for real". ++ * This also means that we don't allocate space until we are sure that the ++ * thing really will compile successfully, and we never have to move the ++ * code and thus invalidate pointers into it. (Note that it has to be in ++ * one piece because free() must be able to free it all.) ++ * ++ * Beware that the optimization-preparation code in here knows about some ++ * of the structure of the compiled regexp. ++ */ ++regexp * ++regcomp(char *exp,int *patternsize) ++{ ++ register regexp *r; ++ register char *scan; ++ register char *longest; ++ register int len; ++ int flags; ++ struct match_globals g; ++ ++ /* commented out by ethan ++ extern char *malloc(); ++ */ ++ ++ if (exp == NULL) ++ FAIL("NULL argument"); ++ ++ /* First pass: determine size, legality. */ ++ g.regparse = exp; ++ g.regnpar = 1; ++ g.regsize = 0L; ++ g.regcode = &g.regdummy; ++ regc(&g, MAGIC); ++ if (reg(&g, 0, &flags) == NULL) ++ return(NULL); ++ ++ /* Small enough for pointer-storage convention? */ ++ if (g.regsize >= 32767L) /* Probably could be 65535L. */ ++ FAIL("regexp too big"); ++ ++ /* Allocate space. */ ++ *patternsize=sizeof(regexp) + (unsigned)g.regsize; ++ r = (regexp *)malloc(sizeof(regexp) + (unsigned)g.regsize); ++ if (r == NULL) ++ FAIL("out of space"); ++ ++ /* Second pass: emit code. */ ++ g.regparse = exp; ++ g.regnpar = 1; ++ g.regcode = r->program; ++ regc(&g, MAGIC); ++ if (reg(&g, 0, &flags) == NULL) ++ return(NULL); ++ ++ /* Dig out information for optimizations. */ ++ r->regstart = '\0'; /* Worst-case defaults. */ ++ r->reganch = 0; ++ r->regmust = NULL; ++ r->regmlen = 0; ++ scan = r->program+1; /* First BRANCH. */ ++ if (OP(regnext(&g, scan)) == END) { /* Only one top-level choice. */ ++ scan = OPERAND(scan); ++ ++ /* Starting-point info. */ ++ if (OP(scan) == EXACTLY) ++ r->regstart = *OPERAND(scan); ++ else if (OP(scan) == BOL) ++ r->reganch++; ++ ++ /* ++ * If there's something expensive in the r.e., find the ++ * longest literal string that must appear and make it the ++ * regmust. Resolve ties in favor of later strings, since ++ * the regstart check works with the beginning of the r.e. ++ * and avoiding duplication strengthens checking. Not a ++ * strong reason, but sufficient in the absence of others. ++ */ ++ if (flags&SPSTART) { ++ longest = NULL; ++ len = 0; ++ for (; scan != NULL; scan = regnext(&g, scan)) ++ if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { ++ longest = OPERAND(scan); ++ len = strlen(OPERAND(scan)); ++ } ++ r->regmust = longest; ++ r->regmlen = len; ++ } ++ } ++ ++ return(r); ++} ++ ++/* ++ - reg - regular expression, i.e. main body or parenthesized thing ++ * ++ * Caller must absorb opening parenthesis. ++ * ++ * Combining parenthesis handling with the base level of regular expression ++ * is a trifle forced, but the need to tie the tails of the branches to what ++ * follows makes it hard to avoid. ++ */ ++static char * ++reg(struct match_globals *g, int paren, int *flagp /* Parenthesized? */ ) ++{ ++ register char *ret; ++ register char *br; ++ register char *ender; ++ register int parno = 0; /* 0 makes gcc happy */ ++ int flags; ++ ++ *flagp = HASWIDTH; /* Tentatively. */ ++ ++ /* Make an OPEN node, if parenthesized. */ ++ if (paren) { ++ if (g->regnpar >= NSUBEXP) ++ FAIL("too many ()"); ++ parno = g->regnpar; ++ g->regnpar++; ++ ret = regnode(g, OPEN+parno); ++ } else ++ ret = NULL; ++ ++ /* Pick up the branches, linking them together. */ ++ br = regbranch(g, &flags); ++ if (br == NULL) ++ return(NULL); ++ if (ret != NULL) ++ regtail(g, ret, br); /* OPEN -> first. */ ++ else ++ ret = br; ++ if (!(flags&HASWIDTH)) ++ *flagp &= ~HASWIDTH; ++ *flagp |= flags&SPSTART; ++ while (*g->regparse == '|') { ++ g->regparse++; ++ br = regbranch(g, &flags); ++ if (br == NULL) ++ return(NULL); ++ regtail(g, ret, br); /* BRANCH -> BRANCH. */ ++ if (!(flags&HASWIDTH)) ++ *flagp &= ~HASWIDTH; ++ *flagp |= flags&SPSTART; ++ } ++ ++ /* Make a closing node, and hook it on the end. */ ++ ender = regnode(g, (paren) ? CLOSE+parno : END); ++ regtail(g, ret, ender); ++ ++ /* Hook the tails of the branches to the closing node. */ ++ for (br = ret; br != NULL; br = regnext(g, br)) ++ regoptail(g, br, ender); ++ ++ /* Check for proper termination. */ ++ if (paren && *g->regparse++ != ')') { ++ FAIL("unmatched ()"); ++ } else if (!paren && *g->regparse != '\0') { ++ if (*g->regparse == ')') { ++ FAIL("unmatched ()"); ++ } else ++ FAIL("junk on end"); /* "Can't happen". */ ++ /* NOTREACHED */ ++ } ++ ++ return(ret); ++} ++ ++/* ++ - regbranch - one alternative of an | operator ++ * ++ * Implements the concatenation operator. ++ */ ++static char * ++regbranch(struct match_globals *g, int *flagp) ++{ ++ register char *ret; ++ register char *chain; ++ register char *latest; ++ int flags; ++ ++ *flagp = WORST; /* Tentatively. */ ++ ++ ret = regnode(g, BRANCH); ++ chain = NULL; ++ while (*g->regparse != '\0' && *g->regparse != '|' && *g->regparse != ')') { ++ latest = regpiece(g, &flags); ++ if (latest == NULL) ++ return(NULL); ++ *flagp |= flags&HASWIDTH; ++ if (chain == NULL) /* First piece. */ ++ *flagp |= flags&SPSTART; ++ else ++ regtail(g, chain, latest); ++ chain = latest; ++ } ++ if (chain == NULL) /* Loop ran zero times. */ ++ (void) regnode(g, NOTHING); ++ ++ return(ret); ++} ++ ++/* ++ - regpiece - something followed by possible [*+?] ++ * ++ * Note that the branching code sequences used for ? and the general cases ++ * of * and + are somewhat optimized: they use the same NOTHING node as ++ * both the endmarker for their branch list and the body of the last branch. ++ * It might seem that this node could be dispensed with entirely, but the ++ * endmarker role is not redundant. ++ */ ++static char * ++regpiece(struct match_globals *g, int *flagp) ++{ ++ register char *ret; ++ register char op; ++ register char *next; ++ int flags; ++ ++ ret = regatom(g, &flags); ++ if (ret == NULL) ++ return(NULL); ++ ++ op = *g->regparse; ++ if (!ISMULT(op)) { ++ *flagp = flags; ++ return(ret); ++ } ++ ++ if (!(flags&HASWIDTH) && op != '?') ++ FAIL("*+ operand could be empty"); ++ *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH); ++ ++ if (op == '*' && (flags&SIMPLE)) ++ reginsert(g, STAR, ret); ++ else if (op == '*') { ++ /* Emit x* as (x&|), where & means "self". */ ++ reginsert(g, BRANCH, ret); /* Either x */ ++ regoptail(g, ret, regnode(g, BACK)); /* and loop */ ++ regoptail(g, ret, ret); /* back */ ++ regtail(g, ret, regnode(g, BRANCH)); /* or */ ++ regtail(g, ret, regnode(g, NOTHING)); /* null. */ ++ } else if (op == '+' && (flags&SIMPLE)) ++ reginsert(g, PLUS, ret); ++ else if (op == '+') { ++ /* Emit x+ as x(&|), where & means "self". */ ++ next = regnode(g, BRANCH); /* Either */ ++ regtail(g, ret, next); ++ regtail(g, regnode(g, BACK), ret); /* loop back */ ++ regtail(g, next, regnode(g, BRANCH)); /* or */ ++ regtail(g, ret, regnode(g, NOTHING)); /* null. */ ++ } else if (op == '?') { ++ /* Emit x? as (x|) */ ++ reginsert(g, BRANCH, ret); /* Either x */ ++ regtail(g, ret, regnode(g, BRANCH)); /* or */ ++ next = regnode(g, NOTHING); /* null. */ ++ regtail(g, ret, next); ++ regoptail(g, ret, next); ++ } ++ g->regparse++; ++ if (ISMULT(*g->regparse)) ++ FAIL("nested *?+"); ++ ++ return(ret); ++} ++ ++/* ++ - regatom - the lowest level ++ * ++ * Optimization: gobbles an entire sequence of ordinary characters so that ++ * it can turn them into a single node, which is smaller to store and ++ * faster to run. Backslashed characters are exceptions, each becoming a ++ * separate node; the code is simpler that way and it's not worth fixing. ++ */ ++static char * ++regatom(struct match_globals *g, int *flagp) ++{ ++ register char *ret; ++ int flags; ++ ++ *flagp = WORST; /* Tentatively. */ ++ ++ switch (*g->regparse++) { ++ case '^': ++ ret = regnode(g, BOL); ++ break; ++ case '$': ++ ret = regnode(g, EOL); ++ break; ++ case '.': ++ ret = regnode(g, ANY); ++ *flagp |= HASWIDTH|SIMPLE; ++ break; ++ case '[': { ++ register int class; ++ register int classend; ++ ++ if (*g->regparse == '^') { /* Complement of range. */ ++ ret = regnode(g, ANYBUT); ++ g->regparse++; ++ } else ++ ret = regnode(g, ANYOF); ++ if (*g->regparse == ']' || *g->regparse == '-') ++ regc(g, *g->regparse++); ++ while (*g->regparse != '\0' && *g->regparse != ']') { ++ if (*g->regparse == '-') { ++ g->regparse++; ++ if (*g->regparse == ']' || *g->regparse == '\0') ++ regc(g, '-'); ++ else { ++ class = UCHARAT(g->regparse-2)+1; ++ classend = UCHARAT(g->regparse); ++ if (class > classend+1) ++ FAIL("invalid [] range"); ++ for (; class <= classend; class++) ++ regc(g, class); ++ g->regparse++; ++ } ++ } else ++ regc(g, *g->regparse++); ++ } ++ regc(g, '\0'); ++ if (*g->regparse != ']') ++ FAIL("unmatched []"); ++ g->regparse++; ++ *flagp |= HASWIDTH|SIMPLE; ++ } ++ break; ++ case '(': ++ ret = reg(g, 1, &flags); ++ if (ret == NULL) ++ return(NULL); ++ *flagp |= flags&(HASWIDTH|SPSTART); ++ break; ++ case '\0': ++ case '|': ++ case ')': ++ FAIL("internal urp"); /* Supposed to be caught earlier. */ ++ break; ++ case '?': ++ case '+': ++ case '*': ++ FAIL("?+* follows nothing"); ++ break; ++ case '\\': ++ if (*g->regparse == '\0') ++ FAIL("trailing \\"); ++ ret = regnode(g, EXACTLY); ++ regc(g, *g->regparse++); ++ regc(g, '\0'); ++ *flagp |= HASWIDTH|SIMPLE; ++ break; ++ default: { ++ register int len; ++ register char ender; ++ ++ g->regparse--; ++ len = my_strcspn((const char *)g->regparse, (const char *)META); ++ if (len <= 0) ++ FAIL("internal disaster"); ++ ender = *(g->regparse+len); ++ if (len > 1 && ISMULT(ender)) ++ len--; /* Back off clear of ?+* operand. */ ++ *flagp |= HASWIDTH; ++ if (len == 1) ++ *flagp |= SIMPLE; ++ ret = regnode(g, EXACTLY); ++ while (len > 0) { ++ regc(g, *g->regparse++); ++ len--; ++ } ++ regc(g, '\0'); ++ } ++ break; ++ } ++ ++ return(ret); ++} ++ ++/* ++ - regnode - emit a node ++ */ ++static char * /* Location. */ ++regnode(struct match_globals *g, char op) ++{ ++ register char *ret; ++ register char *ptr; ++ ++ ret = g->regcode; ++ if (ret == &g->regdummy) { ++ g->regsize += 3; ++ return(ret); ++ } ++ ++ ptr = ret; ++ *ptr++ = op; ++ *ptr++ = '\0'; /* Null "next" pointer. */ ++ *ptr++ = '\0'; ++ g->regcode = ptr; ++ ++ return(ret); ++} ++ ++/* ++ - regc - emit (if appropriate) a byte of code ++ */ ++static void ++regc(struct match_globals *g, char b) ++{ ++ if (g->regcode != &g->regdummy) ++ *g->regcode++ = b; ++ else ++ g->regsize++; ++} ++ ++/* ++ - reginsert - insert an operator in front of already-emitted operand ++ * ++ * Means relocating the operand. ++ */ ++static void ++reginsert(struct match_globals *g, char op, char* opnd) ++{ ++ register char *src; ++ register char *dst; ++ register char *place; ++ ++ if (g->regcode == &g->regdummy) { ++ g->regsize += 3; ++ return; ++ } ++ ++ src = g->regcode; ++ g->regcode += 3; ++ dst = g->regcode; ++ while (src > opnd) ++ *--dst = *--src; ++ ++ place = opnd; /* Op node, where operand used to be. */ ++ *place++ = op; ++ *place++ = '\0'; ++ *place++ = '\0'; ++} ++ ++/* ++ - regtail - set the next-pointer at the end of a node chain ++ */ ++static void ++regtail(struct match_globals *g, char *p, char *val) ++{ ++ register char *scan; ++ register char *temp; ++ register int offset; ++ ++ if (p == &g->regdummy) ++ return; ++ ++ /* Find last node. */ ++ scan = p; ++ for (;;) { ++ temp = regnext(g, scan); ++ if (temp == NULL) ++ break; ++ scan = temp; ++ } ++ ++ if (OP(scan) == BACK) ++ offset = scan - val; ++ else ++ offset = val - scan; ++ *(scan+1) = (offset>>8)&0377; ++ *(scan+2) = offset&0377; ++} ++ ++/* ++ - regoptail - regtail on operand of first argument; nop if operandless ++ */ ++static void ++regoptail(struct match_globals *g, char *p, char *val) ++{ ++ /* "Operandless" and "op != BRANCH" are synonymous in practice. */ ++ if (p == NULL || p == &g->regdummy || OP(p) != BRANCH) ++ return; ++ regtail(g, OPERAND(p), val); ++} ++ ++/* ++ * regexec and friends ++ */ ++ ++ ++/* ++ * Forwards. ++ */ ++STATIC int regtry(struct match_globals *g, regexp *prog, char *string); ++STATIC int regmatch(struct match_globals *g, char *prog); ++STATIC int regrepeat(struct match_globals *g, char *p); ++ ++#ifdef DEBUG ++int regnarrate = 0; ++void regdump(); ++STATIC char *regprop(char *op); ++#endif ++ ++/* ++ - regexec - match a regexp against a string ++ */ ++int ++regexec(regexp *prog, char *string) ++{ ++ register char *s; ++ struct match_globals g; ++ ++ /* Be paranoid... */ ++ if (prog == NULL || string == NULL) { ++ printk("<3>Regexp: NULL parameter\n"); ++ return(0); ++ } ++ ++ /* Check validity of program. */ ++ if (UCHARAT(prog->program) != MAGIC) { ++ printk("<3>Regexp: corrupted program\n"); ++ return(0); ++ } ++ ++ /* If there is a "must appear" string, look for it. */ ++ if (prog->regmust != NULL) { ++ s = string; ++ while ((s = strchr(s, prog->regmust[0])) != NULL) { ++ if (strncmp(s, prog->regmust, prog->regmlen) == 0) ++ break; /* Found it. */ ++ s++; ++ } ++ if (s == NULL) /* Not present. */ ++ return(0); ++ } ++ ++ /* Mark beginning of line for ^ . */ ++ g.regbol = string; ++ ++ /* Simplest case: anchored match need be tried only once. */ ++ if (prog->reganch) ++ return(regtry(&g, prog, string)); ++ ++ /* Messy cases: unanchored match. */ ++ s = string; ++ if (prog->regstart != '\0') ++ /* We know what char it must start with. */ ++ while ((s = strchr(s, prog->regstart)) != NULL) { ++ if (regtry(&g, prog, s)) ++ return(1); ++ s++; ++ } ++ else ++ /* We don't -- general case. */ ++ do { ++ if (regtry(&g, prog, s)) ++ return(1); ++ } while (*s++ != '\0'); ++ ++ /* Failure. */ ++ return(0); ++} ++ ++/* ++ - regtry - try match at specific point ++ */ ++static int /* 0 failure, 1 success */ ++regtry(struct match_globals *g, regexp *prog, char *string) ++{ ++ register int i; ++ register char **sp; ++ register char **ep; ++ ++ g->reginput = string; ++ g->regstartp = prog->startp; ++ g->regendp = prog->endp; ++ ++ sp = prog->startp; ++ ep = prog->endp; ++ for (i = NSUBEXP; i > 0; i--) { ++ *sp++ = NULL; ++ *ep++ = NULL; ++ } ++ if (regmatch(g, prog->program + 1)) { ++ prog->startp[0] = string; ++ prog->endp[0] = g->reginput; ++ return(1); ++ } else ++ return(0); ++} ++ ++/* ++ - regmatch - main matching routine ++ * ++ * Conceptually the strategy is simple: check to see whether the current ++ * node matches, call self recursively to see whether the rest matches, ++ * and then act accordingly. In practice we make some effort to avoid ++ * recursion, in particular by going through "ordinary" nodes (that don't ++ * need to know whether the rest of the match failed) by a loop instead of ++ * by recursion. ++ */ ++static int /* 0 failure, 1 success */ ++regmatch(struct match_globals *g, char *prog) ++{ ++ register char *scan = prog; /* Current node. */ ++ char *next; /* Next node. */ ++ ++#ifdef DEBUG ++ if (scan != NULL && regnarrate) ++ fprintf(stderr, "%s(\n", regprop(scan)); ++#endif ++ while (scan != NULL) { ++#ifdef DEBUG ++ if (regnarrate) ++ fprintf(stderr, "%s...\n", regprop(scan)); ++#endif ++ next = regnext(g, scan); ++ ++ switch (OP(scan)) { ++ case BOL: ++ if (g->reginput != g->regbol) ++ return(0); ++ break; ++ case EOL: ++ if (*g->reginput != '\0') ++ return(0); ++ break; ++ case ANY: ++ if (*g->reginput == '\0') ++ return(0); ++ g->reginput++; ++ break; ++ case EXACTLY: { ++ register int len; ++ register char *opnd; ++ ++ opnd = OPERAND(scan); ++ /* Inline the first character, for speed. */ ++ if (*opnd != *g->reginput) ++ return(0); ++ len = strlen(opnd); ++ if (len > 1 && strncmp(opnd, g->reginput, len) != 0) ++ return(0); ++ g->reginput += len; ++ } ++ break; ++ case ANYOF: ++ if (*g->reginput == '\0' || strchr(OPERAND(scan), *g->reginput) == NULL) ++ return(0); ++ g->reginput++; ++ break; ++ case ANYBUT: ++ if (*g->reginput == '\0' || strchr(OPERAND(scan), *g->reginput) != NULL) ++ return(0); ++ g->reginput++; ++ break; ++ case NOTHING: ++ case BACK: ++ break; ++ case OPEN+1: ++ case OPEN+2: ++ case OPEN+3: ++ case OPEN+4: ++ case OPEN+5: ++ case OPEN+6: ++ case OPEN+7: ++ case OPEN+8: ++ case OPEN+9: { ++ register int no; ++ register char *save; ++ ++ no = OP(scan) - OPEN; ++ save = g->reginput; ++ ++ if (regmatch(g, next)) { ++ /* ++ * Don't set startp if some later ++ * invocation of the same parentheses ++ * already has. ++ */ ++ if (g->regstartp[no] == NULL) ++ g->regstartp[no] = save; ++ return(1); ++ } else ++ return(0); ++ } ++ break; ++ case CLOSE+1: ++ case CLOSE+2: ++ case CLOSE+3: ++ case CLOSE+4: ++ case CLOSE+5: ++ case CLOSE+6: ++ case CLOSE+7: ++ case CLOSE+8: ++ case CLOSE+9: ++ { ++ register int no; ++ register char *save; ++ ++ no = OP(scan) - CLOSE; ++ save = g->reginput; ++ ++ if (regmatch(g, next)) { ++ /* ++ * Don't set endp if some later ++ * invocation of the same parentheses ++ * already has. ++ */ ++ if (g->regendp[no] == NULL) ++ g->regendp[no] = save; ++ return(1); ++ } else ++ return(0); ++ } ++ break; ++ case BRANCH: { ++ register char *save; ++ ++ if (OP(next) != BRANCH) /* No choice. */ ++ next = OPERAND(scan); /* Avoid recursion. */ ++ else { ++ do { ++ save = g->reginput; ++ if (regmatch(g, OPERAND(scan))) ++ return(1); ++ g->reginput = save; ++ scan = regnext(g, scan); ++ } while (scan != NULL && OP(scan) == BRANCH); ++ return(0); ++ /* NOTREACHED */ ++ } ++ } ++ break; ++ case STAR: ++ case PLUS: { ++ register char nextch; ++ register int no; ++ register char *save; ++ register int min; ++ ++ /* ++ * Lookahead to avoid useless match attempts ++ * when we know what character comes next. ++ */ ++ nextch = '\0'; ++ if (OP(next) == EXACTLY) ++ nextch = *OPERAND(next); ++ min = (OP(scan) == STAR) ? 0 : 1; ++ save = g->reginput; ++ no = regrepeat(g, OPERAND(scan)); ++ while (no >= min) { ++ /* If it could work, try it. */ ++ if (nextch == '\0' || *g->reginput == nextch) ++ if (regmatch(g, next)) ++ return(1); ++ /* Couldn't or didn't -- back up. */ ++ no--; ++ g->reginput = save + no; ++ } ++ return(0); ++ } ++ break; ++ case END: ++ return(1); /* Success! */ ++ break; ++ default: ++ printk("<3>Regexp: memory corruption\n"); ++ return(0); ++ break; ++ } ++ ++ scan = next; ++ } ++ ++ /* ++ * We get here only if there's trouble -- normally "case END" is ++ * the terminating point. ++ */ ++ printk("<3>Regexp: corrupted pointers\n"); ++ return(0); ++} ++ ++/* ++ - regrepeat - repeatedly match something simple, report how many ++ */ ++static int ++regrepeat(struct match_globals *g, char *p) ++{ ++ register int count = 0; ++ register char *scan; ++ register char *opnd; ++ ++ scan = g->reginput; ++ opnd = OPERAND(p); ++ switch (OP(p)) { ++ case ANY: ++ count = strlen(scan); ++ scan += count; ++ break; ++ case EXACTLY: ++ while (*opnd == *scan) { ++ count++; ++ scan++; ++ } ++ break; ++ case ANYOF: ++ while (*scan != '\0' && strchr(opnd, *scan) != NULL) { ++ count++; ++ scan++; ++ } ++ break; ++ case ANYBUT: ++ while (*scan != '\0' && strchr(opnd, *scan) == NULL) { ++ count++; ++ scan++; ++ } ++ break; ++ default: /* Oh dear. Called inappropriately. */ ++ printk("<3>Regexp: internal foulup\n"); ++ count = 0; /* Best compromise. */ ++ break; ++ } ++ g->reginput = scan; ++ ++ return(count); ++} ++ ++/* ++ - regnext - dig the "next" pointer out of a node ++ */ ++static char* ++regnext(struct match_globals *g, char *p) ++{ ++ register int offset; ++ ++ if (p == &g->regdummy) ++ return(NULL); ++ ++ offset = NEXT(p); ++ if (offset == 0) ++ return(NULL); ++ ++ if (OP(p) == BACK) ++ return(p-offset); ++ else ++ return(p+offset); ++} ++ ++#ifdef DEBUG ++ ++STATIC char *regprop(); ++ ++/* ++ - regdump - dump a regexp onto stdout in vaguely comprehensible form ++ */ ++void ++regdump(regexp *r) ++{ ++ register char *s; ++ register char op = EXACTLY; /* Arbitrary non-END op. */ ++ register char *next; ++ /* extern char *strchr(); */ ++ ++ ++ s = r->program + 1; ++ while (op != END) { /* While that wasn't END last time... */ ++ op = OP(s); ++ printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */ ++ next = regnext(s); ++ if (next == NULL) /* Next ptr. */ ++ printf("(0)"); ++ else ++ printf("(%d)", (s-r->program)+(next-s)); ++ s += 3; ++ if (op == ANYOF || op == ANYBUT || op == EXACTLY) { ++ /* Literal string, where present. */ ++ while (*s != '\0') { ++ putchar(*s); ++ s++; ++ } ++ s++; ++ } ++ putchar('\n'); ++ } ++ ++ /* Header fields of interest. */ ++ if (r->regstart != '\0') ++ printf("start `%c' ", r->regstart); ++ if (r->reganch) ++ printf("anchored "); ++ if (r->regmust != NULL) ++ printf("must have \"%s\"", r->regmust); ++ printf("\n"); ++} ++ ++/* ++ - regprop - printable representation of opcode ++ */ ++static char * ++regprop(char *op) ++{ ++#define BUFLEN 50 ++ register char *p; ++ static char buf[BUFLEN]; ++ ++ strcpy(buf, ":"); ++ ++ switch (OP(op)) { ++ case BOL: ++ p = "BOL"; ++ break; ++ case EOL: ++ p = "EOL"; ++ break; ++ case ANY: ++ p = "ANY"; ++ break; ++ case ANYOF: ++ p = "ANYOF"; ++ break; ++ case ANYBUT: ++ p = "ANYBUT"; ++ break; ++ case BRANCH: ++ p = "BRANCH"; ++ break; ++ case EXACTLY: ++ p = "EXACTLY"; ++ break; ++ case NOTHING: ++ p = "NOTHING"; ++ break; ++ case BACK: ++ p = "BACK"; ++ break; ++ case END: ++ p = "END"; ++ break; ++ case OPEN+1: ++ case OPEN+2: ++ case OPEN+3: ++ case OPEN+4: ++ case OPEN+5: ++ case OPEN+6: ++ case OPEN+7: ++ case OPEN+8: ++ case OPEN+9: ++ snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "OPEN%d", OP(op)-OPEN); ++ p = NULL; ++ break; ++ case CLOSE+1: ++ case CLOSE+2: ++ case CLOSE+3: ++ case CLOSE+4: ++ case CLOSE+5: ++ case CLOSE+6: ++ case CLOSE+7: ++ case CLOSE+8: ++ case CLOSE+9: ++ snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "CLOSE%d", OP(op)-CLOSE); ++ p = NULL; ++ break; ++ case STAR: ++ p = "STAR"; ++ break; ++ case PLUS: ++ p = "PLUS"; ++ break; ++ default: ++ printk("<3>Regexp: corrupted opcode\n"); ++ break; ++ } ++ if (p != NULL) ++ strncat(buf, p, BUFLEN-strlen(buf)); ++ return(buf); ++} ++#endif ++ ++ +--- /dev/null ++++ b/net/ipv4/netfilter/weburl_deps/regexp.h +@@ -0,0 +1,41 @@ ++/* ++ * Definitions etc. for regexp(3) routines. ++ * ++ * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof], ++ * not the System V one. ++ */ ++ ++#ifndef REGEXP_H ++#define REGEXP_H ++ ++ ++/* ++http://www.opensource.apple.com/darwinsource/10.3/expect-1/expect/expect.h , ++which contains a version of this library, says: ++ ++ * ++ * NSUBEXP must be at least 10, and no greater than 117 or the parser ++ * will not work properly. ++ * ++ ++However, it looks rather like this library is limited to 10. If you think ++otherwise, let us know. ++*/ ++ ++#define NSUBEXP 10 ++typedef struct regexp { ++ char *startp[NSUBEXP]; ++ char *endp[NSUBEXP]; ++ char regstart; /* Internal use only. */ ++ char reganch; /* Internal use only. */ ++ char *regmust; /* Internal use only. */ ++ int regmlen; /* Internal use only. */ ++ char program[1]; /* Unwarranted chumminess with compiler. */ ++} regexp; ++ ++regexp * regcomp(char *exp, int *patternsize); ++int regexec(regexp *prog, char *string); ++void regsub(regexp *prog, char *source, char *dest); ++void regerror(char *s); ++ ++#endif +--- /dev/null ++++ b/net/ipv4/netfilter/weburl_deps/regmagic.h +@@ -0,0 +1,5 @@ ++/* ++ * The first byte of the regexp internal "program" is actually this magic ++ * number; the start node begins in the second byte. ++ */ ++#define MAGIC 0234 +--- /dev/null ++++ b/net/ipv4/netfilter/weburl_deps/regsub.c +@@ -0,0 +1,95 @@ ++/* ++ * regsub ++ * @(#)regsub.c 1.3 of 2 April 86 ++ * ++ * Copyright (c) 1986 by University of Toronto. ++ * Written by Henry Spencer. Not derived from licensed software. ++ * ++ * Permission is granted to anyone to use this software for any ++ * purpose on any computer system, and to redistribute it freely, ++ * subject to the following restrictions: ++ * ++ * 1. The author is not responsible for the consequences of use of ++ * this software, no matter how awful, even if they arise ++ * from defects in it. ++ * ++ * 2. The origin of this software must not be misrepresented, either ++ * by explicit claim or by omission. ++ * ++ * 3. Altered versions must be plainly marked as such, and must not ++ * be misrepresented as being the original software. ++ * ++ * ++ * This code was modified by Ethan Sommer to work within the kernel ++ * (it now uses kmalloc etc..) ++ * ++ */ ++#include "regexp.h" ++#include "regmagic.h" ++#include ++ ++ ++#ifndef CHARBITS ++#define UCHARAT(p) ((int)*(unsigned char *)(p)) ++#else ++#define UCHARAT(p) ((int)*(p)&CHARBITS) ++#endif ++ ++#if 0 ++//void regerror(char * s) ++//{ ++// printk("regexp(3): %s", s); ++// /* NOTREACHED */ ++//} ++#endif ++ ++/* ++ - regsub - perform substitutions after a regexp match ++ */ ++void ++regsub(regexp * prog, char * source, char * dest) ++{ ++ register char *src; ++ register char *dst; ++ register char c; ++ register int no; ++ register int len; ++ ++ /* Not necessary and gcc doesn't like it -MLS */ ++ /*extern char *strncpy();*/ ++ ++ if (prog == NULL || source == NULL || dest == NULL) { ++ regerror("NULL parm to regsub"); ++ return; ++ } ++ if (UCHARAT(prog->program) != MAGIC) { ++ regerror("damaged regexp fed to regsub"); ++ return; ++ } ++ ++ src = source; ++ dst = dest; ++ while ((c = *src++) != '\0') { ++ if (c == '&') ++ no = 0; ++ else if (c == '\\' && '0' <= *src && *src <= '9') ++ no = *src++ - '0'; ++ else ++ no = -1; ++ ++ if (no < 0) { /* Ordinary character. */ ++ if (c == '\\' && (*src == '\\' || *src == '&')) ++ c = *src++; ++ *dst++ = c; ++ } else if (prog->startp[no] != NULL && prog->endp[no] != NULL) { ++ len = prog->endp[no] - prog->startp[no]; ++ (void) strncpy(dst, prog->startp[no], len); ++ dst += len; ++ if (len != 0 && *(dst-1) == '\0') { /* strncpy hit NUL. */ ++ regerror("damaged match string"); ++ return; ++ } ++ } ++ } ++ *dst++ = '\0'; ++} +--- /dev/null ++++ b/net/ipv4/netfilter/weburl_deps/tree_map.h +@@ -0,0 +1,1084 @@ ++/* ++ * Copyright © 2008 by Eric Bishop ++ * ++ * This work 'as-is' we provide. ++ * No warranty, express or implied. ++ * We've done our best, ++ * to debug and test. ++ * Liability for damages denied. ++ * ++ * Permission is granted hereby, ++ * to copy, share, and modify. ++ * Use as is fit, ++ * free or for profit. ++ * On this notice these rights rely. ++ * ++ * ++ * ++ * Note that unlike other portions of Gargoyle this code ++ * does not fall under the GPL, but the rather whimsical ++ * 'Poetic License' above. ++ * ++ * Basically, this library contains a bunch of utilities ++ * that I find useful. I'm sure other libraries exist ++ * that are just as good or better, but I like these tools ++ * because I personally wrote them, so I know their quirks. ++ * (i.e. I know where the bodies are buried). I want to ++ * make sure that I can re-use these utilities for whatever ++ * code I may want to write in the future be it ++ * proprietary or open-source, so I've put them under ++ * a very, very permissive license. ++ * ++ * If you find this code useful, use it. If not, don't. ++ * I really don't care. ++ * ++ */ ++ ++ ++#if __KERNEL__ ++ #define malloc(foo) kmalloc(foo,GFP_ATOMIC) ++ #define free(foo) kfree(foo) ++ #define printf(format,args...) printk(format,##args) ++ ++ /* kernel strdup */ ++ static inline char *kernel_strdup(const char *str); ++ static inline char *kernel_strdup(const char *str) ++ { ++ char *tmp; ++ long int s; ++ s=strlen(str) + 1; ++ tmp = kmalloc(s, GFP_ATOMIC); ++ if (tmp != NULL) ++ { ++ memcpy(tmp, str, s); ++ } ++ return tmp; ++ } ++ #define strdup kernel_strdup ++ ++#endif ++ ++ ++ ++/* tree_map structs / prototypes */ ++typedef struct long_tree_map_node ++{ ++ unsigned long key; ++ void* value; ++ ++ signed char balance; ++ struct long_tree_map_node* left; ++ struct long_tree_map_node* right; ++} long_map_node; ++ ++typedef struct ++{ ++ long_map_node* root; ++ unsigned long num_elements; ++ ++}long_map; ++ ++typedef struct ++{ ++ long_map lm; ++ unsigned char store_keys; ++ unsigned long num_elements; ++ ++}string_map; ++ ++ ++ ++/* long map functions */ ++long_map* initialize_long_map(void); ++void* get_long_map_element(long_map* map, unsigned long key); ++void* get_smallest_long_map_element(long_map* map, unsigned long* smallest_key); ++void* get_largest_long_map_element(long_map* map, unsigned long* largest_key); ++void* remove_smallest_long_map_element(long_map* map, unsigned long* smallest_key); ++void* remove_largest_long_map_element(long_map* map, unsigned long* largest_key); ++void* set_long_map_element(long_map* map, unsigned long key, void* value); ++void* remove_long_map_element(long_map* map, unsigned long key); ++unsigned long* get_sorted_long_map_keys(long_map* map, unsigned long* num_keys_returned); ++void** get_sorted_long_map_values(long_map* map, unsigned long* num_values_returned); ++void** destroy_long_map(long_map* map, int destruction_type, unsigned long* num_destroyed); ++void apply_to_every_long_map_value(long_map* map, void (*apply_func)(unsigned long key, void* value)); ++ ++/* string map functions */ ++string_map* initialize_string_map(unsigned char store_keys); ++void* get_string_map_element(string_map* map, const char* key); ++void* set_string_map_element(string_map* map, const char* key, void* value); ++void* remove_string_map_element(string_map* map, const char* key); ++char** get_string_map_keys(string_map* map, unsigned long* num_keys_returned); ++void** get_string_map_values(string_map* map, unsigned long* num_values_returned); ++void** destroy_string_map(string_map* map, int destruction_type, unsigned long* num_destroyed); ++void apply_to_every_string_map_value(string_map* map, void (*apply_func)(char* key, void* value)); ++ ++ ++/* ++ * three different ways to deal with values when data structure is destroyed ++ */ ++#define DESTROY_MODE_RETURN_VALUES 20 ++#define DESTROY_MODE_FREE_VALUES 21 ++#define DESTROY_MODE_IGNORE_VALUES 22 ++ ++ ++/* ++ * for convenience & backwards compatibility alias _string_map_ functions to ++ * _map_ functions since string map is used more often than long map ++ */ ++#define initialize_map initialize_string_map ++#define set_map_element set_string_map_element ++#define get_map_element get_string_map_element ++#define remove_map_element remove_string_map_element ++#define get_map_keys get_string_map_keys ++#define get_map_values get_string_map_values ++#define destroy_map destroy_string_map ++ ++ ++/* internal utility structures/ functions */ ++typedef struct stack_node_struct ++{ ++ long_map_node** node_ptr; ++ signed char direction; ++ struct stack_node_struct* previous; ++} stack_node; ++ ++static void free_stack(stack_node* stack); ++static void** destroy_long_map_values(long_map* map, int destruction_type, unsigned long* num_destroyed); ++static void apply_to_every_long_map_node(long_map_node* node, void (*apply_func)(unsigned long key, void* value)); ++static void apply_to_every_string_map_node(long_map_node* node, unsigned char has_key, void (*apply_func)(char* key, void* value)); ++static void get_sorted_node_keys(long_map_node* node, unsigned long* key_list, unsigned long* next_key_index, int depth); ++static void get_sorted_node_values(long_map_node* node, void** value_list, unsigned long* next_value_index, int depth); ++static signed char rebalance (long_map_node** n, signed char direction, signed char update_op); ++static void rotate_right (long_map_node** parent); ++static void rotate_left (long_map_node** parent); ++ ++/* internal for string map */ ++typedef struct ++{ ++ char* key; ++ void* value; ++} string_map_key_value; ++static unsigned long sdbm_string_hash(const char *key); ++ ++ ++ ++ ++/*************************************************** ++ * For testing only ++ ***************************************************/ ++/* ++void print_list(stack_node *l); ++ ++void print_list(stack_node *l) ++{ ++ if(l != NULL) ++ { ++ printf(" list key = %ld, dir=%d, \n", (*(l->node_ptr))->key, l->direction); ++ print_list(l->previous); ++ } ++} ++*/ ++/****************************************************** ++ * End testing Code ++ *******************************************************/ ++ ++ ++ ++ ++/*************************************************** ++ * string_map function definitions ++ ***************************************************/ ++ ++string_map* initialize_string_map(unsigned char store_keys) ++{ ++ string_map* map = (string_map*)malloc(sizeof(string_map)); ++ if(map != NULL) ++ { ++ map->store_keys = store_keys; ++ map->lm.root = NULL; ++ map->lm.num_elements = 0; ++ map->num_elements = map->lm.num_elements; ++ } ++ return map; ++} ++ ++void* get_string_map_element(string_map* map, const char* key) ++{ ++ unsigned long hashed_key = sdbm_string_hash(key); ++ void* return_value = get_long_map_element( &(map->lm), hashed_key); ++ if(return_value != NULL && map->store_keys) ++ { ++ string_map_key_value* r = (string_map_key_value*)return_value; ++ return_value = r->value; ++ } ++ map->num_elements = map->lm.num_elements; ++ return return_value; ++} ++ ++void* set_string_map_element(string_map* map, const char* key, void* value) ++{ ++ unsigned long hashed_key = sdbm_string_hash(key); ++ void* return_value = NULL; ++ if(map->store_keys) ++ { ++ string_map_key_value* kv = (string_map_key_value*)malloc(sizeof(string_map_key_value)); ++ if(kv == NULL) /* deal with malloc failure */ ++ { ++ return NULL; ++ } ++ kv->key = strdup(key); ++ if(kv->key == NULL) /* deal with malloc failure */ ++ { ++ free(kv); ++ return NULL; ++ } ++ kv->value = value; ++ return_value = set_long_map_element( &(map->lm), hashed_key, kv); ++ if(return_value != NULL) ++ { ++ string_map_key_value* r = (string_map_key_value*)return_value; ++ return_value = r->value; ++ free(r->key); ++ free(r); ++ } ++ } ++ else ++ { ++ return_value = set_long_map_element( &(map->lm), hashed_key, value); ++ } ++ map->num_elements = map->lm.num_elements; ++ return return_value; ++} ++ ++void* remove_string_map_element(string_map* map, const char* key) ++{ ++ unsigned long hashed_key = sdbm_string_hash(key); ++ void* return_value = remove_long_map_element( &(map->lm), hashed_key); ++ ++ if(return_value != NULL && map->store_keys) ++ { ++ string_map_key_value* r = (string_map_key_value*)return_value; ++ return_value = r->value; ++ free(r->key); ++ free(r); ++ } ++ map->num_elements = map->lm.num_elements; ++ return return_value; ++} ++ ++char** get_string_map_keys(string_map* map, unsigned long* num_keys_returned) ++{ ++ char** str_keys; ++ str_keys = (char**)malloc((map->num_elements+1)*sizeof(char*)); ++ if(str_keys == NULL) /* deal with malloc failure */ ++ { ++ return NULL; ++ } ++ str_keys[0] = NULL; ++ *num_keys_returned = 0; ++ if(map->store_keys && map->num_elements > 0) ++ { ++ unsigned long list_length; ++ void** long_values = get_sorted_long_map_values( &(map->lm), &list_length); ++ unsigned long key_index; ++ /*list_length will be 0 on malloc failure in get_sorted_long_map_values, so this code shouldn't seg fault if that happens */ ++ for(key_index = 0; key_index < list_length; key_index++) ++ { ++ str_keys[key_index] = strdup( ((string_map_key_value*)(long_values[key_index]))->key); ++ if(str_keys[key_index] == NULL) /* deal with malloc failure */ ++ { ++ //just return the incomplete list (hey, it's null terminated...) ++ free(long_values); ++ return str_keys; ++ } ++ *num_keys_returned = *num_keys_returned + 1; ++ } ++ str_keys[list_length] = NULL; ++ free(long_values); ++ } ++ return str_keys; ++} ++ ++ ++void** get_string_map_values(string_map* map, unsigned long* num_values_returned) ++{ ++ void** values = NULL; ++ if(map != NULL) ++ { ++ values = get_sorted_long_map_values ( &(map->lm), num_values_returned ); ++ } ++ return values; ++} ++ ++ ++void** destroy_string_map(string_map* map, int destruction_type, unsigned long* num_destroyed) ++{ ++ void** return_values = NULL; ++ if(map != NULL) ++ { ++ if(map->store_keys) ++ { ++ void** kvs = destroy_long_map_values( &(map->lm), DESTROY_MODE_RETURN_VALUES, num_destroyed ); ++ unsigned long kv_index = 0; ++ for(kv_index=0; kv_index < *num_destroyed; kv_index++) ++ { ++ string_map_key_value* kv = (string_map_key_value*)kvs[kv_index]; ++ void* value = kv->value; ++ ++ free(kv->key); ++ free(kv); ++ if(destruction_type == DESTROY_MODE_FREE_VALUES) ++ { ++ free(value); ++ } ++ if(destruction_type == DESTROY_MODE_RETURN_VALUES) ++ { ++ kvs[kv_index] = value; ++ } ++ } ++ if(destruction_type == DESTROY_MODE_RETURN_VALUES) ++ { ++ return_values = kvs; ++ } ++ else ++ { ++ free(kvs); ++ } ++ } ++ else ++ { ++ return_values = destroy_long_map_values( &(map->lm), destruction_type, num_destroyed ); ++ } ++ free(map); ++ } ++ return return_values; ++} ++ ++ ++ ++ ++/*************************************************** ++ * long_map function definitions ++ ***************************************************/ ++ ++long_map* initialize_long_map(void) ++{ ++ long_map* map = (long_map*)malloc(sizeof(long_map)); ++ if(map != NULL) /* test for malloc failure */ ++ { ++ map->root = NULL; ++ map->num_elements = 0; ++ } ++ return map; ++} ++ ++void* get_long_map_element(long_map* map, unsigned long key) ++{ ++ void* value = NULL; ++ ++ if(map->root != NULL) ++ { ++ long_map_node* parent_node = map->root; ++ long_map_node* next_node; ++ while( key != parent_node->key && (next_node = (long_map_node *)(key < parent_node->key ? parent_node->left : parent_node->right)) != NULL) ++ { ++ parent_node = next_node; ++ } ++ if(parent_node->key == key) ++ { ++ value = parent_node->value; ++ } ++ } ++ return value; ++} ++ ++void* get_smallest_long_map_element(long_map* map, unsigned long* smallest_key) ++{ ++ void* value = NULL; ++ if(map->root != NULL) ++ { ++ long_map_node* next_node = map->root; ++ while( next_node->left != NULL) ++ { ++ next_node = next_node->left; ++ } ++ value = next_node->value; ++ *smallest_key = next_node->key; ++ } ++ return value; ++} ++ ++void* get_largest_long_map_element(long_map* map, unsigned long* largest_key) ++{ ++ void* value = NULL; ++ if(map->root != NULL) ++ { ++ long_map_node* next_node = map->root; ++ while( next_node->right != NULL) ++ { ++ next_node = next_node->right; ++ } ++ value = next_node->value; ++ *largest_key = next_node->key; ++ } ++ return value; ++} ++ ++void* remove_smallest_long_map_element(long_map* map, unsigned long* smallest_key) ++{ ++ get_smallest_long_map_element(map, smallest_key); ++ return remove_long_map_element(map, *smallest_key); ++} ++ ++void* remove_largest_long_map_element(long_map* map, unsigned long* largest_key) ++{ ++ get_largest_long_map_element(map, largest_key); ++ return remove_long_map_element(map, *largest_key); ++} ++ ++ ++/* if replacement performed, returns replaced value, otherwise null */ ++void* set_long_map_element(long_map* map, unsigned long key, void* value) ++{ ++ stack_node* parent_list = NULL; ++ void* old_value = NULL; ++ int old_value_found = 0; ++ ++ long_map_node* parent_node; ++ long_map_node* next_node; ++ stack_node* next_parent; ++ stack_node* previous_parent; ++ signed char new_balance; ++ ++ ++ long_map_node* new_node = (long_map_node*)malloc(sizeof(long_map_node)); ++ if(new_node == NULL) ++ { ++ return NULL; ++ } ++ new_node->value = value; ++ new_node->key = key; ++ new_node->left = NULL; ++ new_node->right = NULL; ++ new_node->balance = 0; ++ ++ ++ ++ if(map->root == NULL) ++ { ++ map->root = new_node; ++ } ++ else ++ { ++ parent_node = map->root; ++ ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ free(new_node); ++ return NULL; /* won't insert but won't seg fault */ ++ } ++ next_parent->node_ptr = &(map->root); ++ next_parent->previous = parent_list; ++ parent_list = next_parent; ++ ++ while( key != parent_node->key && (next_node = (key < parent_node->key ? parent_node->left : parent_node->right) ) != NULL) ++ { ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ free(new_node); ++ return NULL; ++ } ++ next_parent->node_ptr = key < parent_node->key ? &(parent_node->left) : &(parent_node->right); ++ next_parent->previous = parent_list; ++ next_parent->previous->direction = key < parent_node->key ? -1 : 1; ++ parent_list = next_parent; ++ ++ parent_node = next_node; ++ } ++ ++ ++ if(key == parent_node->key) ++ { ++ old_value = parent_node->value; ++ old_value_found = 1; ++ parent_node->value = value; ++ free(new_node); ++ /* we merely replaced a node, no need to rebalance */ ++ } ++ else ++ { ++ if(key < parent_node->key) ++ { ++ parent_node->left = (void*)new_node; ++ parent_list->direction = -1; ++ } ++ else ++ { ++ parent_node->right = (void*)new_node; ++ parent_list->direction = 1; ++ } ++ ++ ++ /* we inserted a node, rebalance */ ++ previous_parent = parent_list; ++ new_balance = 1; /* initial value is not used, but must not be 0 for initial loop condition */ ++ ++ ++ while(previous_parent != NULL && new_balance != 0) ++ { ++ new_balance = rebalance(previous_parent->node_ptr, previous_parent->direction, 1); ++ previous_parent = previous_parent->previous; ++ } ++ } ++ } ++ ++ free_stack(parent_list); ++ ++ if(old_value_found == 0) ++ { ++ map->num_elements = map->num_elements + 1; ++ } ++ ++ return old_value; ++} ++ ++ ++void* remove_long_map_element(long_map* map, unsigned long key) ++{ ++ ++ void* value = NULL; ++ ++ long_map_node* root_node = map->root; ++ stack_node* parent_list = NULL; ++ ++ ++ long_map_node* remove_parent; ++ long_map_node* remove_node; ++ long_map_node* next_node; ++ ++ long_map_node* replacement; ++ long_map_node* replacement_parent; ++ long_map_node* replacement_next; ++ ++ stack_node* next_parent; ++ stack_node* previous_parent; ++ stack_node* replacement_stack_node; ++ ++ ++ signed char new_balance; ++ ++ ++ ++ if(root_node != NULL) ++ { ++ remove_parent = root_node; ++ remove_node = key < remove_parent->key ? remove_parent->left : remove_parent->right; ++ ++ if(remove_node != NULL && key != remove_parent->key) ++ { ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ return NULL; ++ } ++ next_parent->node_ptr = &(map->root); ++ next_parent->previous = parent_list; ++ parent_list = next_parent; ++ while( key != remove_node->key && (next_node = (key < remove_node->key ? remove_node->left : remove_node->right)) != NULL) ++ { ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ next_parent->node_ptr = key < remove_parent->key ? &(remove_parent->left) : &(remove_parent->right); ++ next_parent->previous = parent_list; ++ next_parent->previous->direction = key < remove_parent->key ? -1 : 1; ++ parent_list = next_parent; ++ ++ ++ remove_parent = remove_node; ++ remove_node = next_node; ++ } ++ parent_list->direction = key < remove_parent-> key ? -1 : 1; ++ } ++ else ++ { ++ remove_node = remove_parent; ++ } ++ ++ ++ if(key == remove_node->key) ++ { ++ ++ /* find replacement for node we are deleting */ ++ if( remove_node->right == NULL ) ++ { ++ replacement = remove_node->left; ++ } ++ else if( remove_node->right->left == NULL) ++ { ++ ++ replacement = remove_node->right; ++ replacement->left = remove_node->left; ++ replacement->balance = remove_node->balance; ++ ++ /* put pointer to replacement node into list for balance update */ ++ replacement_stack_node = (stack_node*)malloc(sizeof(stack_node)); ++ if(replacement_stack_node == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ replacement_stack_node->previous = parent_list; ++ replacement_stack_node->direction = 1; /* replacement is from right */ ++ if(remove_node == remove_parent) /* special case for root node */ ++ { ++ replacement_stack_node->node_ptr = &(map->root); ++ } ++ else ++ { ++ replacement_stack_node->node_ptr = key < remove_parent-> key ? &(remove_parent->left) : &(remove_parent->right); ++ } ++ parent_list = replacement_stack_node; ++ ++ } ++ else ++ { ++ /* put pointer to replacement node into list for balance update */ ++ replacement_stack_node = (stack_node*)malloc(sizeof(stack_node)); ++ if(replacement_stack_node == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ ++ replacement_stack_node->previous = parent_list; ++ replacement_stack_node->direction = 1; /* we always look for replacement on right */ ++ if(remove_node == remove_parent) /* special case for root node */ ++ { ++ replacement_stack_node->node_ptr = &(map->root); ++ } ++ else ++ { ++ replacement_stack_node->node_ptr = key < remove_parent-> key ? &(remove_parent->left) : &(remove_parent->right); ++ } ++ ++ parent_list = replacement_stack_node; ++ ++ ++ /* ++ * put pointer to replacement node->right into list for balance update ++ * this node will have to be updated with the proper pointer ++ * after we have identified the replacement ++ */ ++ replacement_stack_node = (stack_node*)malloc(sizeof(stack_node)); ++ if(replacement_stack_node == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ ++ replacement_stack_node->previous = parent_list; ++ replacement_stack_node->direction = -1; /* we always look for replacement to left of this node */ ++ parent_list = replacement_stack_node; ++ ++ /* find smallest node on right (large) side of tree */ ++ replacement_parent = remove_node->right; ++ replacement = replacement_parent->left; ++ ++ while((replacement_next = replacement->left) != NULL) ++ { ++ next_parent = (stack_node*)malloc(sizeof(stack_node)); ++ if(next_parent == NULL) /* deal with malloc failure */ ++ { ++ /* free previous stack nodes to prevent memory leak */ ++ free_stack(parent_list); ++ return NULL; ++ } ++ ++ next_parent->node_ptr = &(replacement_parent->left); ++ next_parent->previous = parent_list; ++ next_parent->direction = -1; /* we always go left */ ++ parent_list = next_parent; ++ ++ replacement_parent = replacement; ++ replacement = replacement_next; ++ ++ } ++ ++ replacement_parent->left = replacement->right; ++ ++ replacement->left = remove_node->left; ++ replacement->right = remove_node->right; ++ replacement->balance = remove_node->balance; ++ replacement_stack_node->node_ptr = &(replacement->right); ++ } ++ ++ /* insert replacement at proper location in tree */ ++ if(remove_node == remove_parent) ++ { ++ map->root = replacement; ++ } ++ else ++ { ++ remove_parent->left = remove_node == remove_parent->left ? replacement : remove_parent->left; ++ remove_parent->right = remove_node == remove_parent->right ? replacement : remove_parent->right; ++ } ++ ++ ++ /* rebalance tree */ ++ previous_parent = parent_list; ++ new_balance = 0; ++ while(previous_parent != NULL && new_balance == 0) ++ { ++ new_balance = rebalance(previous_parent->node_ptr, previous_parent->direction, -1); ++ previous_parent = previous_parent->previous; ++ } ++ ++ ++ ++ ++ /* ++ * since we found a value to remove, decrease number of elements in map ++ * set return value to the deleted node's value and free the node ++ */ ++ map->num_elements = map->num_elements - 1; ++ value = remove_node->value; ++ free(remove_node); ++ } ++ } ++ ++ free_stack(parent_list); ++ ++ return value; ++} ++ ++ ++/* note: returned keys are dynamically allocated, you need to free them! */ ++unsigned long* get_sorted_long_map_keys(long_map* map, unsigned long* num_keys_returned) ++{ ++ unsigned long* key_list = (unsigned long*)malloc((map->num_elements)*sizeof(unsigned long)); ++ unsigned long next_key_index; ++ if(key_list == NULL) ++ { ++ *num_keys_returned = 0; ++ return NULL; ++ } ++ next_key_index = 0; ++ get_sorted_node_keys(map->root, key_list, &next_key_index, 0); ++ ++ *num_keys_returned = map->num_elements; ++ ++ return key_list; ++} ++ ++ ++void** get_sorted_long_map_values(long_map* map, unsigned long* num_values_returned) ++{ ++ void** value_list = (void**)malloc((map->num_elements+1)*sizeof(void*)); ++ unsigned long next_value_index; ++ ++ if(value_list == NULL) ++ { ++ *num_values_returned = 0; ++ return NULL; ++ } ++ next_value_index = 0; ++ get_sorted_node_values(map->root, value_list, &next_value_index, 0); ++ value_list[map->num_elements] = NULL; /* since we're dealing with pointers make list null terminated */ ++ ++ *num_values_returned = map->num_elements; ++ return value_list; ++ ++} ++ ++ ++ ++void** destroy_long_map(long_map* map, int destruction_type, unsigned long* num_destroyed) ++{ ++ void** return_values = destroy_long_map_values(map, destruction_type, num_destroyed); ++ free(map); ++ return return_values; ++} ++ ++ ++ ++void apply_to_every_long_map_value(long_map* map, void (*apply_func)(unsigned long key, void* value)) ++{ ++ apply_to_every_long_map_node(map->root, apply_func); ++} ++void apply_to_every_string_map_value(string_map* map, void (*apply_func)(char* key, void* value)) ++{ ++ apply_to_every_string_map_node( (map->lm).root, map->store_keys, apply_func); ++} ++ ++ ++/*************************************************** ++ * internal utility function definitions ++ ***************************************************/ ++static void free_stack(stack_node* stack) ++{ ++ while(stack != NULL) ++ { ++ stack_node* prev_node = stack; ++ stack = prev_node->previous; ++ free(prev_node); ++ } ++ ++} ++ ++static void** destroy_long_map_values(long_map* map, int destruction_type, unsigned long* num_destroyed) ++{ ++ void** return_values = NULL; ++ unsigned long return_index = 0; ++ ++ *num_destroyed = 0; ++ ++ if(destruction_type == DESTROY_MODE_RETURN_VALUES) ++ { ++ return_values = (void**)malloc((map->num_elements+1)*sizeof(void*)); ++ if(return_values == NULL) /* deal with malloc failure */ ++ { ++ destruction_type = DESTROY_MODE_IGNORE_VALUES; /* could cause memory leak, but there's no other way to be sure we won't seg fault */ ++ } ++ else ++ { ++ return_values[map->num_elements] = NULL; ++ } ++ } ++ while(map->num_elements > 0) ++ { ++ unsigned long smallest_key; ++ void* removed_value = remove_smallest_long_map_element(map, &smallest_key); ++ if(destruction_type == DESTROY_MODE_RETURN_VALUES) ++ { ++ return_values[return_index] = removed_value; ++ } ++ if(destruction_type == DESTROY_MODE_FREE_VALUES) ++ { ++ free(removed_value); ++ } ++ return_index++; ++ *num_destroyed = *num_destroyed + 1; ++ } ++ return return_values; ++} ++ ++static void apply_to_every_long_map_node(long_map_node* node, void (*apply_func)(unsigned long key, void* value)) ++{ ++ if(node != NULL) ++ { ++ apply_to_every_long_map_node(node->left, apply_func); ++ ++ apply_func(node->key, node->value); ++ ++ apply_to_every_long_map_node(node->right, apply_func); ++ } ++} ++static void apply_to_every_string_map_node(long_map_node* node, unsigned char has_key, void (*apply_func)(char* key, void* value)) ++{ ++ if(node != NULL) ++ { ++ apply_to_every_string_map_node(node->left, has_key, apply_func); ++ ++ if(has_key) ++ { ++ string_map_key_value* kv = (string_map_key_value*)(node->value); ++ apply_func(kv->key, kv->value); ++ } ++ else ++ { ++ apply_func(NULL, node->value); ++ } ++ apply_to_every_string_map_node(node->right, has_key, apply_func); ++ } ++} ++ ++ ++ ++static void get_sorted_node_keys(long_map_node* node, unsigned long* key_list, unsigned long* next_key_index, int depth) ++{ ++ if(node != NULL) ++ { ++ get_sorted_node_keys(node->left, key_list, next_key_index, depth+1); ++ ++ key_list[ *next_key_index ] = node->key; ++ (*next_key_index)++; ++ ++ get_sorted_node_keys(node->right, key_list, next_key_index, depth+1); ++ } ++} ++ ++static void get_sorted_node_values(long_map_node* node, void** value_list, unsigned long* next_value_index, int depth) ++{ ++ if(node != NULL) ++ { ++ get_sorted_node_values(node->left, value_list, next_value_index, depth+1); ++ ++ value_list[ *next_value_index ] = node->value; ++ (*next_value_index)++; ++ ++ get_sorted_node_values(node->right, value_list, next_value_index, depth+1); ++ } ++} ++ ++ ++ ++/* ++ * direction = -1 indicates left subtree updated, direction = 1 for right subtree ++ * update_op = -1 indicates delete node, update_op = 1 for insert node ++ */ ++static signed char rebalance (long_map_node** n, signed char direction, signed char update_op) ++{ ++ /* ++ printf( "original: key = %ld, balance = %d, update_op=%d, direction=%d\n", (*n)->key, (*n)->balance, update_op, direction); ++ */ ++ ++ (*n)->balance = (*n)->balance + (update_op*direction); ++ ++ if( (*n)->balance < -1) ++ { ++ if((*n)->left->balance < 0) ++ { ++ rotate_right(n); ++ (*n)->right->balance = 0; ++ (*n)->balance = 0; ++ } ++ else if((*n)->left->balance == 0) ++ { ++ rotate_right(n); ++ (*n)->right->balance = -1; ++ (*n)->balance = 1; ++ } ++ else if((*n)->left->balance > 0) ++ { ++ rotate_left( &((*n)->left) ); ++ rotate_right(n); ++ /* ++ if( (*n)->balance < 0 ) ++ { ++ (*n)->left->balance = 0; ++ (*n)->right->balance = 1; ++ } ++ else if( (*n)->balance == 0 ) ++ { ++ (*n)->left->balance = 0; ++ (*n)->right->balance = 0; ++ } ++ else if( (*n)->balance > 0 ) ++ { ++ (*n)->left->balance = -1; ++ (*n)->right->balance = 0; ++ } ++ */ ++ (*n)->left->balance = (*n)->balance > 0 ? -1 : 0; ++ (*n)->right->balance = (*n)->balance < 0 ? 1 : 0; ++ (*n)->balance = 0; ++ } ++ } ++ if( (*n)->balance > 1) ++ { ++ if((*n)->right->balance > 0) ++ { ++ rotate_left(n); ++ (*n)->left->balance = 0; ++ (*n)->balance = 0; ++ } ++ else if ((*n)->right->balance == 0) ++ { ++ rotate_left(n); ++ (*n)->left->balance = 1; ++ (*n)->balance = -1; ++ } ++ else if((*n)->right->balance < 0) ++ { ++ rotate_right( &((*n)->right) ); ++ rotate_left(n); ++ /* ++ if( (*n)->balance < 0 ) ++ { ++ (*n)->left->balance = 0; ++ (*n)->right->balance = 1; ++ } ++ else if( (*n)->balance == 0 ) ++ { ++ (*n)->left->balance = 0; ++ (*n)->right->balance = 0; ++ } ++ else if( (*n)->balance > 0 ) ++ { ++ (*n)->left->balance = -1; ++ (*n)->right->balance = 0; ++ } ++ */ ++ (*n)->left->balance = (*n)->balance > 0 ? -1 : 0; ++ (*n)->right->balance = (*n)->balance < 0 ? 1 : 0; ++ (*n)->balance = 0; ++ } ++ } ++ ++ /* ++ printf( "key = %ld, balance = %d\n", (*n)->key, (*n)->balance); ++ */ ++ ++ return (*n)->balance; ++} ++ ++ ++static void rotate_right (long_map_node** parent) ++{ ++ long_map_node* old_parent = *parent; ++ long_map_node* pivot = old_parent->left; ++ old_parent->left = pivot->right; ++ pivot->right = old_parent; ++ ++ *parent = pivot; ++} ++ ++static void rotate_left (long_map_node** parent) ++{ ++ long_map_node* old_parent = *parent; ++ long_map_node* pivot = old_parent->right; ++ old_parent->right = pivot->left; ++ pivot->left = old_parent; ++ ++ *parent = pivot; ++} ++ ++ ++ ++/*************************************************************************** ++ * This algorithm was created for the sdbm database library (a public-domain ++ * reimplementation of ndbm) and seems to work relatively well in ++ * scrambling bits ++ * ++ * ++ * This code was derived from code found at: ++ * http://www.cse.yorku.ca/~oz/hash.html ++ ***************************************************************************/ ++static unsigned long sdbm_string_hash(const char *key) ++{ ++ unsigned long hashed_key = 0; ++ ++ int index = 0; ++ unsigned int nextch; ++ while(key[index] != '\0') ++ { ++ nextch = key[index]; ++ hashed_key = nextch + (hashed_key << 6) + (hashed_key << 16) - hashed_key; ++ index++; ++ } ++ return hashed_key; ++} ++ ++