diff --git a/README.md b/README.md new file mode 100644 index 0000000000..2b2f551ab5 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +# gargoyle +Known Devices + +The idea is to be able to set policy (Quota, QoS, Restrictions etc) over a +Group of Known Devices (owned by an individual for example). To achieve this, +the Known Devices need to be identified by their MAC address' and then arranged +into Groups. Both Devices and Groups can be named by the Gargoyle Administrator. + +Up until now, the best way for a Gargoyle admin to apply policy to users with +a number of devices has been to assign a static IP address to each device such +that each users' devices are in an IP range. Then, the policy is applied to the +IP range, and the IP range is displayed throughout the Gargoyle GUI. Known Devices +and Groups enable the Gargoyle admin to work with meaningful names rather than IP +ranges, and relieves the need to assign static IP addresses. + +Status +- Gargoyle-Connection-Devices has a Section for Known Devices and another for Device Groups. +- Device Groups may be used: + - Gargoyle-Firewall-Quotas-BandwidthQuotas-AppliesTo + - Gargoyle-Firewall-Restrictions-AccessRestrictions-RuleAppliesTo + - Gargoyle-Firewall-Restrictions-Exceptions(Whitelist)-RuleAppliesTo + - Gargoyle-Forewall-QoS(Upload)-ClassificationRules + - Gargoyle-Forewall-QoS(Download)-ClassificationRules + +Technical +- Approx 17k in size +- Known Devices are stored as a host in uci /etc/config/dhcp +- Each host has a supplementary Group section +- Each Device Group is represented by an ipset (each set = 550 bytes min) +- IP addresses in each ipset are dynamically adjusted when dnsmasq issues a dhcp lease +- Existing iptables quota rules can check against the membership of an ipset diff --git a/package/gargoyle-firewall-util/src/Makefile b/package/gargoyle-firewall-util/src/Makefile index 9c1f4417be..634f500e9f 100644 --- a/package/gargoyle-firewall-util/src/Makefile +++ b/package/gargoyle-firewall-util/src/Makefile @@ -4,7 +4,7 @@ print_quotas: print_quotas.c $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ -lericstools -luci -liptbwctl restore_quotas: restore_quotas.c - $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ -lericstools -luci -liptbwctl + $(CC) $(CFLAGS) $(LDFLAGS) uci-util.c restore_quotas.c -o restore_quotas -lericstools -luci -liptbwctl backup_quotas: backup_quotas.c $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ -lericstools -luci -liptbwctl @@ -13,7 +13,7 @@ delete_chain_from_table: delete_chain_from_table.c $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ -lericstools make_iptables_rules: make_iptables_rules.c - $(CC) $(CFLAGS) $(LDFLAGS) make_iptables_rules.c -o make_iptables_rules -lericstools -luci -lm + $(CC) $(CFLAGS) $(LDFLAGS) uci-util.c make_iptables_rules.c -o make_iptables_rules -lericstools -luci -lm clean: rm -rf make_iptables_rules delete_chain_from_table print_quotas backup_quotas restore_quotas *.o *~ .*sw* diff --git a/package/gargoyle-firewall-util/src/make_iptables_rules.c b/package/gargoyle-firewall-util/src/make_iptables_rules.c index 24904dcd62..bdadbe0ae7 100644 --- a/package/gargoyle-firewall-util/src/make_iptables_rules.c +++ b/package/gargoyle-firewall-util/src/make_iptables_rules.c @@ -29,6 +29,7 @@ #include #include +#include "uci-util.h" #define malloc safe_malloc #define strdup safe_strdup @@ -36,12 +37,12 @@ #define MATCH_IP_INDEX 0 #define MATCH_IP_RANGE_INDEX 1 #define MATCH_MAC_INDEX 2 +#define MATCH_GROUP_INDEX 3 -string_map* get_rule_definition(char* config, char* section); -char* get_option_value_string(struct uci_option* uopt); -int parse_option(char* option_name, char* option_value, string_map* definition); +string_map* get_rule_definition(struct uci_context* ctx, char* config, char* section); +int parse_option(struct uci_context* ctx, char* option_name, char* option_value, string_map* definition); -char*** parse_ips_and_macs(char* addr_str); +char*** parse_ips_and_macs(struct uci_context* ctx, char* addr_str); char** parse_ports(char* port_str); char** parse_marks(char* list_str, unsigned long max_mask); list* parse_quoted_list(char* list_str, char quote_char, char escape_char, char add_remainder_if_uneven_quotes); @@ -51,6 +52,7 @@ int truncate_if_starts_with(char* test_str, char* prefix); char** compute_rules(string_map *rule_def, char* table, char* chain, int is_ingress, char* target, char* target_options); int compute_multi_rules(char** def, list* multi_rules, char** single_check, int never_single, char* rule_prefix, char* test_prefix1, char* test_prefix2, int is_negation, int mask_byte_index, char* proto, int requires_proto, int quoted_args); + int main(int argc, char **argv) { int c; @@ -110,21 +112,22 @@ int main(int argc, char **argv) } if(package != NULL && section != NULL && table != NULL && chain != NULL && target != NULL) { - string_map* def = get_rule_definition(package, section); + struct uci_context *ctx = uci_alloc_context(); + string_map* def = get_rule_definition(ctx, package, section); if(def != NULL) { char** rules = compute_rules(def, table, chain, 0, target, target_options); - + int rindex = 0; for(rindex=0; rules[rindex] != NULL; rindex++) - { + { if(run_commands == 0) { printf("%s\n", rules[rindex]); } else { - system(rules[rindex]); + system(rules[rindex]); } } } @@ -133,6 +136,7 @@ int main(int argc, char **argv) fprintf(stderr, "ERROR: Invalid package / section\n"); } + uci_free_context(ctx); } else if(!usage_printed) { @@ -141,17 +145,17 @@ int main(int argc, char **argv) } - + return 0; } -/* +/* * Note we've currently maxed out out one whole byte of address space * in the connmark at this point. If we want to match in - * further dimensions, we will have to be greedy and take + * further dimensions, we will have to be greedy and take * even more address space */ char** compute_rules(string_map *rule_def, char* table, char* chain, int is_ingress, char* target, char* target_options) @@ -174,7 +178,7 @@ char** compute_rules(string_map *rule_def, char* table, char* chain, int is_ingr dcat_and_free(&single_check, &tmp, 1, 1); } else if(active_hours != NULL && active_weekdays != NULL) - { + { char* tmp = dynamic_strcat(5, " -m timerange --hours \"", active_hours, "\" --weekdays \"", active_weekdays, "\" " ); dcat_and_free(&single_check, &tmp, 1, 1); } @@ -243,12 +247,12 @@ char** compute_rules(string_map *rule_def, char* table, char* chain, int is_ingr list* final_mask_list = initialize_list(); - - + + /* url matches are a bit of a special case, handle them first */ /* we have to save this mask_byte_index specially, because it must be set separately, so it only gets set if packet is http request */ int url_mask_byte_index = mask_byte_index; - + char* url_match_vars[] = { "url_contains", "url_regex", "url_exact", "url_domain_contains", "url_domain_regex", "url_domain_exact" }; char* url_neg_match_vars[] = { "not_url_contains", "not_url_regex", "not_url_exact", "not_url_domain_contains", "not_url_domain_regex", "not_url_domain_exact" }; char* url_prefixes[] = { " -m weburl --contains ", " -m weburl --contains_regex ", " -m weburl --matches_exactly ", " -m weburl --domain_only --contains ", " -m weburl --domain_only --contains_regex ", " -m weburl --domain_only --matches_exactly " }; @@ -256,7 +260,7 @@ char** compute_rules(string_map *rule_def, char* table, char* chain, int is_ingr int url_var_index=0; int url_rule_count=0; int url_is_negated=0; - + for(url_is_negated=0; url_is_negated < 2 && url_rule_count == 0; url_is_negated++) { char** url_vars = url_is_negated ? url_neg_match_vars : url_match_vars; @@ -271,7 +275,7 @@ char** compute_rules(string_map *rule_def, char* table, char* chain, int is_ingr } } url_is_negated--; - + proto = url_rule_count > 0 ? "tcp" : proto; int url_is_multi = url_rule_count <= 1 ? 0 : 1; @@ -303,7 +307,7 @@ char** compute_rules(string_map *rule_def, char* table, char* chain, int is_ingr int mark_is_multi = compute_multi_rules(mark_def, multi_rules, &single_check, mark_is_negated, rule_prefix, " -m mark ", " --mark ", mark_is_negated, mask_byte_index, proto, include_proto, 0) == 2; push_list(initial_mask_list, (void*)&mark_is_negated); push_list(final_mask_list, (void*)&mark_is_multi); - mask_byte_index++; + mask_byte_index++; /* connmark matches */ char** connmark_def = get_map_element(rule_def, "connmark"); @@ -313,14 +317,14 @@ char** compute_rules(string_map *rule_def, char* table, char* chain, int is_ingr int connmark_is_multi = compute_multi_rules(connmark_def, multi_rules, &single_check, 0, rule_prefix, " -m connmark ", " --mark ", connmark_is_negated, mask_byte_index, proto, include_proto, 0) == 2; push_list(initial_mask_list, (void*)&connmark_is_negated); push_list(final_mask_list, (void*)&connmark_is_multi); - mask_byte_index++; + mask_byte_index++; /* - * for ingress source = remote, destination = local + * for ingress source = remote, destination = local * for egress source = local, destination = remote * - * addresses are a bit tricky, since we need to handle 3 different kinds of matches: ips, ip ranges and macs + * addresses are a bit tricky, since we need to handle 4 different kinds of matches: ips, ip ranges, macs and groups */ char*** src_def = get_map_element(rule_def, (is_ingress ? "remote_addr" : "local_addr")); char*** not_src_def = get_map_element(rule_def, (is_ingress ? "not_remote_addr" : "not_local_addr")); @@ -334,8 +338,8 @@ char** compute_rules(string_map *rule_def, char* table, char* chain, int is_ingr char*** addr_defs[2] = { src_def, dst_def }; int addr_negated[2] = { src_is_negated, dst_is_negated }; - char* addr_prefix1[2][3] = { { " -s ", " -m iprange ", " -m mac --mac-source " }, { " -d", "-m iprange ", NULL } }; - char* addr_prefix2[2][3] = { {"", " --src-range ", "" }, { "", " --dst-range ", NULL } }; + char* addr_prefix1[2][4] = { { " -s ", " -m iprange ", " -m mac --mac-source ", " -m set " }, { " -d", "-m iprange ", NULL, " -m set " } }; + char* addr_prefix2[2][4] = { {"", " --src-range ", "", " --match-set " }, { "", " --dst-range ", NULL, " --match-set " } }; int addr_index = 0; int is_true = 1; @@ -370,7 +374,7 @@ char** compute_rules(string_map *rule_def, char* table, char* chain, int is_ingr } } } - + push_list(initial_mask_list, (void*)(addr_negated + addr_index)); push_list(final_mask_list, (void*)(is_multi == 1 ? &is_true : &is_false) ); mask_byte_index++; @@ -400,8 +404,8 @@ char** compute_rules(string_map *rule_def, char* table, char* chain, int is_ingr list* all_rules = initialize_list(); - - + + //if no target_options specified, make sure it's an empty string, not null target_options = (target_options == NULL) ? "" : target_options; //if target_options is empty and we're rejecting and proto is tcp, set options to --reject-with tcp-reset instead of default @@ -415,7 +419,7 @@ char** compute_rules(string_map *rule_def, char* table, char* chain, int is_ingr compute_multi_rules(dummy_multi, multi_rules, &single_check, 1, rule_prefix, " ", "", 0, mask_byte_index, proto, requires_proto, 0); mask_byte_index++; } - + /* printf("final mask length = %ld\n", final_mask_list->length); @@ -444,7 +448,7 @@ char** compute_rules(string_map *rule_def, char* table, char* chain, int is_ingr { *next_is_multi = 1; } - + unsigned long next_mark_bit = 0x01000000 * (unsigned long)pow(2, next_mask_index) * (*next_is_multi); final_match = final_match + next_mark_bit; @@ -475,7 +479,7 @@ char** compute_rules(string_map *rule_def, char* table, char* chain, int is_ingr sprintf(mark, "0x%lX", initial_url_mark); push_list(all_rules, dynamic_strcat(5, rule_prefix, " -p tcp -m weburl --contains http -j CONNMARK --set-mark ", mark, "/", mark)); } - + //put all rules in place from multi_rules list while(multi_rules->length > 0) { @@ -487,7 +491,7 @@ char** compute_rules(string_map *rule_def, char* table, char* chain, int is_ingr //if final mark matches perfectly with mask of 0xFF000000, jump to (REJECT/ACCEPT) target char final_match_str[12]; sprintf(final_match_str, "0x%lX", final_match); - + //if we're rejecting, no target options are specified, and no proto is specified add two rules: one for tcp with tcp-reject, and one for everything else if(safe_strcmp(target, "REJECT") == 0 && safe_strcmp(target_options, "") == 0 && safe_strcmp(proto, "both")) { @@ -506,7 +510,7 @@ char** compute_rules(string_map *rule_def, char* table, char* chain, int is_ingr else { if( strcmp(proto, "both") == 0 ) - { + { if( dport_def == NULL && sport_def == NULL ) { if(safe_strcmp(target, "REJECT") == 0 && safe_strcmp(target_options, "") == 0 ) @@ -560,7 +564,7 @@ int compute_multi_rules(char** def, list* multi_rules, char** single_check, int int parse_type = 0; if(def != NULL) { - int num_rules; + int num_rules; for(num_rules=0; def[num_rules] != NULL; num_rules++){} if(num_rules == 1 && !never_single) { @@ -611,12 +615,10 @@ int compute_multi_rules(char** def, list* multi_rules, char** single_check, int -string_map* get_rule_definition(char* package, char* section) +string_map* get_rule_definition(struct uci_context* ctx, char* package, char* section) { string_map* definition = NULL; - struct uci_context *ctx; struct uci_package *p = NULL; - ctx = uci_alloc_context(); if(uci_load(ctx, package, &p) == UCI_OK) { struct uci_ptr ptr; @@ -629,13 +631,13 @@ string_map* get_rule_definition(char* package, char* section) { struct uci_element *e; definition = initialize_string_map(1); - - uci_foreach_element(&s->options, e) + + uci_foreach_element(&s->options, e) { char* option_name = strdup(e->name); to_lowercase(option_name); char* option_value = get_option_value_string(uci_to_option(e)); - parse_option(option_name, option_value, definition); + parse_option(ctx, option_name, option_value, definition); free(option_name); free(option_value); } @@ -643,25 +645,25 @@ string_map* get_rule_definition(char* package, char* section) } } uci_free_context(ctx); - + return definition; } -int parse_option(char* option_name, char* option_value, string_map* definition) +int parse_option(struct uci_context* ctx, char* option_name, char* option_value, string_map* definition) { int valid_option = 0; if( safe_strcmp(option_name, "proto") == 0 || safe_strcmp(option_name, "layer7") == 0 || safe_strcmp(option_name, "ipp2p") == 0 || - safe_strcmp(option_name, "max_pkt_size") == 0 || - safe_strcmp(option_name, "min_pkt_size") ==0 + safe_strcmp(option_name, "max_pkt_size") == 0 || + safe_strcmp(option_name, "min_pkt_size") ==0 ) { valid_option = 1; set_map_element(definition, option_name, strdup(option_value)); } - else if( safe_strcmp(option_name, "active_hours") == 0 || - safe_strcmp(option_name, "active_weekly_ranges") == 0 || + else if( safe_strcmp(option_name, "active_hours") == 0 || + safe_strcmp(option_name, "active_weekly_ranges") == 0 || safe_strcmp(option_name, "active_weekdays") == 0 ) { @@ -684,10 +686,10 @@ int parse_option(char* option_name, char* option_value, string_map* definition) else if( safe_strcmp(option_name, "remote_addr") == 0 || safe_strcmp(option_name, "local_addr") == 0 || safe_strcmp(option_name, "not_remote_addr") == 0 || - safe_strcmp(option_name, "not_local_addr") == 0 + safe_strcmp(option_name, "not_local_addr") == 0 ) { - char*** parsed_addr = parse_ips_and_macs(option_value); + char*** parsed_addr = parse_ips_and_macs(ctx, option_value); if(parsed_addr != NULL) { valid_option = 1; @@ -701,7 +703,7 @@ int parse_option(char* option_name, char* option_value, string_map* definition) else if( safe_strcmp(option_name, "remote_port") == 0 || safe_strcmp(option_name, "local_port") == 0 || safe_strcmp(option_name, "not_remote_port") == 0 || - safe_strcmp(option_name, "not_local_port") == 0 + safe_strcmp(option_name, "not_local_port") == 0 ) { char** parsed_ports = parse_ports(option_value); @@ -722,13 +724,13 @@ int parse_option(char* option_name, char* option_value, string_map* definition) truncate_if_starts_with(option_name, "not_url_exact") || truncate_if_starts_with(option_name, "not_url_domain_contains") || truncate_if_starts_with(option_name, "not_url_domain_regex") || - truncate_if_starts_with(option_name, "not_url_domain_exact") + truncate_if_starts_with(option_name, "not_url_domain_exact") ) { /* * may be a quoted list of urls to block, so attempt to parse this * if no quotes found, match on unquoted expresssion - * we don't need to de-escape quotes because when we define rule, + * we don't need to de-escape quotes because when we define rule, * we call iptables from system, and through the shell, which will de-escape quotes for us */ list* parsed_quoted = parse_quoted_list(option_value, '\"', '\\', 0); @@ -752,53 +754,17 @@ int parse_option(char* option_name, char* option_value, string_map* definition) } -// this function dynamically allocates memory for -// the option string, but since this program exits -// almost immediately (after printing variable info) -// the massive memory leak we're opening up shouldn't -// cause any problems. This is your reminder/warning -// that this might be an issue if you use this code to -// do anything fancy. -char* get_option_value_string(struct uci_option* uopt) -{ - char* opt_str = NULL; - if(uopt->type == UCI_TYPE_STRING) - { - opt_str = strdup(uopt->v.string); - } - if(uopt->type == UCI_TYPE_LIST) - { - struct uci_element* e; - uci_foreach_element(&uopt->v.list, e) - { - if(opt_str == NULL) - { - opt_str = strdup(e->name); - } - else - { - char* tmp; - tmp = dynamic_strcat(3, opt_str, " ", e->name); - free(opt_str); - opt_str = tmp; - } - } - } - - return opt_str; -} - - - -char*** parse_ips_and_macs(char* addr_str) +char*** parse_ips_and_macs(struct uci_context* ctx, char* addr_str) { unsigned long num_pieces; char** addr_parts = split_on_separators(addr_str, ",", 1, -1, 0, &num_pieces); list* ip_list = initialize_list(); list* ip_range_list = initialize_list(); list* mac_list = initialize_list(); - + list* group_list = initialize_list(); + char* groups = get_groups(ctx); + int ip_part_index; for(ip_part_index=0; addr_parts[ip_part_index] != NULL; ip_part_index++) { @@ -820,7 +786,7 @@ char*** parse_ips_and_macs(char* addr_str) 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) { //get_ip_range_strs(start_ip, end_ip, "", 4, ip_list); @@ -828,10 +794,14 @@ char*** parse_ips_and_macs(char* addr_str) } free(start); - free(end); + free(end); free(range_parts); //free(next_str); } + else if(strstr(groups, next_str) != NULL) + { + push_list(group_list, next_str); + } else { int parsed_ip[4]; @@ -843,18 +813,20 @@ char*** parse_ips_and_macs(char* addr_str) } } free(addr_parts); - - unsigned long num1, num2, num3; + + unsigned long num1, num2, num3, num4; char*** return_value = (char***)malloc(3*sizeof(char**)); return_value[MATCH_IP_INDEX] = (char**)destroy_list(ip_list, DESTROY_MODE_RETURN_VALUES, &num1); return_value[MATCH_IP_RANGE_INDEX] = (char**)destroy_list(ip_range_list, DESTROY_MODE_RETURN_VALUES, &num2); return_value[MATCH_MAC_INDEX] = (char**)destroy_list(mac_list, DESTROY_MODE_RETURN_VALUES, &num3); + return_value[MATCH_GROUP_INDEX] = (char**)destroy_list(group_list, DESTROY_MODE_RETURN_VALUES, &num4); - if(num1 + num2 + num3 == 0) + if(num1 + num2 + num3 + num4 == 0) { free(return_value[0]); free(return_value[1]); free(return_value[2]); + free(return_value[3]); free(return_value); return_value = NULL; } @@ -888,8 +860,8 @@ char** parse_ports(char* port_str) * (by defining [mark]/[mask]) this is bitwise-anded with the maximum * mask to get the final mask. This is especially necessary for * connmarks, because the mechanism to handle negation when multiple - * test rules are needed uses the last (high) byte of the connmark - * address space, so this HAS to be masked out when matching + * test rules are needed uses the last (high) byte of the connmark + * address space, so this HAS to be masked out when matching * connmarks, using max_mask=0x00FFFFFF */ char** parse_marks(char* list_str, unsigned long max_mask) @@ -904,7 +876,7 @@ char** parse_marks(char* list_str, unsigned long max_mask) free(marks); marks = NULL; } - else + else { int mark_index; for(mark_index = 0; marks[mark_index] != NULL; mark_index++) @@ -920,9 +892,9 @@ char** parse_marks(char* list_str, unsigned long max_mask) unsigned long mask = 0xFFFFFFFF; mask_start++; sscanf(mask_start, "%lX", &mask); - + mask = mask & max_mask; - + *(mask_start) = '\0'; char new_mask_str[12]; sprintf(new_mask_str, "0x%lX", mask); @@ -943,13 +915,13 @@ char** parse_marks(char* list_str, unsigned long max_mask) } -/* - * parses list of quoted strings, ignoring escaped quote characters that are not themselves escaped +/* + * parses list of quoted strings, ignoring escaped quote characters that are not themselves escaped * Note that we don't de-escape anything here. If necessary that should be done elsewhere. - */ + */ list* parse_quoted_list(char* list_str, char quote_char, char escape_char, char add_remainder_if_uneven_quotes) { - + long num_quotes = 0; long list_index = 0; char previous_is_quoted = 0; @@ -958,11 +930,11 @@ list* parse_quoted_list(char* list_str, char quote_char, char escape_char, char num_quotes = num_quotes + ( list_str[list_index] == quote_char && !previous_is_quoted ? 1 : 0); previous_is_quoted = list_str[list_index] == escape_char && !previous_is_quoted ? 1 : 0; } - + char** pieces = (char**)malloc( ((long)(num_quotes/2)+2) * sizeof(char*) ); long piece_index = 0; long next_start_index=-1; - previous_is_quoted = 0; + previous_is_quoted = 0; for(list_index=0; list_str[list_index] != '\0'; list_index++) { if( list_str[list_index] == quote_char && !previous_is_quoted ) @@ -1010,7 +982,7 @@ list* parse_quoted_list(char* list_str, char quote_char, char escape_char, char push_list(quoted_list, strdup(list_str)); } free(pieces);//but do free array of char* pointers, we don't need it anymore - + return quoted_list; } diff --git a/package/gargoyle-firewall-util/src/restore_quotas.c b/package/gargoyle-firewall-util/src/restore_quotas.c index 3030b16707..8d78ebd840 100644 --- a/package/gargoyle-firewall-util/src/restore_quotas.c +++ b/package/gargoyle-firewall-util/src/restore_quotas.c @@ -1,4 +1,4 @@ -/* restore_quotas -- Used to initialize and restore bandwidth quotas based on UCI config files +/* restore_quotas -- Used to initialize and restore bandwidth quotas based on UCI config files * and any previously saved quota data in /usr/data/quotas * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) * @@ -33,6 +33,7 @@ #include #include #include +#include "uci-util.h" #define malloc safe_malloc #define strdup safe_strdup @@ -51,9 +52,7 @@ void delete_chain_from_table(char* table, char* delete_chain); void run_shell_command(char* command, int free_command_str); void free_split_pieces(char** split_pieces); -list* get_all_sections_of_type(struct uci_context *ctx, char* package, char* section_type); -char* get_uci_option(struct uci_context* ctx,char* package_name, char* section_name, char* option_name); -char* get_option_value_string(struct uci_option* uopt); + int main(int argc, char** argv) { @@ -64,7 +63,7 @@ int main(int argc, char** argv) char* death_mask = NULL; char* crontab_line = NULL; int ret; - + unsigned char full_qos_active = 0; char c; @@ -153,7 +152,7 @@ int main(int argc, char** argv) char* base_id = get_uci_option(ctx, "firewall", next_quota, "id"); char* exceeded_up_speed_str = get_uci_option(ctx, "firewall", next_quota, "exceeded_up_speed"); char* exceeded_down_speed_str = get_uci_option(ctx, "firewall", next_quota, "exceeded_down_speed"); - + if(base_id != NULL) { @@ -175,7 +174,7 @@ int main(int argc, char** argv) if(oldval != NULL) { free(oldval); } oldval = set_long_map_element(down_speeds, down, strdup(exceeded_down_speed_str) ); if(oldval != NULL) { free(oldval); } - + } } } @@ -219,13 +218,13 @@ int main(int argc, char** argv) - + /* initialize chains */ run_shell_command(dynamic_strcat(3, "iptables -t ", quota_table, " -N forward_quotas 2>/dev/null"), 1); run_shell_command(dynamic_strcat(3, "iptables -t ", quota_table, " -N egress_quotas 2>/dev/null"), 1); run_shell_command(dynamic_strcat(3, "iptables -t ", quota_table, " -N ingress_quotas 2>/dev/null"), 1); run_shell_command(dynamic_strcat(3, "iptables -t ", quota_table, " -N combined_quotas 2>/dev/null"), 1); - + run_shell_command("iptables -t nat -N quota_redirects 2>/dev/null", 0); run_shell_command("iptables -t nat -A quota_redirects -j CONNMARK --set-mark 0x0/0xFF000000 2>/dev/null", 0); run_shell_command("iptables -t nat -I zone_lan_prerouting -j quota_redirects 2>/dev/null", 0); @@ -235,7 +234,7 @@ int main(int argc, char** argv) run_shell_command(dynamic_strcat(6, "iptables -t ", quota_table, " -I INPUT 2 -i ", wan_if, no_death_mark_test, " -j combined_quotas 2>/dev/null"), 1); run_shell_command(dynamic_strcat(6, "iptables -t ", quota_table, " -I OUTPUT 1 -o ", wan_if, no_death_mark_test, " -j egress_quotas 2>/dev/null"), 1); run_shell_command(dynamic_strcat(6, "iptables -t ", quota_table, " -I OUTPUT 2 -o ", wan_if, no_death_mark_test, " -j combined_quotas 2>/dev/null"), 1); - + run_shell_command(dynamic_strcat(3, "iptables -t ", quota_table, " -I FORWARD -j forward_quotas 2>/dev/null"), 1); run_shell_command(dynamic_strcat(6, "iptables -t ", quota_table, " -A forward_quotas -o ", wan_if, no_death_mark_test, " -j egress_quotas 2>/dev/null"), 1); @@ -257,6 +256,7 @@ int main(int argc, char** argv) char* set_death_mark = dynamic_strcat(5, " -j CONNMARK --set-mark ", death_mark, "/", death_mask, " "); list* other_quota_section_names = initialize_list(); list* defined_ip_groups = initialize_list(); + char* groups = get_groups(ctx); unlock_bandwidth_semaphore_on_exit(); while(quota_sections->length > 0 || other_quota_section_names->length > 0) @@ -283,7 +283,7 @@ int main(int argc, char** argv) } } free(quota_enabled_var); - + if(enabled) { char* ip = get_uci_option(ctx, "firewall", next_quota, "ip"); @@ -297,7 +297,6 @@ int main(int argc, char** argv) if(exceeded_up_speed_str == NULL) { exceeded_up_speed_str = strdup(" "); } if(exceeded_down_speed_str == NULL) { exceeded_down_speed_str = strdup(" "); } - if(ip == NULL) { ip = strdup("ALL"); } if(strlen(ip) == 0) { ip = strdup("ALL"); } @@ -314,8 +313,13 @@ int main(int argc, char** argv) ip = dynamic_replace(ip, "- ", "-"); free(tmp_ip); } - - + + if (strstr(groups, ip) != NULL) + { // ensure that an ipset corresponding to the group exists + run_shell_command(dynamic_strcat(3, "ipset create ", ip, " hash:ip hashsize 64 2>/dev/null"), 1); + } + + if( (strcmp(ip, "ALL_OTHERS_COMBINED") == 0 || strcmp(ip, "ALL_OTHERS_INDIVIDUAL") == 0) && (!process_other_quota) ) { push_list(other_quota_section_names, strdup(next_quota)); @@ -328,7 +332,7 @@ int main(int argc, char** argv) /* this is an explicitly defined ip or ip range, so save it for later, to deal with individual other overlap problem */ push_list(defined_ip_groups, strdup(ip)); } - + /* compute proper base id for rule, adding variable to uci if necessary */ char* quota_base_id = get_uci_option(ctx, "firewall", next_quota, "id"); if(quota_base_id == NULL) @@ -338,7 +342,7 @@ int main(int argc, char** argv) char** split_ip = split_on_separators(ip, id_breaks, 3, -1, 0, &num_pieces); char* first_ip = dynamic_replace(split_ip[0], "/", "_"); free_null_terminated_string_array(split_ip); - + quota_base_id = strdup(first_ip); unsigned long next_postfix_count = 0; while( get_string_map_element(defined_base_ids, quota_base_id) != NULL) @@ -359,7 +363,7 @@ int main(int argc, char** argv) /* D for dummy place holder */ set_string_map_element(defined_base_ids, quota_base_id, strdup("D")); - + /* add id we've decided on to UCI */ char* var_set = dynamic_strcat(4, "firewall.", next_quota, ".id=", quota_base_id); if (uci_lookup_ptr(ctx, &ptr, var_set, true) == UCI_OK) @@ -375,7 +379,7 @@ int main(int argc, char** argv) do_restore = strcmp(ignore_backup, "1") == 0 ? 0 : 1; if(!do_restore) { - //remove variable from uci + //remove variable from uci char* var_name = dynamic_strcat(3, "firewall.", next_quota, ".ignore_backup_at_next_restore"); if (uci_lookup_ptr(ctx, &ptr, var_name, true) == UCI_OK) { @@ -385,7 +389,7 @@ int main(int argc, char** argv) } } free(ignore_backup); - + char* reset_interval = get_uci_option(ctx, "firewall", next_quota, "reset_interval"); @@ -393,7 +397,7 @@ int main(int argc, char** argv) if(reset_interval != NULL) { char* reset_time = get_uci_option(ctx, "firewall", next_quota, "reset_time"); - + char* interval_option = strdup(" --reset_interval "); reset = dcat_and_free(&reset, &interval_option, 1, 1); reset = dcat_and_free(&reset, &reset_interval, 1, 1); @@ -404,9 +408,9 @@ int main(int argc, char** argv) reset = dcat_and_free(&reset, &reset_time, 1, 1); } } - - char* time_match_str = strdup(""); - + + char* time_match_str = strdup(""); + char* offpeak_hours = get_uci_option(ctx, "firewall", next_quota, "offpeak_hours"); char* offpeak_weekdays = get_uci_option(ctx, "firewall", next_quota, "offpeak_weekdays"); char* offpeak_weekly_ranges = get_uci_option(ctx, "firewall", next_quota, "offpeak_weekly_ranges"); @@ -432,15 +436,15 @@ int main(int argc, char** argv) time_match_str = dcat_and_free(&time_match_str, &timerange_match, 1,1); if(hours_var != NULL && weekly_ranges_var == NULL) { - time_match_str = dcat_and_free(&time_match_str, &hour_match, 1, 1); - time_match_str = dcat_and_free(&time_match_str, &hours_var, 1, 1); - time_match_str = dcat_and_free(&time_match_str, "e_end, 1, 0); + time_match_str = dcat_and_free(&time_match_str, &hour_match, 1, 1); + time_match_str = dcat_and_free(&time_match_str, &hours_var, 1, 1); + time_match_str = dcat_and_free(&time_match_str, "e_end, 1, 0); } if(weekdays_var != NULL && weekly_ranges_var == NULL) { - time_match_str = dcat_and_free(&time_match_str, &weekday_match, 1, 1); + time_match_str = dcat_and_free(&time_match_str, &weekday_match, 1, 1); time_match_str = dcat_and_free(&time_match_str, &weekdays_var, 1, 1); - time_match_str = dcat_and_free(&time_match_str, "e_end, 1, 0); + time_match_str = dcat_and_free(&time_match_str, "e_end, 1, 0); } if(weekly_ranges_var != NULL) { @@ -450,11 +454,11 @@ int main(int argc, char** argv) } free(quote_end); } - + char* types[] = { "ingress_limit", "egress_limit", "combined_limit" }; char* postfixes[] = { "_ingress", "_egress", "_combined" }; char* chains[] = { "ingress_quotas", "egress_quotas", "combined_quotas" }; - + int type_index; for(type_index=0; type_index < 3; type_index++) { @@ -463,28 +467,42 @@ int main(int argc, char** argv) char* subnet_definition = strdup(""); char* limit = get_uci_option(ctx, "firewall", next_quota, types[type_index]); - + char* type_id = dynamic_strcat(2, quota_base_id, postfixes[type_index] ); - + char* up_qos_mark = get_string_map_element(upload_qos_marks, exceeded_up_speed_str); char* down_qos_mark = get_string_map_element(download_qos_marks, exceeded_down_speed_str); if(full_qos_active) { up_qos_mark = get_uci_option(ctx, "firewall", next_quota, "exceeded_up_class_mark"); down_qos_mark = get_uci_option(ctx, "firewall", next_quota, "exceeded_down_class_mark"); - } + } - /* + /* * need to do ip test even if limit is null, because ALL_OTHERS quotas should not apply when any of the three types of explicit limit is defined * and we therefore need to use this test to set mark indicating an explicit quota has been checked */ - char* ip_test = strdup(""); + char* ip_test = strdup(""); if( strcmp(ip, "ALL_OTHERS_COMBINED") != 0 && strcmp(ip, "ALL_OTHERS_INDIVIDUAL") != 0 && strcmp(ip, "ALL") != 0 ) { + char* src_test; + char* dst_test; + if (strstr(groups, ip) != NULL) + { + src_test = dynamic_strcat(3, " -m set --match-set ", ip, " src "); + dst_test = dynamic_strcat(3, " -m set --match-set ", ip, " dst "); + } + else if (strstr(ip, "-") == NULL) + { + src_test = dynamic_strcat(3, " --src ", ip, " "); + dst_test = dynamic_strcat(3, " --dst ", ip, " "); + } + else + { + src_test = dynamic_strcat(3, " -m iprange --src-range ", ip, " "); + dst_test = dynamic_strcat(3, " -m iprange --dst-range ", ip, " "); + } - char* src_test = strstr(ip, "-") == NULL ? dynamic_strcat(3, " --src ", ip, " ") : dynamic_strcat(3, " -m iprange --src-range ", ip, " "); - char* dst_test = strstr(ip, "-") == NULL ? dynamic_strcat(3, " --dst ", ip, " ") : dynamic_strcat(3, " -m iprange --dst-range ", ip, " "); - if(strstr(ip, ",") != NULL || strstr(ip, " ") != NULL || strstr(ip, "\t") != NULL ) { char ip_breaks[] = { ',', ' ', '\t' }; @@ -494,8 +512,23 @@ int main(int argc, char** argv) for(ip_index=0; ip_index < num_ips; ip_index++) { char *next_ip = ip_list[ip_index]; - char* egress_test = strstr(next_ip, "-") == NULL ? dynamic_strcat(3, " --src ", next_ip, " ") : dynamic_strcat(3, " -m iprange --src-range ", next_ip, " "); - char* ingress_test = strstr(next_ip, "-") == NULL ? dynamic_strcat(3, " --dst ", next_ip, " ") : dynamic_strcat(3, " -m iprange --dst-range ", next_ip, " "); + char* egress_test; + char* ingress_test; + if (strstr(groups, next_ip) != NULL) + { + egress_test = dynamic_strcat(3, " -m set --match-set ", next_ip, " src "); + ingress_test = dynamic_strcat(3, " -m set --match-set ", next_ip, " dst "); + } + else if (strstr(ip, "-") == NULL) + { + egress_test = dynamic_strcat(3, " --src ", next_ip, " "); + ingress_test = dynamic_strcat(3, " --dst ", next_ip, " "); + } + else + { + egress_test = dynamic_strcat(3, " -m iprange --src-range ", next_ip, " "); + ingress_test = dynamic_strcat(3, " -m iprange --dst-range ", next_ip, " "); + } if(strcmp(types[type_index], "egress_limit") == 0) { @@ -560,13 +593,13 @@ int main(int argc, char** argv) { applies_to = strdup("individual_local"); } - + char *subnet_option = strdup(" --subnet "); subnet_definition = dcat_and_free(&subnet_definition, &subnet_option, 1, 1); subnet_definition = dcat_and_free(&subnet_definition, &local_subnet, 1, 0); } } - + if(up_qos_mark != NULL && down_qos_mark != NULL) @@ -582,11 +615,11 @@ int main(int argc, char** argv) char* other_type_id = dynamic_strcat(2, quota_base_id, postfixes[other_type_index] ); if(type_index == EGRESS_INDEX) { - run_shell_command(dynamic_strcat(10, "iptables -t ", quota_table, " -A ", chains[type_index], ip_test, time_match_str, " -m bandwidth --id \"", other_type_id, "\" --bcheck_with_src_dst_swap ", set_egress_mark), 1); + run_shell_command(dynamic_strcat(10, "iptables -t ", quota_table, " -A ", chains[type_index], ip_test, time_match_str, " -m bandwidth --id \"", other_type_id, "\" --bcheck_with_src_dst_swap ", set_egress_mark), 1); } else { - run_shell_command(dynamic_strcat(10, "iptables -t ", quota_table, " -A ", chains[type_index], ip_test, time_match_str, " -m bandwidth --id \"", other_type_id, "\" --bcheck_with_src_dst_swap ", set_ingress_mark), 1); + run_shell_command(dynamic_strcat(10, "iptables -t ", quota_table, " -A ", chains[type_index], ip_test, time_match_str, " -m bandwidth --id \"", other_type_id, "\" --bcheck_with_src_dst_swap ", set_ingress_mark), 1); } free(other_type_id); } @@ -595,13 +628,13 @@ int main(int argc, char** argv) free(set_egress_mark); free(set_ingress_mark); } - + if(limit != NULL) { if(up_qos_mark != NULL && down_qos_mark != NULL) { char* set_egress_mark = dynamic_strcat(2, " -j MARK --set-mark ", up_qos_mark); - char* set_ingress_mark = dynamic_strcat(2, " -j MARK --set-mark ", down_qos_mark); + char* set_ingress_mark = dynamic_strcat(2, " -j MARK --set-mark ", down_qos_mark); if(strcmp(types[type_index], "egress_limit") == 0) { run_shell_command(dynamic_strcat(15, "iptables -t ", quota_table, " -A ", chains[type_index], ip_test, time_match_str, " -m bandwidth --id \"", type_id, "\" --type ", applies_to, subnet_definition, " --greater_than ", limit, reset, set_egress_mark), 1); @@ -624,7 +657,7 @@ int main(int argc, char** argv) //insert quota block rule run_shell_command(dynamic_strcat(15, "iptables -t ", quota_table, " -A ", chains[type_index], ip_test, time_match_str, " -m bandwidth --id \"", type_id, "\" --type ", applies_to, subnet_definition, " --greater_than ", limit, reset, set_death_mark), 1); - + //insert redirect rule if(strcmp(ip, "ALL") == 0 || strcmp(ip, "ALL_OTHERS_INDIVIDUAL") == 0) { @@ -695,6 +728,7 @@ int main(int argc, char** argv) free(next_quota); } + free(groups); run_shell_command("iptables -t nat -A quota_redirects -j CONNMARK --set-mark 0x0/0xFF000000 2>/dev/null", 0); run_shell_command(dynamic_strcat(3,"iptables -t ", quota_table, " -A egress_quotas -j CONNMARK --set-mark 0x0/0xFF000000 2>/dev/null"), 1); @@ -804,17 +838,17 @@ void restore_backup_for_id(char* id, char* quota_backup_dir, unsigned char is_in ip_bw* ptr = loaded_backup_data + ip_index; push_list(ip_bw_list, ptr); } - + unsigned long num_groups = 0; char** group_strs = (char**)get_list_values(defined_ip_groups, &num_groups); unsigned long group_index; - + for(group_index = 0; group_index < num_groups; group_index++) { filter_group_from_list(&ip_bw_list, group_strs[group_index]); } - - + + //rebuild the backup data array from the filtered list if(num_ips != ip_bw_list->length) { @@ -828,7 +862,7 @@ void restore_backup_for_id(char* id, char* quota_backup_dir, unsigned char is_in free(loaded_backup_data); loaded_backup_data = adj_backup; } - + destroy_list(ip_bw_list, DESTROY_MODE_IGNORE_VALUES, &num_groups); free(group_strs); //don't want to destroy values, they're still contained in list, so just destroy container array } @@ -871,7 +905,7 @@ list* filter_group_from_list(list** orig_ip_bw_list, char* ip_group_str) unsigned long num_groups = 0; char** split_group = split_on_separators(dyn_group_str, group_breaks, 3, -1, 0, &num_groups); unsigned long group_index; - + for(group_index = 0; group_index < num_groups; group_index++) { uint32_t* range = ip_range_to_host_ints( split_group[group_index] ); @@ -906,8 +940,8 @@ uint32_t* ip_range_to_host_ints(char* ip_str) uint32_t* ret_val = (uint32_t*)malloc(2*sizeof(uint32_t)); uint32_t start = 0; uint32_t end = 0; - - + + unsigned long num_pieces = 0; char ip_breaks[] = "/-"; char** split_ip = split_on_separators(ip_str, ip_breaks, 2, -1, 0, &num_pieces); @@ -957,7 +991,7 @@ void delete_chain_from_table(char* table, char* delete_chain) char *command = dynamic_strcat(3, "iptables -t ", table, " -L -n --line-numbers 2>/dev/null"); unsigned long num_lines = 0; char** table_dump = get_shell_command_output_lines(command, &num_lines ); - free(command); + free(command); unsigned long line_index; char* current_chain = NULL; @@ -975,7 +1009,7 @@ void delete_chain_from_table(char* table, char* delete_chain) if(current_chain != NULL) { free(current_chain); } current_chain = strdup(line_pieces[1]); } - else + else { unsigned long line_num; int read = sscanf(line_pieces[0], "%ld", &line_num); @@ -994,7 +1028,7 @@ void delete_chain_from_table(char* table, char* delete_chain) free_null_terminated_string_array(line_pieces); } free_null_terminated_string_array(table_dump); - + /* final two commands to flush chain being deleted and whack it */ unshift_list(delete_commands, dynamic_strcat(5, "iptables -t ", table, " -F ", delete_chain, " 2>/dev/null")); unshift_list(delete_commands, dynamic_strcat(5, "iptables -t ", table, " -X ", delete_chain, " 2>/dev/null")); @@ -1017,89 +1051,3 @@ void run_shell_command(char* command, int free_command_str) free(command); } } - -list* get_all_sections_of_type(struct uci_context *ctx, char* package, char* section_type) -{ - - struct uci_package *p = NULL; - struct uci_element *e = NULL; - - list* sections_of_type = initialize_list(); - if(uci_load(ctx, package, &p) == UCI_OK) - { - uci_foreach_element( &p->sections, e) - { - struct uci_section *section = uci_to_section(e); - if(safe_strcmp(section->type, section_type) == 0) - { - push_list(sections_of_type, strdup(section->e.name)); - } - } - } - return sections_of_type; -} - - -char* get_uci_option(struct uci_context* ctx, char* package_name, char* section_name, char* option_name) -{ - char* option_value = NULL; - struct uci_ptr ptr; - char* lookup_str = dynamic_strcat(5, package_name, ".", section_name, ".", option_name); - int ret_value = uci_lookup_ptr(ctx, &ptr, lookup_str, 1); - if(ret_value == UCI_OK) - { - if( !(ptr.flags & UCI_LOOKUP_COMPLETE)) - { - ret_value = UCI_ERR_NOTFOUND; - } - else - { - struct uci_element *e = (struct uci_element*)ptr.o; - option_value = get_option_value_string(uci_to_option(e)); - } - } - free(lookup_str); - - return option_value; -} - - - - -// this function dynamically allocates memory for -// the option string, but since this program exits -// almost immediately (after printing variable info) -// the massive memory leak we're opening up shouldn't -// cause any problems. This is your reminder/warning -// that this might be an issue if you use this code to -// do anything fancy. -char* get_option_value_string(struct uci_option* uopt) -{ - char* opt_str = NULL; - if(uopt->type == UCI_TYPE_STRING) - { - opt_str = strdup(uopt->v.string); - } - if(uopt->type == UCI_TYPE_LIST) - { - struct uci_element* e; - uci_foreach_element(&uopt->v.list, e) - { - if(opt_str == NULL) - { - opt_str = strdup(e->name); - } - else - { - char* tmp; - tmp = dynamic_strcat(3, opt_str, " ", e->name); - free(opt_str); - opt_str = tmp; - } - } - } - - return opt_str; -} - - diff --git a/package/gargoyle-firewall-util/src/uci-util.c b/package/gargoyle-firewall-util/src/uci-util.c new file mode 100644 index 0000000000..ed07a12abb --- /dev/null +++ b/package/gargoyle-firewall-util/src/uci-util.c @@ -0,0 +1,124 @@ + + +#include +#include + + +list* get_all_sections_of_type(struct uci_context *ctx, char* package, char* section_type); +char* get_uci_option(struct uci_context* ctx,char* package_name, char* section_name, char* option_name); +char* get_option_value_string(struct uci_option* uopt); +char* get_groups(struct uci_context* ctx); + + +list* get_all_sections_of_type(struct uci_context *ctx, char* package, char* section_type) +{ + + struct uci_package *p = NULL; + struct uci_element *e = NULL; + + list* sections_of_type = initialize_list(); + if(uci_load(ctx, package, &p) == UCI_OK) + { + uci_foreach_element( &p->sections, e) + { + struct uci_section *section = uci_to_section(e); + if(safe_strcmp(section->type, section_type) == 0) + { + push_list(sections_of_type, strdup(section->e.name)); + } + } + } + return sections_of_type; +} + + +char* get_uci_option(struct uci_context* ctx, char* package_name, char* section_name, char* option_name) +{ + char* option_value = NULL; + struct uci_ptr ptr; + char* lookup_str = dynamic_strcat(5, package_name, ".", section_name, ".", option_name); + int ret_value = uci_lookup_ptr(ctx, &ptr, lookup_str, 1); + if(ret_value == UCI_OK) + { + if( !(ptr.flags & UCI_LOOKUP_COMPLETE)) + { + ret_value = UCI_ERR_NOTFOUND; + } + else + { + struct uci_element *e = (struct uci_element*)ptr.o; + option_value = get_option_value_string(uci_to_option(e)); + } + } + free(lookup_str); + + return option_value; +} + + + + +// this function dynamically allocates memory for +// the option string, but since this program exits +// almost immediately (after printing variable info) +// the massive memory leak we're opening up shouldn't +// cause any problems. This is your reminder/warning +// that this might be an issue if you use this code to +// do anything fancy. +char* get_option_value_string(struct uci_option* uopt) +{ + char* opt_str = NULL; + if(uopt->type == UCI_TYPE_STRING) + { + opt_str = strdup(uopt->v.string); + } + if(uopt->type == UCI_TYPE_LIST) + { + struct uci_element* e; + uci_foreach_element(&uopt->v.list, e) + { + if(opt_str == NULL) + { + opt_str = strdup(e->name); + } + else + { + char* tmp; + tmp = dynamic_strcat(3, opt_str, " ", e->name); + free(opt_str); + opt_str = tmp; + } + } + } + + return opt_str; +} + + +/** +* returns a space delimited string of group names +*/ +char* get_groups(struct uci_context* ctx) +{ + char* groups = ""; + list* hosts = get_all_sections_of_type(ctx, "dhcp", "host"); + + while(hosts->length > 0) + { + char* next_host = shift_list(hosts); + char* group = get_uci_option(ctx, "dhcp", next_host, "group"); + + if (group != NULL && strlen(group) > 0 && strstr(groups, group) == NULL) + { + char* tmp = groups; + groups= dynamic_strcat(3, groups, " ", strdup(group)); + free(tmp); + } + free(next_host); + free(group); + } + unsigned long num_destroyed; + destroy_list(hosts, DESTROY_MODE_FREE_VALUES, &num_destroyed); + + return groups; +} diff --git a/package/gargoyle-firewall-util/src/uci-util.h b/package/gargoyle-firewall-util/src/uci-util.h new file mode 100644 index 0000000000..d94ac9d8e5 --- /dev/null +++ b/package/gargoyle-firewall-util/src/uci-util.h @@ -0,0 +1,9 @@ + +#include +#include + + +list* get_all_sections_of_type(struct uci_context *ctx, char* package, char* section_type); +char* get_uci_option(struct uci_context* ctx,char* package_name, char* section_name, char* option_name); +char* get_option_value_string(struct uci_option* uopt); +char* get_groups(struct uci_context* ctx); diff --git a/package/gargoyle/files/etc/config/gargoyle b/package/gargoyle/files/etc/config/gargoyle index 2be0104cfc..734bd6d51a 100644 --- a/package/gargoyle/files/etc/config/gargoyle +++ b/package/gargoyle/files/etc/config/gargoyle @@ -117,4 +117,3 @@ config bandwidth_display bandwidth_display config help help option ddns_1 1 - diff --git a/package/gargoyle/files/etc/dnsmasq.conf b/package/gargoyle/files/etc/dnsmasq.conf new file mode 100644 index 0000000000..4cc45f5afe --- /dev/null +++ b/package/gargoyle/files/etc/dnsmasq.conf @@ -0,0 +1 @@ +dhcp-script=/usr/lib/gargoyle/post_lease.sh diff --git a/package/gargoyle/files/usr/lib/gargoyle/post_lease.sh b/package/gargoyle/files/usr/lib/gargoyle/post_lease.sh new file mode 100755 index 0000000000..2d3966e520 --- /dev/null +++ b/package/gargoyle/files/usr/lib/gargoyle/post_lease.sh @@ -0,0 +1,51 @@ +#!/bin/sh +# +# This program is copyright © 2015 John Brown and is distributed under the terms of the GNU GPL +# version 2.0 with a special clarification/exception that permits adapting the program to +# configure proprietary "back end" software provided that all modifications to the web interface +# itself remain covered by the GPL. +# See http://gargoyle-router.com/faq.html#qfoss for more information + +# This script was written as part of the Device Groups implementation (see: /www/dhcp.sh) +# Know Devices may be assigned to a Group (see: uci show known.device) +# An ipset is created for each Group (see: ipset list -n) +# Each new dhcp lease must update the ipset (see: uci show dnsmasq.dhcp-script) +# This script searches Know Devices for a matching MAC and will add/del the IP to/from the Group's ipset + + +event=$1 +mac="$(echo $2 | awk '{print toupper($0)}')" +ip=$3 +host=$4 + +OIFS=$IFS; + +#echo "" >>//tmp/post_lease +#echo $event $mac $ip $host >>/tmp/post_lease + +uciHosts=$(uci show dhcp 2>>//dev/null | grep '=host' | sed s/dhcp.// | sed s/=host// | awk '{ print $1 ; } ') + +for uciHost in $uciHosts; do + + IFS=" " + uciMacs=$(uci get dhcp.$uciHost.mac 2>>//dev/null) + for uciMac in $uciMacs ; do + if [ "$uciMac" = "$mac" ]; then + + uciGroup=$(uci get dhcp.$uciHost.group 2>>//dev/null) + if [ "${#uciGroup}" -gt 0 ]; then + + if [ "$event" = "add" ] || [ "$event" = "old" ]; then + ipset create $uciGroup hash:ip hashsize 64 2>>//dev/null # create ipset if not exist + ipset add $uciGroup $ip 2>>//dev/null + fi + if [ "$event" = "del" ]; then + ipset del $uciGroup $ip -exist 2>>//dev/null + fi + #ipset list $uciGroup >>//tmp/post_lease + IFS=$OIFS; + return 0 # remove to continue searching for Macs in other Hosts + fi + fi + done +done diff --git a/package/gargoyle/files/www/dhcp.sh b/package/gargoyle/files/www/dhcp.sh index 2b6e410862..64b55ad668 100755 --- a/package/gargoyle/files/www/dhcp.sh +++ b/package/gargoyle/files/www/dhcp.sh @@ -89,7 +89,7 @@ for (etherIndex in etherData)
- <%~ SIPs %> + <%~ KnDev %>
@@ -98,28 +98,47 @@ for (etherIndex in etherData)
-
- +
+ +
-
- <%in templates/static_ip_template %> -
-
- +
+ <%in templates/device_template %>
-
- <%~ AsSIP %>: +
+ <%~ KnDev %>:
-
+
+ +
+ <%~ DevGp %> + +
+ + +
+
+
+ <%in templates/group_template %> +
+
+ +
+ <%~ DevGp %>: +
+
+
+
+
+ + diff --git a/package/gargoyle/files/www/js/common.js b/package/gargoyle/files/www/js/common.js index 365fd0767c..b9e6196ed1 100644 --- a/package/gargoyle/files/www/js/common.js +++ b/package/gargoyle/files/www/js/common.js @@ -256,106 +256,88 @@ function execute(cmd) // they are defined with set method function UCIContainer() { - this.keys = new Array(); - this.values = new Array(); - this.listOptions = new Array(); - + this.values = new Object(); + const DOT = "."; this.createListOption = function(pkg,section,option,destroy_existing_nonlist) { destroy_existing_nonlist = destroy_existing_nonlist == null ? true : false; - var list_key = pkg + "\." + section + "\." + option; - if( this.listOptions[ list_key ] != null ) + var list_key = pkg + DOT + section + DOT + option; + var existing_value = this.values[ list_key ]; + if( existing_value instanceof Array ) { return; } - - this.listOptions[ list_key ] = 1; - if( this.values[list_key] != null ) + else if(destroy_existing_nonlist) { - var old = this.values[list_key]; - this.values[list_key] = (!destroy_existing_nonlist) && old != null ? [old] : [] ; + this.values[ list_key ] = []; } else { - this.keys.push(list_key); - this.values[list_key] = []; + this.values[ list_key ] = [existing_value]; } } + this.set = function(pkg, section, option, value, preserveExistingListValues) { preserveExistingListValues = preserveExistingListValues == null ? false : preserveExistingListValues; - var next_key = pkg + "\." + section; - if(option != null && option != "" ) + var key = pkg + DOT + section; + if(option != null && option != "" ) { - next_key = next_key + "\." + option; + key = key + DOT + option; } - if(this.values[next_key] != null) - { - if (this.listOptions[ next_key ] != null) - { - var set = this.values[next_key]; - while(set.length > 0 && (!preserveExistingListValues)) + + if(this.values.hasOwnProperty(key)) + { // an existing key + if (preserveExistingListValues) + { // guarantee that the existing values are an Array + var existing = this.values[ key ]; + if (!(existing instanceof Array)) { - set.pop(); + this.values[ key ] = (existing == null) ? [] : [existing]; } - if( value instanceof Array ) + + var existingValues = this.values[ key ]; + var newValues = ( value instanceof Array ) ? value : [value]; + for(nvIx=0; nvIx 0) - { - var nextKey = this.keys.shift(); - if(nextKey != removeKey){ newKeys.push(nextKey); } - } - this.keys = newKeys; - } - else - { - value = '' - } + value = (value == null) ? '' : value; + this.values[removeKey] = null; return value; } + this.removeSection = function(pkg, section) { removeKeys = new Array(); sectionDefined = false; - for (keyIndex in this.keys) + for (var key in this.values) { - key = this.keys[keyIndex]; - testExp = new RegExp(pkg + "\\." + section + "\\."); - if(key.match(testExp)) + if (this.values.hasOwnProperty(key)) { - var splitKey = key.split("\."); - removeKeys.push(splitKey[2]); - } - if(key == pkg + "." + section) - { - sectionDefined = true; + testExp = new RegExp(pkg + "\\." + section + "\\."); + if(key.match(testExp)) + { + var splitKey = key.split(DOT); + removeKeys.push(splitKey[2]); + } + if(key == pkg + "." + section) + { + sectionDefined = true; + } } - } for (rkIndex in removeKeys) { @@ -476,30 +451,31 @@ function UCIContainer() { var copy = new UCIContainer(); var keyIndex = 0; - for(keyIndex = 0; keyIndex < this.keys.length; keyIndex++) - { - var key = this.keys[keyIndex]; - var val = this.values[key] - if( this.listOptions[ key ] != null ) - { - copy.listOptions[ key ] = 1; - } - - var splitKey = key.match(/^([^\.]+)\.([^\.]+)\.([^\.]+)$/); - if(splitKey == null) + for (var key in this.values) { + if (this.values.hasOwnProperty(key)) { - splitKey = key.match(/^([^\.]+)\.([^\.]+)$/); - if(splitKey != null) + var val = this.values[key] + if (val instanceof Array) { - splitKey.push(""); + val = val.slice(0); } - else + + var splitKey = key.match(/^([^\.]+)\.([^\.]+)\.([^\.]+)$/); + if(splitKey == null) { + splitKey = key.match(/^([^\.]+)\.([^\.]+)$/); + if(splitKey != null) + { + splitKey.push(""); + } + else + { //should never get here -- if problems put debugging code here //val = val; // good enough for a breakpoint to be set. + } } + copy.set(splitKey[1], splitKey[2], splitKey[3], val); } - copy.set(splitKey[1], splitKey[2], splitKey[3], val, true); } return copy; } @@ -508,16 +484,18 @@ function UCIContainer() { var str=""; var keyIndex=0; - for(keyIndex=0; keyIndex < this.keys.length; keyIndex++) + for (var key in this.values) { - var key = this.keys[keyIndex] - if(this.values[key] instanceof Array ) + if (this.values.hasOwnProperty(key)) { - str=str+ "\n" + key + " = \"" + this.values[key].join(",") + "\""; - } - else - { - str=str+ "\n" + key + " = \"" + this.values[key] + "\""; + if(this.values[key] instanceof Array ) + { + str=str+ "\n" + key + " = \"" + this.values[key].join(",") + "\""; + } + else + { + str=str+ "\n" + key + " = \"" + this.values[key] + "\""; + } } } return str; @@ -530,84 +508,86 @@ function UCIContainer() var listsWithoutUpdates = []; - var keyIndex=0; - for(keyIndex=0; keyIndex < oldSettings.keys.length; keyIndex++) + for (var key in oldSettings.values) { - var key = oldSettings.keys[keyIndex]; - var oldValue = oldSettings.values[key]; - var newValue = this.values[key]; - - if( (oldValue instanceof Array && !(newValue instanceof Array)) || (newValue instanceof Array && !(oldValue instanceof Array)) ) + if (oldSettings.values.hasOwnProperty(key)) { - commandArray.push( "uci del " + key); - } - else if (oldValue instanceof Array && newValue instanceof Array) - { - var matches = oldValue.length == newValue.length; - if(matches) + var oldValue = oldSettings.values[key]; + var newValue = this.values[key]; + + if( (oldValue instanceof Array && !(newValue instanceof Array)) || (newValue instanceof Array && !(oldValue instanceof Array)) ) { - var sortedOld = oldValue.sort() - var sortedNew = newValue.sort() - var sortedIndex; - for(sortedIndex=0; matches && sortedIndex 255 in at least one field //5 = improper format + //6 = invalid characters var errorCode = 0; if(address == "0.0.0.0") @@ -1635,6 +1627,59 @@ function validateMac(mac) return errorCode; } +/* +* Each Group is reflected by an ipset of the same name so the Group Name must + be a valid ipset name. +*/ +function validateGroup(name) +{ + var errorCode = 0; + if(name.match(/^[a-zA-Z0-9-_.:]{2,63}$/) == null) + { + errorCode = 6; + } + return errorCode; +} + + +/* +* Host names may include a-z, A-Z, 0-9 and - +*/ +function validateHost(name) +{ + var errorCode = 0; + if(name.match(/^[a-zA-Z0-9-]{2,63}$/) == null) + { + errorCode = 6; + } + return errorCode; +} + + +/* +* UCI names may include a-z, A-Z, 0-9 and _ +*/ +function validateUCI(name) +{ + var errorCode = 0; + if(name.match(/^[a-zA-Z0-9_]{2,63}$/) == null) + { + errorCode = 6; + } + return errorCode; +} + + +function validateMultipleIpsOrGroups(ips) +{ + var errorCode = validateGroup(ips); + if (errorCode != 0) + { + errorCode = validateMultipleIps(ips); + } + return errorCode; +} + function validateMultipleIps(ips) { ips = ips.replace(/^[\t ]+/g, ""); @@ -1665,7 +1710,7 @@ function validateMultipleIps(ips) } return valid; } -function validateMultipleIpsOrMacs(addresses) +function validateMultipleIpsMacsOrGroups(addresses) { var addr = addresses.replace(/^[\t ]+/g, ""); addr = addr.replace(/[\t ]+$/g, ""); @@ -1692,15 +1737,62 @@ function validateMultipleIpsOrMacs(addresses) { valid = validateMac(nextAddr); } - else + else if(nextAddr.match(/\//)) { valid = validateIpRange(nextAddr); } + else + { + valid = validateGroup(nextAddr); + } } return valid; } +function validateMultipleMacs(macs) +{ + macs = macs.replace(/^[\t ]+/g, ""); + macs = macs.replace(/[\t ]+$/g, ""); + var splitMacs = macs.split(/[\t ]+/); + var valid = splitMacs.length > 0 ? 0 : 1; //1= error, 0=true + while(valid == 0 && splitMacs.length > 0) + { + var nextMac = splitMacs.pop(); + valid = validateMac(nextMac); + } + return valid; +} + +function validateMultipleHosts(hosts) +{ + hosts = hosts.replace(/^[\t ]+/g, ""); + hosts = hosts.replace(/[\t ]+$/g, ""); + var splitHosts = hosts.split(/[\t ]*,[\t ]*/); + var valid = splitHosts.length > 0 ? 0 : 1; //1= error, 0=true + while(valid == 0 && splitHosts.length > 0) + { + var nextHost = splitHosts.pop(); + valid = validateHost(nextHost); + } + return valid; +} + +function validateMultipleUCIs(ucis) +{ + ucis = ucis.replace(/^[\t ]+/g, ""); + ucis = ucis.replace(/[\t ]+$/g, ""); + var splitUCIs = ucis.split(/[\t ]* [\t ]*/); + var valid = splitUCIs.length > 0 ? 0 : 1; //1= error, 0=true + while(valid == 0 && splitUCIs.length > 0) + { + var nextUCI = splitUCIs.pop(); + valid = validateUCI(nextUCI); + } + return valid; +} + + function validateDecimal(num) { var errorCode = num.match(/^[\d]*\.?[\d]+$/) != null || num.match(/^[\d]+\.?[\d]*$/) != null ? 0 : 1; @@ -1833,6 +1925,14 @@ function validateIpRange(range) return valid; } +function validateIpRangeOrGroup(range) +{ + if (range.indexOf(".") != -1) + { + return validateIpRange(range); + } + return validateGroup(range); +} function validateLengthRange(text,min,max) { @@ -1924,17 +2024,49 @@ function proofreadIpRange(input) { proofreadText(input, validateIpRange, 0); } +function proofreadIpRangeOrGroup(input) +{ + proofreadText(input, validateIpRangeOrGroup, 0); +} function proofreadMac(input) { proofreadText(input, validateMac, 0); } +function proofreadMultipleMacs(input) +{ + proofreadText(input, validateMultipleMacs, 0); +} function proofreadMultipleIps(input) { proofreadText(input, validateMultipleIps, 0); } -function proofreadMultipleIpsOrMacs(input) +function proofreadMultipleIpsMacsOrGroups(input) +{ + proofreadText(input, validateMultipleIpsMacsOrGroups, 0); +} +function proofreadMultipleIpsOrGroup(input) +{ + proofreadText(input, validateMultipleIpsOrGroups, 0); +} +function proofreadHost(input) +{ + proofreadText(input, validateHost, 0); +} +function proofreadMultipleHosts(input) +{ + proofreadText(input, validateMultipleHosts, 0); +} +function proofreadUCI(input) { - proofreadText(input, validateMultipleIpsOrMacs, 0); + proofreadText(input, validateUCI, 0); +} +function proofreadMultipleUCIs(input) +{ + proofreadText(input, validateMultipleUCIs, 0); +} +function proofreadGroup(input) +{ + proofreadText(input, validateGroup, 0); } function proofreadDecimal(input) @@ -2122,10 +2254,11 @@ function textListToSpanElement(textList, addCommas, controlDocument) return spanEl; } -function addAddressStringToTable(controlDocument, newAddrs, tableContainerId, tableId, macsValid, ipValidType, alertOnError, tableWidth) +function addAddressStringToTable(controlDocument, newAddrs, tableContainerId, tableId, macsValid, ipValidType, groupsValid, alertOnError, tableWidth) { //ipValidType: 0=none, 1=ip only, 2=ip or ip subnet, 3>=ip, ip subnet or ip range macsValid = macsValid == null ? true : macsValid; + groupsValid = groupsValid == null ? true : groupsValid; ipValidType = ipValidType == null ? 3 : ipValidType; var ipValidFunction; if(ipValidType == 0) @@ -2156,7 +2289,12 @@ function addAddressStringToTable(controlDocument, newAddrs, tableContainerId, ta for(rowIndex=0; rowIndex < data.length; rowIndex++) { var addr = data[rowIndex][0]; - if(validateMac(addr) == 0) + if (validateGroup(addr) == 0) + { + var groupMacs = uciOriginal("dhcp", addr, "mac"); + allCurrentMacs.push(groupMacs); + } + else if(validateMac(addr) == 0) { allCurrentMacs.push(addr); } @@ -2178,9 +2316,9 @@ function addAddressStringToTable(controlDocument, newAddrs, tableContainerId, ta var addr = splitAddrs[splitIndex]; var macValid = (macsValid && validateMac(addr) == 0); var ipValid = (ipValidFunction(addr) == 0); - if(macValid || ipValid) + if(macValid || ipValid || groupsValid) { - var currAddrs = macValid ? allCurrentMacs : allCurrentIps; + var currAddrs = (macValid || groupsValid) ? allCurrentMacs : allCurrentIps; valid = currAddrs.length == 0 || (!testAddrOverlap(addr, currAddrs.join(","))) ? 0 : 1; if(valid == 0) { @@ -2225,11 +2363,11 @@ function addAddressStringToTable(controlDocument, newAddrs, tableContainerId, ta } -function addAddressesToTable(controlDocument, textId, tableContainerId, tableId, macsValid, ipValidType, alertOnError, tableWidth) +function addAddressesToTable(controlDocument, textId, tableContainerId, tableId, macsValid, ipValidType, groupsValid, alertOnError, tableWidth) { var newAddrs = controlDocument.getElementById(textId).value; - var valid = addAddressStringToTable(controlDocument, newAddrs, tableContainerId, tableId, macsValid, ipValidType, alertOnError, tableWidth) + var valid = addAddressStringToTable(controlDocument, newAddrs, tableContainerId, tableId, macsValid, ipValidType, groupsValid, alertOnError, tableWidth) if(valid) { controlDocument.getElementById(textId).value = ""; @@ -2335,6 +2473,16 @@ function testSingleAddrOverlap(addrStr1, addrStr2) function testAddrOverlap(addrStr1, addrStr2) { + var groups = uciOriginal.getAllSectionsOfType("dhcp", "mac"); + if (groups.indexOf(addrStr1) > -1) + { + addrStr1 = groupIPs(addrStr1).join(); + } + if (groups.indexOf(addrStr2) > -1) + { + addrStr2 = groupIPs(addrStr2).join(); + } + addrStr1 = addrStr1.replace(/^[\t ]+/, ""); addrStr1 = addrStr1.replace(/[\t ]+$/, ""); addrStr2 = addrStr2.replace(/^[\t ]+/, ""); @@ -2775,7 +2923,8 @@ function query(queryHeader, queryText, buttonNameList, continueFunction ) } //apparently these var fbS=new Object(); //part of i18n -> objects do not have a length (it is undefined) -function ObjLen(an_obj) { +function ObjLen(an_obj) +{ var len=0; for (item in an_obj) { len++; @@ -2784,6 +2933,72 @@ function ObjLen(an_obj) { } +function groupHosts(group) +{ + var groupHosts = []; + var hosts = uciOriginal.getAllSectionsOfType("dhcp", "host"); + for (hIndex=0; hIndex < hosts.length; hIndex++) + { // survey all of the devices + var host = hosts[hIndex]; + var hostGroup = uciOriginal.get("dhcp", host, "group"); + if (hostGroup.localeCompare(group) == 0) + { + groupHosts.push(host); + } + } + return groupHosts; +} + + +function groupIPs(group) +{ + var groupIPs = []; + var macs = uciOriginal("dhcp", group, "mac"); + var ldMacIndex = 0; + var ldIpIndex = 1; + for (ldIndex=0; ldIndex < leaseData.length; ldIndex++) + { + var mac = leaseData[ldIndex][ldMacIndex]; + if (typeof mac === 'string' && macs.indexOf(mac.toUpperCase()) > -1) + { + var ip = leaseData[ldIndex][ldIpIndex]; + if (ip != null && ip.length > 0){ + groupIPs.push(ip); + } + } + } + return groupIPs; +} + + +function resetGroupOptions(selectId, controlDocument) +{ + controlDocument = controlDocument == null ? document : controlDocument; + selectElement = controlDocument.getElementById(selectId); + if (selectElement != null) + { + var groups = uciOriginal.getAllSectionsOfType("dhcp", "mac"); + if (groups.length == 0) + { + selectElement.disabled = true; + } + else + { + var mgVals = [ "" ]; + var mgText = [ UI.SelGrp ]; + + for(gIndex = 0; gIndex < groups.length; gIndex++) + { + var group = groups[gIndex]; + mgVals.push(group); + mgText.push(group); + } + setAllowableSelections(selectId, mgVals, mgText, controlDocument) + selectElement.disabled = false; + } + } +} + function reregisterTableSort() { TSort_Classes = new Array ('odd', 'even'); // table sorting zebra row support diff --git a/package/gargoyle/files/www/js/dhcp.js b/package/gargoyle/files/www/js/dhcp.js index 2ea9f6bf89..4a57514694 100644 --- a/package/gargoyle/files/www/js/dhcp.js +++ b/package/gargoyle/files/www/js/dhcp.js @@ -11,7 +11,7 @@ var TSort_Data = new Array ('static_ip_table', 's', 's', 'p', '', ''); function saveChanges() { - errorList = proofreadAll(); + errorList = proofreadDhcpForm(); if(errorList.length > 0) { errorString = errorList.join("\n") + "\n\n"+UI.ErrChanges; @@ -20,8 +20,9 @@ function saveChanges() else { setControlsEnabled(false, true); - uci = uciOriginal.clone(); + + // save dhcp changes uci.remove('dhcp', dhcpSection, 'ignore'); uci.set('dhcp', dhcpSection, 'interface', 'lan'); dhcpIds = ['dhcp_start', ['dhcp_start','dhcp_end'], 'dhcp_lease']; @@ -48,31 +49,100 @@ function saveChanges() dhcpWillBeEnabled = false; } + // save Device changes to uci + uci.removeAllSectionsOfType("dhcp", "host"); + var etherMap = map(etherData, 0); + var hostMap = map(hostData, 0); + var macMap = new Object(); + deviceTable = document.getElementById('device_table_container').firstChild; + tableData = getTableDataArray(deviceTable, true, false); + for (rowIndex = 0; rowIndex < tableData.length; rowIndex++) + { + rowData = tableData[rowIndex]; + var host = rowData[0]; + var macs = rowData[1]; + var ip = rowData[2]; + if (uci.get("dhcp", host).length == 0){ + uci.set("dhcp", host, null, "host"); + uci.set("dhcp", host, "name", host); + } + uci.set("dhcp", host, "mac", macs); + uci.set("dhcp", host, "ip", ip); + macMap[host]=macs; - staticIpTable = document.getElementById('staticip_table_container').firstChild; - tableData = getTableDataArray(staticIpTable, true, false); + // remove devices moved to uci from /etc/ethers & /etc/hosts + var macList = macs.split("\t"); + for (mIndex = 0; mIndex < macList.length; mIndex++) + { + uciMac = macList[mIndex]; + if (etherMap.hasOwnProperty(uciMac)) + { + etherIP = etherMap[uciMac][1]; + delete etherMap[uciMac]; + if (hostMap.hasOwnProperty(etherIP)) + { + delete hostMap[etherIP]; + } + } + } + } + // recreate /etc/ethers without redundant entries createEtherCommands = [ "touch /etc/ethers", "rm /etc/ethers" ]; - staticIpTableData = new Array(); - for (rowIndex in tableData) + for (mac in etherMap) { - rowData = tableData[rowIndex]; - createEtherCommands.push("echo \"" + rowData[1].toLowerCase() + "\t" + rowData[2] + "\" >> /etc/ethers"); - staticIpTableData.push( [ rowData[0], rowData[1], rowData[2] ] ); - if(rowData[0] != '-') + if (etherMap.hasOwnProperty(mac)) { - ipHostHash[ rowData[2] ] = rowData[0]; + createEtherCommands.push("echo \"" + mac.toLowerCase() + "\t" + etherMap[mac][1] + "\" >> /etc/ethers"); } } - + // recreate /etc/hosts without redundant entries createHostCommands = [ "touch /etc/hosts", "rm /etc/hosts" ]; - for (ip in ipHostHash) + for (ip in hostMap) { - host= ipHostHash[ip]; - createHostCommands.push("echo \"" + ip + "\t" + host + "\" >> /etc/hosts"); + if (hostMap.hasOwnProperty(ip)) + { + createHostCommands.push("echo \"" + ip + "\t" + hostMap[ip][1] + "\" >> /etc/hosts"); + } } + // save Group changes + uci.removeAllSectionsOfType("dhcp", "mac"); + var ipsetCommands = ["ipset destroy"]; // fails on ipsets with existing references + + groupTable = document.getElementById('group_table_container').firstChild; + tableData = getTableDataArray(groupTable, true, false); + for (rowIndex = 0; rowIndex < tableData.length; rowIndex++) + { + rowData = tableData[rowIndex]; + var group = rowData[0]; + var devices = rowData[1].split(" "); + + if (uci.get("dhcp", group).length == 0){ + uci.set("dhcp", group, null, "mac"); + uci.set("dhcp", group, "networkid", group); + } + + for(dIndex=0; dIndex < devices.length; dIndex++) + { + var host = devices[dIndex]; + if(macMap.hasOwnProperty(host)) + { + var macs = macMap[host].split(" "); + for (mIndex=0; mIndex < macs.length; mIndex++) + { + uci.set("dhcp", group, "mac", macs[mIndex]); + } + uci.set("dhcp", host, "group", group); + } + } + + // create ipset + ipsetCommands.push("ipset create " + group + " iphash"); + } + + // save blockMismatches changes var firewallCommands = []; var firewallDefaultSections = uci.getAllSectionsOfType("firewall", "defaults"); var oldBlockMismatches = uciOriginal.get("firewall", firewallDefaultSections[0], "enforce_dhcp_assignments") == "1" ? true : false; @@ -92,14 +162,14 @@ function saveChanges() firewallCommands.push("uci commit"); } + commands = uci.getScriptCommands(uciOriginal) + "\n"; + commands += ipsetCommands.join("\n") + "\n"; + commands += createEtherCommands.join("\n") + "\n"; + commands += createHostCommands.join("\n") + "\n"; + commands += firewallCommands.join("\n") + "\n"; //need to restart firewall here because for add/remove of static ips, we need to restart bandwidth monitor, as well as for firewall commands above if we have any - var restartDhcpCommand = "\n/etc/init.d/dnsmasq restart ; \nsh /usr/lib/gargoyle/restart_firewall.sh\n" ; - - commands = uci.getScriptCommands(uciOriginal) + "\n" + createEtherCommands.join("\n") + "\n" + createHostCommands.join("\n") + "\n" + firewallCommands.join("\n") + "\n" + restartDhcpCommand ; - - //document.getElementById("output").value = commands; - + commands += "\n/etc/init.d/dnsmasq restart ; \nsh /usr/lib/gargoyle/restart_firewall.sh\n" ; var param = getParameterDefinition("commands", commands) + "&" + getParameterDefinition("hash", document.cookie.replace(/^.*hash=/,"").replace(/[\t ;]+.*$/, "")); @@ -119,137 +189,259 @@ function saveChanges() } } -function createEditButton() + +function createEditButton(onclick) { var editButton = createInput("button"); editButton.value = UI.Edit; editButton.className="default_button"; - editButton.onclick = editStatic; + editButton.onclick = onclick; return editButton; } + function resetData() { - dhcpEnabled = uciOriginal.get("dhcp", "lan", "ignore") == "1" ? false : true; - var rowIndex=0; - for(rowIndex=0; rowIndex < staticIpTableData.length ; rowIndex++) + resetDhcp(); + + var firewallDefaultSections = uciOriginal.getAllSectionsOfType("firewall", "defaults"); + var blockMismatches = uciOriginal.get("firewall", firewallDefaultSections[0], "block_static_ip_mismatches") == "1" ? true : false; + document.getElementById("block_mismatches").checked = blockMismatches; + + resetDeviceTable(); + resetGroupTable(); + + resetMacList(); + resetGroupList(); + resetDeviceList(); + + setEnabled(document.getElementById('dhcp_enabled').checked); + + var host = document.getElementById("add_host").value = ""; + var macs = document.getElementById("add_mac").value = ""; + var group = document.getElementById("add_group").value = ""; + var devices = document.getElementById("add_device").value = ""; +} + +function resetDhcp() +{ + dhcpEnabled = uciOriginal.get("dhcp", "lan", "ignore") == "1" ? false : true; + dhcpIds = ['dhcp_start', 'dhcp_end', 'dhcp_lease']; + dhcpPkgs = ['dhcp',['dhcp','dhcp'],'dhcp']; + dhcpSections = [dhcpSection,[dhcpSection,dhcpSection],dhcpSection]; + dhcpOptions = ['start', ['start','limit'], 'leasetime']; + + enabledTest = function(value){return value != 1;}; + endCombineFunc= function(values) { return (parseInt(values[0])+parseInt(values[1])-1); }; + leaseModFunc = function(value) + { + var leaseHourValue; + if(value.match(/.*h/)) + { + leaseHourValue=value.substr(0,value.length-1); + } + else if(value.match(/.*m/)) + { + leaseHourValue=value.substr(0,value.length-1)/(60); + } + else if(value.match(/.*s/)) + { + leaseHourValue=value.substr(0,value.length-1)/(60*60); + } + return leaseHourValue; + }; + + dhcpParams = [100, [endCombineFunc,150],[12,leaseModFunc]]; + dhcpFunctions = [loadValueFromVariable, loadValueFromMultipleVariables, loadValueFromModifiedVariable]; + + loadVariables(uciOriginal, dhcpIds, dhcpPkgs, dhcpSections, dhcpOptions, dhcpParams, dhcpFunctions); + + document.getElementById("dhcp_enabled").checked = dhcpEnabled; +} + +function resetDeviceTable() +{ + var deviceTableData = new Array(); + + // process uci dhcp hosts + var hosts = uciOriginal.getAllSectionsOfType("dhcp", "host"); + var uciMacs = []; + for (hIndex=0; hIndex < hosts.length; hIndex++) + { // process the MAC's assigned to each device + var host = hosts[hIndex]; + var macs = uciOriginal.get("dhcp", host, "mac"); + var ip = uciOriginal.get("dhcp", host, "ip"); + ip = ip == null ? "" : ip; + deviceTableData.push([host, macs, ip, createEditButton(editDevice)]); + uciMacs = uciMacs.concat(macs.split(" ")); + } + + // process /etc/hosts & /etc/ethers + var etherMap = map(etherData, 0); + var hostMap = map(hostData, 0); + for (mac in etherMap) { - var rowData = staticIpTableData[rowIndex]; - rowData.push(createEditButton()); - staticIpTableData[rowIndex] = rowData; + ucMAC = mac.toUpperCase(); + if (etherMap.hasOwnProperty(mac) && uciMacs.indexOf(ucMAC) == -1) + { + ip = etherMap[mac][1]; + host = (hostMap.hasOwnProperty(ip)) ? hostMap[ip][1] : ""; + deviceTableData.push([host, ucMAC, ip, createEditButton(editDevice)]); + } } - columnNames=[UI.HsNm, 'MAC', 'IP', '']; - staticIpTable=createTable(columnNames, staticIpTableData, "static_ip_table", true, false, removeStaticIp ); - tableContainer = document.getElementById('staticip_table_container'); + + // create the device Table and place it into the document + var columnNames=[dhcpS.DevNm, "MACs", dhcpS.StcIP, '']; + var deviceTable=createTable(columnNames, deviceTableData, "device_table", true, false, removeDevice ); + var tableContainer = document.getElementById('device_table_container'); if(tableContainer.firstChild != null) { tableContainer.removeChild(tableContainer.firstChild); } - tableContainer.appendChild(staticIpTable); - + tableContainer.appendChild(deviceTable); +} - dhcpIds = ['dhcp_start', 'dhcp_end', 'dhcp_lease']; - dhcpPkgs = ['dhcp',['dhcp','dhcp'],'dhcp']; - dhcpSections = [dhcpSection,[dhcpSection,dhcpSection],dhcpSection]; - dhcpOptions = ['start', ['start','limit'], 'leasetime']; - enabledTest = function(value){return value != 1;}; - endCombineFunc= function(values) { return (parseInt(values[0])+parseInt(values[1])-1); }; - leaseModFunc = function(value) - { - var leaseHourValue; - if(value.match(/.*h/)) - { - leaseHourValue=value.substr(0,value.length-1); - } - else if(value.match(/.*m/)) - { - leaseHourValue=value.substr(0,value.length-1)/(60); - } - else if(value.match(/.*s/)) +function resetGroupTable() +{ + var groupTableData = new Array(); + + var groups = new Object(); + var hosts = uciOriginal.getAllSectionsOfType("dhcp", "host"); + for (hIndex=0; hIndex < hosts.length; hIndex++) + { // survey all of the devices and groups + var host = hosts[hIndex]; + var group = uciOriginal.get("dhcp", host, "group"); + if (group != null && group.length > 0) { - leaseHourValue=value.substr(0,value.length-1)/(60*60); + if (groups.hasOwnProperty(group)) + { + groups[group].push(host); + } + else + { + groups[group] = [host]; + } } - return leaseHourValue; - }; - - dhcpParams = [100, [endCombineFunc,150],[12,leaseModFunc]]; - dhcpFunctions = [loadValueFromVariable, loadValueFromMultipleVariables, loadValueFromModifiedVariable]; - - loadVariables(uciOriginal, dhcpIds, dhcpPkgs, dhcpSections, dhcpOptions, dhcpParams, dhcpFunctions); - - - document.getElementById("dhcp_enabled").checked = dhcpEnabled; - setEnabled(document.getElementById('dhcp_enabled').checked); + } var firewallDefaultSections = uciOriginal.getAllSectionsOfType("firewall", "defaults"); var blockMismatches = uciOriginal.get("firewall", firewallDefaultSections[0], "enforce_dhcp_assignments") == "1" ? true : false; document.getElementById("block_mismatches").checked = blockMismatches; + for (var group in groups) + { // place each group in an array + if (groups.hasOwnProperty(group)) + { // with a list of member devices + groupTableData.push([group, groups[group].join(" "), createEditButton(editGroup)]); + } + } - //setup hostname/mac list - resetHostnameMacList(); + // create the group Table and place it into the document + var columnNames=[dhcpS.GpNm, dhcpS.DevNms, '']; + var groupTable=createTable(columnNames, groupTableData, "group_table", true, false, removeGroup ); + var tableContainer = document.getElementById('group_table_container'); + if(tableContainer.firstChild != null) + { + tableContainer.removeChild(tableContainer.firstChild); + } + tableContainer.appendChild(groupTable); } -function removeStaticIp(table, row) -{ - var removedIp = row.childNodes[2].firstChild.data - delete ipHostHash[removedIp] - resetHostnameMacList() -} -function resetHostnameMacList() +/** +* Populates a Select id=mac_list with know MACs which have not yet been assigned +* to a Known Device. +*/ +function resetMacList() { - var staticTable = document.getElementById("staticip_table_container").firstChild; - var staticTableData = staticTable == null ? [] : getTableDataArray(staticTable, true, false); - var staticMacs = []; - var staticIndex=0; - for(staticIndex=0; staticIndex < staticTableData.length; staticIndex++) - { - var mac = (staticTableData[staticIndex][1]).toUpperCase(); - staticMacs[ mac ] = 1; + var kmMacIndex = 0; + var kmHostIndex = 2; + var knownMac = knownMacLookup(); + + var deviceTable = document.getElementById("device_table_container").firstChild; + var dtdMacIx = 1; + var deviceTableData = (deviceTable == null) ? [] : getTableDataArray(deviceTable, true, false); + var deviceMacs = ''; + for (dtdIndex=0; dtdIndex < deviceTableData.length; dtdIndex++){ + deviceMacs = deviceMacs.concat(deviceTableData[dtdIndex][dtdMacIx], ","); } - var hmVals = [ "none" ]; - var hmText = [ dhcpS.SelH ]; - var leaseIndex = 0; - for(leaseIndex=0; leaseIndex < leaseData.length; leaseIndex++) + var hlVals = [ "" ]; + var hlText = [ dhcpS.SelM ]; + for (var mac in knownMac) { - var lease = leaseData[leaseIndex]; - var mac = (lease[0]).toUpperCase(); - if( staticMacs[ mac ] == null ) + if (knownMac.hasOwnProperty(mac)) { - hmVals.push( lease[2] + "," + mac ); - hmText.push( (lease[2] == "" || lease[2] == "*" ? lease[1] : lease[2] ) + " (" + mac + ")" ); + if( deviceMacs.indexOf(mac) == -1 ) + { // exclude MAC's that are already assigned to a device + var host = knownMac[mac][kmHostIndex]; + hlVals.push( host + "," + mac ); + hlText.push((host == "" || host == "*" ? mac : host ) + " " + mac); + } } } - setAllowableSelections("static_from_connected", hmVals, hmText); - - var hmEnabled = hmText.length > 1 && document.getElementById('dhcp_enabled').checked ? true : false; - setElementEnabled(document.getElementById("static_from_connected"), hmEnabled, "none"); + setAllowableSelections("mac_list", hlVals, hlText, document); } +/** +* Populates a Select id=group_list with know MACs which have not yet been assigned +* to a Known Device. +*/ +function resetGroupList() +{ + var groups = []; + var hosts = uciOriginal.getAllSectionsOfType("dhcp", "host"); + for (hIndex=0; hIndex < hosts.length; hIndex++) + { // survey all of the devices and groups + var host = hosts[hIndex]; + var group = uciOriginal.get("dhcp", host, "group"); + if (group.length > 0 && groups.indexOf(group) == -1) + { + groups.push(group); + } + } + var gpVals = [ "" ]; + var gpText = [ dhcpS.SelG ]; + for (gIx = 0; gIx < groups.length; gIx++) + { + var group = groups[gIx]; + gpVals.push( group ); + gpText.push( group); + } + setAllowableSelections("group_list", gpVals, gpText, document); +} -function staticFromConnected() +/** +* Populates a Select id=device_list with know MACs which have not yet been assigned +* to a Known Device. +*/ +function resetDeviceList() { - var selectedVal = getSelectedValue("static_from_connected"); - if(selectedVal != "none") - { - var host = (selectedVal.split(/,/))[0]; - var mac = (selectedVal.split(/,/))[1]; - document.getElementById("add_host").value = host; - document.getElementById("add_mac").value = mac; - setSelectedValue("static_from_connected", "none"); + var gpVals = [ "" ]; + var gpText = [ dhcpS.SelD ]; + + var hosts = uciOriginal.getAllSectionsOfType("dhcp", "host"); + for (hIndex=0; hIndex < hosts.length; hIndex++) + { // survey all of the devices and groups + var host = hosts[hIndex]; + var group = uciOriginal.get("dhcp", host, "group"); + if (group == null || group.length == 0) + { + gpVals.push( host ); + gpText.push( host ); + } } + setAllowableSelections("device_list", gpVals, gpText, document); } function setEnabled(enabled) { - var ids=['dhcp_start', 'dhcp_end', 'dhcp_lease', 'block_mismatches', 'add_host', 'add_mac', 'add_ip', 'add_button']; + var ids=['dhcp_start', 'dhcp_end', 'dhcp_lease', 'block_mismatches', 'add_host', 'add_mac', 'add_ip', 'add_device_button', 'mac_list', 'add_group', 'add_device', 'add_device_to_group_button', 'device_list']; var idIndex; for (idIndex in ids) { @@ -257,96 +449,304 @@ function setEnabled(enabled) setElementEnabled(element, enabled, ""); } - var staticIpTable = document.getElementById('staticip_table_container').firstChild; - setRowClasses(staticIpTable, enabled); + var deviceTable = document.getElementById('device_table_container').firstChild; + setRowClasses(deviceTable, enabled); + resetDeviceList(); + + var groupTable = document.getElementById('group_table_container').firstChild; + setRowClasses(groupTable, enabled); + resetGroupList(); +} + + + +/******************************************************************************* +* Event functions +*******************************************************************************/ + + +function macSelected() +{ + var selectedVal = getSelectedValue("mac_list"); + var host = document.getElementById("add_host"); + var macs = document.getElementById("add_mac"); + if(selectedVal == "") + { + host.value = ""; + macs.value = ""; + } + else + { + if (host.value == "") + { + var selectedHost = (selectedVal.split(/,/))[0]; + host.value = selectedHost.replace(/-| /g,"_"); + } + var selMac = (selectedVal.split(/,/))[1]; + macs.value = (macs.value == "") ? selMac : macs.value.concat(" ", selMac); + setSelectedValue("mac_list", ""); + } +} + - resetHostnameMacList(); +function deviceSelected() +{ + var selectedVal = getSelectedValue("device_list"); + var devices = document.getElementById("add_device"); + devices.value = selectedValue + " " + devices.value; +} + +function addDevice() +{ + errors = proofReadDeviceForm(); + if(errors.length > 0) + { + alert(errors.join("\n") + "\n\n" + dhcpS.AErr); + } + else + { + var host = document.getElementById("add_host"); + var macs = document.getElementById("add_mac"); + var ip = document.getElementById("add_ip"); + if (host.value != null && macs.value != null) + { + var deviceTable = document.getElementById('device_table_container').firstChild; + var values = [host.value, macs.value, ip.value, createEditButton("editDevice")]; + addTableRow(deviceTable, values, true, false, resetMacList); + addNewOption('device_list', host.value, host.value); + host.value = ""; + macs.value = ""; + ip.value = ""; + } + } } -function addStatic() + + +function addDeviceToGroup() { - errors = proofreadStatic(document); + errors = proofReadGroupForm(); if(errors.length > 0) { alert(errors.join("\n") + "\n\n" + dhcpS.AErr); } else { - values = new Array(); - ids = ['add_host', 'add_mac', 'add_ip']; - for (idIndex in ids) + var group = document.getElementById("add_group"); + var devices = document.getElementById("add_device"); + if (group.value != null && devices.value != null) { - v = document.getElementById(ids[idIndex]).value; - v = v== '' ? '-' : v; - values.push(v); - document.getElementById(ids[idIndex]).value = ""; + var groupTable = document.getElementById('group_table_container').firstChild; + var values = [group.value, devices.value, createEditButton("editGroup")]; + addTableRow(groupTable,values, true, false, resetDeviceList); + group.value=""; + devices.value=""; } - values.push(createEditButton()); - staticIpTable = document.getElementById('staticip_table_container').firstChild; - addTableRow(staticIpTable,values, true, false, resetHostnameMacList); - resetHostnameMacList(); } } -function proofreadStatic(controlDocument, tableDocument, excludeRow) + +function editDevice() { - controlDocument = controlDocument == null ? document : controlDocument; - tableDocument = tableDocument == null ? document : tableDocument; + location.hash="#device_form"; + editRow=this.parentNode.parentNode; + editRow.parentNode.removeChild(editRow); + document.getElementById('add_host').value = editRow.childNodes[0].firstChild.data; + document.getElementById('add_mac').value = editRow.childNodes[1].firstChild.data; + document.getElementById('add_ip').value = editRow.childNodes[2].firstChild.data; +} - addIds=['add_mac', 'add_ip']; - labelIds= ['add_mac_label', 'add_ip_label']; - functions = [validateMac, validateIP]; - returnCodes = [0,0]; - visibilityIds=addIds; - errors = proofreadFields(addIds, labelIds, functions, returnCodes, visibilityIds, controlDocument); - if(errors.length == 0) + +function editGroup() +{ + location.hash="#group_form"; + editRow=this.parentNode.parentNode; + editRow.parentNode.removeChild(editRow); + document.getElementById('add_group').value = editRow.childNodes[0].firstChild.data; + document.getElementById('add_device').value = editRow.childNodes[1].firstChild.data; +} + + + +function removeDevice(table, row) +{ + resetMacList(); +} + + +function removeGroup(table, row) +{ + resetGroupList(); +} + + +/******************************************************************************* +* Utility functions +*******************************************************************************/ + +/** +* Returns an Object able to be used as a lookup table indexed by the MAC and +* containing an [MAC, IP, hostname] for hosts listed in /etc/ethers and +* /tmp/dhcp.leases +*/ +function knownMacLookup() +{ // gather known MACs from /etc/ethers + var knownMac = mergeEtherHost(); + var kmMacIndex = 0; + var macMap = map(knownMac, kmMacIndex); + + var ldMacIndex = 0; + var ldIpIndex = 1; + var ldHostIndex = 2; + for(ldIndex=0; ldIndex < leaseData.length; ldIndex++) + { // gather known MACs from dhcp leases + var leaseRow = leaseData[ldIndex]; + var mac = leaseRow[ldMacIndex].toUpperCase(); + if(macMap[mac] == null) + { + macMap[mac] = [mac, leaseRow[ldIpIndex], leaseRow[ldHostIndex]]; + } + } + return macMap; +} + + +/** +* Returns an Array of Arrays containing [mac, ip, host] generated by +* combining /etc/hosts with /etc/ethers +*/ +function mergeEtherHost() +{ + var hdIpIndex = 0; + var hdHostIndex = 1; + var hostLookup = map(hostData, hdIpIndex); + + var mhdMacIndex = 0; + var mhdIpIndex = 1; + var mhdHostIndex = 2; + var macHostData = etherData.slice(); + for(var mhdIndex=0; mhdIndex < macHostData.length; mhdIndex++) + { + var host = null; + var mhdRow = macHostData[mhdIndex]; + mhdRow[mhdMacIndex] = mhdRow[mhdMacIndex].toUpperCase(); + var ip = mhdRow[mhdIpIndex]; + var hdRow = hostLookup[ ip ]; + if (hdRow instanceof Array) + { + host = hdRow[hdHostIndex] ; + } + mhdRow[mhdHostIndex] = (host == null) ? "?" : host ; + } + return macHostData; +} + + +/** +* Returns an Object able to be used as a Lookup table on the elements of the +* supplied data[][], indexed by the data[][field]. +* data: an Array of Arrays +* field: the index of a field in the inner array to be used as the lookup index. +*/ +function map(data, field) +{ + var map = new Object(); + if (data instanceof Array) { - var staticIpTable = tableDocument.getElementById('staticip_table_container').firstChild; - var currentData = getTableDataArray(staticIpTable, true, false); - var rowDataIndex = 0; - for (rowDataIndex=0; rowDataIndex < currentData.length ; rowDataIndex++) + for (index=0; index 0) + { + addIds.push('add_ip'); + labelIds.push('add_ip_label'); + functions.push(validateIP); + returnCodes.push(0); + } + visibilityIds=addIds; + errors = proofreadFields(addIds, labelIds, functions, returnCodes, visibilityIds, document); + + if(errors.length == 0) + { // check that the host and mac are not duplicates of existing values + var newHost = document.getElementById('add_host').value; + var newMac = document.getElementById('add_mac').value; + var deviceTable = document.getElementById('device_table_container').firstChild; + var currentData = getTableDataArray(deviceTable, true, false); + for (cdIndex=0; cdIndex < currentData.length ; cdIndex++) { - errors.push(dhcpS.ipErr); + var rowData = currentData[cdIndex]; + var oldHost = rowData[0]; + var oldMac = rowData[1]; + if(oldHost != '' && oldHost != '-' && oldHost == newHost) + { + errors.push(dhcpS.dHErr); + } + if(oldMac == newMac) + { + errors.push(dhcpS.dMErr); + } } } return errors; } -function proofreadAll() + +/** +* Checks for invalid host or group names +*/ +function proofReadGroupForm() +{ + addIds=['add_group', 'add_device']; + labelIds= ['add_group_label', 'add_known_device_label']; + functions = [validateGroup, validateMultipleUCIs]; + returnCodes = [0,0]; + visibilityIds=addIds; + return proofreadFields(addIds, labelIds, functions, returnCodes, visibilityIds, document); +} + +function proofreadDhcpForm() { dhcpIds = ['dhcp_start', 'dhcp_end', 'dhcp_lease']; labelIds= ['dhcp_start_label', 'dhcp_end_label', 'dhcp_lease_label']; diff --git a/package/gargoyle/files/www/js/login.js b/package/gargoyle/files/www/js/login.js index 8e749566c9..92024662f9 100644 --- a/package/gargoyle/files/www/js/login.js +++ b/package/gargoyle/files/www/js/login.js @@ -1,8 +1,8 @@ /* - * This program is copyright © 2008,2009-2013 Eric Bishop and is distributed under the terms of the GNU GPL - * version 2.0 with a special clarification/exception that permits adapting the program to + * This program is copyright © 2008,2009-2013 Eric Bishop and is distributed under the terms of the GNU GPL + * version 2.0 with a special clarification/exception that permits adapting the program to * configure proprietary "back end" software provided that all modifications to the web interface - * itself remain covered by the GPL. + * itself remain covered by the GPL. * See http://gargoyle-router.com/faq.html#qfoss for more information */ var logS=new Object(); //part of i18n @@ -17,7 +17,7 @@ function doLogin() else { setControlsEnabled(false, true, logS.Lging); - + sessionExpired=false; passInvalid=false; loggedOut=false; @@ -41,13 +41,13 @@ function doLogin() var cookie = cookieLines[cIndex].replace(/^.*ookie:/, "").replace(/\";.*$/, ""); if(cookie.match(/=/)) { - document.cookie=cookie; + document.cookie=cookie; } } window.location.href = window.location.href; } setControlsEnabled(true); - + } } runAjax("POST", "/utility/get_password_cookie.sh", param, stateChangeFunction); @@ -175,7 +175,7 @@ function createQuotaDiv(quotaId, fieldsetIp, quotaNumber, normalFontParams, used var pct = quotaPercents[quotaId][ip]; var lim = quotaLimits[quotaId][ip]; - + var parentDiv = document.createElement("div"); if(quotaNumber > 0) { @@ -191,7 +191,7 @@ function createQuotaDiv(quotaId, fieldsetIp, quotaNumber, normalFontParams, used var timeSpan = document.createElement("span"); var timeActiveSpan = document.createElement("span"); var timeParamSpan = document.createElement("span"); - + timeActiveSpan.appendChild(document.createTextNode("Active " + (quotaTimes[quotaId][3] == "only" ? "Only:" : "All Times Except:"))); timeActiveSpan.style.fontSize = normalFontParams[2]; timeActiveSpan.style.marginLeft="25px"; @@ -199,7 +199,7 @@ function createQuotaDiv(quotaId, fieldsetIp, quotaNumber, normalFontParams, used timeActiveSpan.style.width="150px"; timeActiveSpan.style.cssFloat="left"; timeActiveSpan.style.styleFloat="left"; - + timeParamSpan.appendChild(document.createTextNode(timeLines.shift())); timeParamSpan.style.fontSize = normalFontParams[2]; timeParamSpan.style.marginLeft = "25px"; @@ -242,11 +242,11 @@ function createQuotaDiv(quotaId, fieldsetIp, quotaNumber, normalFontParams, used var par = document.createElement("p"); if(quotaNumber > 0) { - par.appendChild( document.createTextNode(perc + "% "+logS.of+" " + name + " "+logS.fQuo + quotaNumber + " "+logS.husd+" (" + used + "/" + limit + ")")); + par.appendChild( document.createTextNode(perc + "% "+logS.of+" " + name + " "+logS.fQuo + quotaNumber + " "+logS.husd+" (" + used + " / " + limit + ")")); } else { - par.appendChild( document.createTextNode(perc + "% "+logS.of+" " + name + " "+logS.qusd+" (" + used + "/" + limit + ")")); + par.appendChild( document.createTextNode(perc + "% "+logS.of+" " + name + " "+logS.qusd+" (" + used + " / " + limit + ")")); } var fontParams = (pct[typeIndex] == 100) ? usedFontParams : normalFontParams; par.style.color = fontParams[0]; @@ -271,8 +271,8 @@ function timeParamsToLines(timeParameters) var days = timeParameters[1]; var weekly = timeParameters[2]; var active = timeParameters[3]; - - + + var textList = []; if(active == "always") { @@ -292,5 +292,3 @@ function timeParamsToLines(timeParameters) } return textList; } - - diff --git a/package/gargoyle/files/www/js/qos.js b/package/gargoyle/files/www/js/qos.js index 81e0926b82..3e167050ce 100644 --- a/package/gargoyle/files/www/js/qos.js +++ b/package/gargoyle/files/www/js/qos.js @@ -5,7 +5,7 @@ * itself remain covered by the GPL. * See http://gargoyle-router.com/faq.html#qfoss for more information */ - + var qosStr=new Object; // part of i18n function saveChanges() @@ -152,7 +152,7 @@ function saveChanges() uci.set("qos_gargoyle", classId, "max_bandwidth", maxBandwidth); } - if (classData[classIndex][4] == qosStr.YES) + if (classData[classIndex][4] == qosStr.YES) { uci.set("qos_gargoyle",classId,"minRTT","Yes"); } @@ -171,7 +171,7 @@ function saveChanges() { classId = classIds[getSelectedText("default_class")]; } - else + else { classId = classIds[ ruleData[ruleIndex][1] ]; } @@ -283,7 +283,7 @@ function init_classtable() totalPercent = totalPercent + parseInt(uciOriginal.get("qos_gargoyle", classSections[classIndex], "percent_bandwidth")); } - //This array setup such that classIndex+1 = the classno (ie dclass_x, where x=classno) + //This array setup such that classIndex+1 = the classno (ie dclass_x, where x=classno) for (classIndex=0; classIndex < classSections.length; classIndex++) { var classSection = classSections[classIndex]; @@ -338,7 +338,7 @@ function bpsToKbpsString(bps) else if (bpsn < 1) { kbps = bpsn.toFixed(1) + ''; - } + } else { kbps = bpsn.toFixed(0) + ''; @@ -365,30 +365,29 @@ function update_classtable() dynamic_update = false; } } - - var rowIndex; - for (rowIndex=1; rowIndex <= classTable.length; rowIndex++) { - var row = table.rows[rowIndex]; - - //Search for the matching class - for (classIndex=0; classIndex < classTable.length; classIndex++) - { - var rowData = classTable[classIndex]; - var newDataList = [ rowData.Name, rowData.Percent + "%", rowData.MinBW, rowData.MaxBW, rowData.MinRTT, bpsToKbpsString(rowData.bps) ]; - - //If found then update the data - if (rowData.Name == row.childNodes[0].firstChild.data) { - var cellIndex=0; - for (cellIndex=0; cellIndex < newDataList.length; cellIndex++) - { - row.childNodes[cellIndex].firstChild.data = newDataList[ cellIndex ]; - } - break; - } - } + var rowIndex; + for (rowIndex=1; rowIndex <= classTable.length; rowIndex++) + { + var row = table.rows[rowIndex]; - } + //Search for the matching class + for (classIndex=0; classIndex < classTable.length; classIndex++) + { + var rowData = classTable[classIndex]; + var newDataList = [ rowData.Name, rowData.Percent + "%", rowData.MinBW, rowData.MaxBW, rowData.MinRTT, bpsToKbpsString(rowData.bps) ]; + //If found then update the data + if (rowData.Name == row.childNodes[0].firstChild.data) + { + var cellIndex=0; + for (cellIndex=0; cellIndex < newDataList.length; cellIndex++) + { + row.childNodes[cellIndex].firstChild.data = newDataList[ cellIndex ]; + } + break; + } + } + } } @@ -407,7 +406,7 @@ function resetData() initializeDescriptionVisibility(uciOriginal, "qos_down_3"); initializeDescriptionVisibility(uciOriginal, "qos_down_4"); } - + uciOriginal.removeSection("gargoyle", "help"); //necessary, or we over-write the help settings when we save @@ -470,7 +469,7 @@ function resetData() } classification = uciOriginal.get("qos_gargoyle", ruleSection, "class"); - idx = parseInt(classification.match(/class_([0-9]+)/)[1])-1; + idx = parseInt(classification.match(/class_([0-9]+)/)[1])-1; ruleTableData.push( [ruleText, classTable[idx].Name, createRuleTableEditButton()] ); } @@ -541,14 +540,21 @@ function resetData() { //Run updateqosmon once to clear away any old data. setTimeout("updateqosmon()", 100); - } + } } //The default screen updater. if (timerid == null) { timerid=setInterval("updatetc()", 2500); } + resetGroupOptions("group"); + document.getElementById((direction == "download") ? "dest_ip" : "source_ip" ).value=""; } +function groupSelected(id) +{ + document.getElementById(id).value = getSelectedValue("group"); + setSelectedValue("group", ""); +} function qosQuotasExist() { @@ -654,7 +660,8 @@ function proofreadClassificationRule(controlDocument) validatePktSize = function(text){ return validateNumericRange(text, 1, 1500); }; validateCBSize = function(text){ return validateNumericRange(text, 0, 4194393); }; alwaysValid = function(text){return 0;}; - ruleValidationFunctions = [ validateIpRange, validatePortOrPortRange, validateIpRange, validatePortOrPortRange, validatePktSize, validatePktSize, alwaysValid, validateCBSize, alwaysValid ]; + var validateDest = (controlDocument.getElementById("group") != null) ? validateIpRangeOrGroup : validateIpRange; + ruleValidationFunctions = [ validateSource, validatePortOrPortRange, validateDest, validatePortOrPortRange, validatePktSize, validatePktSize, alwaysValid, validateCBSize, alwaysValid ]; labelIds = new Array(); returnCodes = new Array(); toggledMatchCriteria = 0; @@ -687,6 +694,12 @@ function resetRuleControls() checkbox.checked =false; enableAssociatedField( checkbox, ruleControlIds[ruleControlIndex], ""); } + + var element = document.getElementById("group"); + if (element != null) + { + setElementEnabled(element, false, ""); + } } function addServiceClass() @@ -933,10 +946,6 @@ function editRuleTableRow() } setSelectedText("classification", editRuleWindowRow.childNodes[1].firstChild.data, editRuleWindow.document); - - - - //exit & save functions closeButton.onclick = function() { @@ -988,6 +997,19 @@ function editRuleTableRow() editRuleWindow.focus(); update_done = true; } + + container = (direction == "download") ? "dest_group_container" : "source_group_container"; + groupSpan = editRuleWindow.document.getElementById(container); + if (groupSpan != null) + { + groupSelect = editRuleWindow.document.createElement('select'); + groupSelect.id = "group"; + groupSelected = (direction == "download") ? "groupSelected('dest_ip')" : "groupSelected('source_ip')"; + groupSelect.setAttribute("onchange", groupSelected); + groupSpan.appendChild(groupSelect); + } + + resetGroupOptions("group", editRuleWindow.document); } if(update_done == false) { @@ -1071,15 +1093,15 @@ function editClassTableRow() if (direction == "upload") {editClassWindow.document.getElementById('rttdiv').style.display = 'none';} - /* Watch the window in case the user just closes without using one of the buttons. + /* Watch the window in case the user just closes without using one of the buttons. in which case we must re-enable the class bandwidth updater */ - var timer = setInterval(function() { - if(editClassWindow.closed) { - clearInterval(timer); - updateInProgress = false; - dynamic_update=true; - } - }, 700); + var timer = setInterval(function() { + if(editClassWindow.closed) { + clearInterval(timer); + updateInProgress = false; + dynamic_update=true; + } + }, 700); editClassWindow.document.getElementById("bottom_button_container").appendChild(saveButton); @@ -1121,7 +1143,7 @@ function editClassTableRow() closeButton.onclick = function() { - clearInterval(timer); + clearInterval(timer); editClassWindow.close(); } @@ -1177,7 +1199,7 @@ function editClassTableRow() } } } - + var rowData = classTable[ editClassWindowRow.childNodes[6].firstChild.id ]; rowData.Name = editClassWindow.document.getElementById("class_name").value ; rowData.Percent = editClassWindow.document.getElementById("percent_bandwidth").value ; @@ -1192,7 +1214,7 @@ function editClassTableRow() editClassWindowRow.childNodes[4].firstChild.data = rowData.MinRTT; updateInProgress = false; - clearInterval(timer); + clearInterval(timer); editClassWindow.close(); } } @@ -1259,8 +1281,8 @@ function updatetc() } else { - /* - * NOTE: This NEEDS to be "currentWanName" variable NOT "currentWanIf" Variable!!! + /* + * NOTE: This NEEDS to be "currentWanName" variable NOT "currentWanIf" Variable!!! * If this doesn't work the problem is in gargoyle_header_footer utility, not here */ commands = commands + currentWanName; @@ -1282,7 +1304,7 @@ function updatetc() { for(i = 0; i < lines.length; i++) { - //Here the minor number of the qdisc ID is equal to class number+1 or index+2 + //Here the minor number of the qdisc ID is equal to class number+1 or index+2 var idx=parseInt(lines[i].match(/hfsc\s1:([0-9]+)/)[1])-2; var lastbytes; @@ -1327,7 +1349,7 @@ function updateqosmon() if(req.readyState == 4) { var lines = req.responseText.split("\n"); - + if (lines[0].substr(0,6) == "State:") { document.getElementById("qstate").innerHTML = lines[0]; @@ -1342,7 +1364,7 @@ function updateqosmon() for (i=0; i 0) + { + setSelectedValue("applies_to_type", "only", controlDocument); + controlDocument.getElementById("add_ip").value = ip; + } else { setSelectedValue("applies_to_type", "only", controlDocument); controlDocument.getElementById("add_ip").value = ip; - var valid = addAddressesToTable(controlDocument,"add_ip","quota_ip_table_container","quota_ip_table",false, 3, false,250); + var valid = addAddressesToTable(controlDocument,"add_ip","quota_ip_table_container","quota_ip_table",false, 3, false, false,250); if(!valid) { controlDocument.getElementById("add_ip").value = ""; @@ -352,7 +367,7 @@ function addNewQuota() var ip = getIpFromDocument(document); var timeParameters = getTimeParametersFromUci(uci, "quota_" + quotaNum); var limitStr = getLimitStrFromUci(pkg, "quota_" + quotaNum); - addTableRow(table, [ ipToTableSpan(ip), timeParamsToTableSpan(timeParameters), limitStr, enabledCheck, createEditButton(true)], true, false, removeQuotaCallback); + addTableRow(table, [ hostsToTableSpan(ip), timeParamsToTableSpan(timeParameters), limitStr, enabledCheck, createEditButton(true)], true, false, removeQuotaCallback); setDocumentFromUci(document, new UCIContainer(), ""); @@ -663,7 +678,7 @@ function validateQuota(controlDocument, originalQuotaId, originalQuotaIp) //add any ips in add_ip box, if it is visible and isn't empty if(errors.length == 0 && getSelectedValue("applies_to_type", controlDocument) == "only" && controlDocument.getElementById("add_ip").value != "") { - var valid = addAddressesToTable(controlDocument,"add_ip","quota_ip_table_container","quota_ip_table",false, 3, false,250); + var valid = addAddressesToTable(controlDocument,"add_ip","quota_ip_table_container","quota_ip_table",false, 3, true, false,250); if(!valid) { errors.push("\"" + controlDocument.getElementById("add_ip").value + "\" is not a valid IP or IP range"); @@ -1176,8 +1191,7 @@ function editQuota() closeButton.className = "default_button"; var editRow=this.parentNode.parentNode; - var editId = editRow.childNodes[rowCheckIndex].firstChild.id; - + var editId = editRow.childNodes[rowCheckIndex].firstChild.id; var editIp; var editSection = ""; @@ -1239,8 +1253,7 @@ function editQuota() changedIds[newId] = 1; } - - setElementAtColumn(ipToTableSpan(newIp), 0); + setElementAtColumn(hostsToTableSpan(newIp), 0); editRow.childNodes[rowCheckIndex].firstChild.id = newId; } setElementAtColumn(timeParamsToTableSpan(getTimeParametersFromUci(uci, editSection, 1)), 1); @@ -1254,6 +1267,7 @@ function editQuota() updateDone = true; } + resetGroupOptions("group", editQuotaWindow.document); } if(!updateDone) { diff --git a/package/gargoyle/files/www/js/restrictions.js b/package/gargoyle/files/www/js/restrictions.js index 2250fffe09..3831b718c4 100644 --- a/package/gargoyle/files/www/js/restrictions.js +++ b/package/gargoyle/files/www/js/restrictions.js @@ -5,7 +5,7 @@ var pkg = "firewall"; function saveChanges() { setControlsEnabled(false, true); - + var enabledRuleFound = false; var runCommands = []; @@ -24,11 +24,11 @@ function saveChanges() for(ruleIndex =0; ruleIndex < ruleData.length; ruleIndex++) { var check = ruleData[ruleIndex][1]; - enabledRuleFound = enabledRuleFound || check.checked; + enabledRuleFound = enabledRuleFound || check.checked; uci.set(pkg, check.id, "enabled", check.checked ? "1" : "0"); } - + //delete all sections of type in uciOriginal & remove them from uciOriginal var originalSections = uciOriginal.getAllSectionsOfType(pkg, ruleTypes[typeIndex]); var sectionIndex = 0; @@ -41,7 +41,7 @@ function saveChanges() deleteSectionCommands.push("uci del " + pkg + "." + originalSections[sectionIndex]); } } - + //create/initialize sections in uci var newSections = uci.getAllSectionsOfType(pkg, ruleTypes[typeIndex]); for(sectionIndex=0; sectionIndex < newSections.length; sectionIndex++) @@ -51,7 +51,7 @@ function saveChanges() } deleteSectionCommands.push("uci commit"); createSectionCommands.push("uci commit"); - + var commands = deleteSectionCommands.join("\n") + "\n" + createSectionCommands.join("\n") + "\n" + uci.getScriptCommands(uciOriginal) + "\n" + runCommands.join("\n") + "\n" + "sh /usr/lib/gargoyle/restart_firewall.sh"; @@ -59,10 +59,10 @@ function saveChanges() var stateChangeFunction = function(req) { if(req.readyState == 4) - { + { uciOriginal = uci.clone(); resetData(); - setControlsEnabled(true); + setControlsEnabled(true); //alert(req.responseText); } } @@ -91,25 +91,25 @@ function resetData() { var description = uciOriginal.get(pkg, sections[sectionIndex], "description"); description = description == "" ? sections[sectionIndex] : description; - + var enabledStr = uciOriginal.get(pkg, sections[sectionIndex], "enabled"); var enabledBool = (enabledStr == "" || enabledStr == "1" || enabledStr == "true") ; var enabledCheck = createEnabledCheckbox(enabledBool); enabledCheck.id = sections[sectionIndex]; //save section id as checkbox name (yeah, it's kind of sneaky...) - + checkElements.push(enabledCheck); areChecked.push(enabledBool); - + ruleTableData.push([description, enabledCheck, createEditButton(enabledBool)]); } } - + var firstColumn = ruleType == "restriction_rule" ? restStr.RDesc : restStr.ESect; columnNames=[firstColumn, UI.Enabled, ""]; ruleTable = createTable(columnNames, ruleTableData, rulePrefix + "table", true, false, removeRuleCallback); - + tableContainer = document.getElementById(rulePrefix + 'table_container'); - + if(tableContainer.firstChild != null) { tableContainer.removeChild(tableContainer.firstChild); @@ -126,6 +126,23 @@ function resetData() setDocumentFromUci(document, new UCIContainer(), "", ruleType, rulePrefix); setVisibility(document, rulePrefix); } + resetGroupOptions("group"); + resetGroupOptions("group_exception"); + document.getElementById("rule_applies_to_addr").value=""; + document.getElementById("exception_applies_to_addr").value=""; +} + + +function groupRestriction() +{ + document.getElementById("rule_applies_to_addr").value = getSelectedValue("group"); + setSelectedValue("group", ""); +} + +function groupException() +{ + document.getElementById("exception_applies_to_addr").value = getSelectedValue("group_exception"); + setSelectedValue("group_exception", ""); } @@ -141,7 +158,7 @@ function addNewRule(ruleType, rulePrefix) var tableContainer = document.getElementById(rulePrefix + 'table_container'); var table = tableContainer.firstChild; var tableData = getTableDataArray(table); - + var newIndex = tableData.length+1; var newId = rulePrefix + "" + newIndex; while( uci.get(pkg, newId, "") != "" ) @@ -149,7 +166,7 @@ function addNewRule(ruleType, rulePrefix) newIndex++; newId = rulePrefix + "" + newIndex; } - + setUciFromDocument(document, newId, ruleType, rulePrefix); var description = uci.get(pkg, newId, "description"); @@ -157,8 +174,8 @@ function addNewRule(ruleType, rulePrefix) var enabledCheck = createEnabledCheckbox(true); enabledCheck.id = newId; //save section id as checkbox name (yeah, it's kind of sneaky...) - - addTableRow(table, [description, enabledCheck, createEditButton(true)], true, false, removeRuleCallback); + + addTableRow(table, [description, enabledCheck, createEditButton(true)], true, false, removeRuleCallback); setDocumentFromUci(document, new UCIContainer(), "", ruleType, rulePrefix); @@ -169,8 +186,8 @@ function addNewRule(ruleType, rulePrefix) function setVisibility(controlDocument, rulePrefix) { controlDocument = controlDocument == null ? document : controlDocument; - - + + setInvisibleIfAnyChecked([rulePrefix + "all_access"], rulePrefix + "resources", "block", controlDocument); setInvisibleIfAnyChecked([rulePrefix + "all_day"], rulePrefix + "hours_active_container", "block", controlDocument); setInvisibleIfAnyChecked([rulePrefix + "every_day"], rulePrefix + "days_active", "block", controlDocument); @@ -227,7 +244,7 @@ function setInvisibleIfIdMatches(selectId, invisibleOptionValue, associatedEleme controlDocument = controlDocument == null ? document : controlDocument; defaultDisplayMode = defaultDisplayMode == null ? "restriction_rule" : defaultDisplayMode; var visElement = controlDocument.getElementById(associatedElementId); - + if(getSelectedValue(selectId, controlDocument) == invisibleOptionValue && visElement != null) { visElement.style.display = "none"; @@ -254,7 +271,7 @@ function createEditButton(enabled, ruleType, rulePrefix) editButton.value = UI.Edit; editButton.className="default_button"; editButton.onclick = editRule; - + editButton.className = enabled ? "default_button" : "default_button_disabled" ; editButton.disabled = enabled ? false : true; @@ -304,7 +321,7 @@ function editRule() catch(e){} } - + try { xCoor = window.screenX + 225; @@ -318,7 +335,7 @@ function editRule() editRuleWindow = window.open(editRuleType == "restriction_rule" ? "restriction_edit_rule.sh" : "whitelist_edit_rule.sh", "edit", "width=560,height=600,left=" + xCoor + ",top=" + yCoor ); - + saveButton = createInput("button", editRuleWindow.document); closeButton = createInput("button", editRuleWindow.document); saveButton.value = UI.CApplyChanges; @@ -328,7 +345,7 @@ function editRule() editRuleSectionId = editRow.childNodes[1].firstChild.id; - runOnEditorLoaded = function () + runOnEditorLoaded = function () { updateDone=false; if(editRuleWindow.document != null) @@ -337,7 +354,7 @@ function editRule() { editRuleWindow.document.getElementById("bottom_button_container").appendChild(saveButton); editRuleWindow.document.getElementById("bottom_button_container").appendChild(closeButton); - + setDocumentFromUci(editRuleWindow.document, uci, editRuleSectionId, editRuleType, editRulePrefix); setVisibility(editRuleWindow.document, editRulePrefix); @@ -362,13 +379,14 @@ function editRule() } editRuleWindow.close(); } - + } editRuleWindow.moveTo(xCoor,yCoor); editRuleWindow.focus(); updateDone = true; - + } + resetGroupOptions("group", editRuleWindow.document); } if(!updateDone) { @@ -383,7 +401,7 @@ function addAddressesToTable(controlDocument, textId, tableContainerId, tableId, { controlDocument = controlDocument == null ? document : controlDocument; var newAddrs = controlDocument.getElementById(textId).value; - var valid = macsValid ? validateMultipleIpsOrMacs(newAddrs) : validateMultipleIps(newAddrs); + var valid = macsValid ? validateMultipleIpsMacsOrGroups(newAddrs) : validateMultipleIpsorGroups(newAddrs); if(valid == 0) { var tableContainer = controlDocument.getElementById(tableContainerId); @@ -391,12 +409,12 @@ function addAddressesToTable(controlDocument, textId, tableContainerId, tableId, newAddrs = newAddrs.replace(/^[\t ]*/, ""); newAddrs = newAddrs.replace(/[\t ]*$/, ""); var addrs = newAddrs.split(/[\t ]*,[\t ]*/); - + while(addrs.length > 0) { addTableRow(table, [ addrs.shift() ], true, false); } - + if(tableContainer.childNodes.length == 0) { tableContainer.appendChild(table); @@ -412,7 +430,7 @@ function addAddressesToTable(controlDocument, textId, tableContainerId, tableId, function addUrlToTable(controlDocument, textId, selectId, tableContainerId, tableId) { controlDocument = controlDocument == null ? document : controlDocument; - + var newUrl = controlDocument.getElementById(textId).value; var urlType = getSelectedValue(selectId, controlDocument); var valid = validateUrl(newUrl, selectId, controlDocument); @@ -454,65 +472,9 @@ function validateRule(controlDocument, rulePrefix) visibilityIds[2] = rulePrefix + "resources"; visibilityIds[3] = rulePrefix + "resources"; } - - return proofreadFields(inputIds, labelIds, functions, validReturnCodes, visibilityIds, controlDocument ); -} -function validateMultipleIps(ips) -{ - ips = ips.replace(/^[\t ]+/g, ""); - ips = ips.replace(/[\t ]+$/g, ""); - var splitIps = ips.split(/[\t ]*,[\t ]*/); - var valid = splitIps.length > 0 ? 0 : 1; - while(valid == 0 && splitIps.length > 0) - { - var nextIp = splitIps.pop(); - if(nextIp.match(/-/)) - { - var nextSplit = nextIp.split(/[\t ]*-[\t ]*/); - valid = nextSplit.length==2 && validateIP(nextSplit[0]) == 0 && validateIP(nextSplit[1]) == 0 ? 0 : 1; - } - else - { - valid = validateIpRange(nextIp); - } - } - return valid; -} -function proofreadMultipleIps(input) -{ - proofreadText(input, validateMultipleIps, 0); -} -function proofreadMultipleIpsOrMacs(input) -{ - proofreadText(input, validateMultipleIpsOrMacs, 0); -} -function validateMultipleIpsOrMacs(addresses) -{ - var addr = addresses.replace(/^[\t ]+/g, ""); - addr = addr.replace(/[\t ]+$/g, ""); - var splitAddr = addr.split(/[\t ]*,[\t ]*/); - var valid = splitAddr.length > 0 ? 0 : 1; - while(valid == 0 && splitAddr.length > 0) - { - var nextAddr = splitAddr.pop(); - if(nextAddr.match(/-/)) - { - var nextSplit = nextAddr.split(/[\t ]*-[\t ]*/); - valid = nextSplit.length==2 && validateIP(nextSplit[0]) == 0 && validateIP(nextSplit[1]) == 0 ? 0 : 1; - } - else if(nextAddr.match(/:/)) - { - valid = validateMac(nextAddr); - } - else - { - valid = validateIpRange(nextAddr); - } - } - return valid; + return proofreadFields(inputIds, labelIds, functions, validReturnCodes, visibilityIds, controlDocument ); } - function validateMultiplePorts(portStr) { portStr = portStr.replace(/^[\t ]+/g, ""); @@ -548,7 +510,7 @@ function proofreadUrl(input) function createUrlSpan(urlStr, controlDocument) { controlDocument = controlDocument == null ? document : controlDocument; - + var splitUrl = []; while(urlStr.length > 0) { @@ -556,7 +518,7 @@ function createUrlSpan(urlStr, controlDocument) urlStr = urlStr.substr(30); splitUrl.push(urlStr.length > 0 ? next + "-" : next); } - + var urlSpan = controlDocument.createElement('span'); while(splitUrl.length > 0) { @@ -593,7 +555,7 @@ function setDocumentFromUci(controlDocument, sourceUci, sectionId, ruleType, rul controlDocument = controlDocument == null ? document : controlDocument; var description = sourceUci.get(pkg, sectionId, "description"); - description = description == "" ? sectionId : description; + description = description == "" ? sectionId : description; controlDocument.getElementById(rulePrefix + "name").value = description; setIpTableAndSelectFromUci(controlDocument, sourceUci, pkg, sectionId, "local_addr", rulePrefix + "applies_to_table_container", rulePrefix + "applies_to_table", rulePrefix + "applies_to", rulePrefix + "applies_to_addr"); @@ -618,7 +580,7 @@ function setDocumentFromUci(controlDocument, sourceUci, sectionId, ruleType, rul { days = dayStr.split(/,/); } - + var everyDay = (daysAndHours == ""); var dayIndex=0; for(dayIndex = 0; dayIndex < allDays.length; dayIndex++) @@ -650,7 +612,7 @@ function setDocumentFromUci(controlDocument, sourceUci, sectionId, ruleType, rul app_proto_type = app_proto == "" ? "all" : app_proto_type; setSelectedValue(rulePrefix + "app_protocol_type", app_proto_type, controlDocument); setSelectedValue(rulePrefix + "app_protocol", app_proto, controlDocument); - + @@ -680,7 +642,7 @@ function setDocumentFromUci(controlDocument, sourceUci, sectionId, ruleType, rul } } setSelectedValue(rulePrefix + "url_type", urlMatchType, controlDocument); - + var urlTableContainer = controlDocument.getElementById(rulePrefix + "url_match_table_container"); if(urlTableContainer.childNodes.length > 0) { @@ -711,7 +673,7 @@ function setDocumentFromUci(controlDocument, sourceUci, sectionId, ruleType, rul controlDocument.getElementById(rulePrefix + "url_match").value = ""; - + var allResourcesBlocked = true; var resourceTypeIds = ["remote_ip_type", "remote_port_type", "local_port_type", "transport_protocol", "app_protocol_type", "url_type" ]; for(typeIndex=0; typeIndex < resourceTypeIds.length; typeIndex++) @@ -740,21 +702,20 @@ function setIpTableAndSelectFromUci(controlDocument, sourceUci, pkg, sectionId, if(tableContainer.childNodes.length > 0) { tableContainer.removeChild(tableContainer.firstChild); - } + } if(optionValue != "") { optionValue = optionValue.replace(/^[\t ]*/, ""); optionValue = optionValue.replace(/[\t ]*$/, ""); var ips = optionValue.split(/[\t ]*,[\t ]*/); - var table = createTable([""], [], tableId, true, false, null, null, controlDocument); while(ips.length > 0) { addTableRow(table, [ ips.shift() ], true, false, null, null, controlDocument); } tableContainer.appendChild(table); - + controlDocument.getElementById(textId).value = ""; } } @@ -777,17 +738,17 @@ function setTextAndSelectFromUci(controlDocument, sourceUci, pkg, sectionId, opt function setUciFromDocument(controlDocument, sectionId, ruleType, rulePrefix) { - // note: we assume error checking has already been done + // note: we assume error checking has already been done uci.removeSection(pkg, sectionId); uci.set(pkg, sectionId, "", ruleType); uci.set(pkg, sectionId, "is_ingress", "0"); controlDocument = controlDocument == null ? document : controlDocument; - + uci.set(pkg, sectionId, "", ruleType); uci.set(pkg, sectionId, "description", controlDocument.getElementById(rulePrefix + "name").value); - + setFromIpTable(controlDocument, pkg, sectionId, "local_addr", rulePrefix + "applies_to_table_container", rulePrefix + "applies_to"); var daysActive = controlDocument.getElementById(rulePrefix + "days_active"); @@ -795,7 +756,7 @@ function setUciFromDocument(controlDocument, sectionId, ruleType, rulePrefix) { var daysActive = []; var dayIds = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"]; - + for(dayIndex =0; dayIndex < dayIds.length; dayIndex++) { if(controlDocument.getElementById(rulePrefix + dayIds[dayIndex]).checked) @@ -816,7 +777,7 @@ function setUciFromDocument(controlDocument, sectionId, ruleType, rulePrefix) setFromIpTable(controlDocument, pkg, sectionId, "remote_addr", rulePrefix + "remote_ip_table_container", rulePrefix + "remote_ip_type"); setIfVisible(controlDocument, pkg, sectionId, "remote_port", rulePrefix + "remote_port", rulePrefix + "remote_port", rulePrefix + "remote_port_type"); setIfVisible(controlDocument, pkg, sectionId, "local_port", rulePrefix + "local_port", rulePrefix + "local_port", rulePrefix + "local_port_type"); - + uci.set(pkg, sectionId, "proto", getSelectedValue(rulePrefix + "transport_protocol", controlDocument)); var appProtocolType = getSelectedValue(rulePrefix + "app_protocol_type", controlDocument); diff --git a/package/gargoyle/files/www/js/wol.js b/package/gargoyle/files/www/js/wol.js index 42d30b8fdc..8c68ddfd8d 100644 --- a/package/gargoyle/files/www/js/wol.js +++ b/package/gargoyle/files/www/js/wol.js @@ -18,26 +18,39 @@ function initWolTable() initializeDescriptionVisibility(uciOriginal, "wol_help"); // set description visibility uciOriginal.removeSection("gargoyle", "help"); // necessary, or we overwrite the help settings when we save - arpLines.shift(); // skip header + var hosts = uciOriginal.getAllSectionsOfType("dhcp", "host"); var lineIndex = 0; + for(lineIndex=0; lineIndex < hosts.length; lineIndex++) + { + var host = hosts[lineIndex]; + var ip = uciOriginal.get("dhcp", host, "ip"); + var mac = uciOriginal.get("dhcp", host, "mac"); + dataList.push( [ host, ip, mac, createWakeUpButton() ] ); + ipToHostAndMac[ip] = 1; + } + + arpLines.shift(); // skip header for(lineIndex=0; lineIndex < arpLines.length; lineIndex++) { var nextLine = arpLines[lineIndex]; var splitLine = nextLine.split(/[\t ]+/); - var mac = splitLine[3].toUpperCase(); var ip = splitLine[0]; - dataList.push( [ getHostname(ip), ip, mac, createWakeUpButton() ] ); - ipToHostAndMac[ip] = 1; + if(ipToHostAndMac[ip] == null) + { + var mac = splitLine[3].toUpperCase(); + dataList.push( [ getHostname(ip), ip, mac, createWakeUpButton() ] ); + ipToHostAndMac[ip] = 1; + } } for(lineIndex=0; lineIndex < dhcpLeaseLines.length; lineIndex++) { var leaseLine = dhcpLeaseLines[lineIndex]; var splitLease = leaseLine.split(/[\t ]+/); - var mac = splitLease[1].toUpperCase(); var ip = splitLease[2]; if(ipToHostAndMac[ip] == null) { + var mac = splitLease[1].toUpperCase(); dataList.push( [ getHostname(ip), ip, mac, createWakeUpButton() ] ); ipToHostAndMac[ip] = 1; } @@ -46,10 +59,10 @@ function initWolTable() for(lineIndex=0; lineIndex < etherData.length; lineIndex++) { var ether = etherData[lineIndex]; - var mac = ether[0].toUpperCase(); var ip = ether[1]; if(ipToHostAndMac[ip] == null) { + var mac = ether[0].toUpperCase(); dataList.push( [ getHostname(ip), ip, mac, createWakeUpButton() ] ); ipToHostAndMac[ip] = 1; } diff --git a/package/gargoyle/files/www/login.sh b/package/gargoyle/files/www/login.sh index 9426be80ff..f378542ec1 100755 --- a/package/gargoyle/files/www/login.sh +++ b/package/gargoyle/files/www/login.sh @@ -9,12 +9,12 @@ valid=$( eval $( gargoyle_session_validator -c "$COOKIE_hash" -e "$COOKIE_exp" -a "$HTTP_USER_AGENT" -i "$REMOTE_ADDR" -t $(uci get gargoyle.global.session_timeout) -b "$COOKIE_browser_time" ) | grep "Set-Cookie" ) require=$(uci get gargoyle.global.require_web_password) if [ "$require" = "0" ] ; then - eval $( gargoyle_session_validator -g -a "$HTTP_USER_AGENT" -i "$REMOTE_ADDR" -t $(uci get gargoyle.global.session_timeout) ) + eval $( gargoyle_session_validator -g -a "$HTTP_USER_AGENT" -i "$REMOTE_ADDR" -t $(uci get gargoyle.global.session_timeout) ) valid="1" fi if [ -n "$valid" ] ; then firstboot=$( uci get gargoyle.global.is_first_boot 2>/dev/null ) - echo "Status: 302 Found" + echo "Status: 302 Found" if [ "$firstboot" = "1" ] ; then echo "Location: /firstboot.sh" else @@ -39,7 +39,7 @@ fi - gargoyle_header_footer -h -c "internal.css" -j "$js" -z "$js $lang_js" gargoyle + gargoyle_header_footer -h -c "internal.css" -j "$js" -z "$js $lang_js" gargoyle dhcp %> @@ -62,6 +62,12 @@ var passInvalid = false; print_quotas . /usr/lib/gargoyle/current_time.sh + + echo ""; + echo "var leaseData = new Array();"; + if [ -e /tmp/dhcp.leases ] ; then + awk ' $0 ~ /[a-z,A-Z,0-9]+/ {print "leaseData.push([\""$2"\",\""$3"\",\""$4"\"]);"};' /tmp/dhcp.leases + fi %> //--> diff --git a/package/gargoyle/files/www/qos_download.sh b/package/gargoyle/files/www/qos_download.sh index 15f43e1507..3205c5f36e 100755 --- a/package/gargoyle/files/www/qos_download.sh +++ b/package/gargoyle/files/www/qos_download.sh @@ -6,7 +6,7 @@ # itself remain covered by the GPL. # See http://gargoyle-router.com/faq.html#qfoss for more information eval $( gargoyle_session_validator -c "$COOKIE_hash" -e "$COOKIE_exp" -a "$HTTP_USER_AGENT" -i "$REMOTE_ADDR" -r "login.sh" -t $(uci get gargoyle.global.session_timeout) -b "$COOKIE_browser_time" ) - gargoyle_header_footer -h -s "firewall" -p "qosdownload" -c "internal.css" -j "qos.js table.js" -z "qos.js" qos_gargoyle firewall gargoyle -i qos_gargoyle + gargoyle_header_footer -h -s "firewall" -p "qosdownload" -c "internal.css" -j "qos.js table.js" -z "qos.js" qos_gargoyle firewall gargoyle dhcp -i qos_gargoyle %> @@ -36,7 +36,7 @@
-

<%~ QoSAbout %> +

<%~ QoSAbout %>

@@ -80,10 +80,15 @@
- - + +
- + + + +
@@ -279,7 +284,7 @@    - +
@@ -287,7 +292,7 @@    - +
diff --git a/package/gargoyle/files/www/qos_edit_rule.sh b/package/gargoyle/files/www/qos_edit_rule.sh index 762a8e8801..bae840810a 100755 --- a/package/gargoyle/files/www/qos_edit_rule.sh +++ b/package/gargoyle/files/www/qos_edit_rule.sh @@ -16,6 +16,7 @@ +
@@ -30,6 +31,7 @@
+
diff --git a/package/gargoyle/files/www/qos_upload.sh b/package/gargoyle/files/www/qos_upload.sh index 8f5b417e12..f542590ee3 100755 --- a/package/gargoyle/files/www/qos_upload.sh +++ b/package/gargoyle/files/www/qos_upload.sh @@ -6,7 +6,7 @@ # itself remain covered by the GPL. # See http://gargoyle-router.com/faq.html#qfoss for more information eval $( gargoyle_session_validator -c "$COOKIE_hash" -e "$COOKIE_exp" -a "$HTTP_USER_AGENT" -i "$REMOTE_ADDR" -r "login.sh" -t $(uci get gargoyle.global.session_timeout) -b "$COOKIE_browser_time" ) - gargoyle_header_footer -h -s "firewall" -p "qosupload" -c "internal.css" -j "qos.js table.js" -z "qos.js" qos_gargoyle firewall gargoyle -i qos_gargoyle + gargoyle_header_footer -h -s "firewall" -p "qosupload" -c "internal.css" -j "qos.js table.js" -z "qos.js" qos_gargoyle firewall gargoyle dhcp -i qos_gargoyle %>