resolve package problems

This commit is contained in:
CN_SZTL 2019-07-02 19:05:14 +08:00
parent e04f9ff3cc
commit d6572e679f
No known key found for this signature in database
GPG Key ID: 6850B6345C862176
61 changed files with 2 additions and 13412 deletions

View File

@ -1,71 +0,0 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=gargoyle-firewall-util
PKG_VERSION:=1.0.0
PKG_RELEASE:=1
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
include $(INCLUDE_DIR)/package.mk
define Package/gargoyle-firewall-util
SECTION:=net
CATEGORY:=Gargoyle
SUBMENU:=Network
TITLE:=A couple of shell script routines for firewall initialization
DEPENDS:=+ebtables +libericstools +uci +libiptbwctl +iptables-mod-filter \
+iptables-mod-ipopt +iptables-mod-conntrack-extra +iptables-mod-nat-extra \
+iptables-mod-extra +iptables-mod-iprange +iptables-mod-bandwidth \
+iptables-mod-timerange +iptables-mod-weburl +kmod-gre +kmod-pptp \
+kmod-tun +kmod-nf-nathelper +kmod-nf-nathelper-extra
MAINTAINER:=Eric Bishop <eric@gargoyle-router.com>
endef
define Package/gargoyle-firewall-util/description
A couple of shell script routines for firewall initialization
endef
define Build/Prepare
echo PACKAGE BUILD DIR = $(PACKAGE_BUILD_DIR)
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Build/Configure
endef
define Build/Compile
$(MAKE) -C $(PKG_BUILD_DIR) \
$(TARGET_CONFIGURE_OPTS) \
STAGING_DIR="$(STAGING_DIR)" \
CFLAGS="$(TARGET_CFLAGS) -I $(STAGING_DIR)/usr/include" \
LDFLAGS="$(TARGET_LDFLAGS) -L $(STAGING_DIR)/usr/lib"
endef
define Package/gargoyle-firewall-util/postinst
#!/bin/sh
included=$$(cat $${IPKG_INSTROOT}/etc/config/firewall | grep 'gargoyle_additions.firewall' )
if [ -z "$$included" ] ; then printf "config include\n\toption type script\n\toption path /usr/lib/gargoyle_firewall_util/gargoyle_additions.firewall\n\toption family IPv4\n\toption reload 1\n\n" >> $${IPKG_INSTROOT}/etc/config/firewall ; fi
endef
define Package/gargoyle-firewall-util/install
$(INSTALL_DIR) $(1)/usr/lib/gargoyle_firewall_util/
$(INSTALL_DIR) $(1)/etc/hotplug.d/iface/
$(INSTALL_DIR) $(1)/usr/bin/
$(INSTALL_DIR) $(1)/etc/init.d/
$(INSTALL_DIR) $(1)/etc/ppp/ip-up.d/
$(INSTALL_BIN) $(PKG_BUILD_DIR)/make_iptables_rules $(1)/usr/bin/make_iptables_rules
$(INSTALL_BIN) $(PKG_BUILD_DIR)/delete_chain_from_table $(1)/usr/bin/delete_chain_from_table
$(INSTALL_BIN) $(PKG_BUILD_DIR)/backup_quotas $(1)/usr/bin/backup_quotas
$(INSTALL_BIN) $(PKG_BUILD_DIR)/restore_quotas $(1)/usr/bin/restore_quotas
$(INSTALL_BIN) $(PKG_BUILD_DIR)/print_quotas $(1)/usr/bin/print_quotas
$(INSTALL_BIN) ./files/gargoyle_firewall_util.sh $(1)/usr/lib/gargoyle_firewall_util/gargoyle_firewall_util.sh
$(INSTALL_BIN) ./files/gargoyle_additions.firewall $(1)/usr/lib/gargoyle_firewall_util/gargoyle_additions.firewall
$(INSTALL_BIN) ./files/gargoyle_firewall.hotplug $(1)/etc/hotplug.d/iface/21-gargoyle_firewall
$(INSTALL_BIN) ./files/set_kernel_timezone.init $(1)/etc/init.d/set_kernel_timezone
$(INSTALL_BIN) ./files/modemaccess.pppoe $(1)/etc/ppp/ip-up.d/modemaccess.sh
endef
$(eval $(call BuildPackage,gargoyle-firewall-util))

View File

@ -1,2 +0,0 @@
. /usr/lib/gargoyle_firewall_util/gargoyle_firewall_util.sh
initialize_firewall

View File

@ -1,42 +0,0 @@
#!/bin/sh
if [ "$INTERFACE" = "wan" ]; then
. /usr/lib/gargoyle_firewall_util/gargoyle_firewall_util.sh
if [ "$ACTION" = "ifup" ]; then
# previously we waited until firewall was up here, testing firewall.core.loaded in /var/state
# unfortunately that was removed in barrier breaker, but new firewall (fw3) loads very FAST as it's a binary
# So... just wait 2 seconds
sleep 2
#Bring up the parts of the firewall that depend on device name and WAN IP address.
ifup_firewall
#Start up the bandwidth monitor which depends on the device name
if [ -h /etc/rc.d/S55bwmon_gargoyle ]; then
/etc/init.d/bwmon_gargoyle restart
fi
fi
if [ "$ACTION" = "ifdown" ]; then
quota_chains_exist=$(iptables -t mangle -L combined_quotas 2>/dev/null)
if [ -n "$quota_chains_exist" ]; then
backup_quotas
fi
fi
fi
if [ "$INTERFACE" = "lan" ]; then
wan_exists=$(uci -q get network.wan)
if [ -z "$wan_exists" ]; then
if [ "$ACTION" = "ifup" ]; then
/etc/init.d/bwmon_gargoyle restart
fi
if [ "$ACTION" = "ifdown" ]; then
/etc/init.d/bwmon_gargoyle stop
fi
fi
fi

View File

@ -1,585 +0,0 @@
# Copyright Eric Bishop, 2008-2010
# This is free software licensed under the terms of the GNU GPL v2.0
#
. /lib/functions.sh
include /lib/network
ra_mask="0x0080"
ra_mark="$ra_mask/$ra_mask"
death_mask=0x8000
death_mark="$death_mask"
wan_if=""
mask_to_cidr()
{
mask="$1"
bits=0;
mask_parts=$(echo $mask | sed 's/\./ /g')
for p in $mask_parts ; do
case $p in
255)
bits=$(($bits + 8)) ;;
254)
bits=$(($bits + 7)) ;;
252)
bits=$(($bits + 6)) ;;
248)
bits=$(($bits + 5)) ;;
240)
bits=$(($bits + 4)) ;;
224)
bits=$(($bits + 3)) ;;
192)
bits=$(($bits + 2)) ;;
128)
bits=$(($bits + 1)) ;;
esac
done
echo $bits
}
define_wan_if()
{
if [ -z "$wan_if" ]; then
#Wait for up to 15 seconds for the wan interface to indicate it is up.
wait_sec=15
while [ -z "$(uci -P /var/state get network.wan.up 2>/dev/null)" ] && [ "$wait_sec" -gt 0 ]; do
sleep 1
wait_sec=$(($wait_sec - 1))
done
#The interface name will depend on if pppoe is used or not. If pppoe is used then
#the name we are looking for is in network.wan.ifname. If there is nothing there
#use the device named by network.wan.device
wan_if=$(uci -P /var/state get network.wan.ifname 2>/dev/null)
if [ -z "$wan_if" ]; then
wan_if=$(uci -P /var/state get network.wan.device 2>/dev/null)
fi
fi
}
# parse remote_accept sections in firewall config and add necessary rules
insert_remote_accept_rules()
{
local config_name="firewall"
local section_type="remote_accept"
ssh_max_attempts=$(uci -q get dropbear.@dropbear[0].max_remote_attempts)
ssh_port=$(uci -q get dropbear.@dropbear[0].Port)
if [ -z "$ssh_max_attempts" ] || [ "$ssh_max_attempts" = "unlimited" ]; then
ssh_max_attempts=""
else
ssh_max_attempts=$(( $ssh_max_attempts + 1 ))
fi
#add rules for remote_accepts
parse_remote_accept_config()
{
vars="local_port remote_port start_port end_port proto zone"
proto="tcp udp"
zone="wan"
for var in $vars ; do
config_get $var $1 $var
done
if [ "$proto" = "tcpudp" ] || [ -z "$proto" ]; then
proto="tcp udp"
fi
for prot in $proto ; do
if [ -n "$local_port" ]; then
if [ -z "$remote_port" ]; then
remote_port="$local_port"
fi
#Discourage brute force attacks on ssh from the WAN by limiting failed conneciton attempts.
#Each attempt gets a maximum of 10 password tries by dropbear.
if [ -n "$ssh_max_attempts" ] && [ "$local_port" = "$ssh_port" ] && [ "$prot" = "tcp" ]; then
iptables -t filter -A "input_${zone}_rule" -p "$prot" --dport $ssh_port -m recent --set --name SSH_CHECK
iptables -t filter -A "input_${zone}_rule" -m recent --update --seconds 300 --hitcount $ssh_max_attempts --name SSH_CHECK -j DROP
fi
if [ "$remote_port" != "$local_port" ]; then
#since we're inserting with -I, insert redirect rule first which will then be hit second, after setting connmark
iptables -t nat -I "zone_"$zone"_prerouting" -p "$prot" --dport "$remote_port" -j REDIRECT --to-ports "$local_port"
iptables -t nat -I "zone_"$zone"_prerouting" -p "$prot" --dport "$remote_port" -j CONNMARK --set-mark "$ra_mark"
iptables -t filter -A "input_${zone}_rule" -p $prot --dport "$local_port" -m connmark --mark "$ra_mark" -j ACCEPT
else
iptables -t nat -I "zone_"$zone"_prerouting" -p "$prot" --dport "$remote_port" -j REDIRECT --to-ports "$local_port"
iptables -t filter -A "input_${zone}_rule" -p "$prot" --dport "$local_port" -j ACCEPT
fi
elif [ -n "$start_port" ] && [ -n "$end_port" ]; then
iptables -t nat -I "zone_"$zone"_prerouting" -p "$prot" --dport "$start_port:$end_port" -j REDIRECT
iptables -t filter -A "input_${zone}_rule" -p "$prot" --dport "$start_port:$end_port" -j ACCEPT
fi
done
}
config_load "$config_name"
config_foreach parse_remote_accept_config "$section_type"
}
insert_pf_loopback_rules()
{
config_name="firewall"
section_type="redirect"
#Need to always delete the old chains first.
delete_chain_from_table "nat" "pf_loopback_A"
delete_chain_from_table "filter" "pf_loopback_B"
delete_chain_from_table "nat" "pf_loopback_C"
define_wan_if
if [ -z "$wan_if" ] ; then return ; fi
wan_ip=$(uci -p /tmp/state get network.wan.ipaddr)
lan_mask=$(uci -p /tmp/state get network.lan.netmask)
if [ -n "$wan_ip" ] && [ -n "$lan_mask" ]; then
iptables -t nat -N "pf_loopback_A"
iptables -t filter -N "pf_loopback_B"
iptables -t nat -N "pf_loopback_C"
iptables -t nat -I zone_lan_prerouting -d $wan_ip -j pf_loopback_A
iptables -t filter -I zone_lan_forward -j pf_loopback_B
iptables -t nat -I postrouting_rule -o br-lan -j pf_loopback_C
add_pf_loopback()
{
local vars="src dest proto src_dport dest_ip dest_port"
local all_defined="1"
for var in $vars ; do
config_get $var $1 $var
loaded=$(eval echo "\$$var")
#echo $var = $loaded
if [ -z "$loaded" ] && [ ! "$var" = "$src_dport" ]; then
all_defined="0"
fi
done
if [ -z "$src_dport" ]; then
src_dport=$dest_port
fi
sdp_dash=$src_dport
sdp_colon=$(echo $sdp_dash | sed 's/\-/:/g')
dp_dash=$dest_port
dp_colon=$(echo $dp_dash | sed 's/\-/:/g')
if [ "$all_defined" = "1" ] && [ "$src" = "wan" ] && [ "$dest" = "lan" ] ; then
iptables -t nat -A pf_loopback_A -p $proto --dport $sdp_colon -j DNAT --to-destination $dest_ip:$dp_dash
iptables -t filter -A pf_loopback_B -p $proto --dport $dp_colon -d $dest_ip -j ACCEPT
iptables -t nat -A pf_loopback_C -p $proto --dport $dp_colon -d $dest_ip -s $dest_ip/$lan_mask -j MASQUERADE
fi
}
config_load "$config_name"
config_foreach add_pf_loopback "$section_type"
fi
}
insert_dmz_rule()
{
local config_name="firewall"
local section_type="dmz"
#add rules for remote_accepts
parse_dmz_config()
{
vars="to_ip from"
for var in $vars ; do
config_get $var $1 $var
done
if [ -n "$from" ]; then
from_if=$(uci -q -p /tmp/state get network.$from.ifname)
fi
# echo "from_if = $from_if"
if [ -n "$to_ip" ] && [ -n "$from" ] && [ -n "$from_if" ]; then
iptables -t nat -A "zone_"$from"_prerouting" -i $from_if -j DNAT --to-destination $to_ip
# echo "iptables -t nat -A "prerouting_"$from -i $from_if -j DNAT --to-destination $to_ip"
iptables -t filter -I "zone_"$from"_forward" -d $to_ip -j ACCEPT
fi
}
config_load "$config_name"
config_foreach parse_dmz_config "$section_type"
}
insert_restriction_rules()
{
define_wan_if
if [ -z "$wan_if" ] ; then return ; fi
if [ -e /tmp/restriction_init.lock ]; then return ; fi
touch /tmp/restriction_init.lock
egress_exists=$(iptables -t filter -L egress_restrictions 2>/dev/null)
ingress_exists=$(iptables -t filter -L ingress_restrictions 2>/dev/null)
if [ -n "$egress_exists" ]; then
delete_chain_from_table filter egress_whitelist
delete_chain_from_table filter egress_restrictions
fi
if [ -n "$ingress_exists" ]; then
delete_chain_from_table filter ingress_whitelist
delete_chain_from_table filter ingress_restrictions
fi
iptables -t filter -N egress_restrictions
iptables -t filter -N ingress_restrictions
iptables -t filter -N egress_whitelist
iptables -t filter -N ingress_whitelist
iptables -t filter -I FORWARD -o $wan_if -j egress_restrictions
iptables -t filter -I FORWARD -i $wan_if -j ingress_restrictions
iptables -t filter -I egress_restrictions -j egress_whitelist
iptables -t filter -I ingress_restrictions -j ingress_whitelist
package_name="firewall"
parse_rule_config()
{
section=$1
section_type=$(uci get "$package_name"."$section")
config_get "enabled" "$section" "enabled"
if [ -z "$enabled" ]; then enabled="1" ; fi
if [ "$enabled" = "1" ] && ( [ "$section_type" = "restriction_rule" ] || [ "$section_type" = "whitelist_rule" ] ) ; then
#convert app_proto && not_app_proto to connmark here
config_get "app_proto" "$section" "app_proto"
config_get "not_app_proto" "$section" "not_app_proto"
if [ -n "$app_proto" ]; then
app_proto_connmark=$(cat /etc/l7marker.marks 2>/dev/null | grep $app_proto | awk '{ print $2 ; }' )
app_proto_mask=$(cat /etc/l7marker.marks 2>/dev/null | grep $app_proto | awk '{ print $3 ; }' )
uci set "$package_name"."$section".connmark="$app_proto_connmark/$app_proto_mask"
fi
if [ -n "$not_app_proto" ]; then
not_app_proto_connmark=$(cat /etc/l7marker.marks 2>/dev/null | grep "$not_app_proto" | awk '{ print $2 }')
not_app_proto_mask=$(cat /etc/l7marker.marks 2>/dev/null | grep "$not_app_proto" | awk '{ print $3 }')
uci set "$package_name"."$section".not_connmark="$not_app_proto_connmark/$not_app_proto_mask"
fi
table="filter"
chain="egress_restrictions"
ingress=""
target="REJECT"
config_get "is_ingress" "$section" "is_ingress"
if [ "$is_ingress" = "1" ]; then
ingress=" -i "
if [ "$section_type" = "restriction_rule" ]; then
chain="ingress_restrictions"
else
chain="ingress_whitelist"
fi
else
if [ "$section_type" = "restriction_rule" ]; then
chain="egress_restrictions"
else
chain="egress_whitelist"
fi
fi
if [ "$section_type" = "whitelist_rule" ]; then
target="ACCEPT"
fi
make_iptables_rules -p "$package_name" -s "$section" -t "$table" -c "$chain" -g "$target" $ingress
make_iptables_rules -p "$package_name" -s "$section" -t "$table" -c "$chain" -g "$target" $ingress -r
uci del "$package_name"."$section".connmark 2>/dev/null
uci del "$package_name"."$section".not_connmark 2>/dev/null
fi
}
config_load "$package_name"
config_foreach parse_rule_config "whitelist_rule"
config_foreach parse_rule_config "restriction_rule"
rm -rf /tmp/restriction_init.lock
}
initialize_quotas()
{
define_wan_if
if [ -z "$wan_if" ] ; then return ; fi
if [ -e /tmp/quota_init.lock ]; then return ; fi
touch /tmp/quota_init.lock
lan_mask=$(uci -p /tmp/state get network.lan.netmask)
lan_ip=$(uci -p /tmp/state get network.lan.ipaddr)
full_qos_enabled=$(ls /etc/rc.d/*qos_gargoyle 2>/dev/null)
if [ -n "$full_qos_enabled" ]; then
full_up=$(uci get qos_gargoyle.upload.total_bandwidth 2>/dev/null)
full_down=$(uci get qos_gargoyle.download.total_bandwidth 2>/dev/null)
if [ -z "$full_up" ] && [ -z "$full_down" ]; then
full_qos_enabled=""
fi
fi
# restore_quotas does the hard work of building quota chains & rebuilding crontab file to do backups
#
# this initializes qos functions ONLY if we have quotas that
# have up and down speeds defined for when quota is exceeded
# and full qos is not enabled
if [ -z "$full_qos_enabled" ]; then
restore_quotas -w $wan_if -d $death_mark -m $death_mask -s "$lan_ip/$lan_mask" -c "0 0,4,8,12,16,20 * * * /usr/bin/backup_quotas >/dev/null 2>&1"
initialize_quota_qos
else
restore_quotas -q -w $wan_if -d $death_mark -m $death_mask -s "$lan_ip/$lan_mask" -c "0 0,4,8,12,16,20 * * * /usr/bin/backup_quotas >/dev/null 2>&1"
cleanup_old_quota_qos
fi
#enable cron, but only restart cron if it is currently running
#since we initialize this before cron, this will
#make sure we don't start cron twice at boot
/etc/init.d/cron enable
cron_active=$(ps | grep "crond" | grep -v "grep" )
if [ -n "$cron_active" ]; then
/etc/init.d/cron restart
fi
rm -rf /tmp/quota_init.lock
}
load_all_config_sections()
{
local config_name="$1"
local section_type="$2"
all_config_sections=""
section_order=""
config_cb()
{
if [ -n "$2" ] || [ -n "$1" ]; then
if [ -n "$section_type" ]; then
if [ "$1" = "$section_type" ]; then
all_config_sections="$all_config_sections $2"
fi
else
all_config_sections="$all_config_sections $2"
fi
fi
}
config_load "$config_name"
echo "$all_config_sections"
}
cleanup_old_quota_qos()
{
for iface in $(tc qdisc show | awk '{print $5}' | sort -u ); do
tc qdisc del dev "$iface" root >/dev/null 2>&1
done
}
initialize_quota_qos()
{
cleanup_old_quota_qos
#speeds should be in kbyte/sec, units should NOT be present in config file (unit processing should be done by front-end)
quota_sections=$(load_all_config_sections "firewall" "quota")
upload_speeds=""
download_speeds=""
config_load "firewall"
for q in $quota_sections ; do
config_get "exceeded_up_speed" $q "exceeded_up_speed"
config_get "exceeded_down_speed" $q "exceeded_down_speed"
if [ -n "$exceeded_up_speed" ] && [ -n "$exceeded_down_speed" ]; then
if [ $exceeded_up_speed -gt 0 ] && [ $exceeded_down_speed -gt 0 ]; then
upload_speeds="$exceeded_up_speed $upload_speeds"
download_speeds="$exceeded_down_speed $download_speeds"
fi
fi
done
#echo "upload_speeds = $upload_speeds"
unique_up=$( printf "%d\n" $upload_speeds 2>/dev/null | sort -u -n)
unique_down=$( printf "%d\n" $download_speeds 2>/dev/null | sort -u -n)
#echo "unique_up = $unique_up"
num_up_bands=1
num_down_bands=1
if [ -n "$upload_speeds" ]; then
num_up_bands=$((1 + $(printf "%d\n" $upload_speeds 2>/dev/null | sort -u -n | wc -l) ))
fi
if [ -n "$download_speeds" ]; then
num_down_bands=$((1 + $(printf "%d\n" $download_speeds 2>/dev/null | sort -u -n | wc -l) ))
fi
#echo "num_up_bands=$num_up_bands"
#echo "num_down_bands=$num_down_bands"
if [ -n "$wan_if" ] && [ $num_up_bands -gt 1 ] && [ $num_down_bands -gt 1 ]; then
insmod sch_prio >/dev/null 2>&1
insmod sch_tbf >/dev/null 2>&1
insmod cls_fw >/dev/null 2>&1
ifconfig imq0 down >/dev/null 2>&1
ifconfig imq1 down >/dev/null 2>&1
rmmod imq >/dev/null 2>&1
insmod imq numdevs=1 hook_chains="INPUT,FORWARD" hook_tables="mangle,mangle" >/dev/null 2>&1
ip link set imq0 up
#egress/upload
tc qdisc del dev $wan_if root >/dev/null 2>&1
tc qdisc add dev $wan_if handle 1:0 root prio bands $num_up_bands priomap 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
cur_band=2
upload_shift=0
for rate_kb in $unique_up ; do
kbit=$(echo $((rate_kb*8))kbit)
mark=$(($cur_band << $upload_shift))
tc filter add dev $wan_if parent 1:0 prio $cur_band protocol ip handle $mark fw flowid 1:$cur_band
tc qdisc add dev $wan_if parent 1:$cur_band handle $cur_band: tbf rate $kbit burst $kbit limit $kbit
cur_band=$(($cur_band+1))
done
#ingress/download
tc qdisc del dev imq0 root >/dev/null 2>&1
tc qdisc add dev imq0 handle 1:0 root prio bands $num_down_bands priomap 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
cur_band=2
download_shift=8
for rate_kb in $unique_down ; do
kbit=$(echo $((rate_kb*8))kbit)
mark=$(($cur_band << $download_shift))
tc filter add dev imq0 parent 1:0 prio $cur_band protocol ip handle $mark fw flowid 1:$cur_band
tc qdisc add dev imq0 parent 1:$cur_band handle $cur_band: tbf rate $kbit burst $kbit limit $kbit
cur_band=$(($cur_band+1))
done
iptables -t mangle -I ingress_quotas -i $wan_if -j IMQ --todev 0
#tc -s qdisc show dev $wan_if
#tc -s qdisc show dev imq0
fi
}
enforce_dhcp_assignments()
{
enforce_assignments=$(uci -q get firewall.@defaults[0].enforce_dhcp_assignments)
delete_chain_from_table "filter" "lease_mismatch_check"
local pairs1
local pairs2
local pairs
pairs1=""
pairs2=""
if [ -e /tmp/dhcp.leases ]; then
pairs1=$(cat /tmp/dhcp.leases | sed '/^[ \t]*$/d' | awk ' { print $2"^"$3"\n" ; } ' )
fi
if [ -e /etc/ethers ]; then
pairs2=$(cat /etc/ethers | sed '/^[ \t]*$/d' | awk ' { print $1"^"$2"\n" ; } ' )
fi
pairs=$( printf "$pairs1\n$pairs2\n" | sort | uniq )
if [ "$enforce_assignments" = "1" ] && [ -n "$pairs" ]; then
iptables -t filter -N lease_mismatch_check
local p
for p in $pairs ; do
local mac
local ip
mac=$(echo $p | sed 's/\^.*$//g')
ip=$(echo $p | sed 's/^.*\^//g')
if [ -n "$ip" ] && [ -n "$mac" ]; then
iptables -t filter -A lease_mismatch_check ! -s "$ip" -m mac --mac-source "$mac" -j REJECT
iptables -t filter -A lease_mismatch_check -s "$ip" -m mac ! --mac-source "$mac" -j REJECT
fi
done
iptables -t filter -I delegate_forward -j lease_mismatch_check
fi
}
force_router_dns()
{
force_router_dns=$(uci get firewall.@defaults[0].force_router_dns 2> /dev/null)
if [ "$force_router_dns" = "1" ]; then
iptables -t nat -I zone_lan_prerouting -p tcp --dport 53 -j REDIRECT
iptables -t nat -I zone_lan_prerouting -p udp --dport 53 -j REDIRECT
fi
}
add_adsl_modem_routes()
{
wan_proto=$(uci -q get network.wan.proto)
if [ "$wan_proto" = "pppoe" ]; then
wan_dev=$(uci -q get network.wan.ifname) #not really the interface, but the device
iptables -A postrouting_rule -t nat -o $wan_dev -j MASQUERADE
iptables -A forwarding_rule -o $wan_dev -j ACCEPT
/etc/ppp/ip-up.d/modemaccess.sh firewall $wan_dev
fi
}
initialize_firewall()
{
iptables -I zone_lan_forward -i br-lan -o br-lan -j ACCEPT
insert_remote_accept_rules
insert_dmz_rule
enforce_dhcp_assignments
force_router_dns
add_adsl_modem_routes
isolate_guest_networks
}
guest_mac_from_uci()
{
local is_guest_network
local macaddr
config_get is_guest_network "$1" is_guest_network
if [ "$is_guest_network" = "1" ]; then
config_get macaddr "$1" macaddr
echo "$macaddr"
fi
}
get_guest_macs()
{
config_load "wireless"
config_foreach guest_mac_from_uci "wifi-iface"
}
isolate_guest_networks()
{
ebtables -t filter -F FORWARD
ebtables -t filter -F INPUT
local guest_macs=$( get_guest_macs )
if [ -n "$guest_macs" ]; then
local lanifs=`brctl show br-lan 2>/dev/null | awk ' $NF !~ /interfaces/ { print $NF } '`
local lif
local lan_ip=$(uci -p /tmp/state get network.lan.ipaddr)
for lif in $lanifs ; do
for gmac in $guest_macs ; do
local is_guest=$(ifconfig "$lif" 2>/dev/null | grep -i "$gmac")
if [ -n "$is_guest" ]; then
echo "$lif with mac $gmac is wireless guest"
#Allow access to WAN but not other LAN hosts for anyone on guest network
ebtables -t filter -A FORWARD -i "$lif" --logical-out br-lan -j DROP
#Only allow DHCP/DNS access to router for anyone on guest network
ebtables -t filter -A INPUT -i "$lif" -p ARP -j ACCEPT
ebtables -t filter -A INPUT -i "$lif" -p IPV4 --ip-protocol UDP --ip-destination-port 53 -j ACCEPT
ebtables -t filter -A INPUT -i "$lif" -p IPV4 --ip-protocol UDP --ip-destination-port 67 -j ACCEPT
ebtables -t filter -A INPUT -i "$lif" -p IPV4 --ip-destination $lan_ip -j DROP
fi
done
done
fi
}
ifup_firewall()
{
insert_restriction_rules
initialize_quotas
insert_pf_loopback_rules
}

View File

@ -1,49 +0,0 @@
#!/bin/sh
#This script allows access to the ADSL modem web interface when pppoe is used.
#For this to work configure your modem in bridge mode with DHCP enabled on the modem.
#This will cause the modem to dish an address to the router interface when requested below.
#
#Alternatively you can manually set the below variable ROUTER_IP with the IP address
#you want to use. Make sure the IP address is on the same network as the modem.
#ROUTER_IP=10.0.0.2
#Main case statement used only by udhcp which only calls with one of the
#following four key words in parameter 1.
case "$1" in
deconfig)
ifconfig "$interface" 0.0.0.0
exit 0
;;
renew)
ifconfig $interface $ip netmask ${subnet:-255.255.255.0} broadcast ${broadcast:-+}
exit 0
;;
bound)
ifconfig $interface $ip netmask ${subnet:-255.255.255.0} broadcast ${broadcast:-+}
exit 0
;;
nak)
exit 0
;;
leasefail)
exit 0
;;
esac
#if we get here then udhcp did not call us. Must be from pppd or /usr/lib/gargoyle_firewall_util
#configure the ethernet interface.
if [ -n "$ROUTER_IP" ]; then
#In manual mode the user gave us an IP address for our interface
ifconfig $2 $ROUTER_IP netmask 255.255.255.0
else
#In auto mode we first check if we have an ip address already.
ROUTER_IP=$(ifconfig $2 | grep "inet addr:")
if [ -z "$ROUTER_IP" ]; then
#Dont have one so try an get one.
udhcpc -f -i $2 -n -q -s /etc/ppp/ip-up.d/modemaccess.sh
fi
fi
exit 0

View File

@ -1,20 +0,0 @@
#!/bin/sh /etc/rc.common
START=30
start()
{
/usr/bin/set_kernel_timezone
touch /etc/crontabs/root
if ! grep -q "set_kernel_timezone" /etc/crontabs/root; then
echo '0,1,11,21,31,41,51 * * * * /usr/bin/set_kernel_timezone >/dev/null 2>&1' >> /etc/crontabs/root
/etc/init.d/cron enable
#only restart cron if it is currently running
#since we initialize this before cron, this will
#make sure we don't start cron twice at boot
cron_active=$(ps | grep "crond" | grep -v "grep" )
if [ -n "$cron_active" ]; then
/etc/init.d/cron restart
fi
fi
}

View File

@ -1,19 +0,0 @@
all: make_iptables_rules delete_chain_from_table backup_quotas restore_quotas print_quotas
print_quotas: print_quotas.c
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ -lericstools -luci -liptbwctl
restore_quotas: restore_quotas.c
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ -lericstools -luci -liptbwctl
backup_quotas: backup_quotas.c
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ -lericstools -luci -liptbwctl
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
clean:
rm -rf make_iptables_rules delete_chain_from_table print_quotas backup_quotas restore_quotas *.o *~ .*sw*

View File

@ -1,217 +0,0 @@
/* backup_quotas -- Used to backup quota data from iptables rules that use the "bandwidth" module
* Originally designed for use with Gargoyle router firmware (gargoyle-router.com)
*
*
* Copyright © 2009 by Eric Bishop <eric@gargoyle-router.com>
*
* This file is free software: you may copy, redistribute and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 2 of the License, or (at your
* option) any later version.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <erics_tools.h>
#include <uci.h>
#include <ipt_bwctl.h>
#define malloc safe_malloc
#define strdup safe_strdup
list* get_all_sections_of_type(struct uci_context *ctx, char* package, char* section_type);
void backup_quota(char* quota_id, char* quota_backup_dir);
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(void)
{
struct uci_context *ctx = uci_alloc_context();
list* quota_sections = get_all_sections_of_type(ctx, "firewall", "quota");
system("mkdir -p /usr/data/quotas");
unlock_bandwidth_semaphore_on_exit();
while(quota_sections->length > 0)
{
char* next_quota = shift_list(quota_sections);
char* ignore_backup = get_uci_option(ctx, "firewall", next_quota, "ignore_backup_at_next_restore");
int do_backup = 1;
if(ignore_backup != NULL)
{
if(strcmp(ignore_backup, "1") == 0)
{
do_backup = 0;
}
free(ignore_backup);
}
if(do_backup)
{
//do backup
/* base id for quota is the ip associated with it*/
char* backup_id = get_uci_option(ctx, "firewall", next_quota, "id");
char* ip = get_uci_option(ctx, "firewall", next_quota, "ip");
if(ip == NULL)
{
ip = strdup("ALL");
}
else if(strcmp(ip, "") == 0)
{
free(ip);
ip = strdup("ALL");
}
if(backup_id == NULL)
{
backup_id = strdup(ip);
}
else if(strcmp(backup_id, "") == 0)
{
free(backup_id);
backup_id = strdup(ip);
}
char* types[] = { "ingress_limit", "egress_limit", "combined_limit" };
char* postfixes[] = { "_ingress", "_egress", "_combined" };
int type_index;
for(type_index=0; type_index < 3; type_index++)
{
char* defined = get_uci_option(ctx, "firewall", next_quota, types[type_index]);
if(defined != NULL)
{
char* type_id = dynamic_strcat(2, backup_id, postfixes[type_index]);
backup_quota(type_id, "/usr/data/quotas" );
free(type_id);
free(defined);
}
}
free(backup_id);
free(ip);
}
free(next_quota);
}
unsigned long num;
destroy_list(quota_sections, DESTROY_MODE_FREE_VALUES, &num);
uci_free_context(ctx);
return 0;
}
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;
}
void backup_quota(char* id, char* quota_backup_dir)
{
/* if we ever bother to allow quotas to apply to subnets
* specified with '/', this may be necessary
*/
char* quota_file_name;
if(strstr(id, "/") != NULL)
{
char* quota_file_name = dynamic_replace(id, "/", "_");
}
else
{
quota_file_name = strdup(id);
}
char* quota_file_path = dynamic_strcat(3, quota_backup_dir, "/quota_", quota_file_name);
unsigned long num_ips;
ip_bw *ip_buf = NULL;
int query_succeeded = get_all_bandwidth_usage_for_rule_id(id, &num_ips, &ip_buf, 5000);
if(query_succeeded)
{
save_usage_to_file(ip_buf, num_ips, quota_file_path);
free(ip_buf);
}
free(quota_file_path);
free(quota_file_name);
}
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;
}

View File

@ -1,76 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <erics_tools.h>
#define malloc safe_malloc
#define strdup safe_strdup
void free_split_pieces(char** split_pieces);
int main(int argc, char **argv)
{
char *table = argv[1];
char *delete_chain = argv[2];
if(argc != 3)
{
printf("USAGE: %s [TABLE] [CHAIN TO DELETE]\n\n", argv[0]);
return 0;
}
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);
unsigned long line_index;
char* current_chain = NULL;
list* delete_commands = initialize_list();
for(line_index=0; line_index < num_lines; line_index++)
{
char* line = table_dump[line_index];
unsigned long num_pieces = 0;
char whitespace[] = { '\t', ' ', '\r', '\n' };
char** line_pieces = split_on_separators(line, whitespace, 4, -1, 0, &num_pieces);
if(strcmp(line_pieces[0], "Chain") == 0)
{
if(current_chain != NULL) { free(current_chain); }
current_chain = strdup(line_pieces[1]);
}
else
{
unsigned long line_num;
int read = sscanf(line_pieces[0], "%ld", &line_num);
if(read > 0 && current_chain != NULL && num_pieces >1)
{
if(strcmp(line_pieces[1], delete_chain) == 0)
{
char* delete_command = dynamic_strcat(7, "iptables -t ", table, " -D ", current_chain, " ", line_pieces[0], " 2>/dev/null");
push_list(delete_commands, delete_command);
}
}
}
//free line_pieces
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"));
/* run delete commands */
while(delete_commands->length > 0)
{
char *next_command = (char*)pop_list(delete_commands);
char **out = get_shell_command_output_lines(next_command, &num_lines);
free_null_terminated_string_array(out);
}
return 0;
}

View File

@ -1,984 +0,0 @@
/* make_iptables_rules -- A tool to generate firewall rules from options in UCI config files
* Originally designed for use with Gargoyle router firmware (gargoyle-router.com)
*
*
* Copyright © 2009 by Eric Bishop <eric@gargoyle-router.com>
*
* This file is free software: you may copy, redistribute and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 2 of the License, or (at your
* option) any later version.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
#include <math.h>
#include <erics_tools.h>
#include <uci.h>
#define malloc safe_malloc
#define strdup safe_strdup
/* these are indices don't change! */
#define MATCH_IP_INDEX 0
#define MATCH_IP_RANGE_INDEX 1
#define MATCH_MAC_INDEX 2
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);
char*** parse_ips_and_macs(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);
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;
char* package = NULL;
char* section = NULL;
char* table = NULL;
char* chain = NULL;
char* target = NULL;
char* target_options = NULL;
int is_ingress = 0;
int run_commands = 0;
int usage_printed = 0;
while((c = getopt(argc, argv, "P:p:S:s:T:t:C:c:G:g:O:o:IiRrUu")) != -1) //section, page, css includes, javascript includes, title, output interface variables
{
switch(c)
{
case 'P':
case 'p':
package = strdup(optarg);
break;
case 'S':
case 's':
section = strdup(optarg);
break;
case 'T':
case 't':
table = strdup(optarg);
break;
case 'C':
case 'c':
chain = strdup(optarg);
break;
case 'G':
case 'g':
target = strdup(optarg);
break;
case 'O':
case 'o':
target_options = strdup(optarg);
break;
case 'I':
case 'i':
is_ingress = 1;
break;
case 'R':
case 'r':
run_commands = 1;
break;
case 'U':
case 'u':
default:
fprintf(stderr, "USAGE: %s -p [PACKAGE] -s [SECTION] -t [TABLE] -c [CHAIN] -g [TARGET] [OPTIONS]\n -o [TARGET_OPTIONS]\n -i indicates that this rule applies to ingress packets\n -r implies computed commands should be executed instead of just printed\n -u print usage and exit\n\n", argv[0]);
usage_printed = 1;
break;
}
}
if(package != NULL && section != NULL && table != NULL && chain != NULL && target != NULL)
{
string_map* def = get_rule_definition(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]);
}
}
}
else
{
fprintf(stderr, "ERROR: Invalid package / section\n");
}
}
else if(!usage_printed)
{
fprintf(stderr, "USAGE: %s -p [PACKAGE] -s [SECTION] -t [TABLE] -c [CHAIN] -g [TARGET] [OPTIONS]\n -o [TARGET_OPTIONS]\n -i indicates that this rule applies to ingress packets\n -r implies computed commands should be executed instead of just printed\n -u print usage and exit\n\n", argv[0]);
}
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
* even more address space
*/
char** compute_rules(string_map *rule_def, char* table, char* chain, int is_ingress, char* target, char* target_options)
{
list* multi_rules = initialize_list();
char* single_check = strdup("");
char* rule_prefix = dynamic_strcat(5, "iptables -t ", table, " -A ", chain, " ");
target = strdup(target);
to_uppercase(target);
/* get timerange vars first */
char* active_hours = get_map_element(rule_def, "active_hours");
char* active_weekdays = get_map_element(rule_def, "active_weekdays");
char* active_weekly_ranges = get_map_element(rule_def, "active_weekly_ranges");
if(active_weekly_ranges != NULL)
{
char* tmp = dynamic_strcat(3, " -m timerange --weekly_ranges \"", active_weekly_ranges, "\" " );
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);
}
else if(active_hours != NULL)
{
char* tmp = dynamic_strcat(3, " -m timerange --hours \"", active_hours, "\" " );
dcat_and_free(&single_check, &tmp, 1, 1);
}
else if(active_weekdays != NULL)
{
char* tmp = dynamic_strcat(3, " -m timerange --weekdays \"", active_weekdays, "\" " );
dcat_and_free(&single_check, &tmp, 1, 1);
}
/*
* layer7 && ipp2p can not be negated. To negate them
* set a mark/connmark and negate that
*/
char* layer7_def = get_map_element(rule_def, "layer7");
if(layer7_def != NULL)
{
char* tmp = dynamic_strcat(2, " -m layer7 --l7proto ", layer7_def );
dcat_and_free(&single_check, &tmp, 1, 1);
}
char* ipp2p_def = get_map_element(rule_def, "ipp2p");
if(ipp2p_def != NULL)
{
char* tmp = dynamic_strcat(2, " -m ipp2p --", ipp2p_def );
dcat_and_free(&single_check, &tmp, 1, 1);
}
char* min_def = get_map_element(rule_def, "min_pkt_size");
char* max_def = get_map_element(rule_def, "max_pkt_size");
if(min_def != NULL && max_def != NULL)
{
min_def = min_def == NULL ? "0" : min_def;
max_def = max_def == NULL ? "3000" : max_def; //typical max transmission size is 1500, let's make default max 2x that
char* tmp = dynamic_strcat(5, " -m length --length ", min_def, ":", max_def, " " );
dcat_and_free(&single_check, &tmp, 1, 1);
}
/* make sure proto is lower case */
char* proto = get_map_element(rule_def, "proto");
if(proto == NULL)
{
proto = strdup("both");
set_map_element(rule_def, "proto", proto);
}
to_lowercase(proto);
if( safe_strcmp(proto, "udp") != 0 && safe_strcmp(proto, "tcp") != 0 && safe_strcmp(proto, "both") != 0 )
{
char* tmp;
tmp = set_map_element(rule_def, "proto", strdup("both"));
free(tmp);
proto = (char*)get_map_element(rule_def, "proto");
}
int include_proto = strcmp(proto, "both") == 0 ? 0 : 1;
/* parse multi rules */
int mask_byte_index = 0;
list* initial_mask_list = initialize_list();
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 " };
list* url_lists[6];
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;
for(url_var_index=0; url_var_index < 6; url_var_index++)
{
list* url_list = get_map_element(rule_def, url_vars[url_var_index]);
if(url_list != NULL)
{
url_rule_count = url_rule_count + url_list->length;
}
url_lists[url_var_index] = url_list;
}
}
url_is_negated--;
proto = url_rule_count > 0 ? "tcp" : proto;
int url_is_multi = url_rule_count <= 1 ? 0 : 1;
for(url_var_index=0; url_var_index < 6; url_var_index++)
{
list* url_list = url_lists[url_var_index];
if(url_list != NULL)
{
if(url_list->length > 0)
{
unsigned long num_vals;
char** url_def = (char**)get_list_values(url_list, &num_vals);
compute_multi_rules( url_def, multi_rules, &single_check, url_is_multi, rule_prefix, url_prefixes[url_var_index], "", url_is_negated, mask_byte_index, proto, include_proto, 1 );
free(url_def);
}
}
}
push_list(initial_mask_list, (void*)&url_is_negated);
push_list(final_mask_list, (void*)&url_is_multi);
mask_byte_index++;
/* mark matches */
char** mark_def = get_map_element(rule_def, "mark");
int mark_is_negated = mark_def == NULL ? 1 : 0;
mark_def = mark_def == NULL ? get_map_element(rule_def, "not_mark") : mark_def;
mark_is_negated = mark_def == NULL ? 0 : mark_is_negated;
/* we can't do single negation with mark match, so always add seperate multi-match if mark is negated */
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++;
/* connmark matches */
char** connmark_def = get_map_element(rule_def, "connmark");
int connmark_is_negated = connmark_def == NULL ? 1 : 0;
connmark_def = connmark_def == NULL ? get_map_element(rule_def, "not_connmark") : connmark_def;
connmark_is_negated = connmark_def == NULL ? 0 : connmark_is_negated;
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++;
/*
* 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
*/
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"));
int src_is_negated = src_def == NULL && not_src_def != NULL ? 1 : 0;
src_def = src_is_negated == 1 ? not_src_def : src_def;
char*** dst_def = get_map_element(rule_def, (is_ingress ? "local_addr" : "remote_addr"));
char*** not_dst_def = get_map_element(rule_def, (is_ingress ? "not_local_addr" : "not_remote_addr"));
int dst_is_negated = dst_def == NULL && not_dst_def != NULL ? 1 : 0;
dst_def = dst_is_negated == 1 ? not_dst_def : dst_def;
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 } };
int addr_index = 0;
int is_true = 1;
int is_false = 0;
for(addr_index = 0; addr_index < 2; addr_index++)
{
char*** addrs = addr_defs[addr_index];
if(addrs != NULL)
{
int total_rules = 0;
int test_list_index;
for(test_list_index=0; test_list_index < 3; test_list_index++)
{
char** test_list = addrs[test_list_index];
if(test_list != NULL && addr_prefix1[addr_index][test_list_index] != NULL)
{
while(test_list[0] != NULL){ test_list++; total_rules++; }
}
}
int is_multi = total_rules > 1 ? 1 : 0;
int is_negated = addr_negated[addr_index];
//printf("is negated = %d for addr_index = %d\n", is_negated, addr_index);
for(test_list_index=0; test_list_index < 3; test_list_index++)
{
char** test_list = addrs[test_list_index];
if(test_list != NULL && addr_prefix1[addr_index][test_list_index] != NULL)
{
if(test_list[0] != NULL)
{
compute_multi_rules(test_list, multi_rules, &single_check, is_multi, rule_prefix, addr_prefix1[addr_index][test_list_index], addr_prefix2[addr_index][test_list_index],is_negated, mask_byte_index, proto, include_proto, 0);
}
}
}
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++;
}
}
char** sport_def = get_map_element(rule_def, (is_ingress ? "remote_port" : "local_port"));
int sport_is_negated = sport_def == NULL ? 1 : 0;
sport_def = sport_def == NULL ? get_map_element(rule_def, (is_ingress ? "not_remote_port" : "not_local_port")) : sport_def;
sport_is_negated = sport_def == NULL ? 0 : sport_is_negated;
int sport_is_multi = compute_multi_rules(sport_def, multi_rules, &single_check, 0, rule_prefix, " --sport ", "", sport_is_negated, mask_byte_index, proto, 1, 0) == 2;
push_list(initial_mask_list, (void*)&sport_is_negated);
push_list(final_mask_list, (void*)&sport_is_multi);
mask_byte_index++;
char** dport_def = get_map_element(rule_def, (is_ingress ? "local_port" : "remote_port"));
int dport_is_negated = dport_def == NULL ? 1 : 0;
dport_def = dport_def == NULL ? get_map_element(rule_def, (is_ingress ? "not_local_port" : "not_remote_port")) : dport_def;
dport_is_negated = dport_def == NULL ? 0 : dport_is_negated;
int dport_is_multi = compute_multi_rules(dport_def, multi_rules, &single_check, 0, rule_prefix, " --dport ", "", dport_is_negated, mask_byte_index, proto, 1, 0) == 2;
push_list(initial_mask_list, (void*)&dport_is_negated);
push_list(final_mask_list, (void*)&dport_is_multi);
mask_byte_index++;
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
target_options = strlen(target_options) == 0 && (strcmp(proto, "tcp") == 0) && strcmp(target, "REJECT") == 0 ? " --reject-with tcp-reset " : target_options;
if(multi_rules->length > 0)
{
if(strlen(single_check) > 0)
{
char* dummy_multi[] = { single_check, NULL };
int requires_proto = strcmp(proto, "both") == 0 && sport_def == NULL && dport_def == NULL ? 0 : 1;
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);
printf("src is multi = %d\n", src_is_multi);
unsigned long mi;
int* one = shift_list(final_mask_list);
int* two = shift_list(final_mask_list);
printf("one = %d, two = %d\n", *one, *two);
unshift_list(final_mask_list, two);
unshift_list(final_mask_list, one);
*/
unsigned long initial_url_mark = 0x01000000 * url_is_negated * url_is_multi;
unsigned long initial_main_mark = 0;
unsigned long final_match = 0;
int next_mask_index;
for(next_mask_index = 0; next_mask_index <mask_byte_index ; next_mask_index++)
{
int tmp = 1;
int* next_is_multi = &tmp;
if(final_mask_list->length > 0)
{
next_is_multi = shift_list(final_mask_list);
}
else
{
*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;
if(initial_mask_list->length > 0)
{
int* is_negation = (int*)shift_list(initial_mask_list);
if(*is_negation == 1 && next_mask_index != url_mask_byte_index )
{
//printf("nonzero byte index for main mark = %d\n", next_mask_index);
initial_main_mark = initial_main_mark + next_mark_bit;
}
}
/* else it's last single_check mark which is never initialized to one */
}
if(initial_main_mark > 0)
{
//set main_mark unconditionally
char mark[12];
sprintf(mark, "0x%lX", initial_main_mark);
push_list(all_rules, dynamic_strcat(4, rule_prefix, " -j CONNMARK --set-mark ", mark, "/0xFF000000" ));
}
if(initial_url_mark > 0) //do url_mark second since because in order to set main mark we use full mask of 0xFF000000
{
//set proper mark if this is an http request
char mark[12];
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)
{
push_list(all_rules, shift_list(multi_rules));
}
unsigned long tmp_length;
destroy_list(multi_rules, DESTROY_MODE_IGNORE_VALUES, &tmp_length);
//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"))
{
push_list(all_rules, dynamic_strcat(4, rule_prefix, " -p tcp -m connmark --mark ", final_match_str, "/0xFF000000 -j REJECT --reject-with tcp-reset"));
push_list(all_rules, dynamic_strcat(4, rule_prefix, " -m connmark --mark ", final_match_str, "/0xFF000000 -j REJECT"));
}
else
{
char* final_proto = strstr(target_options, "tcp-reset") == NULL ? "" : " -p tcp ";
push_list(all_rules, dynamic_strcat(7, rule_prefix, final_proto, " -m connmark --mark ", final_match_str, "/0xFF000000 -j ", target, target_options ));
}
//if final mark does not match (i.e. we didn't reject), unconditionally reset mark to 0x0 with mask of 0xFF000000
push_list(all_rules, dynamic_strcat(2, rule_prefix, " -j CONNMARK --set-mark 0x0/0xFF000000" ));
}
else
{
if( strcmp(proto, "both") == 0 )
{
if( dport_def == NULL && sport_def == NULL )
{
if(safe_strcmp(target, "REJECT") == 0 && safe_strcmp(target_options, "") == 0 )
{
push_list(all_rules, dynamic_strcat(4, rule_prefix, " -p tcp ", single_check, " -j REJECT --reject-with tcp-reset"));
}
push_list(all_rules, dynamic_strcat(5, rule_prefix, single_check, " -j ",target, target_options ));
}
else
{
if(safe_strcmp(target, "REJECT") == 0 && safe_strcmp(target_options, "") == 0 )
{
push_list(all_rules, dynamic_strcat(4, rule_prefix, " -p tcp ", single_check, " -j REJECT --reject-with tcp-reset" ));
}
else
{
push_list(all_rules, dynamic_strcat(6, rule_prefix, " -p tcp ", single_check, " -j ", target, target_options ));
}
push_list(all_rules, dynamic_strcat(6, rule_prefix, " -p udp ", single_check, " -j ", target, target_options ));
}
}
else
{
push_list(all_rules, dynamic_strcat(8, rule_prefix, " -p ", proto, " ", single_check, " -j ", target, target_options ));
}
}
/* handle very special case: if we're white-listing a URL we need to make
* sure other, non-request packets in connection get through too. So, we allow all traffic
* with a destination port of 80 that is NOT an HTTP request. This should allow
* HTTP connections to persist but prevent connections to any websites but those
* specified
*/
if(url_rule_count > 0 && safe_strcmp(target, "ACCEPT") == 0 && is_ingress == 0)
{
push_list(all_rules, dynamic_strcat(2, rule_prefix, " -p tcp -m weburl --contains http -j CONNMARK --set-mark 0xFF000000/0xFF000000" ));
push_list(all_rules, dynamic_strcat(2, rule_prefix, " -p tcp --dport 80 -m connmark ! --mark 0xFF000000/0xFF000000 -j ACCEPT " ));
push_list(all_rules, dynamic_strcat(2, rule_prefix, " -j CONNMARK --set-mark 0x0/0xFF000000" ));
}
unsigned long num_rules;
char** block_rule_list = (char**) destroy_list( all_rules, DESTROY_MODE_RETURN_VALUES, &num_rules);
return block_rule_list;
}
/* returns 0 if no rules found, 1 if one rule found AND included in single_check, otherwise 2 */
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 parse_type = 0;
if(def != NULL)
{
int num_rules;
for(num_rules=0; def[num_rules] != NULL; num_rules++){}
if(num_rules == 1 && !never_single)
{
parse_type = 1;
char* tmp = dynamic_strcat(7, " ", test_prefix1, (is_negation ? " ! " : " "), test_prefix2, (quoted_args ? " \"" : " "), def[0], (quoted_args ? "\" " : " ") );
dcat_and_free(&tmp, single_check, 1, 1 );
}
else
{
parse_type = 2;
unsigned long mask = 0x01000000 * (unsigned long)pow(2, mask_byte_index);
char mask_str[12];
sprintf(mask_str, "0x%lX", mask);
char* connmark_part = dynamic_strcat(4, " -j CONNMARK --set-mark ", (is_negation ? "0x0" : mask_str), "/", mask_str);
int rule_index =0;
for(rule_index=0; def[rule_index] != NULL; rule_index++)
{
char* common_part = dynamic_strcat(7, test_prefix1, " ", test_prefix2, (quoted_args ? " \"" : " "), def[rule_index], (quoted_args ? "\" " : " "), connmark_part);
if(strcmp(proto, "both") == 0)
{
if(requires_proto)
{
push_list(multi_rules, dynamic_strcat(3, rule_prefix, " -p tcp ", common_part));
push_list(multi_rules, dynamic_strcat(3, rule_prefix, " -p udp ", common_part));
}
else
{
push_list(multi_rules, dynamic_strcat(3, rule_prefix, " ", common_part ));
}
}
else
{
push_list(multi_rules, dynamic_strcat(5, rule_prefix, " -p ", proto, " ", common_part));
}
free(common_part);
}
free(connmark_part);
}
}
return parse_type;
}
string_map* get_rule_definition(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;
char* lookup_str = dynamic_strcat(3, package, ".", section);
int ret_value = uci_lookup_ptr(ctx, &ptr, lookup_str, 1);
if(ret_value == UCI_OK)
{
struct uci_section *s = ptr.s;
if(s != NULL)
{
struct uci_element *e;
definition = initialize_string_map(1);
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);
free(option_name);
free(option_value);
}
}
}
}
uci_free_context(ctx);
return definition;
}
int parse_option(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
)
{
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 ||
safe_strcmp(option_name, "active_weekdays") == 0
)
{
to_lowercase(option_value);
if( safe_strcmp(option_value, "all") != 0 )
{
valid_option = 1;
set_map_element(definition, option_name, strdup(option_value));
}
}
else if( safe_strcmp(option_name, "mark") == 0 ||
safe_strcmp(option_name, "connmark") == 0 ||
safe_strcmp(option_name, "not_mark") == 0 ||
safe_strcmp(option_name, "not_connmark") == 0
)
{
valid_option = 1;
set_map_element(definition, option_name, parse_marks(option_value, 0xFFFFFFFF));
}
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)
{
char*** parsed_addr = parse_ips_and_macs(option_value);
if(parsed_addr != NULL)
{
valid_option = 1;
if( safe_strcmp(option_name, "not_remote_addr") == 0 || safe_strcmp(option_name, "remote_addr") == 0 )
{
parsed_addr[MATCH_MAC_INDEX][0] = NULL; //doesn't make sense to match remote MAC address
}
set_map_element(definition, option_name, parsed_addr);
}
}
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)
{
char** parsed_ports = parse_ports(option_value);
if(parsed_ports != NULL)
{
valid_option = 1;
set_map_element(definition, option_name, parsed_ports);
}
}
else if(truncate_if_starts_with(option_name, "url_contains") ||
truncate_if_starts_with(option_name, "url_regex") ||
truncate_if_starts_with(option_name, "url_exact") ||
truncate_if_starts_with(option_name, "url_domain_contains") ||
truncate_if_starts_with(option_name, "url_domain_regex") ||
truncate_if_starts_with(option_name, "url_domain_exact") ||
truncate_if_starts_with(option_name, "not_url_contains") ||
truncate_if_starts_with(option_name, "not_url_regex") ||
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"))
{
/*
* 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 call iptables from system, and through the shell, which will de-escape quotes for us
*/
list* parsed_quoted = parse_quoted_list(option_value, '\"', '\\', 0);
list* old_parsed = get_map_element(definition, option_name);
if(old_parsed != NULL)
{
while(parsed_quoted->length > 0)
{
push_list(old_parsed, shift_list(parsed_quoted));
}
free(parsed_quoted);
parsed_quoted = old_parsed;
}
if(parsed_quoted->length > 0)
{
valid_option = 1;
set_map_element(definition, option_name, parsed_quoted);
}
}
return valid_option;
}
// 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)
{
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();
int ip_part_index;
for(ip_part_index=0; addr_parts[ip_part_index] != NULL; ip_part_index++)
{
char* next_str = addr_parts[ip_part_index];
if(strchr(next_str, ':'))
{
trim_flanking_whitespace(next_str);
if(strlen(next_str) == 17)
{
push_list(mac_list, trim_flanking_whitespace(next_str));
}
}
else if(strchr(next_str, '-') != NULL)
{
char** range_parts = split_on_separators(next_str, "-", 1, 2, 1, &num_pieces);
char* start = trim_flanking_whitespace(range_parts[0]);
char* end = trim_flanking_whitespace(range_parts[1]);
int start_ip[4];
int end_ip[4];
int start_valid = sscanf(start, "%d.%d.%d.%d", start_ip, start_ip+1, start_ip+2, start_ip+3);
int end_valid = sscanf(end, "%d.%d.%d.%d", end_ip, end_ip+1, end_ip+2, end_ip+3);
if(start_valid == 4 && end_valid == 4)
{
//get_ip_range_strs(start_ip, end_ip, "", 4, ip_list);
push_list(ip_range_list, trim_flanking_whitespace(next_str));
}
free(start);
free(end);
free(range_parts);
//free(next_str);
}
else
{
int parsed_ip[4];
int valid = sscanf(next_str, "%d.%d.%d.%d", parsed_ip, parsed_ip+1, parsed_ip+2, parsed_ip+3);
if(valid == 4)
{
push_list(ip_list, trim_flanking_whitespace(next_str));
}
}
}
free(addr_parts);
unsigned long num1, num2, num3;
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);
if(num1 + num2 + num3 == 0)
{
free(return_value[0]);
free(return_value[1]);
free(return_value[2]);
free(return_value);
return_value = NULL;
}
return return_value;
}
char** parse_ports(char* port_str)
{
unsigned long num_pieces;
char** ports = split_on_separators(port_str, ",", 1, -1, 0, &num_pieces);
int port_index = 0;
for(port_index=0; ports[port_index] != NULL; port_index++)
{
char* dash_ptr;
while((dash_ptr=strchr(ports[port_index], '-')) != NULL)
{
dash_ptr[0] = ':';
}
trim_flanking_whitespace( ports[port_index] );
}
return ports;
}
/*
* parses a list of marks/connmarks
* the max_mask parameter specfies a maximal mask that will be used
* when matching marks/connmarks. If a user-defined mask is specified
* (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
* connmarks, using max_mask=0x00FFFFFF
*/
char** parse_marks(char* list_str, unsigned long max_mask)
{
char** marks = NULL;
if(list_str != NULL)
{
unsigned long num_pieces;
marks = split_on_separators(list_str, ",", 1, -1, 0, &num_pieces);
if(marks[0] == NULL)
{
free(marks);
marks = NULL;
}
else
{
int mark_index;
for(mark_index = 0; marks[mark_index] != NULL; mark_index++)
{
trim_flanking_whitespace(marks[mark_index]);
if(max_mask != 0xFFFFFFFF)
{
char* m = marks[mark_index];
char* mask_start;
if( (mask_start = strchr(m, '/')) != NULL )
{
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);
marks[mark_index] = dynamic_strcat(2, m, new_mask_str);
}
else
{
char new_mask_str[12];
sprintf(new_mask_str, "0x%lX", max_mask);
marks[mark_index] = dynamic_strcat(3, m, "/", new_mask_str);
}
free(m);
}
}
}
}
return marks;
}
/*
* 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;
for(list_index=0; list_str[list_index] != '\0'; list_index++)
{
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;
for(list_index=0; list_str[list_index] != '\0'; list_index++)
{
if( list_str[list_index] == quote_char && !previous_is_quoted )
{
if(next_start_index < 0)
{
next_start_index = list_index+1;
}
else
{
long length = list_index-next_start_index;
char* next_piece = (char*)malloc( (1+length)*sizeof(char) );
memcpy(next_piece, list_str+next_start_index, length);
next_piece[length] = '\0';
pieces[piece_index] = next_piece;
piece_index++;
next_start_index = -1;
}
}
previous_is_quoted = list_str[list_index] == escape_char && !previous_is_quoted ? 1 : 0;
}
if(add_remainder_if_uneven_quotes && next_start_index >= 0)
{
long length = 1+list_index-next_start_index;
char* next_piece = (char*)malloc( (1+length)*sizeof(char) );
memcpy(next_piece, list_str+next_start_index, length);
next_piece[length] = '\0';
pieces[piece_index] = next_piece;
piece_index++;
}
pieces[piece_index] = NULL;
list* quoted_list = initialize_list();
if(pieces[0] != NULL)
{
for(piece_index=0; pieces[piece_index] != NULL; piece_index++)
{
push_list(quoted_list, pieces[piece_index]);
//don't free pieces[piece], we're just putting it in list
}
}
else
{
//if no quotes at all, just return list_str
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;
}
int truncate_if_starts_with(char* test_str, char* prefix)
{
int prefix_length = strlen(prefix);
int test_length = strlen(test_str);
int matches = 0;
if(prefix_length <= test_length)
{
matches = strncmp(test_str, prefix, prefix_length) == 0 ? 1 : 0;
if(matches)
{
test_str[prefix_length] = '\0';
}
}
return matches;
}

View File

@ -1,410 +0,0 @@
/* print_quotas -- Used to print quota data from iptables rules that use the "bandwidth" module
* Originally designed for use with Gargoyle router firmware (gargoyle-router.com)
*
*
* Copyright © 2009-2010 by Eric Bishop <eric@gargoyle-router.com>
*
* This file is free software: you may copy, redistribute and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 2 of the License, or (at your
* option) any later version.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <erics_tools.h>
#include <uci.h>
#include <ipt_bwctl.h>
#define malloc safe_malloc
#define strdup safe_strdup
list* get_all_sections_of_type(struct uci_context *ctx, char* package, char* section_type);
void backup_quota(char* quota_id, char* quota_backup_dir);
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(void)
{
struct uci_context *ctx = uci_alloc_context();
list* quota_sections = get_all_sections_of_type(ctx, "firewall", "quota");
unlock_bandwidth_semaphore_on_exit();
/* for each ip have uint64_t[6], */
string_map *id_ip_to_bandwidth = initialize_string_map(1);
string_map *id_ip_to_percents = initialize_string_map(1);
string_map *id_ip_to_limits = initialize_string_map(1);
list *id_to_time = initialize_list();
while(quota_sections->length > 0)
{
char* next_quota = shift_list(quota_sections);
/* base id for quota is the ip associated with it*/
char *id = get_uci_option(ctx, "firewall", next_quota, "id");
char* ip = get_uci_option(ctx, "firewall", next_quota, "ip");
if(ip == NULL)
{
ip = strdup("ALL");
}
else if(strcmp(ip, "") == 0)
{
free(ip);
ip = strdup("ALL");
}
if(id == NULL)
{
id = strdup(ip);
}
else if(strcmp(id, "") == 0)
{
free(id);
id = strdup(ip);
}
string_map* ip_to_bandwidth = get_string_map_element(id_ip_to_bandwidth, id);
ip_to_bandwidth = ip_to_bandwidth == NULL ? initialize_string_map(1) : ip_to_bandwidth;
set_string_map_element(id_ip_to_bandwidth, id, ip_to_bandwidth);
string_map* ip_to_percents = get_string_map_element(id_ip_to_percents, id);
ip_to_percents = ip_to_percents == NULL ? initialize_string_map(1) : ip_to_percents;
set_string_map_element(id_ip_to_percents, id, ip_to_percents);
string_map* ip_to_limits = get_string_map_element(id_ip_to_limits, id);
ip_to_limits = ip_to_limits == NULL ? initialize_string_map(1) : ip_to_limits;
set_string_map_element(id_ip_to_limits, id, ip_to_limits);
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");
char* onpeak_hours = get_uci_option(ctx, "firewall", next_quota, "onpeak_hours");
char* onpeak_weekdays = get_uci_option(ctx, "firewall", next_quota, "onpeak_weekdays");
char* onpeak_weekly_ranges = get_uci_option(ctx, "firewall", next_quota, "onpeak_weekly_ranges");
if(offpeak_hours != NULL || offpeak_weekdays != NULL || offpeak_weekly_ranges != NULL || onpeak_hours != NULL || onpeak_weekdays != NULL || onpeak_weekly_ranges != NULL)
{
unsigned char is_off_peak = (offpeak_hours != NULL || offpeak_weekdays != NULL || offpeak_weekly_ranges != NULL) ? 1 : 0;
char* hours_var = is_off_peak ? offpeak_hours : onpeak_hours;
char* weekdays_var = is_off_peak ? offpeak_weekdays : onpeak_weekdays;
char* weekly_ranges_var = is_off_peak ? offpeak_weekly_ranges : onpeak_weekly_ranges;
char* active_var = is_off_peak ? strdup("except") : strdup("only");
if(weekly_ranges_var != NULL)
{
if(hours_var != NULL) { free(hours_var); hours_var=NULL; }
if(weekdays_var != NULL) { free(weekly_ranges_var); weekly_ranges_var=NULL; }
}
hours_var = hours_var == NULL ? strdup("") : hours_var;
weekdays_var = weekdays_var == NULL ? strdup("") : weekdays_var;
weekly_ranges_var = weekly_ranges_var == NULL ? strdup("") : weekly_ranges_var;
push_list(id_to_time, dynamic_strcat(11, "quotaTimes[\"", id, "\"] = [\"", hours_var, "\", \"", weekdays_var, "\", \"", weekly_ranges_var ,"\", \"", active_var, "\"];"));
free(hours_var);
free(weekdays_var);
free(weekly_ranges_var);
free(active_var);
}
else
{
push_list(id_to_time, dynamic_strcat(3, "quotaTimes[\"", id, "\"] = [\"\", \"\", \"\", \"always\"];"));
}
char* types[] = { "combined_limit", "ingress_limit", "egress_limit" };
char* postfixes[] = { "_combined", "_ingress", "_egress" };
int type_index;
for(type_index=0; type_index < 3; type_index++)
{
char* limit = get_uci_option(ctx, "firewall", next_quota, types[type_index]);
if(limit != NULL)
{
char* type_id = dynamic_strcat(2, id, postfixes[type_index]);
ip_bw* ip_buf;
unsigned long num_ips = 0;
int query_succeeded = get_all_bandwidth_usage_for_rule_id(type_id, &num_ips, &ip_buf, 5000);
if(query_succeeded && num_ips > 0)
{
unsigned long ip_index = 0;
for(ip_index = 0; ip_index < num_ips; ip_index++)
{
ip_bw next = ip_buf[ip_index];
char* next_ip = NULL;
if(next.ip == 0)
{
next_ip = strdup(ip);
}
else
{
struct in_addr addr;
addr.s_addr = next.ip;
next_ip = strdup(inet_ntoa(addr));
}
uint64_t *bw_list = get_string_map_element(ip_to_bandwidth,next_ip);
if(bw_list == NULL)
{
bw_list = (uint64_t*)malloc(sizeof(uint64_t)*6);
bw_list[0] = 0;
bw_list[1] = 0;
bw_list[2] = 0;
bw_list[3] = 0;
bw_list[4] = 0;
bw_list[5] = 0;
set_string_map_element(ip_to_bandwidth, next_ip, bw_list);
}
bw_list[type_index] = 1;
bw_list[type_index+3] = next.bw;
char bw_str[50];
sprintf(bw_str, "%lld", next.bw);
double bw_percent;
double bw_limit;
uint64_t bw_limit_64;
sscanf(bw_str, "%lf", &bw_percent);
sscanf(limit, "%lf", &bw_limit);
sscanf(limit, "%lld", &bw_limit_64);
if(bw_limit > 0)
{
bw_percent = (bw_percent*100.0)/bw_limit;
bw_percent = bw_percent > 100.0 ? 100.0 : bw_percent;
}
else
{
bw_percent = 100.0;
}
double* percent_list = get_string_map_element(ip_to_percents, next_ip);
if(percent_list == NULL)
{
percent_list = (double*)malloc(sizeof(double)*3);
percent_list[0] = -1;
percent_list[1] = -1;
percent_list[2] = -1;
set_string_map_element(ip_to_percents, next_ip, percent_list);
}
percent_list[type_index] = bw_percent;
uint64_t* limit_list = get_string_map_element(ip_to_limits, next_ip);
if(limit_list == NULL)
{
limit_list = (uint64_t*)malloc(sizeof(uint64_t)*3);
limit_list[0] = -1;
limit_list[1] = -1;
limit_list[2] = -1;
set_string_map_element(ip_to_limits, next_ip, limit_list);
}
limit_list[type_index] = bw_limit_64;
}
}
free(type_id);
free(limit);
}
}
free(id);
free(ip);
free(next_quota);
}
unsigned long num_ids;
char** id_list = (char**)get_string_map_keys(id_ip_to_bandwidth, &num_ids);
printf("var quotaIdList = [ ");
char print_comma[10] = "";
unsigned long id_index;
for(id_index=0; id_index < num_ids; id_index++)
{
printf("%s\"%s\"", print_comma, id_list[id_index]);
sprintf(print_comma, ", ");
}
printf(" ];\n");
printf("var quotaIpLists = [];\n");
for(id_index=0; id_index < num_ids; id_index++)
{
string_map* ip_to_bandwidth = get_string_map_element(id_ip_to_bandwidth, id_list[id_index]);
if(ip_to_bandwidth != NULL)
{
unsigned long num_ips = 0;
unsigned long ip_index = 0;
char** ip_list = (char**)get_string_map_keys(ip_to_bandwidth, &num_ips);
printf("quotaIpLists[\"%s\"] = [ ", id_list[id_index]);
sprintf(print_comma, "");
for(ip_index=0; ip_index < num_ips; ip_index++)
{
printf("%s\"%s\"", print_comma, ip_list[ip_index]);
sprintf(print_comma, ", ");
}
printf("];\n");
}
}
printf("var quotaTimes = new Array();\n");
printf("var quotaUsed = new Array();\n");
printf("var quotaLimits = new Array();\n");
printf("var quotaPercents = new Array();\n");
char* next_time = shift_list(id_to_time);
while(next_time != NULL)
{
printf("%s\n", next_time);
next_time = shift_list(id_to_time);
}
for(id_index=0; id_index < num_ids; id_index++)
{
char* next_id = id_list[id_index];
string_map* ip_to_bandwidth = get_string_map_element(id_ip_to_bandwidth, next_id);
string_map* ip_to_percents = get_string_map_element(id_ip_to_percents, next_id);
string_map* ip_to_limits = get_string_map_element(id_ip_to_limits, next_id);
printf("quotaUsed[ \"%s\" ] = [];\n", next_id);
printf("quotaPercents[ \"%s\" ] = [];\n", next_id);
printf("quotaLimits[ \"%s\" ] = [];\n", next_id);
if(ip_to_bandwidth != NULL)
{
unsigned long num_ips;
unsigned long ip_index = 0;
char** ip_list = (char**)get_string_map_keys(ip_to_bandwidth, &num_ips);
for(ip_index=0; ip_index < num_ips; ip_index++)
{
char* next_ip = ip_list[ip_index];
uint64_t* used = (uint64_t*)get_string_map_element(ip_to_bandwidth, next_ip);
double* percents = (double*)get_string_map_element(ip_to_percents, next_ip);
uint64_t* limits = (uint64_t*)get_string_map_element(ip_to_limits, next_ip);
if(used != NULL)
{
int type_index;
printf("quotaUsed[ \"%s\" ][ \"%s\" ] = [ ", next_id, next_ip);
sprintf(print_comma, "");
for(type_index=0; type_index < 3; type_index++)
{
if(!used[type_index])
{
printf("%s-1", print_comma);
}
else
{
printf("%s%lld", print_comma, (long long int)used[type_index+3]);
}
sprintf(print_comma, ", ");
}
printf(" ];\n");
printf("quotaPercents[ \"%s\" ][ \"%s\" ] = [ ", next_id, next_ip);
sprintf(print_comma, "");
for(type_index=0; type_index < 3; type_index++)
{
printf("%s%6.3lf", print_comma, percents[type_index]);
sprintf(print_comma, ", ");
}
printf(" ];\n");
printf("quotaLimits[ \"%s\" ][ \"%s\" ] = [ ", next_id, next_ip);
sprintf(print_comma, "");
for(type_index=0; type_index < 3; type_index++)
{
printf("%s%lld", print_comma,limits[type_index]);
sprintf(print_comma, ", ");
}
printf(" ];\n");
}
}
}
}
unsigned long num;
destroy_list(quota_sections, DESTROY_MODE_FREE_VALUES, &num);
uci_free_context(ctx);
return 0;
}
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;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,54 +0,0 @@
#
# Copyright (C) 2006 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
# $Id: Makefile 9907 2007-12-25 01:59:55Z nbd $
include $(TOPDIR)/rules.mk
PKG_NAME:=libericstools
PKG_VERSION:=1.0.0
PKG_RELEASE:=1
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
include $(INCLUDE_DIR)/package.mk
define Package/libericstools
SECTION:=libs
CATEGORY:=Gargoyle
SUBMENU:=Libraries
TITLE:=Erics Tools
URL:=http://www.gargoyle-router.com
MAINTAINER:=Eric Bishop <eric@gargoyle-router.com>
endef
define Package/libericstools/description
A bunch of routines/utilities written by Eric Bishop,
a library primarily used in Gargoyle Web Interface for OpenWrt
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)
endef
define Build/Configure
endef
define Build/InstallDev
mkdir -p $(STAGING_DIR)/usr/include/
$(CP) $(PKG_BUILD_DIR)/*.h $(STAGING_DIR)/usr/include/
mkdir -p $(STAGING_DIR)/usr/lib
$(CP) $(PKG_BUILD_DIR)/*.so* $(STAGING_DIR)/usr/lib/
endef
define Package/libericstools/install
$(INSTALL_DIR) $(1)/usr/lib
$(CP) $(PKG_BUILD_DIR)/*.so* $(1)/usr/lib/
endef
$(eval $(call BuildPackage,libericstools))

View File

@ -1,78 +0,0 @@
all: erics_tools
VERSION=1
ifeq ($(CC),)
CC=gcc
endif
ifeq ($(LD),)
LD=ld
endif
ifeq ($(AR),)
AR=ar
endif
ifeq ($(RANLIB),)
RANLIB=ranlib
endif
CFLAGS:=$(CFLAGS) -Os
WARNING_FLAGS=-Wall -Wstrict-prototypes -pedantic
MINIMAL_WARNING_FLAGS=-Wall -Wstrict-prototypes
OS=$(shell uname)
ifeq ($(OS),Darwin)
LINK=$(LD)
SHLIB_EXT=dylib
SHLIB_FLAGS=-dylib
SHLIB_FILE=libericstools.$(SHLIB_EXT).$(VERSION)
CFLAGS:=$(CFLAGS) -arch i386
else
LINK=$(CC)
SHLIB_EXT=so
SHLIB_FILE=libericstools.$(SHLIB_EXT).$(VERSION)
SHLIB_FLAGS=-shared -Wl,-soname,$(SHLIB_FILE)
endif
test_list_and_queue: test_list_and_queue.c libericstools.a
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
test_list_and_queue.o: test_list_and_queue.c
$(CC) $(CFLAGS) $(MINIMAL_WARNING_FLAGS) -o $@ -c $^
test_string: test_string.o libericstools.a
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
test_string.o: test_string.c
$(CC) $(CFLAGS) $(MINIMAL_WARNING_FLAGS) -c $^ -o $@
test_map: test_map.o libericstools.a
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
test_map.o: test_map.c
$(CC) $(CFLAGS) $(MINIMAL_WARNING_FLAGS) -c $^ -o $@
all: erics_tools
erics_tools: libericstools.$(SHLIB_EXT) libericstools.a
libericstools.a: list_static.o priority_queue_static.o tree_map_static.o string_util_static.o file_util_static.o safe_malloc_static.o
if [ -e $@ ] ; then rm $@ ; fi
$(AR) rc $@ $^
$(RANLIB) $@
libericstools.$(SHLIB_EXT) : list_dyn.o priority_queue_dyn.o tree_map_dyn.o string_util_dyn.o file_util_dyn.o safe_malloc_dyn.o
if [ -e libericstools.$(SHLIB_EXT) ] ; then rm libericstools.$(SHLIB_EXT)* ; fi
$(LINK) $(LDFLAGS) $(SHLIB_FLAGS) -o $(SHLIB_FILE) $^ -lc
ln -s $(SHLIB_FILE) libericstools.$(SHLIB_EXT)
%_dyn.o: %.c
$(CC) $(CFLAGS) -fPIC $(WARNING_FLAGS) -o $@ -c $^
%_static.o: %.c
$(CC) $(CFLAGS) $(WARNING_FLAGS) -o $@ -c $^
clean:
rm -rf *.a *.o .*sw* *~ test_map test_string test_list_and_queue
if [ "$(SHLIB_EXT)" != "" ] ; then rm -rf *.$(SHLIB_EXT)* ; fi
install:
cp *.h /usr/include
cp *.$(SHLIB_EXT)* /usr/lib

View File

@ -1,276 +0,0 @@
/*
* Copyright © 2008 by Eric Bishop <eric@gargoyle-router.com>
*
* This work 'as-is' we provide.
* No warranty, express or implied.
* We've done our best,
* to debug and test.
* Liability for damages denied.
*
* Permission is granted hereby,
* to copy, share, and modify.
* Use as is fit,
* free or for profit.
* On this notice these rights rely.
*
*
*
* Note that unlike other portions of Gargoyle this code
* does not fall under the GPL, but the rather whimsical
* 'Poetic License' above.
*
* Basically, this library contains a bunch of utilities
* that I find useful. I'm sure other libraries exist
* that are just as good or better, but I like these tools
* because I personally wrote them, so I know their quirks.
* (i.e. I know where the bodies are buried). I want to
* make sure that I can re-use these utilities for whatever
* code I may want to write in the future be it
* proprietary or open-source, so I've put them under
* a very, very permissive license.
*
* If you find this code useful, use it. If not, don't.
* I really don't care.
*
*/
#ifndef ERICS_TOOLS_H
#define ERICS_TOOLS_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include <regex.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#ifndef stricmp
#define stricmp strcasecmp
#endif
/* tree_map structs / prototypes */
typedef struct long_tree_map_node
{
unsigned long key;
void* value;
signed char balance;
struct long_tree_map_node* left;
struct long_tree_map_node* right;
} long_map_node;
typedef struct
{
long_map_node* root;
unsigned long num_elements;
} long_map;
typedef struct
{
long_map lm;
unsigned char store_keys;
unsigned long num_elements;
} string_map;
/* long map functions */
extern long_map* initialize_long_map(void);
extern void* get_long_map_element(long_map* map, unsigned long key);
void* get_smallest_long_map_element(long_map* map, unsigned long* smallest_key);
void* get_largest_long_map_element(long_map* map, unsigned long* largest_key);
void* remove_smallest_long_map_element(long_map* map, unsigned long* smallest_key);
void* remove_largest_long_map_element(long_map* map, unsigned long* largest_key);
extern void* set_long_map_element(long_map* map, unsigned long key, void* value);
extern void* remove_long_map_element(long_map* map, unsigned long key);
extern unsigned long* get_sorted_long_map_keys(long_map* map, unsigned long* num_keys_returned);
extern void** get_sorted_long_map_values(long_map* map, unsigned long* num_values_returned);
extern void** destroy_long_map(long_map* map, int destruction_type, unsigned long* num_destroyed);
extern void apply_to_every_long_map_value(long_map* map, void (*apply_func)(unsigned long key, void* value));
/* string map functions */
extern string_map* initialize_string_map(unsigned char store_keys);
extern void* get_string_map_element(string_map* map, const char* key);
extern void* set_string_map_element(string_map* map, const char* key, void* value);
extern void* remove_string_map_element(string_map* map, const char* key);
extern char** get_string_map_keys(string_map* map, unsigned long* num_keys_returned);
extern void** get_string_map_values(string_map* map, unsigned long* num_values_returned);
extern void** destroy_string_map(string_map* map, int destruction_type, unsigned long* num_destroyed);
extern void apply_to_every_string_map_value(string_map* map, void (*apply_func)(char* key, void* value));
/*
* three different ways to deal with values when data structure is destroyed
*/
#define DESTROY_MODE_RETURN_VALUES 20
#define DESTROY_MODE_FREE_VALUES 21
#define DESTROY_MODE_IGNORE_VALUES 22
/*
* for convenience & backwards compatibility alias _string_map_ functions to
* _map_ functions since string map is used more often than long map
*/
#define initialize_map initialize_string_map
#define set_map_element set_string_map_element
#define get_map_element get_string_map_element
#define remove_map_element remove_string_map_element
#define get_map_keys get_string_map_keys
#define get_map_values get_string_map_values
#define destroy_map destroy_string_map
/* list structs / prototypes */
typedef struct list_node_struct
{
struct list_node_struct* next;
struct list_node_struct* previous;
void* value;
} list_node;
typedef struct list_struct
{
long length;
list_node* head;
list_node* tail;
} list;
extern list* initialize_list(void); /* O(1) */
extern void* shift_list(list* l); /* O(1) */
extern void unshift_list(list* l, void* value); /* O(1) */
extern void* pop_list(list* l); /* O(1) */
extern void push_list(list*l, void* value); /* O(1) */
extern void** destroy_list(list* l, int destruction_type, unsigned long* num_destroyed); /* O(n) */
extern void* list_element_at(list* l, unsigned long index); /* O(n) */
extern void** get_list_values(list* l, unsigned long* num_values_returned); /* O(n) */
/* The idea behind the remove_internal_node function and
* the other functions below that perform list operations on
* list_node pointers instead of values is as follows:
*
* It is O(n) to remove arbitrary node from list. BUT, if
* we have a pointer to that node already it is O(1). So,
* provide functions to manipulate list with list_node pointers
* instead of values & a function to remove an internal node
* given a pointer to that node. This means we can have
* access to internal nodes & use another
* data structure to store internal nodes and delete in O(1)
*/
extern void remove_internal_list_node(list*l, list_node* internal); /* O(1) */
extern list_node* create_list_node(void* value); /* O(1) */
extern void* free_list_node(list_node* delete_node); /* O(1) */
extern list_node* shift_list_node(list* l); /* O(1) */
extern void unshift_list_node(list* l, list_node* new_node); /* O(1) */
extern list_node* pop_list_node(list* l); /* O(1) */
extern void push_list_node(list*l, list_node* new_node); /* O(1) */
/* priority_queue structs / prototypes */
typedef struct priority_queue_node_struct
{
unsigned long priority;
char* id;
void* value;
} priority_queue_node;
typedef struct priority_queue_struct
{
long_map* priorities;
string_map* ids;
priority_queue_node* first;
long length;
} priority_queue;
extern priority_queue* initialize_priority_queue(void);
extern priority_queue_node* create_priority_node(unsigned long priority, char* id, void* value);
extern void* free_priority_queue_node(priority_queue_node* pn);
extern void push_priority_queue(priority_queue* pq, unsigned long priority, char* id, void * value);
extern void* shift_priority_queue(priority_queue* pq, unsigned long* priority, char** id);
extern void* peek_priority_queue(priority_queue* pq, unsigned long* priority, char** id, int dynamic_alloc_id);
extern void* get_priority_queue_element_with_id(priority_queue* pq, char* id, long* priority);
extern void* remove_priority_queue_element_with_id(priority_queue* pq, char* id, long* priority);
extern void push_priority_queue_node(priority_queue* pq, priority_queue_node* pn);
extern priority_queue_node* shift_priority_queue_node(priority_queue* pq);
extern priority_queue_node* get_priority_queue_node_with_id(priority_queue* pq, char* id);
extern priority_queue_node* remove_priority_queue_node_with_id(priority_queue* pq, char* id);
extern void set_priority_for_id_in_priority_queue(priority_queue* pq, char* id, unsigned long priority);
extern priority_queue_node* peek_priority_queue_node(priority_queue* pq);
extern void** destroy_priority_queue(priority_queue* pq, int destroy_mode, unsigned long* num_destroyed);
/* string_util structs / prototypes */
typedef struct
{
char* str;
int terminator;
} dyn_read_t;
/* non-dynamic functions */
extern char* replace_prefix(char* original, char* old_prefix, char* new_prefix);
extern char* trim_flanking_whitespace(char* str);
extern int safe_strcmp(const char* str1, const char* str2);
extern void to_lowercase(char* str);
extern void to_uppercase(char* str);
/* dynamic functions (e.g. new memory is allocated, return values must be freed) */
int free_null_terminated_string_array(char** strs);
char** copy_null_terminated_string_array(char** original);
extern char* dynamic_strcat(int num_strs, ...);
extern char* dcat_and_free(char** one, char** two, int free1, int free2);
extern char** split_on_separators(char* line, char* separators, int num_separators, int max_pieces, int include_remainder_at_max, unsigned long* num_pieces); /*if max_pieces < 0, it is ignored */
extern char* join_strs(char* separator, char** parts, int max_parts, int free_parts, int free_parts_array); /*if max_parts < 0, it is ignored*/
extern char* dynamic_replace(char* template_str, char* old_str, char* new_str);
int convert_to_regex(char* str, regex_t* p);
/* functions to dynamically read files */
extern dyn_read_t dynamic_read(FILE* open_file, char* terminators, int num_terminators, unsigned long* read_length);
extern int dyn_read_line(FILE* open_file, char** dest, unsigned long* read_len);
extern unsigned char* read_entire_file(FILE* in, unsigned long read_block_size, unsigned long* read_length);
/* run a command and get (dynamically allocated) output lines */
extern char** get_shell_command_output_lines(char* command, unsigned long* num_lines);
/* comparison functions for qsort */
extern int sort_string_cmp(const void *a, const void *b);
extern int sort_string_icmp(const void *a, const void *b);
/* wrappers for qsort calls */
extern void do_str_sort(char** string_arr, unsigned long string_arr_len);
extern void do_istr_sort(char** string_arr, unsigned long string_arr_len);
/* safe malloc & strdup functions used by all others (actually aliased to malloc / strdup and used) */
extern void* safe_malloc(size_t size);
extern char* safe_strdup(const char* str);
/* utility functions to free memory */
extern void free_if_not_null(void* p);
extern void free_and_set_null(void** p);
/* other file utils */
extern int mkdir_p(const char* path, mode_t mode); /* returns 0 on success, 1 on error */
extern void rm_r(const char* path);
extern int create_tmp_dir(const char* tmp_root, char** tmp_dir); /* returns 0 on success, 1 on error */
#define PATH_DOES_NOT_EXIST 0
#define PATH_IS_REGULAR_FILE 1
#define PATH_IS_DIRECTORY 2
#define PATH_IS_SYMLINK 3
#define PATH_IS_OTHER 4
/*
returns:
PATH_DOES_NOT_EXIST (0) if path doesn't exist
PATH_IS_REGULAR_FILE (1) if path is regular file
PATH_IS_DIRECTORY (2) if path is directory
PATH_IS_SYMLINK (3) if path is symbolic link
PATH_IS_OTHER (4) if path exists and is something else
*/
extern int path_exists(const char* path);
extern char** get_file_lines(char* file_path, unsigned long* lines_read);
#endif /* ERICS_TOOLS_H */

View File

@ -1,189 +0,0 @@
/*
* Copyright © 2008 by Eric Bishop <eric@gargoyle-router.com>
*
* This work as-is we provide.
* No warranty, express or implied.
* Weve done our best,
* to debug and test.
* Liability for damages denied.
*
* Permission is granted hereby,
* to copy, share, and modify.
* Use as is fit,
* free or for profit.
* On this notice these rights rely.
*
*
*
* Note that unlike other portions of Gargoyle this code
* does not fall under the GPL, but the rather whimsical
* 'Poetic License' above.
*
* Basically, this library contains a bunch of utilities
* that I find useful. I'm sure other libraries exist
* that are just as good or better, but I like these tools
* because I personally wrote them, so I know their quirks.
* (i.e. I know where the bodies are buried). I want to
* make sure that I can re-use these utilities for whatever
* code I may want to write in the future be it
* proprietary or open-source, so I've put them under
* a very, very permissive license.
*
* If you find this code useful, use it. If not, don't.
* I really don't care.
*
*/
#include "erics_tools.h"
#define malloc safe_malloc
#define strdup safe_strdup
static int __srand_called = 0;
int create_tmp_dir(const char* tmp_root, char** tmp_dir)
{
if(! __srand_called)
{
srand(time(NULL));
__srand_called = 1;
}
sprintf((*tmp_dir), "%s/tmp_%d", tmp_root, rand());
return (mkdir_p(*tmp_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH ));
}
/*
returns:
PATH_DOES_NOT_EXIST (0) if path doesn't exist
PATH_IS_REGULAR_FILE (1) if path is regular file
PATH_IS_DIRECTORY (2) if path is directory
PATH_IS_SYMLINK (3) if path is symbolic link
PATH_IS_OTHER (4) if path exists and is something else
*/
int path_exists(const char* path)
{
struct stat fs;
int exists = lstat(path,&fs) >= 0 ? PATH_IS_OTHER : PATH_DOES_NOT_EXIST;
if(exists > 0)
{
exists = S_ISREG(fs.st_mode) ? PATH_IS_REGULAR_FILE : exists;
exists = S_ISDIR(fs.st_mode) ? PATH_IS_DIRECTORY : exists;
exists = S_ISLNK(fs.st_mode) ? PATH_IS_SYMLINK : exists;
}
return exists;
}
int mkdir_p(const char* path, mode_t mode)
{
int err=0;
struct stat fs;
char* dup_path = strdup(path);
char* sep = strchr(dup_path, '/');
sep = (sep == dup_path) ? strchr(sep+1, '/') : sep;
while(sep != NULL && err == 0)
{
sep[0] = '\0';
if(stat(dup_path,&fs) >= 0)
{
if(!S_ISDIR(fs.st_mode))
{
err = 1;
}
}
else
{
mkdir(dup_path, mode);
}
err =1;
if(stat(dup_path,&fs) >= 0)
{
err = S_ISDIR(fs.st_mode) ? 0 : 1;
}
sep[0] = '/';
sep = strchr(sep+1, '/');
}
if(err == 0)
{
if(stat(dup_path,&fs) >= 0)
{
if(!S_ISDIR(fs.st_mode))
{
err = 1;
}
}
else
{
mkdir(dup_path, mode);
}
err =1;
if(stat(dup_path,&fs) >= 0)
{
err = S_ISDIR(fs.st_mode) ? 0 : 1;
}
}
free(dup_path);
return err;
}
void rm_r(const char* path)
{
struct stat fs;
if(lstat(path,&fs) >= 0)
{
if(S_ISDIR (fs.st_mode))
{
/* remove directory recursively */
struct dirent **entries;
int num_entry_paths;
int entry_path_index;
num_entry_paths = scandir(path, &entries, 0, alphasort);
for(entry_path_index=0; entry_path_index < num_entry_paths; entry_path_index++)
{
struct dirent *dentry = entries[entry_path_index];
if(strcmp(dentry->d_name, "..") != 0 && strcmp(dentry->d_name, ".") != 0)
{
char* entry_path = (char*)malloc(strlen(path) + strlen(dentry->d_name) + 2);
sprintf(entry_path,"%s/%s", path, dentry->d_name);
/* recurse */
rm_r(entry_path);
free(entry_path);
}
}
remove(path);
}
else
{
/* remove regular file, no need to recurse */
remove(path);
}
}
}
char** get_file_lines(char* file_path, unsigned long* lines_read)
{
char** result = NULL;
int path_type = path_exists(file_path);
*lines_read = 0;
if(path_type != PATH_DOES_NOT_EXIST && path_type != PATH_IS_DIRECTORY) /* exists and is not directory */
{
FILE* read_file = fopen(file_path, "r");
unsigned char* file_data = NULL;
if(read_file != NULL)
{
unsigned long file_length;
file_data = read_entire_file(read_file, 1024, &file_length);
fclose(read_file);
}
if(file_data != NULL)
{
char line_seps[] = {'\r', '\n'};
result = split_on_separators((char*)file_data, line_seps , 2, -1, 0, lines_read);
free(file_data);
}
}
return result;
}

View File

@ -1,277 +0,0 @@
/*
* Copyright © 2008 by Eric Bishop <eric@gargoyle-router.com>
*
* This work as-is we provide.
* No warranty, express or implied.
* Weve done our best,
* to debug and test.
* Liability for damages denied.
*
* Permission is granted hereby,
* to copy, share, and modify.
* Use as is fit,
* free or for profit.
* On this notice these rights rely.
*
*
*
* Note that unlike other portions of Gargoyle this code
* does not fall under the GPL, but the rather whimsical
* 'Poetic License' above.
*
* Basically, this library contains a bunch of utilities
* that I find useful. I'm sure other libraries exist
* that are just as good or better, but I like these tools
* because I personally wrote them, so I know their quirks.
* (i.e. I know where the bodies are buried). I want to
* make sure that I can re-use these utilities for whatever
* code I may want to write in the future be it
* proprietary or open-source, so I've put them under
* a very, very permissive license.
*
* If you find this code useful, use it. If not, don't.
* I really don't care.
*
*/
#include "erics_tools.h"
#define malloc safe_malloc
#define strdup safe_strdup
list* initialize_list()
{
list* l = (list*)malloc(sizeof(list));
l->length = 0;
l->head = NULL;
l->tail = NULL;
return l;
}
list_node* create_list_node(void* value)
{
list_node* new_node = (list_node*)malloc(sizeof(list_node));
new_node->value = value;
new_node->previous = NULL;
new_node->next = NULL;
return new_node;
}
void* free_list_node(list_node* delete_node)
{
void* value = NULL;
if(delete_node != NULL)
{
value = delete_node->value;
free(delete_node);
}
return value;
}
list_node* shift_list_node(list* l)
{
list_node* return_node = NULL;
if(l != NULL)
{
if(l->head != NULL)
{
return_node = l->head;
l->head = l->head->next;
if(l->head != NULL) { l->head->previous = NULL; }
l->tail = l->tail == return_node ? NULL : l->tail;
l->length = l->length -1;
return_node->previous = NULL;
return_node->next = NULL;
}
}
return return_node;
}
void unshift_list_node(list* l, list_node* new_node)
{
if(l != NULL && new_node != NULL)
{
new_node->previous = NULL;
if(l->head == NULL) /* list is empty */
{
new_node->next = NULL;
l->tail = new_node;
}
else
{
new_node->next = l->head;
l->head->previous = new_node;
}
l->head = new_node;
l->length = l->length +1;
}
}
list_node* pop_list_node(list* l)
{
list_node* return_node = NULL;
if(l != NULL)
{
if(l->tail != NULL)
{
return_node = l->tail;
l->tail = l->tail->previous;
if(l->tail != NULL) { l->tail->next = NULL; }
l->head = l->head == return_node ? NULL : l->head;
l->length = l->length -1;
return_node->previous = NULL;
return_node->next = NULL;
}
}
return return_node;
}
void push_list_node(list* l, list_node* new_node)
{
if(l != NULL && new_node != NULL)
{
new_node->next = NULL;
if(l->tail == NULL) /* list is empty */
{
new_node->previous = NULL;
l->head = new_node;
}
else
{
new_node->previous = l->tail;
l->tail->next = new_node;
}
l->tail = new_node;
l->length = l->length +1;
}
}
void* shift_list(list* l)
{
return free_list_node ( shift_list_node(l) );
}
void unshift_list(list* l, void* value)
{
list_node* new_node = create_list_node(value);
unshift_list_node(l, new_node);
}
void* pop_list(list* l)
{
return free_list_node ( pop_list_node(l) );
}
void push_list(list*l, void* value)
{
list_node* new_node = create_list_node(value);
push_list_node(l, new_node);
}
void remove_internal_list_node(list* l, list_node* internal)
{
/* note we assume internal is in l, otherwise everything gets FUBAR! */
if(l != NULL && internal != NULL)
{
list_node* next = internal->next;
list_node* previous = internal->previous;
if(previous == NULL) /* internal is head */
{
l->head = next;
if(l->head != NULL) { l->head->previous = NULL; }
}
if(next == NULL) /* internal is tail */
{
l->tail = previous;
if(l->tail != NULL) { l->tail->next = NULL; }
}
if(previous != NULL && next != NULL)
{
previous->next = next;
next->previous = previous;
}
internal->next = NULL;
internal->previous = NULL;
l->length = l->length - 1;
}
}
void** destroy_list(list* l, int destruction_type, unsigned long* num_values)
{
void** values = NULL;
unsigned long value_index = 0;
if(l != NULL)
{
if(destruction_type == DESTROY_MODE_RETURN_VALUES)
{
values = (void**)malloc((1+l->length)*sizeof(void*));
}
for(value_index=0; l->length > 0; value_index++)
{
void* value = shift_list(l);
if(destruction_type == DESTROY_MODE_RETURN_VALUES)
{
values[value_index] = value;
}
else if(destruction_type == DESTROY_MODE_FREE_VALUES)
{
free(value);
}
}
if(destruction_type == DESTROY_MODE_RETURN_VALUES)
{
values[value_index] = NULL;
}
free(l);
}
*num_values = value_index;
return values;
}
void* list_element_at(list* l, unsigned long index)
{
void* return_value = NULL;
if(l != NULL)
{
unsigned long current_index = index - (unsigned long)((l->length)/2) > 0 ? l->length -1 : 0;
list_node* current_node = current_index == 0 ? l->head : l->tail;
while(current_index != index && current_node != NULL)
{
current_node = current_index > index ? current_node->previous : current_node->next;
current_index = current_index > index ? current_index -1 : current_index + 1;
}
if(current_node != NULL)
{
return_value = current_node->value;
}
}
return return_value;
}
void** get_list_values(list* l, unsigned long* num_values) /* returns null terminated array */
{
void** values = NULL;
unsigned long value_index = 0;
if(l != NULL)
{
list_node* current_node = l->head;
values = (void**)malloc((1+l->length)*sizeof(void*));
for(value_index = 0; value_index < l->length; value_index++)
{
values[value_index] = current_node->value;
current_node = current_node->next;
}
values[value_index] = NULL;
}
*num_values = value_index;
return values;
}

View File

@ -1,350 +0,0 @@
/*
* Copyright © 2008 by Eric Bishop <eric@gargoyle-router.com>
*
* This work as-is we provide.
* No warranty, express or implied.
* Weve done our best,
* to debug and test.
* Liability for damages denied.
*
* Permission is granted hereby,
* to copy, share, and modify.
* Use as is fit,
* free or for profit.
* On this notice these rights rely.
*
*
*
* Note that unlike other portions of Gargoyle this code
* does not fall under the GPL, but the rather whimsical
* 'Poetic License' above.
*
* Basically, this library contains a bunch of utilities
* that I find useful. I'm sure other libraries exist
* that are just as good or better, but I like these tools
* because I personally wrote them, so I know their quirks.
* (i.e. I know where the bodies are buried). I want to
* make sure that I can re-use these utilities for whatever
* code I may want to write in the future be it
* proprietary or open-source, so I've put them under
* a very, very permissive license.
*
* If you find this code useful, use it. If not, don't.
* I really don't care.
*
*/
#include "erics_tools.h"
#define malloc safe_malloc
#define strdup safe_strdup
typedef struct id_map_node_struct
{
list* id_list;
list_node* id_node;
} id_map_node;
priority_queue* initialize_priority_queue(void)
{
priority_queue* pq = (priority_queue*)malloc(sizeof(priority_queue));
pq->priorities = initialize_long_map();
pq->ids = initialize_string_map(0);
pq->first = NULL;
pq->length = 0;
return pq;
}
priority_queue_node* create_priority_node(unsigned long priority, char* id, void* value)
{
priority_queue_node* pn = (priority_queue_node*)malloc(sizeof(priority_queue_node));
pn->priority = priority;
pn->id = strdup(id);
pn->value = value;
return pn;
}
void* free_priority_queue_node(priority_queue_node* pn)
{
void *return_value = NULL;
if(pn != NULL)
{
return_value = pn->value;
free(pn->id);
free(pn);
}
return return_value;
}
void push_priority_queue(priority_queue* pq, unsigned long priority, char* id, void * value)
{
if(pq != NULL && id != NULL)
{
priority_queue_node* pn = create_priority_node(priority, id, value);
push_priority_queue_node(pq, pn);
}
}
/* note id is ALWAYS dynamically allocated, need to free */
void* shift_priority_queue(priority_queue* pq, unsigned long* priority, char** id)
{
void* return_value = NULL;
priority_queue_node* pn = shift_priority_queue_node(pq);
if(pn != NULL)
{
*priority = pn->priority;
*id = strdup(pn->id);
return_value = free_priority_queue_node(pn);
}
return return_value;
}
/* last param specified whether to dynamicall allocate id (otherwise pointer to id in priority_queue_node) */
void* peek_priority_queue(priority_queue* pq, unsigned long* priority, char** id, int dynamic_alloc_id)
{
void* return_value = NULL;
*priority = 0;
*id = NULL;
if(pq != NULL)
{
if(pq->first != NULL)
{
return_value = pq->first->value;
*priority = pq->first->priority;
if(dynamic_alloc_id)
{
*id = strdup(pq->first->id);
}
else
{
*id = pq->first->id;
}
}
}
return return_value;
}
void* get_priority_queue_element_with_id(priority_queue* pq, char* id, long* priority)
{
void* return_value = NULL;
priority_queue_node* pn = get_priority_queue_node_with_id(pq, id);
if(pn != NULL)
{
*priority = pn->priority;
return_value = free_priority_queue_node(pn);
}
else
{
*priority = 0;
}
return return_value;
}
void* remove_priority_queue_element_with_id(priority_queue* pq, char* id, long* priority)
{
void* return_value = NULL;
priority_queue_node* pn = remove_priority_queue_node_with_id(pq, id);
if(pn != NULL)
{
*priority = pn->priority;
return_value = free_priority_queue_node(pn);
}
else
{
*priority = 0;
}
return return_value;
}
void push_priority_queue_node(priority_queue* pq, priority_queue_node* pn)
{
if(pq != NULL && pn != NULL)
{
/* assume that most of the time we won't have collisions */
list_node* lpn = create_list_node(pn);
list* new_list = initialize_list();
list* old_list;
id_map_node* idn;
push_list_node(new_list, lpn);
old_list = (list*)set_long_map_element(pq->priorities, pn->priority, new_list);
if(old_list != NULL)
{
push_list_node(old_list, lpn);
set_long_map_element(pq->priorities, pn->priority, old_list);
free(new_list);
new_list = old_list;
}
/* update first */
if(pq->first == NULL)
{
pq->first = pn;
}
else if(pn->priority < pq->first->priority)
{
pq->first = pn;
}
/* save id */
idn = (id_map_node*)malloc(sizeof(id_map_node));
idn->id_list = new_list;
idn->id_node = lpn;
set_string_map_element(pq->ids, pn->id, idn);
pq->length = pq->length + 1;;
}
}
priority_queue_node* shift_priority_queue_node(priority_queue* pq)
{
priority_queue_node* return_node = NULL;
if(pq != NULL)
{
if(pq->first != NULL)
{
list* next_list = (list*)remove_long_map_element(pq->priorities, pq->first->priority);
list* next_first_list = NULL;
list_node* smallest_list_node = shift_list_node(next_list);
id_map_node* idn;
if(next_list->length == 0)
{
unsigned long tmp;
destroy_list(next_list, DESTROY_MODE_IGNORE_VALUES, &tmp);
next_first_list = (list*)get_smallest_long_map_element(pq->priorities, &tmp);
}
else
{
set_long_map_element(pq->priorities, pq->first->priority, next_list);
next_first_list = next_list;
}
return_node = free_list_node(smallest_list_node);
idn = (id_map_node*)remove_string_map_element(pq->ids, return_node->id);
free(idn);
if(next_first_list != NULL)
{
list_node* next_first_node = shift_list_node(next_first_list);
pq->first = (priority_queue_node*)next_first_node->value;
unshift_list_node(next_first_list, next_first_node);
}
else
{
pq->first = NULL;
}
pq->length = pq->length -1;
}
}
return return_node;
}
priority_queue_node* get_priority_queue_node_with_id(priority_queue* pq, char* id)
{
priority_queue_node* return_node = NULL;
if(pq != NULL && id != NULL)
{
id_map_node* idn = (id_map_node*)get_string_map_element(pq->ids, id);
if(idn != NULL)
{
return_node = (priority_queue_node*)idn->id_node->value;
}
}
return return_node;
}
priority_queue_node* remove_priority_queue_node_with_id(priority_queue* pq, char* id)
{
priority_queue_node* return_node = NULL;
if(pq != NULL && id != NULL)
{
id_map_node* idn = (id_map_node*)remove_string_map_element(pq->ids, id);
if(idn != NULL)
{
/* remove relevant node from list */
remove_internal_list_node(idn->id_list, idn->id_node);
return_node = free_list_node(idn->id_node);
/* if list is empty remove it from priority map */
if(idn->id_list->length == 0)
{
unsigned long tmp;
remove_long_map_element(pq->priorities, return_node->priority);
destroy_list(idn->id_list, DESTROY_MODE_IGNORE_VALUES, &tmp);
}
free(idn);
/* if we're removing first node, reset it */
if(return_node == pq->first)
{
unsigned long tmp;
list* next_first_list = (list*)get_smallest_long_map_element(pq->priorities, &tmp);
if(next_first_list != NULL)
{
list_node* next_first_node = shift_list_node(next_first_list);
pq->first = (priority_queue_node*)next_first_node->value;
unshift_list_node(next_first_list, next_first_node);
}
else
{
pq->first = NULL;
}
}
pq->length = pq->length -1;
}
}
return return_node;
}
void set_priority_for_id_in_priority_queue(priority_queue* pq, char* id, unsigned long priority)
{
if(pq != NULL && id != NULL)
{
priority_queue_node* id_pq_node = remove_priority_queue_node_with_id(pq, id);
id_pq_node->priority = priority;
push_priority_queue_node(pq, id_pq_node);
}
}
priority_queue_node* peek_priority_queue_node(priority_queue* pq)
{
return pq->first;
}
void** destroy_priority_queue(priority_queue* pq, int destroy_mode, unsigned long* num_elements_destroyed)
{
void** values = NULL;
unsigned long tmp;
*num_elements_destroyed = 0;
if(pq != NULL)
{
if(destroy_mode == DESTROY_MODE_RETURN_VALUES)
{
values = (void**)malloc((pq->length+1)*sizeof(void*));
}
while(pq->length > 0)
{
priority_queue_node* pqn = shift_priority_queue_node(pq);
void* next_value = free_priority_queue_node(pqn);
if(destroy_mode == DESTROY_MODE_RETURN_VALUES)
{
values[*num_elements_destroyed] = next_value;
}
else if(destroy_mode == DESTROY_MODE_FREE_VALUES)
{
free(next_value);
}
*num_elements_destroyed = *num_elements_destroyed + 1;
}
if(destroy_mode == DESTROY_MODE_RETURN_VALUES)
{
values[*num_elements_destroyed] = NULL;
}
destroy_long_map(pq->priorities, DESTROY_MODE_FREE_VALUES, &tmp);
destroy_string_map(pq->ids, DESTROY_MODE_FREE_VALUES, &tmp);
free(pq);
}
return values;
}

View File

@ -1,80 +0,0 @@
/*
* Copyright © 2008 by Eric Bishop <eric@gargoyle-router.com>
*
* This work as-is we provide.
* No warranty, express or implied.
* Weve done our best,
* to debug and test.
* Liability for damages denied.
*
* Permission is granted hereby,
* to copy, share, and modify.
* Use as is fit,
* free or for profit.
* On this notice these rights rely.
*
*
*
* Note that unlike other portions of Gargoyle this code
* does not fall under the GPL, but the rather whimsical
* 'Poetic License' above.
*
* Basically, this library contains a bunch of utilities
* that I find useful. I'm sure other libraries exist
* that are just as good or better, but I like these tools
* because I personally wrote them, so I know their quirks.
* (i.e. I know where the bodies are buried). I want to
* make sure that I can re-use these utilities for whatever
* code I may want to write in the future be it
* proprietary or open-source, so I've put them under
* a very, very permissive license.
*
* If you find this code useful, use it. If not, don't.
* I really don't care.
*
*/
#include "erics_tools.h"
void *safe_malloc(size_t size)
{
void* val = malloc(size);
if(val == NULL)
{
fprintf(stderr, "ERROR: MALLOC FAILURE!\n");
exit(1);
}
return val;
}
char* safe_strdup(const char* str)
{
char* new_str = NULL;
if(str != NULL)
{
new_str = strdup(str);
if(new_str == NULL)
{
fprintf(stderr, "ERROR: MALLOC FAILURE!\n");
exit(1);
}
}
return new_str;
}
void free_if_not_null(void* p)
{
if(p != NULL)
{
free(p);
}
}
void free_and_set_null(void** p)
{
if(*p != NULL)
{
free(*p);
*p = NULL;
}
}

View File

@ -1,669 +0,0 @@
/*
* Copyright © 2008 by Eric Bishop <eric@gargoyle-router.com>
*
* This work as-is we provide.
* No warranty, express or implied.
* Weve done our best,
* to debug and test.
* Liability for damages denied.
*
* Permission is granted hereby,
* to copy, share, and modify.
* Use as is fit,
* free or for profit.
* On this notice these rights rely.
*
*
*
* Note that unlike other portions of Gargoyle this code
* does not fall under the GPL, but the rather whimsical
* 'Poetic License' above.
*
* Basically, this library contains a bunch of utilities
* that I find useful. I'm sure other libraries exist
* that are just as good or better, but I like these tools
* because I personally wrote them, so I know their quirks.
* (i.e. I know where the bodies are buried). I want to
* make sure that I can re-use these utilities for whatever
* code I may want to write in the future be it
* proprietary or open-source, so I've put them under
* a very, very permissive license.
*
* If you find this code useful, use it. If not, don't.
* I really don't care.
*
*/
#include "erics_tools.h"
#define malloc safe_malloc
#define strdup safe_strdup
char* replace_prefix(char* original, char* old_prefix, char* new_prefix)
{
char* replaced = NULL;
if(original != NULL && old_prefix != NULL && new_prefix != NULL && strstr(original, old_prefix) == original)
{
int old_prefix_length = strlen(old_prefix);
int new_prefix_length = strlen(new_prefix);
int remainder_length = strlen(original) - old_prefix_length;
int new_length = new_prefix_length + remainder_length;
/* printf("%d %d %d %d\n", old_prefix_length, new_prefix_length, remainder_length, new_length); */
replaced = malloc(new_length+1);
memcpy(replaced, new_prefix, new_prefix_length);
memcpy(replaced+new_prefix_length, original+old_prefix_length, remainder_length);
replaced[new_prefix_length+remainder_length] = '\0';
}
return replaced;
}
char* trim_flanking_whitespace(char* str)
{
int new_start = 0;
int new_length = 0;
char whitespace[5] = { ' ', '\t', '\n', '\r', '\0' };
int num_whitespace_chars = 4;
int index = 0;
int is_whitespace = 1;
int test;
while( (test = str[index]) != '\0' && is_whitespace == 1)
{
int whitespace_index;
is_whitespace = 0;
for(whitespace_index = 0; whitespace_index < num_whitespace_chars && is_whitespace == 0; whitespace_index++)
{
is_whitespace = test == whitespace[whitespace_index] ? 1 : 0;
}
index = is_whitespace == 1 ? index+1 : index;
}
new_start = index;
index = strlen(str) - 1;
is_whitespace = 1;
while( index >= new_start && is_whitespace == 1)
{
int whitespace_index;
is_whitespace = 0;
for(whitespace_index = 0; whitespace_index < num_whitespace_chars && is_whitespace == 0; whitespace_index++)
{
is_whitespace = str[index] == whitespace[whitespace_index] ? 1 : 0;
}
index = is_whitespace == 1 ? index-1 : index;
}
new_length = str[new_start] == '\0' ? 0 : index + 1 - new_start;
if(new_start > 0)
{
for(index = 0; index < new_length; index++)
{
str[index] = str[index+new_start];
}
}
str[new_length] = 0;
return str;
}
int safe_strcmp(const char* str1, const char* str2)
{
if(str1 == NULL && str2 == NULL)
{
return 0;
}
else if(str1 == NULL && str2 != NULL)
{
return 1;
}
else if(str1 != NULL && str2 == NULL)
{
return -1;
}
return strcmp(str1, str2);
}
void to_lowercase(char* str)
{
int i;
for(i = 0; str[i] != '\0'; i++)
{
str[i] = tolower(str[i]);
}
}
void to_uppercase(char* str)
{
int i;
for(i = 0; str[i] != '\0'; i++)
{
str[i] = toupper(str[i]);
}
}
/* returns number freed */
int free_null_terminated_string_array(char** strs)
{
unsigned long str_index = 0;
if(strs != NULL)
{
for(str_index=0; strs[str_index] != NULL; str_index++)
{
free(strs[str_index]);
}
free(strs);
}
return str_index;
}
char** copy_null_terminated_string_array(char** original)
{
unsigned long size;
char** new;
for(size=0; original[size] != NULL ; size++) ;
new = (char**)malloc( (size+1)*sizeof(char*));
for(size=0; original[size] != NULL; size++)
{
new[size] = strdup(original[size]);
}
new[size] = NULL;
return new;
}
char* dynamic_strcat(int num_strs, ...)
{
va_list strs;
int new_length = 0;
int i;
int next_start;
char* new_str;
va_start(strs, num_strs);
for(i=0; i < num_strs; i++)
{
char* next_arg = va_arg(strs, char*);
if(next_arg != NULL)
{
new_length = new_length + strlen(next_arg);
}
}
va_end(strs);
new_str = malloc((1+new_length)*sizeof(char));
va_start(strs, num_strs);
next_start = 0;
for(i=0; i < num_strs; i++)
{
char* next_arg = va_arg(strs, char*);
if(next_arg != NULL)
{
int next_length = strlen(next_arg);
memcpy(new_str+next_start,next_arg, next_length);
next_start = next_start+next_length;
}
}
new_str[next_start] = '\0';
return new_str;
}
char* dcat_and_free(char** one, char** two, int free1, int free2)
{
char* s = NULL;
if(one != NULL && two != NULL) { s = dynamic_strcat(2, *one, *two); }
else if(one != NULL) { s = strdup(*one); }
else if(two != NULL) { s = strdup(*two); }
else { s= strdup(""); }
if(free1){ free(*one); *one=s; }
if(free2){ free(*two); *two=s; }
return s;
}
/*
* line is the line to be parsed -- it is not modified in any way
* max_pieces indicates number of pieces to return, if negative this is determined dynamically
* include_remainder_at_max indicates whether the last piece, when max pieces are reached,
* should be what it would normally be (0) or the entire remainder of the line (1)
* if max_pieces < 0 this parameter is ignored
*
*
* returns all non-separator pieces in a line
* result is dynamically allocated, MUST be freed after call-- even if
* line is empty (you still get a valid char** pointer to to a NULL char*)
*/
char** split_on_separators(char* line, char* separators, int num_separators, int max_pieces, int include_remainder_at_max, unsigned long *num_pieces)
{
char** split;
*num_pieces = 0;
if(line != NULL)
{
int split_index;
int non_separator_found;
char* dup_line;
char* start;
if(max_pieces < 0)
{
/* count number of separator characters in line -- this count + 1 is an upperbound on number of pieces */
int separator_count = 0;
int line_index;
for(line_index = 0; line[line_index] != '\0'; line_index++)
{
int sep_index;
int found = 0;
for(sep_index =0; found == 0 && sep_index < num_separators; sep_index++)
{
found = separators[sep_index] == line[line_index] ? 1 : 0;
}
separator_count = separator_count+ found;
}
max_pieces = separator_count + 1;
}
split = (char**)malloc((1+max_pieces)*sizeof(char*));
split_index = 0;
split[split_index] = NULL;
dup_line = strdup(line);
start = dup_line;
non_separator_found = 0;
while(non_separator_found == 0)
{
int matches = 0;
int sep_index;
for(sep_index =0; sep_index < num_separators; sep_index++)
{
matches = matches == 1 || separators[sep_index] == start[0] ? 1 : 0;
}
non_separator_found = matches==0 || start[0] == '\0' ? 1 : 0;
if(non_separator_found == 0)
{
start++;
}
}
while(start[0] != '\0' && split_index < max_pieces)
{
/* find first separator index */
int first_separator_index = 0;
int separator_found = 0;
while( separator_found == 0 )
{
int sep_index;
for(sep_index =0; separator_found == 0 && sep_index < num_separators; sep_index++)
{
separator_found = separators[sep_index] == start[first_separator_index] || start[first_separator_index] == '\0' ? 1 : 0;
}
if(separator_found == 0)
{
first_separator_index++;
}
}
/* copy next piece to split array */
if(first_separator_index > 0)
{
char* next_piece = NULL;
if(split_index +1 < max_pieces || include_remainder_at_max <= 0)
{
next_piece = (char*)malloc((first_separator_index+1)*sizeof(char));
memcpy(next_piece, start, first_separator_index);
next_piece[first_separator_index] = '\0';
}
else
{
next_piece = strdup(start);
}
split[split_index] = next_piece;
split[split_index+1] = NULL;
split_index++;
}
/* find next non-separator index, indicating start of next piece */
start = start+ first_separator_index;
non_separator_found = 0;
while(non_separator_found == 0)
{
int matches = 0;
int sep_index;
for(sep_index =0; sep_index < num_separators; sep_index++)
{
matches = matches == 1 || separators[sep_index] == start[0] ? 1 : 0;
}
non_separator_found = matches==0 || start[0] == '\0' ? 1 : 0;
if(non_separator_found == 0)
{
start++;
}
}
}
free(dup_line);
*num_pieces = split_index;
}
else
{
split = (char**)malloc((1)*sizeof(char*));
split[0] = NULL;
}
return split;
}
char* join_strs(char* separator, char** parts, int max_parts, int free_parts, int free_parts_array)
{
char* joined = NULL;
int num_parts = 0;
for(num_parts=0; parts[num_parts] != NULL && (max_parts < 0 || num_parts < max_parts); num_parts++){}
if(num_parts > 0)
{
num_parts--;
joined = strdup(parts[num_parts]);
if(free_parts){ free(parts[num_parts]); }
num_parts--;
while(num_parts >= 0)
{
char* tmp = joined;
joined = dynamic_strcat(3, parts[num_parts], separator, joined);
free(tmp);
if(free_parts){ free(parts[num_parts]); }
num_parts--;
}
}
if(free_parts_array)
{
free(parts);
}
return joined;
}
char* dynamic_replace(char* template_str, char* old, char* new)
{
char *ret;
int i, count = 0;
int newlen = strlen(new);
int oldlen = strlen(old);
char* dyn_template = strdup(template_str);
char* s = dyn_template;
for (i = 0; s[i] != '\0'; i++)
{
if (strstr(&s[i], old) == &s[i])
{
count++;
i += oldlen - 1;
}
}
ret = malloc(i + 1 + count * (newlen - oldlen));
i = 0;
while (*s)
{
if (strstr(s, old) == s)
{
strcpy(&ret[i], new);
i += newlen;
s += oldlen;
}
else
{
ret[i++] = *s++;
}
}
ret[i] = '\0';
free(dyn_template);
return ret;
}
/*
requires expression to be surrounded by '/' characters, and deals with escape
characters '\/', '\r', '\n', and '\t' when escapes haven't been interpreted
(e.g. after recieving regex string from user)
returns 1 on good regex, 0 on bad regex
*/
int convert_to_regex(char* str, regex_t* p)
{
char* trimmed = trim_flanking_whitespace(strdup(str));
int trimmed_length = strlen(trimmed);
char* new = NULL;
int valid = 1;
/* regex must be defined by surrounding '/' characters */
if(trimmed[0] != '/' || trimmed[trimmed_length-1] != '/')
{
valid = 0;
free(trimmed);
}
if(valid == 1)
{
char* internal = (char*)malloc(trimmed_length*sizeof(char));
int internal_length = trimmed_length-2;
int new_index = 0;
int internal_index = 0;
char previous = '\0';
memcpy(internal, trimmed+1, internal_length);
internal[internal_length] = '\0';
free(trimmed);
new = (char*)malloc(trimmed_length*sizeof(char));
while(internal[internal_index] != '\0' && valid == 1)
{
char next = internal[internal_index];
if(next == '/' && previous != '\\')
{
valid = 0;
}
else if((next == 'n' || next == 'r' || next == 't' || next == '/') && previous == '\\')
{
char previous2 = '\0';
if(internal_index >= 2)
{
previous2 = internal[internal_index-2];
}
new_index = previous2 == '\\' ? new_index : new_index-1;
switch(next)
{
case 'n':
new[new_index] = previous2 == '\\' ? next : '\n';
break;
case 'r':
new[new_index] = previous2 == '\\' ? next : '\r';
break;
case 't':
new[new_index] = previous2 == '\\' ? next : '\t';
break;
case '/':
new[new_index] = previous2 == '\\' ? next : '/';
break;
}
previous = '\0';
internal_index++;
new_index++;
}
else
{
new[new_index] = next;
previous = next;
internal_index++;
new_index++;
}
}
if(valid == 0 || previous == '\\')
{
valid = 0;
free(new);
new = NULL;
}
else
{
new[new_index] = '\0';
}
free(internal);
}
if(valid == 1)
{
valid = regcomp(p,new,REG_EXTENDED) == 0 ? 1 : 0;
if(valid == 0)
{
regfree(p);
}
free(new);
}
return valid;
}
/* note: str element in return value is dynamically allocated, need to free */
dyn_read_t dynamic_read(FILE* open_file, char* terminators, int num_terminators, unsigned long* read_length)
{
fpos_t start_pos;
unsigned long size_to_read = 0;
int terminator_found = 0;
int terminator;
char* str;
dyn_read_t ret_value;
fgetpos(open_file, &start_pos);
while(terminator_found == 0)
{
int nextch = fgetc(open_file);
int terminator_index = 0;
for(terminator_index = 0; terminator_index < num_terminators && terminator_found == 0; terminator_index++)
{
terminator_found = nextch == terminators[terminator_index] ? 1 : 0;
terminator = nextch;
}
terminator_found = nextch == EOF ? 1 : terminator_found;
terminator = nextch == EOF ? EOF : nextch;
if(terminator_found == 0)
{
size_to_read++;
}
}
str = (char*)malloc((size_to_read+1)*sizeof(char));
if(size_to_read > 0)
{
int i;
fsetpos(open_file, &start_pos);
for(i=0; i<size_to_read; i++)
{
str[i] = (char)fgetc(open_file);
}
fgetc(open_file); /* read the terminator */
}
str[size_to_read] = '\0';
*read_length = size_to_read;
ret_value.str = str;
ret_value.terminator = terminator;
return ret_value;
}
/* convenience method for calling dynamic_read with end-of-line separators */
int dyn_read_line(FILE* open_file, char** dest, unsigned long* read_len)
{
char line_seps[] = "\r\n";
dyn_read_t read = dynamic_read(open_file, line_seps, 2, read_len);
*dest = read.str;
return read.terminator;
}
unsigned char* read_entire_file(FILE* in, unsigned long read_block_size, unsigned long *length)
{
int max_read_size = read_block_size;
unsigned char* read_string = (unsigned char*)malloc(max_read_size+1);
unsigned long bytes_read = 0;
int end_found = 0;
while(end_found == 0)
{
int nextch = '?';
while(nextch != EOF && bytes_read < max_read_size)
{
nextch = fgetc(in);
if(nextch != EOF)
{
read_string[bytes_read] = (unsigned char)nextch;
bytes_read++;
}
}
read_string[bytes_read] = '\0';
end_found = (nextch == EOF) ? 1 : 0;
if(end_found == 0)
{
unsigned char *new_str;
max_read_size = max_read_size + read_block_size;
new_str = (unsigned char*)malloc(max_read_size+1);
memcpy(new_str, read_string, bytes_read);
free(read_string);
read_string = new_str;
}
}
*length = bytes_read;
return read_string;
}
char** get_shell_command_output_lines(char* command, unsigned long* num_lines)
{
char** ret = NULL;
if(command != NULL && num_lines != NULL)
{
FILE* shell_out = popen(command, "r");
*num_lines = 0;
if(shell_out != NULL)
{
char linebreaks[] = { '\n', '\r' };
unsigned long read_length;
char* all_data = (char*)read_entire_file(shell_out, 2048, &read_length);
ret = split_on_separators(all_data, linebreaks, 2, -1, 0, num_lines);
free(all_data);
pclose(shell_out);
}
}
return ret;
}
/* comparison functions for qsort */
int sort_string_cmp(const void *a, const void *b)
{
const char **a_ptr = (const char **)a;
const char **b_ptr = (const char **)b;
return strcmp(*a_ptr, *b_ptr);
}
int sort_string_icmp(const void *a, const void *b)
{
const char **a_ptr = (const char **)a;
const char **b_ptr = (const char **)b;
return stricmp(*a_ptr, *b_ptr);
}
/* wrappers for qsort calls */
void do_str_sort(char** string_arr, unsigned long string_arr_len)
{
qsort(string_arr, string_arr_len, sizeof(char*), sort_string_cmp);
}
void do_istr_sort(char** string_arr, unsigned long string_arr_len)
{
qsort(string_arr, string_arr_len, sizeof(char*), sort_string_icmp);
}

View File

@ -1,85 +0,0 @@
#include "erics_tools.h"
int main(void)
{
list* l = initialize_list();
list_node* c_node = create_list_node("c");
list_node* a_node = create_list_node("a");
list_node* z_node = create_list_node("z");
list_node* x_node = create_list_node("x");
push_list_node(l, a_node);
remove_internal_list_node(l, a_node);
push_list_node(l, a_node);
push_list(l, "b");
push_list_node(l, c_node);
unshift_list_node(l, z_node);
unshift_list(l, "y");
unshift_list_node(l, x_node);
remove_internal_list_node(l, z_node);
remove_internal_list_node(l, a_node);
remove_internal_list_node(l, x_node);
free_list_node(z_node);
free_list_node(a_node);
free_list_node(x_node);
while(l->length > 0)
{
printf("%s\n", (char*)pop_list(l));
}
unsigned long dl;
destroy_list(l, DESTROY_MODE_IGNORE_VALUES, &dl);
printf("-------queue test-------------\n");
unsigned long priority;
char* id;
priority_queue* pq = initialize_priority_queue();
push_priority_queue(pq, 30, "id_1", "value_1");
push_priority_queue(pq, 10, "id_2", "value_2");
push_priority_queue(pq, 10, "id_3", "value_3");
push_priority_queue(pq, 40, "id_4", "value_4");
push_priority_queue(pq, 5, "id_5", "value_5");
push_priority_queue(pq, 30, "id_6", "value_6");
push_priority_queue(pq, 30, "id_7", "value_7");
printf("queue length = %ld\n", pq->length);
unsigned long num_destroyed;
char* tmp = peek_priority_queue(pq, &priority, &id, 0);
printf("first is \"%s\"\n", tmp);
set_priority_for_id_in_priority_queue(pq, "id_5", 35);
tmp = peek_priority_queue(pq, &priority, &id, 0);
printf("first is \"%s\"\n", tmp);
set_priority_for_id_in_priority_queue(pq, "id_2", 36);
tmp = peek_priority_queue(pq, &priority, &id, 0);
printf("first is \"%s\"\n", tmp);
/*
char** values = (char**)destroy_priority_queue(pq, DESTROY_MODE_RETURN_VALUES, &num_destroyed);
int index = 0;
for(index = 0; values[index] != NULL; index++)
{
printf("%s\n", values[index]);
}
*/
while(pq->length > 0)
{
char* value = (char*)shift_priority_queue(pq, &priority, &id);
printf("%s\n", value);
free(id);
}
destroy_priority_queue(pq, DESTROY_MODE_FREE_VALUES, &dl);
return 0;
}

View File

@ -1,322 +0,0 @@
/*
* Copyright © 2008 by Eric Bishop <eric@gargoyle-router.com>
*
* This work 'as-is' we provide.
* No warranty, express or implied.
* We've done our best,
* to debug and test.
* Liability for damages denied.
*
* Permission is granted hereby,
* to copy, share, and modify.
* Use as is fit,
* free or for profit.
* On this notice these rights rely.
*
*
*
* Note that unlike other portions of Gargoyle this code
* does not fall under the GPL, but the rather whimsical
* 'Poetic License' above.
*
* Basically, this library contains a bunch of utilities
* that I find useful. I'm sure other libraries exist
* that are just as good or better, but I like these tools
* because I personally wrote them, so I know their quirks.
* (i.e. I know where the bodies are buried). I want to
* make sure that I can re-use these utilities for whatever
* code I may want to write in the future be it
* proprietary or open-source, so I've put them under
* a very, very permissive license.
*
* If you find this code useful, use it. If not, don't.
* I really don't care.
*
*/
#include "erics_tools.h"
#include <time.h>
void get_max_depth(long_map_node* n, unsigned long* max_depth, unsigned long current_depth);
void get_min_depth(long_map_node* n, unsigned long* min_depth, unsigned long current_depth);
void print_map(long_map_node* n, int depth);
int main (void)
{
unsigned long num_insertions = 2500;
unsigned long max_insertion = 5000;
int num_repeats = 3;
unsigned int seed = (unsigned int)time(NULL);
srand (seed);
printf("TESTING LONG MAP....\n\n");
printf("initializing map....\n");
long_map* lm = initialize_long_map();
printf("initialized!\n\n");
int repeat = 0;
for(repeat =0; repeat < num_repeats; repeat++)
{
printf("randomly inserting %ld integers randomly selected (not in order) between 0 and %ld\n", num_insertions, max_insertion);
unsigned long i;
unsigned long dupes = 0;
for(i=0; i<num_insertions; i++)
{
unsigned long r = (unsigned long)(max_insertion*((double)rand()/(double)RAND_MAX));
void* old;
if( (old = set_long_map_element(lm, r, "a")) != NULL)
{
dupes++;
}
}
unsigned long right_depth = 0;
unsigned long left_depth = 0;
get_max_depth(lm->root->left, &left_depth, 0);
get_max_depth(lm->root->right, &right_depth, 0);
unsigned long original_size = lm->num_elements;
printf("insertion complete\n");
printf("after insertion, tree size = %ld \n", original_size);
printf("(note this may be less than %ld because the same numbers can be selected for insertion more than once, replacing the original node)\n", num_insertions);
printf("dupes = %ld\n", dupes);
printf("depth of left branch = %ld, depth of right branch = %ld\n\n", left_depth, right_depth);
if(repeat+1 < num_repeats)
{
printf("randomly selecting %ld numbers between 0 and %ld to remove from tree.\n", num_insertions, max_insertion);
printf("note that these keys will not necessarily be present in tree -- the selection process is entirely random.\n");
int j;
int found = 0;
for(j=0; j < num_insertions; j++)
{
unsigned long r = (unsigned long)(max_insertion*((double)rand()/(double)RAND_MAX));
if( remove_long_map_element(lm, r) != NULL)
{
found++;
}
}
right_depth = 0;
left_depth = 0;
get_max_depth(lm->root->left, &left_depth, 0);
get_max_depth(lm->root->right, &right_depth, 0);
printf("removal complete\n");
printf("after removal, tree size = %ld \n", lm->num_elements);
printf("depth of left branch = %ld, depth of right branch = %ld\n", left_depth, right_depth);
if(original_size - found == lm->num_elements)
{
printf("size consistent with number of nodes successfully removed\n\n");
}
else
{
printf("SIZE IS BAD -- IS NOT CONSISTENT WITH NUMBER OF NODES REMOVED !!!!\n\n");
}
printf("removing remaining nodes in tree in random order\n");
unsigned long length;
unsigned long *keys = get_sorted_long_map_keys(lm, &length);
while(lm->root != NULL && lm->num_elements > 0)
{
unsigned long r = (unsigned long)(length*((double)rand()/(double)RAND_MAX));
if( remove_long_map_element(lm, keys[r]) != NULL)
{
found++;
if(original_size - found != lm->num_elements)
{
printf("SIZE IS BAD!!!!\n");
}
}
}
printf("done removing remaining nodes\n");
printf("tree size is now %ld, and root is %s\n\n", lm->num_elements, lm->root == NULL ? "null" : "not null");
free(keys);
printf("repeating insertion/deletion\n\n");
}
else
{
unsigned long num_destroyed;
printf("destroying map...\n");
void** values = destroy_long_map(lm, DESTROY_MODE_RETURN_VALUES, &num_destroyed);
printf("map destroyed.\n");
int v=0;
for(v=0; values[v] != NULL; v++){}
free(values);
printf("number of values returned after map destruction = %d\n", v);
}
}
printf("LONG MAP TESTING COMPLETE. TESTING STRING MAP (WITH KEY STORAGE) \n\n");
printf("initializing map....\n");
string_map* sm = initialize_string_map(1);
printf("initialized!\n\n");
repeat = 0;
for(repeat =0; repeat < num_repeats; repeat++)
{
printf("randomly inserting %ld integer strings randomly selected (not in order) between 0 and %ld\n", num_insertions, max_insertion);
unsigned long i;
unsigned long dupes = 0;
for(i=0; i<num_insertions; i++)
{
unsigned long r = (unsigned long)(max_insertion*((double)rand()/(double)RAND_MAX));
char* new_str = (char*)malloc(40*sizeof(char));
sprintf(new_str, "%ld", r);
void* old;
if( (old = set_string_map_element(sm, new_str, "a")) != NULL )
{
dupes++;
}
free(new_str);
}
unsigned long right_depth = 0;
unsigned long left_depth = 0;
get_max_depth(sm->lm.root->left, &left_depth, 0);
get_max_depth(sm->lm.root->right, &right_depth, 0);
unsigned long original_size = sm->num_elements;
printf("insertion complete\n");
printf("after insertion, tree size = %ld \n", original_size);
printf("(note this may be less than %ld because the same numbers can be selected for insertion more than once, replacing the original node)\n", num_insertions);
printf("dupes = %ld\n", dupes);
printf("depth of left branch = %ld, depth of right branch = %ld\n\n", left_depth, right_depth);
if(repeat+1 < num_repeats)
{
printf("randomly selecting %ld numbers between 0 and %ld to remove from tree.\n", num_insertions, max_insertion);
printf("note that these keys will not necessarily be present in tree -- the selection process is entirely random.\n");
int j;
int found = 0;
for(j=0; j < num_insertions; j++)
{
unsigned long r = (unsigned long)(max_insertion*((double)rand()/(double)RAND_MAX));
char* new_str = (char*)malloc(40*sizeof(char));
sprintf(new_str, "%ld", r);
void* old;
if( (old = remove_string_map_element(sm, new_str)) != NULL)
{
found++;
}
free(new_str);
}
right_depth = 0;
left_depth = 0;
get_max_depth(sm->lm.root->left, &left_depth, 0);
get_max_depth(sm->lm.root->right, &right_depth, 0);
printf("removal complete\n");
printf("after removal, tree size = %ld \n", sm->num_elements);
printf("depth of left branch = %ld, depth of right branch = %ld\n", left_depth, right_depth);
if(original_size - found == sm->num_elements)
{
printf("size consistent with number of nodes successfully removed\n\n");
}
else
{
printf("SIZE IS BAD -- IS NOT CONSISTENT WITH NUMBER OF NODES REMOVED !!!!\n\n");
}
printf("removing remaining nodes in tree in random order\n");
unsigned long length = sm->num_elements;
char** keys = get_string_map_keys(sm, &length);
while(sm->lm.root != NULL && sm->num_elements > 0)
{
unsigned long r = (unsigned long)(length*((double)rand()/(double)RAND_MAX));
if( remove_string_map_element(sm, keys[r]) != NULL)
{
found++;
if(original_size - found != sm->num_elements)
{
printf("SIZE IS BAD!!!!\n");
}
}
}
int k;
for(k=0; k<length; k++)
{
free(keys[k]);
}
free(keys);
printf("done removing remaining nodes\n");
printf("tree size is now %ld, and root is %s\n\n", sm->num_elements, sm->lm.root == NULL ? "null" : "not null");
printf("repeating insertion/deletion\n\n");
}
else
{
unsigned long num_destroyed;
printf("destroying map...\n");
void** values = destroy_string_map(sm, DESTROY_MODE_RETURN_VALUES, &num_destroyed);
printf("map destroyed.\n");
int v=0;
for(v=0; values[v] != NULL; v++){ }
free(values);
printf("number of values returned after map destruction = %d\n", v);
}
}
return(0);
}
void get_max_depth(long_map_node* n, unsigned long* max_depth, unsigned long current_depth)
{
if(n == NULL)
{
*max_depth = current_depth > *max_depth ? current_depth : *max_depth;
return;
}
else
{
*max_depth = current_depth+1 > *max_depth ? current_depth+1 : *max_depth;
get_max_depth(n->left, max_depth, current_depth+1);
get_max_depth(n->right, max_depth, current_depth+1);
}
}
void get_min_depth(long_map_node* n, unsigned long* min_depth, unsigned long current_depth)
{
if(n == NULL)
{
return;
}
else if(n->left == NULL && n->right == NULL)
{
*min_depth = current_depth < *min_depth ? current_depth : *min_depth;
}
else
{
get_min_depth(n->left, min_depth, current_depth+1);
get_min_depth(n->right, min_depth, current_depth+1);
}
}
void print_map(long_map_node* n, int depth)
{
if(n == NULL)
{
return;
}
int i;
for(i=0; i < depth; i++){ printf("\t");}
printf("%ld (%d)\n", n->key, n->balance);
for(i=0; i < depth; i++){ printf("\t");}
printf("left:\n");
print_map(n->left, depth+1);
for(i=0; i < depth; i++){ printf("\t");}
printf("right:\n");
print_map(n->right, depth+1);
}

View File

@ -1,25 +0,0 @@
#include "erics_tools.h"
int main(void)
{
FILE* f = fopen("tmp", "r");
char terminators[] = "\n\r";
unsigned long length;
char* file_data = (char*)read_entire_file(f, 100, &length);
printf("%s\n", file_data);
fclose(f);
f = fopen("tmp", "r");
dyn_read_t next;
next.terminator = '\n';
while(next.terminator != EOF)
{
next = dynamic_read(f, terminators, 2, &length);
printf("read \"%s\"\n", next.str);
free(next.str);
}
fclose(f);
return 0;
}

View File

@ -1,884 +0,0 @@
/*
* Copyright © 2008 by Eric Bishop <eric@gargoyle-router.com>
*
* This work 'as-is' we provide.
* No warranty, express or implied.
* We've done our best,
* to debug and test.
* Liability for damages denied.
*
* Permission is granted hereby,
* to copy, share, and modify.
* Use as is fit,
* free or for profit.
* On this notice these rights rely.
*
*
*
* Note that unlike other portions of Gargoyle this code
* does not fall under the GPL, but the rather whimsical
* 'Poetic License' above.
*
* Basically, this library contains a bunch of utilities
* that I find useful. I'm sure other libraries exist
* that are just as good or better, but I like these tools
* because I personally wrote them, so I know their quirks.
* (i.e. I know where the bodies are buried). I want to
* make sure that I can re-use these utilities for whatever
* code I may want to write in the future be it
* proprietary or open-source, so I've put them under
* a very, very permissive license.
*
* If you find this code useful, use it. If not, don't.
* I really don't care.
*
*/
#include "erics_tools.h"
#define malloc safe_malloc
#define strdup safe_strdup
/* internal utility structures/ functions */
typedef struct stack_node_struct
{
long_map_node** node_ptr;
signed char direction;
struct stack_node_struct* previous;
} stack_node;
static void** destroy_long_map_values(long_map* map, int destruction_type, unsigned long* num_destroyed);
static void apply_to_every_long_map_node(long_map_node* node, void (*apply_func)(unsigned long key, void* value));
static void apply_to_every_string_map_node(long_map_node* node, unsigned char has_key, void (*apply_func)(char* key, void* value));
static void get_sorted_node_keys(long_map_node* node, unsigned long* key_list, unsigned long* next_key_index, int depth);
static void get_sorted_node_values(long_map_node* node, void** value_list, unsigned long* next_value_index, int depth);
static signed char rebalance (long_map_node** n, signed char direction, signed char update_op);
static void rotate_right (long_map_node** parent);
static void rotate_left (long_map_node** parent);
/* internal for string map */
typedef struct
{
char* key;
void* value;
} string_map_key_value;
static unsigned long sdbm_string_hash(const char *key);
/***************************************************
* For testing only
***************************************************/
/*
void print_list(stack_node *l);
void print_list(stack_node *l)
{
if(l != NULL)
{
printf(" list key = %ld, dir=%d, \n", (*(l->node_ptr))->key, l->direction);
print_list(l->previous);
}
}
*/
/******************************************************
* End testing Code
*******************************************************/
/***************************************************
* string_map function definitions
***************************************************/
string_map* initialize_string_map(unsigned char store_keys)
{
string_map* map = (string_map*)malloc(sizeof(string_map));
map->store_keys = store_keys;
map->lm.root = NULL;
map->lm.num_elements = 0;
map->num_elements = map->lm.num_elements;
return map;
}
void* get_string_map_element(string_map* map, const char* key)
{
unsigned long hashed_key = sdbm_string_hash(key);
void* return_value = get_long_map_element( &(map->lm), hashed_key);
if(return_value != NULL && map->store_keys)
{
string_map_key_value* r = (string_map_key_value*)return_value;
return_value = r->value;
}
map->num_elements = map->lm.num_elements;
return return_value;
}
void* set_string_map_element(string_map* map, const char* key, void* value)
{
unsigned long hashed_key = sdbm_string_hash(key);
void* return_value = NULL;
if(map->store_keys)
{
string_map_key_value* kv = (string_map_key_value*)malloc(sizeof(string_map_key_value));
kv->key = strdup(key);
kv->value = value;
return_value = set_long_map_element( &(map->lm), hashed_key, kv);
if(return_value != NULL)
{
string_map_key_value* r = (string_map_key_value*)return_value;
return_value = r->value;
free(r->key);
free(r);
}
}
else
{
return_value = set_long_map_element( &(map->lm), hashed_key, value);
}
map->num_elements = map->lm.num_elements;
return return_value;
}
void* remove_string_map_element(string_map* map, const char* key)
{
unsigned long hashed_key = sdbm_string_hash(key);
void* return_value = remove_long_map_element( &(map->lm), hashed_key);
if(return_value != NULL && map->store_keys)
{
string_map_key_value* r = (string_map_key_value*)return_value;
return_value = r->value;
free(r->key);
free(r);
}
map->num_elements = map->lm.num_elements;
return return_value;
}
char** get_string_map_keys(string_map* map, unsigned long* num_keys_returned)
{
char** str_keys;
str_keys = (char**)malloc((map->num_elements+1)*sizeof(char*));
str_keys[0] = NULL;
*num_keys_returned = 0;
if(map->store_keys && map->num_elements > 0)
{
unsigned long list_length;
void** long_values = get_sorted_long_map_values( &(map->lm), &list_length);
unsigned long key_index;
for(key_index = 0; key_index < list_length; key_index++)
{
str_keys[key_index] = strdup( ((string_map_key_value*)(long_values[key_index]))->key);
*num_keys_returned = *num_keys_returned + 1;
}
str_keys[list_length] = NULL;
free(long_values);
}
return str_keys;
}
void** get_string_map_values(string_map* map, unsigned long* num_values_returned)
{
void** values = NULL;
if(map != NULL)
{
values = get_sorted_long_map_values ( &(map->lm), num_values_returned );
}
return values;
}
void** destroy_string_map(string_map* map, int destruction_type, unsigned long* num_destroyed)
{
void** return_values = NULL;
if(map != NULL)
{
if(map->store_keys)
{
void** kvs = destroy_long_map_values( &(map->lm), DESTROY_MODE_RETURN_VALUES, num_destroyed );
unsigned long kv_index = 0;
for(kv_index=0; kv_index < *num_destroyed; kv_index++)
{
string_map_key_value* kv = (string_map_key_value*)kvs[kv_index];
void* value = kv->value;
free(kv->key);
free(kv);
if(destruction_type == DESTROY_MODE_FREE_VALUES)
{
free(value);
}
if(destruction_type == DESTROY_MODE_RETURN_VALUES)
{
kvs[kv_index] = value;
}
}
if(destruction_type == DESTROY_MODE_RETURN_VALUES)
{
return_values = kvs;
}
else
{
free(kvs);
}
}
else
{
return_values = destroy_long_map_values( &(map->lm), destruction_type, num_destroyed );
}
free(map);
}
return return_values;
}
/***************************************************
* long_map function definitions
***************************************************/
long_map* initialize_long_map(void)
{
long_map* map = (long_map*)malloc(sizeof(long_map));
map->root = NULL;
map->num_elements = 0;
return map;
}
void* get_long_map_element(long_map* map, unsigned long key)
{
void* value = NULL;
if(map->root != NULL)
{
long_map_node* parent_node = map->root;
long_map_node* next_node;
while( key != parent_node->key && (next_node = (long_map_node *)(key < parent_node->key ? parent_node->left : parent_node->right)) != NULL)
{
parent_node = next_node;
}
if(parent_node->key == key)
{
value = parent_node->value;
}
}
return value;
}
void* get_smallest_long_map_element(long_map* map, unsigned long* smallest_key)
{
void* value = NULL;
if(map->root != NULL)
{
long_map_node* next_node = map->root;
while( next_node->left != NULL)
{
next_node = next_node->left;
}
value = next_node->value;
*smallest_key = next_node->key;
}
return value;
}
void* get_largest_long_map_element(long_map* map, unsigned long* largest_key)
{
void* value = NULL;
if(map->root != NULL)
{
long_map_node* next_node = map->root;
while( next_node->right != NULL)
{
next_node = next_node->right;
}
value = next_node->value;
*largest_key = next_node->key;
}
return value;
}
void* remove_smallest_long_map_element(long_map* map, unsigned long* smallest_key)
{
get_smallest_long_map_element(map, smallest_key);
return remove_long_map_element(map, *smallest_key);
}
void* remove_largest_long_map_element(long_map* map, unsigned long* largest_key)
{
get_largest_long_map_element(map, largest_key);
return remove_long_map_element(map, *largest_key);
}
/* if replacement performed, returns replaced value, otherwise null */
void* set_long_map_element(long_map* map, unsigned long key, void* value)
{
stack_node* parent_list = NULL;
void* old_value = NULL;
int old_value_found = 0;
long_map_node* parent_node;
long_map_node* next_node;
stack_node* next_parent;
stack_node* previous_parent;
signed char new_balance;
long_map_node* new_node = (long_map_node*)malloc(sizeof(long_map_node));
new_node->value = value;
new_node->key = key;
new_node->left = NULL;
new_node->right = NULL;
new_node->balance = 0;
if(map->root == NULL)
{
map->root = new_node;
}
else
{
parent_node = map->root;
next_parent = (stack_node*)malloc(sizeof(stack_node));
next_parent->node_ptr = &(map->root);
next_parent->previous = parent_list;
parent_list = next_parent;
while( key != parent_node->key && (next_node = (key < parent_node->key ? parent_node->left : parent_node->right) ) != NULL)
{
next_parent = (stack_node*)malloc(sizeof(stack_node));
next_parent->node_ptr = key < parent_node->key ? &(parent_node->left) : &(parent_node->right);
next_parent->previous = parent_list;
next_parent->previous->direction = key < parent_node->key ? -1 : 1;
parent_list = next_parent;
parent_node = next_node;
}
if(key == parent_node->key)
{
old_value = parent_node->value;
old_value_found = 1;
parent_node->value = value;
free(new_node);
/* we merely replaced a node, no need to rebalance */
}
else
{
if(key < parent_node->key)
{
parent_node->left = (void*)new_node;
parent_list->direction = -1;
}
else
{
parent_node->right = (void*)new_node;
parent_list->direction = 1;
}
/* we inserted a node, rebalance */
previous_parent = parent_list;
new_balance = 1; /* initial value is not used, but must not be 0 for initial loop condition */
while(previous_parent != NULL && new_balance != 0)
{
new_balance = rebalance(previous_parent->node_ptr, previous_parent->direction, 1);
previous_parent = previous_parent->previous;
}
}
}
while(parent_list != NULL)
{
previous_parent = parent_list;
parent_list = previous_parent->previous;
free(previous_parent);
}
if(old_value_found == 0)
{
map->num_elements = map->num_elements + 1;
}
return old_value;
}
void* remove_long_map_element(long_map* map, unsigned long key)
{
void* value = NULL;
long_map_node* root_node = map->root;
stack_node* parent_list = NULL;
long_map_node* remove_parent;
long_map_node* remove_node;
long_map_node* next_node;
long_map_node* replacement;
long_map_node* replacement_parent;
long_map_node* replacement_next;
stack_node* next_parent;
stack_node* previous_parent;
stack_node* replacement_stack_node;
signed char new_balance;
if(root_node != NULL)
{
remove_parent = root_node;
remove_node = key < remove_parent->key ? remove_parent->left : remove_parent->right;
if(remove_node != NULL && key != remove_parent->key)
{
next_parent = (stack_node*)malloc(sizeof(stack_node));
next_parent->node_ptr = &(map->root);
next_parent->previous = parent_list;
parent_list = next_parent;
while( key != remove_node->key && (next_node = (key < remove_node->key ? remove_node->left : remove_node->right)) != NULL)
{
next_parent = (stack_node*)malloc(sizeof(stack_node));
next_parent->node_ptr = key < remove_parent->key ? &(remove_parent->left) : &(remove_parent->right);
next_parent->previous = parent_list;
next_parent->previous->direction = key < remove_parent->key ? -1 : 1;
parent_list = next_parent;
remove_parent = remove_node;
remove_node = next_node;
}
parent_list->direction = key < remove_parent-> key ? -1 : 1;
}
else
{
remove_node = remove_parent;
}
if(key == remove_node->key)
{
/* find replacement for node we are deleting */
if( remove_node->right == NULL )
{
replacement = remove_node->left;
}
else if( remove_node->right->left == NULL)
{
replacement = remove_node->right;
replacement->left = remove_node->left;
replacement->balance = remove_node->balance;
/* put pointer to replacement node into list for balance update */
replacement_stack_node = (stack_node*)malloc(sizeof(stack_node));;
replacement_stack_node->previous = parent_list;
replacement_stack_node->direction = 1; /* replacement is from right */
if(remove_node == remove_parent) /* special case for root node */
{
replacement_stack_node->node_ptr = &(map->root);
}
else
{
replacement_stack_node->node_ptr = key < remove_parent-> key ? &(remove_parent->left) : &(remove_parent->right);
}
parent_list = replacement_stack_node;
}
else
{
/* put pointer to replacement node into list for balance update */
replacement_stack_node = (stack_node*)malloc(sizeof(stack_node));
replacement_stack_node->previous = parent_list;
replacement_stack_node->direction = 1; /* we always look for replacement on right */
if(remove_node == remove_parent) /* special case for root node */
{
replacement_stack_node->node_ptr = &(map->root);
}
else
{
replacement_stack_node->node_ptr = key < remove_parent-> key ? &(remove_parent->left) : &(remove_parent->right);
}
parent_list = replacement_stack_node;
/*
* put pointer to replacement node->right into list for balance update
* this node will have to be updated with the proper pointer
* after we have identified the replacement
*/
replacement_stack_node = (stack_node*)malloc(sizeof(stack_node));
replacement_stack_node->previous = parent_list;
replacement_stack_node->direction = -1; /* we always look for replacement to left of this node */
parent_list = replacement_stack_node;
/* find smallest node on right (large) side of tree */
replacement_parent = remove_node->right;
replacement = replacement_parent->left;
while((replacement_next = replacement->left) != NULL)
{
next_parent = (stack_node*)malloc(sizeof(stack_node));
next_parent->node_ptr = &(replacement_parent->left);
next_parent->previous = parent_list;
next_parent->direction = -1; /* we always go left */
parent_list = next_parent;
replacement_parent = replacement;
replacement = replacement_next;
}
replacement_parent->left = replacement->right;
replacement->left = remove_node->left;
replacement->right = remove_node->right;
replacement->balance = remove_node->balance;
replacement_stack_node->node_ptr = &(replacement->right);
}
/* insert replacement at proper location in tree */
if(remove_node == remove_parent)
{
map->root = replacement;
}
else
{
remove_parent->left = remove_node == remove_parent->left ? replacement : remove_parent->left;
remove_parent->right = remove_node == remove_parent->right ? replacement : remove_parent->right;
}
/* rebalance tree */
previous_parent = parent_list;
new_balance = 0;
while(previous_parent != NULL && new_balance == 0)
{
new_balance = rebalance(previous_parent->node_ptr, previous_parent->direction, -1);
previous_parent = previous_parent->previous;
}
/*
* since we found a value to remove, decrease number of elements in map
* set return value to the deleted node's value and free the node
*/
map->num_elements = map->num_elements - 1;
value = remove_node->value;
free(remove_node);
}
}
while(parent_list != NULL)
{
previous_parent = parent_list;
parent_list = previous_parent->previous;
free(previous_parent);
}
return value;
}
/* note: returned keys are dynamically allocated, you need to free them! */
unsigned long* get_sorted_long_map_keys(long_map* map, unsigned long* num_keys_returned)
{
unsigned long* key_list = (unsigned long*)malloc((map->num_elements)*sizeof(unsigned long));
unsigned long next_key_index = 0;
get_sorted_node_keys(map->root, key_list, &next_key_index, 0);
*num_keys_returned = map->num_elements;
return key_list;
}
void** get_sorted_long_map_values(long_map* map, unsigned long* num_values_returned)
{
void** value_list = (void**)malloc((map->num_elements+1)*sizeof(void*));
unsigned long next_value_index = 0;
get_sorted_node_values(map->root, value_list, &next_value_index, 0);
value_list[map->num_elements] = NULL; /* since we're dealing with pointers make list null terminated */
*num_values_returned = map->num_elements;
return value_list;
}
void** destroy_long_map(long_map* map, int destruction_type, unsigned long* num_destroyed)
{
void** return_values = NULL;
unsigned long return_index = 0;
*num_destroyed = 0;
if(destruction_type == DESTROY_MODE_RETURN_VALUES)
{
return_values = (void**)malloc((map->num_elements+1)*sizeof(void*));
return_values[map->num_elements] = NULL;
}
while(map->num_elements > 0)
{
unsigned long smallest_key = 0;
void* removed_value = remove_smallest_long_map_element(map, &smallest_key);
if(destruction_type == DESTROY_MODE_RETURN_VALUES)
{
return_values[return_index] = removed_value;
}
if(destruction_type == DESTROY_MODE_FREE_VALUES)
{
free(removed_value);
}
return_index++;
*num_destroyed = *num_destroyed + 1;
}
free(map);
return return_values;
}
void apply_to_every_long_map_value(long_map* map, void (*apply_func)(unsigned long key, void* value))
{
apply_to_every_long_map_node(map->root, apply_func);
}
void apply_to_every_string_map_value(string_map* map, void (*apply_func)(char* key, void* value))
{
apply_to_every_string_map_node( (map->lm).root, map->store_keys, apply_func);
}
/***************************************************
* internal utility function definitions
***************************************************/
static void** destroy_long_map_values(long_map* map, int destruction_type, unsigned long* num_destroyed)
{
void** return_values = NULL;
unsigned long return_index = 0;
*num_destroyed = 0;
if(destruction_type == DESTROY_MODE_RETURN_VALUES)
{
return_values = (void**)malloc((map->num_elements+1)*sizeof(void*));
return_values[map->num_elements] = NULL;
}
while(map->num_elements > 0)
{
unsigned long smallest_key = 0;
void* removed_value = remove_smallest_long_map_element(map, &smallest_key);
if(destruction_type == DESTROY_MODE_RETURN_VALUES)
{
return_values[return_index] = removed_value;
}
if(destruction_type == DESTROY_MODE_FREE_VALUES)
{
free(removed_value);
}
return_index++;
*num_destroyed = *num_destroyed + 1;
}
return return_values;
}
static void apply_to_every_long_map_node(long_map_node* node, void (*apply_func)(unsigned long key, void* value))
{
if(node != NULL)
{
apply_to_every_long_map_node(node->left, apply_func);
apply_func(node->key, node->value);
apply_to_every_long_map_node(node->right, apply_func);
}
}
static void apply_to_every_string_map_node(long_map_node* node, unsigned char has_key, void (*apply_func)(char* key, void* value))
{
if(node != NULL)
{
apply_to_every_string_map_node(node->left, has_key, apply_func);
if(has_key)
{
string_map_key_value* kv = (string_map_key_value*)(node->value);
apply_func(kv->key, kv->value);
}
else
{
apply_func(NULL, node->value);
}
apply_to_every_string_map_node(node->right, has_key, apply_func);
}
}
static void get_sorted_node_keys(long_map_node* node, unsigned long* key_list, unsigned long* next_key_index, int depth)
{
if(node != NULL)
{
get_sorted_node_keys(node->left, key_list, next_key_index, depth+1);
key_list[ *next_key_index ] = node->key;
(*next_key_index)++;
get_sorted_node_keys(node->right, key_list, next_key_index, depth+1);
}
}
static void get_sorted_node_values(long_map_node* node, void** value_list, unsigned long* next_value_index, int depth)
{
if(node != NULL)
{
get_sorted_node_values(node->left, value_list, next_value_index, depth+1);
value_list[ *next_value_index ] = node->value;
(*next_value_index)++;
get_sorted_node_values(node->right, value_list, next_value_index, depth+1);
}
}
/*
* direction = -1 indicates left subtree updated, direction = 1 for right subtree
* update_op = -1 indicates delete node, update_op = 1 for insert node
*/
static signed char rebalance (long_map_node** n, signed char direction, signed char update_op)
{
/*
printf( "original: key = %ld, balance = %d, update_op=%d, direction=%d\n", (*n)->key, (*n)->balance, update_op, direction);
*/
(*n)->balance = (*n)->balance + (update_op*direction);
if( (*n)->balance < -1)
{
if((*n)->left->balance < 0)
{
rotate_right(n);
(*n)->right->balance = 0;
(*n)->balance = 0;
}
else if((*n)->left->balance == 0)
{
rotate_right(n);
(*n)->right->balance = -1;
(*n)->balance = 1;
}
else if((*n)->left->balance > 0)
{
rotate_left( &((*n)->left) );
rotate_right(n);
/*
if( (*n)->balance < 0 )
{
(*n)->left->balance = 0;
(*n)->right->balance = 1;
}
else if( (*n)->balance == 0 )
{
(*n)->left->balance = 0;
(*n)->right->balance = 0;
}
else if( (*n)->balance > 0 )
{
(*n)->left->balance = -1;
(*n)->right->balance = 0;
}
*/
(*n)->left->balance = (*n)->balance > 0 ? -1 : 0;
(*n)->right->balance = (*n)->balance < 0 ? 1 : 0;
(*n)->balance = 0;
}
}
if( (*n)->balance > 1)
{
if((*n)->right->balance > 0)
{
rotate_left(n);
(*n)->left->balance = 0;
(*n)->balance = 0;
}
else if ((*n)->right->balance == 0)
{
rotate_left(n);
(*n)->left->balance = 1;
(*n)->balance = -1;
}
else if((*n)->right->balance < 0)
{
rotate_right( &((*n)->right) );
rotate_left(n);
/*
if( (*n)->balance < 0 )
{
(*n)->left->balance = 0;
(*n)->right->balance = 1;
}
else if( (*n)->balance == 0 )
{
(*n)->left->balance = 0;
(*n)->right->balance = 0;
}
else if( (*n)->balance > 0 )
{
(*n)->left->balance = -1;
(*n)->right->balance = 0;
}
*/
(*n)->left->balance = (*n)->balance > 0 ? -1 : 0;
(*n)->right->balance = (*n)->balance < 0 ? 1 : 0;
(*n)->balance = 0;
}
}
/*
printf( "key = %ld, balance = %d\n", (*n)->key, (*n)->balance);
*/
return (*n)->balance;
}
static void rotate_right (long_map_node** parent)
{
long_map_node* old_parent = *parent;
long_map_node* pivot = old_parent->left;
old_parent->left = pivot->right;
pivot->right = old_parent;
*parent = pivot;
}
static void rotate_left (long_map_node** parent)
{
long_map_node* old_parent = *parent;
long_map_node* pivot = old_parent->right;
old_parent->right = pivot->left;
pivot->left = old_parent;
*parent = pivot;
}
/***************************************************************************
* This algorithm was created for the sdbm database library (a public-domain
* reimplementation of ndbm) and seems to work relatively well in
* scrambling bits
*
*
* This code was derived from code found at:
* http://www.cse.yorku.ca/~oz/hash.html
***************************************************************************/
static unsigned long sdbm_string_hash(const char *key)
{
unsigned long hashed_key = 0;
int index = 0;
unsigned int nextch;
while(key[index] != '\0')
{
nextch = key[index];
hashed_key = nextch + (hashed_key << 6) + (hashed_key << 16) - hashed_key;
index++;
}
return hashed_key;
}

View File

@ -1,74 +0,0 @@
#
# Copyright (C) 2006 OpenWrt.org
# Copyright (C) 2009 Eric Bishop <eric@gargoyle-router.com>
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=libiptbwctl
PKG_VERSION:=1.0.0
PKG_RELEASE:=1
PKG_BUILD_DIR:=$(BUILD_DIR)/libiptbwctl
include $(INCLUDE_DIR)/package.mk
define Package/libiptbwctl
SECTION:=net
CATEGORY:=Gargoyle
SUBMENU:=Network
DEPENDS:=+iptables-mod-bandwidth
TITLE:=IPT bandwidth control library
URL:=http://www.gargoyle-router.com
MAINTAINER:=Eric Bishop <eric@gargoyle-router.com>
endef
define Package/libiptbwctl/description
IPT bandwidth control library
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Build/Configure
endef
define Build/Compile
-$(MAKE) -C $(PKG_BUILD_DIR) clean
$(MAKE) -C $(PKG_BUILD_DIR) \
$(TARGET_CONFIGURE_OPTS) \
STAGING_DIR="$(STAGING_DIR)" \
CFLAGS="$(TARGET_CFLAGS) -I $(STAGING_DIR)/usr/include" \
LDFLAGS="$(TARGET_LDFLAGS) -L $(STAGING_DIR)/usr/lib" \
all
mkdir -p $(STAGING_DIR)/usr/include/
$(CP) $(PKG_BUILD_DIR)/*.h $(STAGING_DIR)/usr/include/
mkdir -p $(STAGING_DIR)/usr/lib
$(CP) $(PKG_BUILD_DIR)/*.so* $(STAGING_DIR)/usr/lib/
$(MAKE) -C $(PKG_BUILD_DIR)/utils \
$(TARGET_CONFIGURE_OPTS) \
STAGING_DIR="$(STAGING_DIR)" \
CFLAGS="$(TARGET_CFLAGS) -I $(STAGING_DIR)/usr/include" \
LDFLAGS="$(TARGET_LDFLAGS) -L $(STAGING_DIR)/usr/lib" \
all
endef
define Package/libiptbwctl/install
$(INSTALL_DIR) $(1)/usr/lib
$(INSTALL_DIR) $(1)/usr/bin
$(CP) $(PKG_BUILD_DIR)/*.so* $(1)/usr/lib/
$(INSTALL_BIN) $(PKG_BUILD_DIR)/utils/bw_get $(1)/usr/bin/bw_get
$(INSTALL_BIN) $(PKG_BUILD_DIR)/utils/bw_set $(1)/usr/bin/bw_set
$(INSTALL_BIN) $(PKG_BUILD_DIR)/utils/bw_print_history_file $(1)/usr/bin/bw_print_history_file
$(INSTALL_BIN) $(PKG_BUILD_DIR)/utils/set_kernel_timezone $(1)/usr/bin/set_kernel_timezone
endef
$(eval $(call BuildPackage,libiptbwctl))

View File

@ -1,60 +0,0 @@
VERSION=1
ifeq ($(CC),)
CC=gcc
endif
ifeq ($(LD),)
LD=ld
endif
ifeq ($(AR),)
AR=ar
endif
ifeq ($(RANLIB),)
RANLIB=ranlib
endif
OS=$(shell uname)
ifeq ($(OS),Darwin)
LINK=$(LD)
SHLIB_EXT=dylib
SHLIB_FLAGS=-dylib
SHLIB_FILE=libiptbwctl.$(SHLIB_EXT).$(VERSION)
else
LINK=$(CC)
SHLIB_EXT=so
SHLIB_FILE=libiptbwctl.$(SHLIB_EXT).$(VERSION)
SHLIB_FLAGS=-shared -Wl,-soname,$(SHLIB_FILE)
endif
CFLAGS:=$(CFLAGS) -Os
WARNING_FLAGS=-Wall -Wstrict-prototypes
all: libiptbwctl
libiptbwctl: libiptbwctl.$(SHLIB_EXT) libiptbwctl.a
libiptbwctl.a: ipt_bwctl_static.o ipt_bwctl_safe_malloc_static.o
if [ -e $@ ] ; then rm $@ ; fi
$(AR) rc $@ $^
$(RANLIB) $@
libiptbwctl.$(SHLIB_EXT) : ipt_bwctl_dyn.o ipt_bwctl_safe_malloc_dyn.o
if [ -e libiptbwctl.$(SHLIB_EXT) ] ; then rm libiptbwctl.$(SHLIB_EXT)* ; fi
$(LINK) $(LDFLAGS) $(SHLIB_FLAGS) -o $(SHLIB_FILE) $^ -lc
ln -s $(SHLIB_FILE) libiptbwctl.$(SHLIB_EXT)
%_dyn.o: %.c
$(CC) $(CFLAGS) -fPIC $(WARNING_FLAGS) -o $@ -c $^
%_static.o: %.c
$(CC) $(CFLAGS) $(WARNING_FLAGS) -o $@ -c $^
clean:
cd utils
rm -rf bw_get bw_set *.a *.o *~ .*sw*
cd ..
if [ -n "$(SHLIB_EXT)" ] ; then rm -rf *.$(SHLIB_EXT)* ; fi
rm -rf *.a *.o *~ .*sw*

File diff suppressed because it is too large Load Diff

View File

@ -1,147 +0,0 @@
/* libiptbwctl -- A userspace library for querying the bandwidth iptables module
* Originally designed for use with Gargoyle router firmware (gargoyle-router.com)
*
*
* Copyright © 2009 by Eric Bishop <eric@gargoyle-router.com>
*
* This file is free software: you may copy, redistribute and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 2 of the License, or (at your
* option) any later version.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <fcntl.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/ipc.h>
#include <errno.h>
#include <sys/sem.h>
#include <sys/time.h>
#define BANDWIDTH_QUERY_LENGTH 16384
/* socket id parameters (for userspace i/o) */
#define BANDWIDTH_SET 2048
#define BANDWIDTH_GET 2049
/* max id length */
#define BANDWIDTH_MAX_ID_LENGTH 50
/* pick something rather random... let's make it end in 666 to
* freak out the crazy fundies out there ;-) */
#define BANDWIDTH_SEMAPHORE_KEY 12699666
/* possible reset intervals */
#define BANDWIDTH_MINUTE 80
#define BANDWIDTH_HOUR 81
#define BANDWIDTH_DAY 82
#define BANDWIDTH_WEEK 83
#define BANDWIDTH_MONTH 84
#define BANDWIDTH_NEVER 85
#pragma pack(push, 1)
typedef struct ip_bw_struct
{
uint32_t ip;
uint64_t bw;
} ip_bw;
/*
* format of response:
* byte 1 : error code (0 for ok)
* bytes 2-5 : total_num_ips found in query (further gets may be necessary to retrieve them)
* bytes 6-9 : start_index, index (in a list of total_num_ips) of first ip in response
* bytes 10-13 : num_ips_in_response, number of ips in this response
* bytes 14-21 : reset_interval (helps deal with DST shifts in userspace)
* bytes 22-29 : reset_time (helps deal with DST shifts in userspace)
* byte 30 : reset_is_constant_interval (helps deal with DST shifts in userspace)
* remaining bytes contain blocks of ip data
* format is dependent on whether history was queried
*/
typedef struct ip_bw_kernel_data_item_struct
{
uint32_t ip;
uint32_t num_nodes;
uint64_t first_start;
uint64_t first_end;
uint64_t last_end;
uint64_t ipbw_data[0];
}ip_bw_kernel_data_item;
typedef struct
{
uint8_t error;
uint32_t ip_total;
uint32_t index_start;
uint32_t ip_num;
uint64_t reset_interval;
uint64_t reset_time;
uint8_t reset_is_constant_interval;
/*payload for history ip bw data*/
ip_bw_kernel_data_item data_item[0];
} ip_bw_kernel_data;
typedef struct history_struct
{
uint32_t ip;
uint32_t num_nodes;
time_t reset_interval;
time_t reset_time;
unsigned char is_constant_interval;
time_t first_start;
time_t first_end;
time_t last_end;
uint64_t* history_bws;
} ip_bw_history;
#pragma pack(pop)
time_t* get_interval_starts_for_history(ip_bw_history history);
extern void free_ip_bw_histories(ip_bw_history* histories, int num_histories);
extern int get_all_bandwidth_history_for_rule_id(char* id, unsigned long* num_ips, ip_bw_history** data, unsigned long max_wait_milliseconds);
extern int get_ip_bandwidth_history_for_rule_id(char* id, char* ip, ip_bw_history** data, unsigned long max_wait_milliseconds);
extern int get_all_bandwidth_usage_for_rule_id(char* id, unsigned long* num_ips, ip_bw** data, unsigned long max_wait_milliseconds);
extern int get_ip_bandwidth_usage_for_rule_id(char* id, char* ip, ip_bw** data, unsigned long max_wait_milliseconds);
extern int set_bandwidth_history_for_rule_id(char* id, unsigned char zero_unset, unsigned long num_ips, ip_bw_history* data, unsigned long max_wait_milliseconds);
extern int set_bandwidth_usage_for_rule_id(char* id, unsigned char zero_unset, unsigned long num_ips, time_t last_backup, ip_bw* data, unsigned long max_wait_milliseconds);
extern int save_usage_to_file(ip_bw* data, unsigned long num_ips, char* out_file_path);
extern int save_history_to_file(ip_bw_history* data, unsigned long num_ips, char* out_file_path);
extern ip_bw* load_usage_from_file(char* in_file_path, unsigned long* num_ips, time_t* last_backup);
extern ip_bw_history* load_history_from_file(char* in_file_path, unsigned long* num_ips);
extern void print_usage(FILE* out, ip_bw* usage, unsigned long num_ips);
extern void print_histories(FILE* out, char* id, ip_bw_history* histories, unsigned long num_histories, char output_type);
extern void unlock_bandwidth_semaphore(void);
extern void unlock_bandwidth_semaphore_on_exit(void);
/* sets kernel timezone minuteswest to match user timezone */
extern int get_minutes_west(time_t now);
extern void set_kernel_timezone(void);
/* safe malloc & strdup functions used to handle malloc errors cleanly */
extern void* ipt_bwctl_safe_malloc(size_t size);
extern char* ipt_bwctl_safe_strdup(const char* str);

View File

@ -1,23 +0,0 @@
#include "ipt_bwctl.h"
void *ipt_bwctl_safe_malloc(size_t size)
{
void* val = malloc(size);
if(val == NULL)
{
fprintf(stderr, "ERROR: MALLOC FAILURE!\n");
exit(1);
}
return val;
}
char* ipt_bwctl_safe_strdup(const char* str)
{
char* new_str = strdup(str);
if(new_str == NULL)
{
fprintf(stderr, "ERROR: MALLOC FAILURE!\n");
exit(1);
}
return new_str;
}

View File

@ -1,12 +0,0 @@
all: bw_set bw_get bw_print_history_file set_kernel_timezone
bw_set: bw_set.c
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ -liptbwctl
bw_get: bw_get.c
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ -liptbwctl
bw_print_history_file: bw_print_history_file.c
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ -liptbwctl
set_kernel_timezone: set_kernel_timezone.c
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ -liptbwctl
clean:
rm -rf bw_set bw_get print_history_file set_kernel_timezone *.o *~ .*sw*

View File

@ -1,175 +0,0 @@
/* libiptbwctl -- A userspace library for querying the bandwidth iptables module
* Originally designed for use with Gargoyle router firmware (gargoyle-router.com)
*
*
* Copyright © 2009 by Eric Bishop <eric@gargoyle-router.com>
*
* This file is free software: you may copy, redistribute and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 2 of the License, or (at your
* option) any later version.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ipt_bwctl.h>
#define malloc ipt_bwctl_safe_malloc
#define strdup ipt_bwctl_safe_strdup
int main(int argc, char **argv)
{
char *id = NULL;
char* out_file_path = NULL;;
char *address = NULL;
unsigned long num_ips;
void *ip_buf;
unsigned long out_index;
int query_succeeded;
int get_history = 0;
char output_type = 'h';
int c;
struct in_addr read_addr;
while((c = getopt(argc, argv, "i:I:a:A:f:F:tThHmMuU")) != -1)
{
switch(c)
{
case 'i':
case 'I':
if(strlen(optarg) < BANDWIDTH_MAX_ID_LENGTH && strlen(optarg) > 0)
{
id = strdup(optarg);
}
else
{
fprintf(stderr, "ERROR: ID length is improper length.\n");
exit(0);
}
break;
case 'a':
case 'A':
if(strcmp(optarg, "combined") == 0 || strcmp(optarg, "COMBINED") == 0)
{
address = strdup("0.0.0.0");
}
else if( inet_aton(optarg, &read_addr) )
{
address = strdup(optarg);
}
else
{
fprintf(stderr, "ERROR: invalid IP address specified\n");
exit(0);
}
break;
case 'f':
case 'F':
out_file_path = strdup(optarg);
break;
case 'h':
case 'H':
get_history = 1;
break;
case 'm':
case 'M':
output_type = 'm';
break;
case 't':
case 'T':
output_type = 't';
break;
case 'u':
case 'U':
default:
fprintf(stderr, "USAGE:\n\t%s -i [ID] -a [IP ADDRESS] -f [OUT_FILE_NAME]\n", argv[0]);
exit(0);
}
}
if(id == NULL)
{
fprintf(stderr, "ERROR: you must specify an id to query\n\n");
exit(0);
}
set_kernel_timezone();
unlock_bandwidth_semaphore_on_exit();
if(get_history == 0)
{
if(address == NULL)
{
query_succeeded = get_all_bandwidth_usage_for_rule_id(id, &num_ips, (ip_bw**)&ip_buf, 1000);
}
else
{
num_ips = 1;
query_succeeded = get_ip_bandwidth_usage_for_rule_id(id, address, (ip_bw**)&ip_buf, 1000);
}
}
else
{
if(address == NULL)
{
query_succeeded = get_all_bandwidth_history_for_rule_id(id, &num_ips, (ip_bw_history**)&ip_buf, 1000);
}
else
{
num_ips = 1;
query_succeeded = get_ip_bandwidth_history_for_rule_id(id, address, (ip_bw_history**)&ip_buf, 1000);
}
}
if(!query_succeeded)
{
fprintf(stderr, "ERROR: Bandwidth query failed, make sure rule with specified id exists, and that you are performing only one query at a time.\n\n");
exit(0);
}
if(out_file_path != NULL)
{
if(get_history == 0)
{
save_usage_to_file( (ip_bw*)ip_buf, num_ips, out_file_path);
}
else
{
save_history_to_file( (ip_bw_history*)ip_buf, num_ips, out_file_path);
}
}
else
{
if(get_history == 0)
{
print_usage(stdout, (ip_bw*)ip_buf, num_ips);
}
else
{
print_histories(stdout, id, (ip_bw_history*)ip_buf, num_ips, output_type );
}
}
if(num_ips == 0)
{
if(output_type != 't' && output_type != 'm')
{
fprintf(stderr, "No data available for id \"%s\"\n", id);
}
}
printf("\n");
if(out_file_path != NULL)
{
free(out_file_path);
}
return 0;
}

View File

@ -1,37 +0,0 @@
/* libiptbwctl -- A userspace library for querying the bandwidth iptables module
* Originally designed for use with Gargoyle router firmware (gargoyle-router.com)
*
*
* Copyright © 2009 by Eric Bishop <eric@gargoyle-router.com>
*
* This file is free software: you may copy, redistribute and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 2 of the License, or (at your
* option) any later version.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ipt_bwctl.h>
#define malloc ipt_bwctl_safe_malloc
#define strdup ipt_bwctl_safe_strdup
int main(int argc, char **argv)
{
if(argc > 1)
{
unsigned long num_ips;
ip_bw_history* histories = load_history_from_file(argv[1], &num_ips);
if(histories != NULL)
{
print_histories(stdout, argv[1], histories, num_ips, 'h');
}
}
return 0;
}

View File

@ -1,184 +0,0 @@
/* libiptbwctl -- A userspace library for querying the bandwidth iptables module
* Originally designed for use with Gargoyle router firmware (gargoyle-router.com)
*
*
* Copyright © 2009 by Eric Bishop <eric@gargoyle-router.com>
*
* This file is free software: you may copy, redistribute and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 2 of the License, or (at your
* option) any later version.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ipt_bwctl.h>
#define malloc ipt_bwctl_safe_malloc
#define strdup ipt_bwctl_safe_strdup
static char* read_entire_file(FILE* in, int read_block_size);
static char** split_on_separators(char* line, char* separators, int num_separators, int max_pieces, int include_remainder_at_max, unsigned long *pieces_read);
int main(int argc, char **argv)
{
char *id = NULL;
char* in_file_path = NULL;
FILE* in_file = NULL;
time_t last_backup = 0;
int last_backup_from_cl = 0;
int is_history_file = 0;
int c;
while((c = getopt(argc, argv, "i:I:b:B:f:F:UuHh")) != -1)
{
switch(c)
{
case 'i':
case 'I':
if(strlen(optarg) < BANDWIDTH_MAX_ID_LENGTH)
{
id = strdup(optarg);
}
else
{
fprintf(stderr, "ERROR: ID length is improper length.\n");
exit(0);
}
break;
case 'b':
case 'B':
if(sscanf(optarg, "%ld", &last_backup) == 0)
{
fprintf(stderr, "ERROR: invalid backup time specified. Should be unix epoch seconds -- number of seconds since 1970 (UTC)\n");
exit(0);
}
last_backup_from_cl = 1;
break;
case 'f':
case 'F':
in_file_path = strdup(optarg);
in_file = fopen(optarg, "rb");
if(in_file == NULL)
{
fprintf(stderr, "ERROR: cannot open specified file for reading\n");
exit(0);
}
fclose(in_file);
break;
case 'h':
case 'H':
is_history_file = 1;
break;
case 'u':
case 'U':
default:
fprintf(stderr, "USAGE:\n\t%s -i [ID] -b [LAST_BACKUP_TIME] -f [IN_FILE_NAME] [ IP BANDWIDTH PAIRS, IF -f NOT SPECIFIED ]\n", argv[0]);
exit(0);
}
}
if(id == NULL)
{
fprintf(stderr, "ERROR: you must specify an id for which to set data\n\n");
exit(0);
}
if(in_file_path == NULL && is_history_file)
{
fprintf(stderr, "ERROR: you need to specify file to load history from\n\t\t(history format is too complex to load from command line)\n");
}
set_kernel_timezone();
unlock_bandwidth_semaphore_on_exit();
int query_succeeded = 0;
if(in_file_path != NULL)
{
if(is_history_file)
{
unsigned long num_ips;
ip_bw_history* history_data = load_history_from_file(in_file_path, &num_ips);
if(history_data != NULL)
{
query_succeeded = set_bandwidth_history_for_rule_id(id, 1, num_ips, history_data, 1000);
}
}
else
{
unsigned long num_ips;
time_t last_backup;
ip_bw* usage_data = load_usage_from_file(in_file_path, &num_ips, &last_backup);
if(usage_data != NULL)
{
query_succeeded = set_bandwidth_usage_for_rule_id(id, 1, num_ips, last_backup, usage_data, 1000);
}
}
}
else
{
char** data_parts;
unsigned long num_data_parts;
data_parts = argv+optind;
num_data_parts = argc - optind;
unsigned long num_ips = num_data_parts/2;
ip_bw* buffer = (ip_bw*)malloc(num_ips*sizeof(ip_bw));
unsigned long data_index = 0;
unsigned long buffer_index = 0;
while(data_index < num_data_parts)
{
ip_bw next;
struct in_addr ipaddr;
int valid = inet_aton(data_parts[data_index], &ipaddr);
if((!valid) && (!last_backup_from_cl))
{
sscanf(data_parts[data_index], "%ld", &last_backup);
}
data_index++;
if(valid && data_index < num_data_parts)
{
next.ip = ipaddr.s_addr;
valid = sscanf(data_parts[data_index], "%lld", (long long int*)&(next.bw) );
data_index++;
}
else
{
valid = 0;
}
if(valid)
{
/* printf("ip=%d, bw=%lld\n", next.ip, (long long int)next.bw); */
buffer[buffer_index] = next;
buffer_index++;
}
}
num_ips = buffer_index; /* number that were successfully read */
query_succeeded = set_bandwidth_usage_for_rule_id(id, 1, num_ips, last_backup, buffer, 1000);
}
if(!query_succeeded)
{
fprintf(stderr, "ERROR: Could not set data. Please try again.\n\n");
}
else
{
fprintf(stderr, "Data set successfully\n\n");
}
if(in_file_path != NULL)
{
free(in_file_path);
}
return 0;
}

View File

@ -1,27 +0,0 @@
/* libiptbwctl -- A userspace library for querying the bandwidth iptables module
* Originally designed for use with Gargoyle router firmware (gargoyle-router.com)
*
*
* Copyright © 2009 by Eric Bishop <eric@gargoyle-router.com>
*
* This file is free software: you may copy, redistribute and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 2 of the License, or (at your
* option) any later version.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ipt_bwctl.h>
int main(void)
{
set_kernel_timezone();
return 0;
}

View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,31 +0,0 @@
#
# Copyright (C) 2017 Xingwang Liao <kuoruan@gmail.com>
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-qos-gargoyle
PKG_VERSION:=1.3.8
PKG_RELEASE:=1
PKG_LICENSE:=Apache-2.0
PKG_MAINTAINER:=Xingwang Liao <kuoruan@gmail.com>
LUCI_TITLE:=LuCI Support for Gargoyle QoS
LUCI_DEPENDS:=+qos-gargoyle
LUCI_PKGARCH:=all
include $(TOPDIR)/feeds/luci/luci.mk
define Package/$(PKG_NAME)/config
# shown in make menuconfig <Help>
help
$(LUCI_TITLE)
.
Version: $(PKG_VERSION)-$(PKG_RELEASE)
$(PKG_MAINTAINER)
endef
# call BuildPackage - OpenWrt buildroot signature

View File

@ -1,5 +0,0 @@
# luci-app-qos-gargoyle
This is my own fork of QoS Gargoyle
QoS Gargoyle: https://github.com/kuoruan/qos-gargoyle

View File

@ -1,100 +0,0 @@
-- Copyright 2017 Xingwang Liao <kuoruan@gmail.com>
-- Licensed to the public under the Apache License 2.0.
module("luci.controller.qos_gargoyle", package.seeall)
local util = require "luci.util"
local http = require "luci.http"
function index()
if not nixio.fs.access("/etc/config/qos_gargoyle") then
return
end
entry({"admin", "network", "qos_gargoyle"},
firstchild(), _("Gargoyle QoS"), 60)
entry({"admin", "network", "qos_gargoyle", "global"},
cbi("qos_gargoyle/global"), _("Global Settings"), 10)
entry({"admin", "network", "qos_gargoyle", "upload"},
cbi("qos_gargoyle/upload"), _("Upload Settings"), 20)
entry({"admin", "network", "qos_gargoyle", "upload", "class"},
cbi("qos_gargoyle/upload_class")).leaf = true
entry({"admin", "network", "qos_gargoyle", "upload", "rule"},
cbi("qos_gargoyle/upload_rule")).leaf = true
entry({"admin", "network", "qos_gargoyle", "download"},
cbi("qos_gargoyle/download"), _("Download Settings"), 30)
entry({"admin", "network", "qos_gargoyle", "download", "class"},
cbi("qos_gargoyle/download_class")).leaf = true
entry({"admin", "network", "qos_gargoyle", "download", "rule"},
cbi("qos_gargoyle/download_rule")).leaf = true
entry({"admin", "network", "qos_gargoyle", "troubleshooting"},
template("qos_gargoyle/troubleshooting"), _("Troubleshooting"), 40)
entry({"admin", "network", "qos_gargoyle", "troubleshooting", "data"},
call("action_troubleshooting_data"))
entry({"admin", "network", "qos_gargoyle", "load_data"},
call("action_load_data")).leaf = true
end
function action_troubleshooting_data()
local uci = require "luci.model.uci".cursor()
local i18n = require "luci.i18n"
local data = {}
local monenabled = uci:get("qos_gargoyle", "download", "qos_monenabled") or "false"
local show_data = util.trim(util.exec("/etc/init.d/qos_gargoyle show 2>/dev/null"))
if show_data == "" then
show_data = i18n.translate("No data found")
end
data.show = show_data
local mon_data
if monenabled == "true" then
mon_data = util.trim(util.exec("cat /tmp/qosmon.status 2>/dev/null"))
if mon_data == "" then
mon_data = i18n.translate("No data found")
end
else
mon_data = i18n.translate("\"Active Congestion Control\" not enabled")
end
data.mon = mon_data
http.prepare_content("application/json")
http.write_json(data)
end
function action_load_data(type)
local device
if type == "download" then
device = "imq0"
elseif type == "upload" then
local qos = require "luci.model.qos_gargoyle"
local wan = qos.get_wan()
device = wan and wan:ifname() or ""
end
if device then
local data
if device ~= "" then
data = util.exec("tc -s class show dev %s 2>/dev/null" % device)
end
http.prepare_content("text/plain")
http.write(data or "")
else
http.status(500, "Bad address")
end
end

View File

@ -1,166 +0,0 @@
-- Copyright 2017 Xingwang Liao <kuoruan@gmail.com>
-- Licensed to the public under the Apache License 2.0.
local wa = require "luci.tools.webadmin"
local uci = require "luci.model.uci".cursor()
local dsp = require "luci.dispatcher"
local http = require "luci.http"
local qos = require "luci.model.qos_gargoyle"
local m, class_s, rule_s, o
local download_classes = {}
local qos_gargoyle = "qos_gargoyle"
uci:foreach(qos_gargoyle, "download_class", function(s)
local class_alias = s.name
if class_alias then
download_classes[#download_classes + 1] = {name = s[".name"], alias = class_alias}
end
end)
m = Map(qos_gargoyle, translate("Download Settings"))
m.template = "qos_gargoyle/list_view"
class_s = m:section(TypedSection, "download_class", translate("Service Classes"),
translate("Each service class is specified by four parameters: percent bandwidth at capacity, "
.. "realtime bandwidth and maximum bandwidth and the minimimze round trip time flag."))
class_s.anonymous = true
class_s.addremove = true
class_s.template = "cbi/tblsection"
class_s.extedit = dsp.build_url("admin/network/qos_gargoyle/download/class/%s")
class_s.create = function(...)
local sid = TypedSection.create(...)
if sid then
m.uci:save(qos_gargoyle)
http.redirect(class_s.extedit % sid)
return
end
end
o = class_s:option(DummyValue, "name", translate("Class Name"))
o.cfgvalue = function(...)
return Value.cfgvalue(...) or translate("None")
end
o = class_s:option(DummyValue, "percent_bandwidth", translate("Percent Bandwidth At Capacity"))
o.cfgvalue = function(...)
local v = tonumber(Value.cfgvalue(...))
if v and v > 0 then
return "%d %%" % v
end
return translate("Not set")
end
o = class_s:option(DummyValue, "min_bandwidth", "%s (kbps)" % translate("Minimum Bandwidth"))
o.cfgvalue = function(...)
local v = tonumber(Value.cfgvalue(...))
return v or translate("Zero")
end
o = class_s:option(DummyValue, "max_bandwidth", "%s (kbps)" % translate("Maximum Bandwidth"))
o.cfgvalue = function(...)
local v = tonumber(Value.cfgvalue(...))
return v or translate("Unlimited")
end
o = class_s:option(DummyValue, "minRTT", translate("Minimize RTT"))
o.cfgvalue = function(...)
local v = Value.cfgvalue(...)
return v and translate(v) or translate("No")
end
o = class_s:option(DummyValue, "_ld", "%s (kbps)" % translate("Load"))
o.rawhtml = true
o.value = "<em class=\"ld-download\">*</em>"
rule_s = m:section(TypedSection, "download_rule", translate("Classification Rules"),
translate("Packets are tested against the rules in the order specified -- rules toward the top "
.. "have priority. As soon as a packet matches a rule it is classified, and the rest of the rules "
.. "are ignored. The order of the rules can be altered using the arrow controls.")
)
rule_s.addremove = true
rule_s.sortable = true
rule_s.anonymous = true
rule_s.template = "cbi/tblsection"
rule_s.extedit = dsp.build_url("admin/network/qos_gargoyle/download/rule/%s")
rule_s.create = function(...)
local sid = TypedSection.create(...)
if sid then
m.uci:save(qos_gargoyle)
http.redirect(rule_s.extedit % sid)
return
end
end
o = rule_s:option(ListValue, "class", translate("Service Class"))
for _, s in ipairs(download_classes) do o:value(s.name, s.alias) end
o = rule_s:option(Value, "proto", translate("Transport Protocol"))
o:value("", translate("All"))
o:value("tcp", "TCP")
o:value("udp", "UDP")
o:value("icmp", "ICMP")
o:value("gre", "GRE")
o.size = "10"
o.cfgvalue = function(...)
local v = Value.cfgvalue(...)
return v and v:upper() or ""
end
o.write = function(self, section, value)
Value.write(self, section, value:lower())
end
o = rule_s:option(Value, "source", translate("Source IP(s)"))
o:value("", translate("All"))
wa.cbi_add_knownips(o)
o.datatype = "ipmask4"
o = rule_s:option(Value, "srcport", translate("Source Port(s)"))
o:value("", translate("All"))
o.datatype = "or(port, portrange)"
o = rule_s:option(Value, "destination", translate("Destination IP(s)"))
o:value("", translate("All"))
wa.cbi_add_knownips(o)
o.datatype = "ipmask4"
o = rule_s:option(Value, "dstport", translate("Destination Port(s)"))
o:value("", translate("All"))
o.datatype = "or(port, portrange)"
o = rule_s:option(DummyValue, "min_pkt_size", translate("Minimum Packet Length"))
o.cfgvalue = function(...)
local v = tonumber(Value.cfgvalue(...))
if v and v > 0 then
return wa.byte_format(v)
end
return translate("Not set")
end
o = rule_s:option(DummyValue, "max_pkt_size", translate("Maximum Packet Length"))
o.cfgvalue = function(...)
local v = tonumber(Value.cfgvalue(...))
if v and v > 0 then
return wa.byte_format(v)
end
return translate("Not set")
end
o = rule_s:option(DummyValue, "connbytes_kb", translate("Connection Bytes Reach"))
o.cfgvalue = function(...)
local v = tonumber(Value.cfgvalue(...))
if v and v > 0 then
return wa.byte_format(v * 1024)
end
return translate("Not set")
end
if qos.has_ndpi() then
o = rule_s:option(DummyValue, "ndpi", translate("DPI Protocol"))
o.cfgvalue = function(...)
local v = Value.cfgvalue(...)
return v and v:upper() or translate("All")
end
end
return m

View File

@ -1,61 +0,0 @@
-- Copyright 2017 Xingwang Liao <kuoruan@gmail.com>
-- Licensed to the public under the Apache License 2.0.
local m, s, o
local sid = arg[1]
local qos_gargoyle = "qos_gargoyle"
m = Map(qos_gargoyle, translate("Edit Download Service Class"))
m.redirect = luci.dispatcher.build_url("admin/network/qos_gargoyle/download")
if m.uci:get(qos_gargoyle, sid) ~= "download_class" then
luci.http.redirect(m.redirect)
return
end
s = m:section(NamedSection, sid, "download_class")
s.anonymous = true
s.addremove = false
o = s:option(Value, "name", translate("Service Class Name"))
o.rmempty = false
o = s:option(Value, "percent_bandwidth", translate("Percent Bandwidth At Capacity"),
translate("The percentage of the total available bandwidth that should be allocated to this class "
.. "when all available bandwidth is being used. If unused bandwidth is available, more can (and "
.. "will) be allocated. The percentages can be configured to equal more (or less) than 100, but "
.. "when the settings are applied the percentages will be adjusted proportionally so that they "
.. "add to 100. This setting only comes into effect when the WAN link is saturated."))
o.datatype = "range(1, 100)"
o.rmempty = false
o = s:option(Value, "min_bandwidth", translate("Minimum Bandwidth"),
translate("The minimum service this class will be allocated when the link is at capacity. Classes "
.. "which specify minimum service are known as realtime classes by the active congestion "
.. "controller. Streaming video, VoIP and interactive online gaming are all examples of "
.. "applications that must have a minimum bandwith to function. To determine what to enter use "
.. "the application on an unloaded LAN and observe how much bandwidth it uses. Then enter a "
.. "number only slightly higher than this into this field. QoS will satisfiy the minimum service "
.. "of all classes first before allocating to other waiting classes so be careful to use minimum "
.. "bandwidths sparingly."))
o:value("0", translate("Zero"))
o.datatype = "uinteger"
o.default = "0"
o = s:option(Value, "max_bandwidth", translate("Maximum Bandwidth"),
translate("The maximum amount of bandwidth this class will be allocated in kbit/s. Even if unused "
.. "bandwidth is available, this service class will never be permitted to use more than this "
.. "amount of bandwidth."))
o:value("", translate("Unlimited"))
o.datatype = "uinteger"
o = s:option(Flag, "minRTT", translate("Minimize RTT"),
translate("Indicates to the active congestion controller that you wish to minimize round trip "
.. "times (RTT) when this class is active. Use this setting for online gaming or VoIP "
.. "applications that need low round trip times (ping times). Minimizing RTT comes at the expense "
.. "of efficient WAN throughput so while these class are active your WAN throughput will decline "
.. "(usually around 20%)."))
o.enabled = "Yes"
o.disabled = "No"
return m

View File

@ -1,87 +0,0 @@
-- Copyright 2017 Xingwang Liao <kuoruan@gmail.com>
-- Licensed to the public under the Apache License 2.0.
local wa = require "luci.tools.webadmin"
local uci = require "luci.model.uci".cursor()
local qos = require "luci.model.qos_gargoyle"
local m, s, o
local sid = arg[1]
local download_classes = {}
local qos_gargoyle = "qos_gargoyle"
uci:foreach(qos_gargoyle, "download_class", function(s)
local class_alias = s.name
if class_alias then
download_classes[#download_classes + 1] = {name = s[".name"], alias = class_alias}
end
end)
m = Map(qos_gargoyle, translate("Edit Download Classification Rule"))
m.redirect = luci.dispatcher.build_url("admin/network/qos_gargoyle/download")
if m.uci:get(qos_gargoyle, sid) ~= "download_rule" then
luci.http.redirect(m.redirect)
return
end
s = m:section(NamedSection, sid, "download_rule")
s.anonymous = true
s.addremove = false
o = s:option(ListValue, "class", translate("Service Class"))
for _, s in ipairs(download_classes) do o:value(s.name, s.alias) end
o = s:option(Value, "proto", translate("Transport Protocol"))
o:value("", translate("All"))
o:value("tcp", "TCP")
o:value("udp", "UDP")
o:value("icmp", "ICMP")
o:value("gre", "GRE")
o.write = function(self, section, value)
Value.write(self, section, value:lower())
end
o = s:option(Value, "source", translate("Source IP(s)"),
translate("Packet's source ip, can optionally have /[mask] after it (see -s option in iptables "
.. "man page)."))
o:value("", translate("All"))
wa.cbi_add_knownips(o)
o.datatype = "ipmask4"
o = s:option(Value, "srcport", translate("Source Port(s)"),
translate("Packet's source port, can be a range (eg. 80-90)."))
o:value("", translate("All"))
o.datatype = "or(port, portrange)"
o = s:option(Value, "destination", translate("Destination IP(s)"),
translate("Packet's destination ip, can optionally have /[mask] after it (see -d option in "
.. "iptables man page)."))
o:value("", translate("All"))
wa.cbi_add_knownips(o)
o.datatype = "ipmask4"
o = s:option(Value, "dstport", translate("Destination Port(s)"),
translate("Packet's destination port, can be a range (eg. 80-90)."))
o:value("", translate("All"))
o.datatype = "or(port, portrange)"
o = s:option(Value, "min_pkt_size", translate("Minimum Packet Length"),
translate("Packet's minimum size (in bytes)."))
o.datatype = "range(1, 1500)"
o = s:option(Value, "max_pkt_size", translate("Maximum Packet Length"),
translate("Packet's maximum size (in bytes)."))
o.datatype = "range(1, 1500)"
o = s:option(Value, "connbytes_kb", translate("Connection Bytes Reach"),
translate("The total size of data transmitted since the establishment of the link (in kBytes)."))
o.datatype = "range(0, 4194303)"
if qos.has_ndpi() then
o = s:option(ListValue, "ndpi", translate("DPI Protocol"))
o:value("", translate("All"))
qos.cbi_add_dpi_protocols(o)
end
return m

View File

@ -1,123 +0,0 @@
-- Copyright 2017 Xingwang Liao <kuoruan@gmail.com>
-- Licensed to the public under the Apache License 2.0.
local sys = require "luci.sys"
local uci = require "luci.model.uci".cursor()
local net = require "luci.model.network".init()
local qos = require "luci.model.qos_gargoyle"
local m, s, o
local upload_classes = {}
local download_classes = {}
local qos_gargoyle = "qos_gargoyle"
local function qos_enabled()
return sys.init.enabled(qos_gargoyle)
end
uci:foreach(qos_gargoyle, "upload_class", function(s)
local class_alias = s.name
if class_alias then
upload_classes[#upload_classes + 1] = {name = s[".name"], alias = class_alias}
end
end)
uci:foreach(qos_gargoyle, "download_class", function(s)
local class_alias = s.name
if class_alias then
download_classes[#download_classes + 1] = {name = s[".name"], alias = class_alias}
end
end)
m = Map(qos_gargoyle, translate("Gargoyle QoS"),
translate("Quality of Service (QoS) provides a way to control how available bandwidth is "
.. "allocated."))
s = m:section(NamedSection, "global", "global", translate("Global Settings"))
s.anonymous = true
o = s:option(Button, "_switch", nil, translate("QoS Switch"))
o.render = function(self, section, scope)
if qos_enabled() then
self.title = translate("Disable QoS")
self.inputstyle = "reset"
else
self.title = translate("Enable QoS")
self.inputstyle = "apply"
end
Button.render(self, section, scope)
end
o.write = function(...)
if qos_enabled() then
sys.init.stop(qos_gargoyle)
sys.init.disable(qos_gargoyle)
else
sys.init.enable(qos_gargoyle)
sys.init.start(qos_gargoyle)
end
end
s = m:section(NamedSection, "upload", "upload", translate("Upload Settings"))
s.anonymous = true
o = s:option(ListValue, "default_class", translate("Default Service Class"),
translate("Specifie how packets that do not match any rule should be classified."))
for _, s in ipairs(upload_classes) do o:value(s.name, s.alias) end
o = s:option(Value, "total_bandwidth", translate("Total Upload Bandwidth"),
translate("Should be set to around 98% of your available upload bandwidth. Entering a number "
.. "which is too high will result in QoS not meeting its class requirements. Entering a number "
.. "which is too low will needlessly penalize your upload speed. You should use a speed test "
.. "program (with QoS off) to determine available upload bandwidth. Note that bandwidth is "
.. "specified in kbps, leave blank to disable update QoS. There are 8 kilobits per kilobyte."))
o.datatype = "uinteger"
s = m:section(NamedSection, "download", "download", translate("Download Settings"))
s.anonymous = true
o = s:option(ListValue, "default_class", translate("Default Service Class"),
translate("Specifie how packets that do not match any rule should be classified."))
for _, s in ipairs(download_classes) do o:value(s.name, s.alias) end
o = s:option(Value, "total_bandwidth", translate("Total Download Bandwidth"),
translate("Specifying correctly is crucial to making QoS work. Note that bandwidth is specified "
.. "in kbps, leave blank to disable download QoS. There are 8 kilobits per kilobyte."))
o.datatype = "uinteger"
o = s:option(Flag, "qos_monenabled", translate("Enable Active Congestion Control"),
translate("<p>The active congestion control (ACC) observes your download activity and "
.. "automatically adjusts your download link limit to maintain proper QoS performance. ACC "
.. "automatically compensates for changes in your ISP's download speed and the demand from your "
.. "network adjusting the link speed to the highest speed possible which will maintain proper QoS "
.. "function. The effective range of this control is between 15% and 100% of the total download "
.. "bandwidth you entered above.</p>") ..
translate("<p>While ACC does not adjust your upload link speed you must enable and properly "
.. "configure your upload QoS for it to function properly.</p>")
)
o.enabled = "true"
o.disabled = "false"
o = s:option(Value, "ptarget_ip", translate("Use Non-standard Ping Target"),
translate("The segment of network between your router and the ping target is where congestion is "
.. "controlled. By monitoring the round trip ping times to the target congestion is detected. By "
.. "default ACC uses your WAN gateway as the ping target. If you know that congestion on your "
.. "link will occur in a different segment then you can enter an alternate ping target. Leave "
.. "empty to use the default settings."))
o:depends("qos_monenabled", "true")
local wan = qos.get_wan()
if wan then o:value(wan:gwaddr()) end
o.datatype = "ipaddr"
o = s:option(Value, "pinglimit", translate("Manual Ping Limit"),
translate("Round trip ping times are compared against the ping limits. ACC controls the link "
.. "limit to maintain ping times under the appropriate limit. By default ACC attempts to "
.. "automatically select appropriate target ping limits for you based on the link speeds you "
.. "entered and the performance of your link it measures during initialization. You cannot change "
.. "the target ping time for the minRTT mode but by entering a manual time you can control the "
.. "target ping time of the active mode. The time you enter becomes the increase in the target "
.. "ping time between minRTT and active mode. Leave empty to use the default settings."))
o:depends("qos_monenabled", "true")
o:value("Auto", translate("Auto"))
o.datatype = "or('Auto', range(10, 250))"
return m

View File

@ -1,160 +0,0 @@
-- Copyright 2017 Xingwang Liao <kuoruan@gmail.com>
-- Licensed to the public under the Apache License 2.0.
local wa = require "luci.tools.webadmin"
local uci = require "luci.model.uci".cursor()
local dsp = require "luci.dispatcher"
local http = require "luci.http"
local qos = require "luci.model.qos_gargoyle"
local m, class_s, rule_s, o
local upload_classes = {}
local qos_gargoyle = "qos_gargoyle"
uci:foreach(qos_gargoyle, "upload_class", function(s)
local class_alias = s.name
if class_alias then
upload_classes[#upload_classes + 1] = {name = s[".name"], alias = class_alias}
end
end)
m = Map(qos_gargoyle, translate("Upload Settings"))
m.template = "qos_gargoyle/list_view"
class_s = m:section(TypedSection, "upload_class", translate("Service Classes"),
translate("Each upload service class is specified by three parameters: percent bandwidth at "
.. "capacity, minimum bandwidth and maximum bandwidth."))
class_s.anonymous = true
class_s.addremove = true
class_s.template = "cbi/tblsection"
class_s.extedit = dsp.build_url("admin/network/qos_gargoyle/upload/class/%s")
class_s.create = function(...)
local sid = TypedSection.create(...)
if sid then
m.uci:save(qos_gargoyle)
http.redirect(class_s.extedit % sid)
return
end
end
o = class_s:option(DummyValue, "name", translate("Class Name"))
o.cfgvalue = function(...)
return Value.cfgvalue(...) or translate("None")
end
o = class_s:option(DummyValue, "percent_bandwidth", translate("Percent Bandwidth At Capacity"))
o.cfgvalue = function(...)
local v = tonumber(Value.cfgvalue(...))
if v and v > 0 then
return "%d %%" % v
end
return translate("Not set")
end
o = class_s:option(DummyValue, "min_bandwidth", "%s (kbps)" % translate("Minimum Bandwidth"))
o.cfgvalue = function(...)
local v = tonumber(Value.cfgvalue(...))
return v or translate("Zero")
end
o = class_s:option(DummyValue, "max_bandwidth", "%s (kbps)" % translate("Maximum Bandwidth"))
o.cfgvalue = function(...)
local v = tonumber(Value.cfgvalue(...))
return v or translate("Unlimited")
end
o = class_s:option(DummyValue, "_ld", "%s (kbps)" % translate("Load"))
o.rawhtml = true
o.value = "<em class=\"ld-upload\">*</em>"
rule_s = m:section(TypedSection, "upload_rule",translate("Classification Rules"),
translate("Packets are tested against the rules in the order specified -- rules toward the top "
.. "have priority. As soon as a packet matches a rule it is classified, and the rest of the rules "
.. "are ignored. The order of the rules can be altered using the arrow controls.")
)
rule_s.addremove = true
rule_s.sortable = true
rule_s.anonymous = true
rule_s.template = "cbi/tblsection"
rule_s.extedit = dsp.build_url("admin/network/qos_gargoyle/upload/rule/%s")
rule_s.create = function(...)
local sid = TypedSection.create(...)
if sid then
m.uci:save(qos_gargoyle)
http.redirect(rule_s.extedit % sid)
return
end
end
o = rule_s:option(ListValue, "class", translate("Service Class"))
for _, s in ipairs(upload_classes) do o:value(s.name, s.alias) end
o = rule_s:option(Value, "proto", translate("Transport Protocol"))
o:value("", translate("All"))
o:value("tcp", "TCP")
o:value("udp", "UDP")
o:value("icmp", "ICMP")
o:value("gre", "GRE")
o.size = "10"
o.cfgvalue = function(...)
local v = Value.cfgvalue(...)
return v and v:upper() or ""
end
o.write = function(self, section, value)
Value.write(self, section, value:lower())
end
o = rule_s:option(Value, "source", translate("Source IP(s)"))
o:value("", translate("All"))
wa.cbi_add_knownips(o)
o.datatype = "ipmask4"
o = rule_s:option(Value, "srcport", translate("Source Port(s)"))
o:value("", translate("All"))
o.datatype = "or(port, portrange)"
o = rule_s:option(Value, "destination", translate("Destination IP(s)"))
o:value("", translate("All"))
wa.cbi_add_knownips(o)
o.datatype = "ipmask4"
o = rule_s:option(Value, "dstport", translate("Destination Port(s)"))
o:value("", translate("All"))
o.datatype = "or(port, portrange)"
o = rule_s:option(DummyValue, "min_pkt_size", translate("Minimum Packet Length"))
o.cfgvalue = function(...)
local v = tonumber(Value.cfgvalue(...))
if v and v > 0 then
return wa.byte_format(v)
end
return translate("Not set")
end
o = rule_s:option(DummyValue, "max_pkt_size", translate("Maximum Packet Length"))
o.cfgvalue = function(...)
local v = tonumber(Value.cfgvalue(...))
if v and v > 0 then
return wa.byte_format(v)
end
return translate("Not set")
end
o = rule_s:option(DummyValue, "connbytes_kb", translate("Connection Bytes Reach"))
o.cfgvalue = function(...)
local v = tonumber(Value.cfgvalue(...))
if v and v > 0 then
return wa.byte_format(v * 1024)
end
return translate("Not set")
end
if qos.has_ndpi() then
o = rule_s:option(DummyValue, "ndpi", translate("DPI Protocol"))
o.cfgvalue = function(...)
local v = Value.cfgvalue(...)
return v and v:upper() or translate("All")
end
end
return m

View File

@ -1,52 +0,0 @@
-- Copyright 2017 Xingwang Liao <kuoruan@gmail.com>
-- Licensed to the public under the Apache License 2.0.
local m, s, o
local sid = arg[1]
local qos_gargoyle = "qos_gargoyle"
m = Map(qos_gargoyle, translate("Edit Upload Service Class"))
m.redirect = luci.dispatcher.build_url("admin/network/qos_gargoyle/upload")
if m.uci:get(qos_gargoyle, sid) ~= "upload_class" then
luci.http.redirect(m.redirect)
return
end
s = m:section(NamedSection, sid, "upload_class")
s.anonymous = true
s.addremove = false
o = s:option(Value, "name", translate("Service Class Name"))
o.rmempty = false
o = s:option(Value, "percent_bandwidth", translate("Percent Bandwidth At Capacity"),
translate("The percentage of the total available bandwidth that should be allocated to this class "
.. "when all available bandwidth is being used. If unused bandwidth is available, more can (and "
.. "will) be allocated. The percentages can be configured to equal more (or less) than 100, but "
.. "when the settings are applied the percentages will be adjusted proportionally so that they "
.. "add to 100. This setting only comes into effect when the WAN link is saturated."))
o.datatype = "range(1, 100)"
o.rmempty = false
o = s:option(Value, "min_bandwidth", translate("Minimum Bandwidth"),
translate("The minimum service this class will be allocated when the link is at capacity. Classes "
.. "which specify minimum service are known as realtime classes by the active congestion "
.. "controller. Streaming video, VoIP and interactive online gaming are all examples of "
.. "applications that must have a minimum bandwith to function. To determine what to enter use "
.. "the application on an unloaded LAN and observe how much bandwidth it uses. Then enter a "
.. "number only slightly higher than this into this field. QoS will satisfiy the minimum service "
.. "of all classes first before allocating to other waiting classes so be careful to use minimum "
.. "bandwidths sparingly."))
o:value("0", translate("Zero"))
o.datatype = "uinteger"
o.default = "0"
o = s:option(Value, "max_bandwidth", translate("Maximum Bandwidth"),
translate("The maximum amount of bandwidth this class will be allocated in kbit/s. Even if unused "
.. "bandwidth is available, this service class will never be permitted to use more than this "
.. "amount of bandwidth."))
o:value("", translate("Unlimited"))
o.datatype = "uinteger"
return m

View File

@ -1,87 +0,0 @@
-- Copyright 2017 Xingwang Liao <kuoruan@gmail.com>
-- Licensed to the public under the Apache License 2.0.
local wa = require "luci.tools.webadmin"
local uci = require "luci.model.uci".cursor()
local qos = require "luci.model.qos_gargoyle"
local m, s, o
local sid = arg[1]
local upload_classes = {}
local qos_gargoyle = "qos_gargoyle"
uci:foreach(qos_gargoyle, "upload_class", function(s)
local class_alias = s.name
if class_alias then
upload_classes[#upload_classes + 1] = {name = s[".name"], alias = class_alias}
end
end)
m = Map(qos_gargoyle, translate("Edit Upload Classification Rule"))
m.redirect = luci.dispatcher.build_url("admin/network/qos_gargoyle/upload")
if m.uci:get(qos_gargoyle, sid) ~= "upload_rule" then
luci.http.redirect(m.redirect)
return
end
s = m:section(NamedSection, sid, "upload_rule")
s.anonymous = true
s.addremove = false
o = s:option(ListValue, "class", translate("Service Class"))
for _, s in ipairs(upload_classes) do o:value(s.name, s.alias) end
o = s:option(Value, "proto", translate("Transport Protocol"))
o:value("", translate("All"))
o:value("tcp", "TCP")
o:value("udp", "UDP")
o:value("icmp", "ICMP")
o:value("gre", "GRE")
o.write = function(self, section, value)
Value.write(self, section, value:lower())
end
o = s:option(Value, "source", translate("Source IP(s)"),
translate("Packet's source ip, can optionally have /[mask] after it (see -s option in iptables "
.. "man page)."))
o:value("", translate("All"))
wa.cbi_add_knownips(o)
o.datatype = "ipmask4"
o = s:option(Value, "srcport", translate("Source Port(s)"),
translate("Packet's source port, can be a range (eg. 80-90)."))
o:value("", translate("All"))
o.datatype = "or(port, portrange)"
o = s:option(Value, "destination", translate("Destination IP(s)"),
translate("Packet's destination ip, can optionally have /[mask] after it (see -d option in "
.. "iptables man page)."))
o:value("", translate("All"))
wa.cbi_add_knownips(o)
o.datatype = "ipmask4"
o = s:option(Value, "dstport", translate("Destination Port(s)"),
translate("Packet's destination port, can be a range (eg. 80-90)."))
o:value("", translate("All"))
o.datatype = "or(port, portrange)"
o = s:option(Value, "min_pkt_size", translate("Minimum Packet Length"),
translate("Packet's minimum size (in bytes)."))
o.datatype = "range(1, 1500)"
o = s:option(Value, "max_pkt_size", translate("Maximum Packet Length"),
translate("Packet's maximum size (in bytes)."))
o.datatype = "range(1, 1500)"
o = s:option(Value, "connbytes_kb", translate("Connection Bytes Reach"),
translate("The total size of data transmitted since the establishment of the link (in kBytes)."))
o.datatype = "range(0, 4194303)"
if qos.has_ndpi() then
o = s:option(ListValue, "ndpi", translate("DPI Protocol"))
o:value("", translate("All"))
qos.cbi_add_dpi_protocols(o)
end
return m

View File

@ -1,31 +0,0 @@
-- Copyright 2017 Xingwang Liao <kuoruan@gmail.com>
-- Licensed to the public under the Apache License 2.0.
module("luci.model.qos_gargoyle", package.seeall)
function has_ndpi()
return luci.sys.call("lsmod | cut -d ' ' -f1 | grep -q 'xt_ndpi'") == 0
end
function cbi_add_dpi_protocols(field)
local util = require "luci.util"
local dpi_protocols = {}
for line in util.execi("iptables -m ndpi --help 2>/dev/null | grep '^--'") do
local _, _, protocol, name = line:find("%-%-([^%s]+) Match for ([^%s]+)")
if protocol and name then
dpi_protocols[protocol] = name
end
end
for p, n in util.kspairs(dpi_protocols) do
field:value(p, n)
end
end
function get_wan()
local net = require "luci.model.network".init()
return net:get_wannet()
end

View File

@ -1,97 +0,0 @@
<%#
Copyright 2017 Xingwang Liao <kuoruan@gmail.com>
Licensed to the public under the Apache License 2.0.
-%>
<%
local dsp = require "luci.dispatcher"
local request = dsp.context.path
local leaf = request[#request]
-%>
<style title="text/css">
/* container for entire page. fixes bootstrap theme's ridiculously small page width */
.container {
max-width: none;
margin: 0 30px;
width: auto;
}
/* column for configuration section name */
table th {
padding: 5px 0;
text-align: center;
vertical-align: middle;
}
/* cells showing the configuration values */
table td {
padding: 5px 0;
text-align: center;
vertical-align: middle;
}
/* sort buttons column */
table.cbi-section-table td.cbi-section-table-cell {
text-align: center;
}
</style>
<%+cbi/map%>
<script type="text/javascript">//<![CDATA[
function bpsToKbpsString(bps) {
var kbps = '*';
var bpsn = parseInt(bps) / 1000;
if (isNaN(bpsn)) {
kbps = '*';
} else if (bpsn < 1) {
kbps = bpsn.toFixed(1) + '';
} else {
kbps = bpsn.toFixed(0) + '';
}
return kbps;
}
(function(doc){
var tbs = doc.getElementsByClassName('ld-<%=leaf%>');
var lastTime = 0;
var dataTable = [];
var updateInProgress = false;
XHR.poll(3, '<%=dsp.build_url("admin", "network", "qos_gargoyle", "load_data", leaf)%>',
null, function(res) {
if (res.readyState == 4 && tbs) {
var timestamp = new Date().getTime();
var timeDiff = timestamp - lastTime;
lastTime = timestamp;
if (updateInProgress) return;
updateInProgress = true;
var lines = res.responseText.match(/hfsc\s1:[0-9]{1,2}\s.+leaf.+\n.+Sent\s[0-9]+/g);
if (lines) {
for (var i = 0, len = lines.length; i < len; i++) {
var line = lines[i];
var idx = parseInt(line.match(/hfsc\s1:([0-9]+)/)[1]) - 2;
var lastBytes;
if (idx < tbs.length) {
lastBytes = dataTable[idx];
var newBytes = line.match(/Sent\s([0-9]+)/)[1];
dataTable[idx] = newBytes;
var bps = NaN;
if (lastBytes) {
var diffBytes = parseInt(newBytes) - parseInt(lastBytes);
if (diffBytes <= 0) {
bps = 0;
} else {
bps = diffBytes * 8000 / timeDiff;
}
}
tbs[idx].innerText = bpsToKbpsString(bps);
}
}
}
updateInProgress = false;
}
}
);
}(document));
//]]></script>

View File

@ -1,52 +0,0 @@
<%#
Copyright 2017 Xingwang Liao <kuoruan@gmail.com>
Licensed to the public under the Apache License 2.0.
-%>
<% css = [[
#troubleshoot_text {
padding: 20px;
text-align: left;
}
#troubleshoot_text pre {
word-break: break-all;
margin: 0;
}
.description {
background-color: #33CCFF;
}
]]
-%>
<%+header%>
<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
<script type="text/javascript">//<![CDATA[
XHR.poll(15, '<%=url("admin/network/qos_gargoyle/troubleshooting/data")%>', null,
function(x, data) {
var tshoot = document.getElementById('troubleshoot_text');
if (data.hasOwnProperty("show")) {
tshoot.innerHTML = String.format(
'<pre>%s%s%s%s</pre>',
'<span class="description">Output of &#34;/etc/init.d/qos_gargoyle show&#34; : </span><br /><br />',
data.show,
'<br /><br /><span class="description">Output of &#34;cat /tmp/qosmon.status&#34; : </span><br /><br />',
data.mon
);
} else {
tshoot.innerHTML = '<strong><%:Error collecting troubleshooting information%></strong>';
}
}
);
//]]></script>
<div id="troubleshoot">
<fieldset class="cbi-section">
<legend><%:Troubleshooting Data%></legend>
<div id="troubleshoot_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /><%:Collecting data...%></div>
</fieldset>
</div>
<%+footer%>

View File

@ -1,292 +0,0 @@
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8"
msgid "\"Active Congestion Control\" not enabled"
msgstr ""
msgid ""
"<p>The active congestion control (ACC) observes your download activity and "
"automatically adjusts your download link limit to maintain proper QoS "
"performance. ACC automatically compensates for changes in your ISP's "
"download speed and the demand from your network adjusting the link speed to "
"the highest speed possible which will maintain proper QoS function. The "
"effective range of this control is between 15% and 100% of the total "
"download bandwidth you entered above.</p>"
msgstr ""
msgid ""
"<p>While ACC does not adjust your upload link speed you must enable and "
"properly configure your upload QoS for it to function properly.</p>"
msgstr ""
msgid "All"
msgstr ""
msgid "Auto"
msgstr ""
msgid "Class Name"
msgstr ""
msgid "Classification Rules"
msgstr ""
msgid "Collecting data..."
msgstr ""
msgid "Connection Bytes Reach"
msgstr ""
msgid "DPI Protocol"
msgstr ""
msgid "Default Service Class"
msgstr ""
msgid "Destination IP(s)"
msgstr ""
msgid "Destination Port(s)"
msgstr ""
msgid "Disable QoS"
msgstr ""
msgid "Download Settings"
msgstr ""
msgid ""
"Each service class is specified by four parameters: percent bandwidth at "
"capacity, realtime bandwidth and maximum bandwidth and the minimimze round "
"trip time flag."
msgstr ""
msgid ""
"Each upload service class is specified by three parameters: percent "
"bandwidth at capacity, minimum bandwidth and maximum bandwidth."
msgstr ""
msgid "Edit Download Classification Rule"
msgstr ""
msgid "Edit Download Service Class"
msgstr ""
msgid "Edit Upload Classification Rule"
msgstr ""
msgid "Edit Upload Service Class"
msgstr ""
msgid "Enable Active Congestion Control"
msgstr ""
msgid "Enable QoS"
msgstr ""
msgid "Error collecting troubleshooting information"
msgstr ""
msgid "Gargoyle QoS"
msgstr ""
msgid "Global Settings"
msgstr ""
msgid ""
"Indicates to the active congestion controller that you wish to minimize "
"round trip times (RTT) when this class is active. Use this setting for "
"online gaming or VoIP applications that need low round trip times (ping "
"times). Minimizing RTT comes at the expense of efficient WAN throughput so "
"while these class are active your WAN throughput will decline (usually "
"around 20%)."
msgstr ""
msgid "Load"
msgstr ""
msgid "Loading"
msgstr ""
msgid "Manual Ping Limit"
msgstr ""
msgid "Maximum Bandwidth"
msgstr ""
msgid "Maximum Packet Length"
msgstr ""
msgid "Minimize RTT"
msgstr ""
msgid "Minimum Bandwidth"
msgstr ""
msgid "Minimum Packet Length"
msgstr ""
msgid "No"
msgstr ""
msgid "No data found"
msgstr ""
msgid "None"
msgstr ""
msgid "Not set"
msgstr ""
msgid ""
"Packet's destination ip, can optionally have /[mask] after it (see -d option "
"in iptables man page)."
msgstr ""
msgid "Packet's destination port, can be a range (eg. 80-90)."
msgstr ""
msgid "Packet's maximum size (in bytes)."
msgstr ""
msgid "Packet's minimum size (in bytes)."
msgstr ""
msgid ""
"Packet's source ip, can optionally have /[mask] after it (see -s option in "
"iptables man page)."
msgstr ""
msgid "Packet's source port, can be a range (eg. 80-90)."
msgstr ""
msgid ""
"Packets are tested against the rules in the order specified -- rules toward "
"the top have priority. As soon as a packet matches a rule it is classified, "
"and the rest of the rules are ignored. The order of the rules can be altered "
"using the arrow controls."
msgstr ""
msgid "Percent Bandwidth At Capacity"
msgstr ""
msgid "QoS Switch"
msgstr ""
msgid ""
"Quality of Service (QoS) provides a way to control how available bandwidth "
"is allocated."
msgstr ""
msgid ""
"Round trip ping times are compared against the ping limits. ACC controls the "
"link limit to maintain ping times under the appropriate limit. By default "
"ACC attempts to automatically select appropriate target ping limits for you "
"based on the link speeds you entered and the performance of your link it "
"measures during initialization. You cannot change the target ping time for "
"the minRTT mode but by entering a manual time you can control the target "
"ping time of the active mode. The time you enter becomes the increase in the "
"target ping time between minRTT and active mode. Leave empty to use the "
"default settings."
msgstr ""
msgid "Service Class"
msgstr ""
msgid "Service Class Name"
msgstr ""
msgid "Service Classes"
msgstr ""
msgid ""
"Should be set to around 98% of your available upload bandwidth. Entering a "
"number which is too high will result in QoS not meeting its class "
"requirements. Entering a number which is too low will needlessly penalize "
"your upload speed. You should use a speed test program (with QoS off) to "
"determine available upload bandwidth. Note that bandwidth is specified in "
"kbps, leave blank to disable update QoS. There are 8 kilobits per kilobyte."
msgstr ""
msgid "Source IP(s)"
msgstr ""
msgid "Source Port(s)"
msgstr ""
msgid "Specifie how packets that do not match any rule should be classified."
msgstr ""
msgid ""
"Specifying correctly is crucial to making QoS work. Note that bandwidth is "
"specified in kbps, leave blank to disable download QoS. There are 8 kilobits "
"per kilobyte."
msgstr ""
msgid ""
"The maximum amount of bandwidth this class will be allocated in kbit/s. Even "
"if unused bandwidth is available, this service class will never be permitted "
"to use more than this amount of bandwidth."
msgstr ""
msgid ""
"The minimum service this class will be allocated when the link is at "
"capacity. Classes which specify minimum service are known as realtime "
"classes by the active congestion controller. Streaming video, VoIP and "
"interactive online gaming are all examples of applications that must have a "
"minimum bandwith to function. To determine what to enter use the application "
"on an unloaded LAN and observe how much bandwidth it uses. Then enter a "
"number only slightly higher than this into this field. QoS will satisfiy the "
"minimum service of all classes first before allocating to other waiting "
"classes so be careful to use minimum bandwidths sparingly."
msgstr ""
msgid ""
"The percentage of the total available bandwidth that should be allocated to "
"this class when all available bandwidth is being used. If unused bandwidth "
"is available, more can (and will) be allocated. The percentages can be "
"configured to equal more (or less) than 100, but when the settings are "
"applied the percentages will be adjusted proportionally so that they add to "
"100. This setting only comes into effect when the WAN link is saturated."
msgstr ""
msgid ""
"The segment of network between your router and the ping target is where "
"congestion is controlled. By monitoring the round trip ping times to the "
"target congestion is detected. By default ACC uses your WAN gateway as the "
"ping target. If you know that congestion on your link will occur in a "
"different segment then you can enter an alternate ping target. Leave empty "
"to use the default settings."
msgstr ""
msgid ""
"The total size of data transmitted since the establishment of the link (in "
"kBytes)."
msgstr ""
msgid "Total Download Bandwidth"
msgstr ""
msgid "Total Upload Bandwidth"
msgstr ""
msgid "Transport Protocol"
msgstr ""
msgid "Troubleshooting"
msgstr ""
msgid "Troubleshooting Data"
msgstr ""
msgid "Unlimited"
msgstr ""
msgid "Upload Settings"
msgstr ""
msgid "Use Non-standard Ping Target"
msgstr ""
msgid "Zero"
msgstr ""

View File

@ -1,334 +0,0 @@
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8\n"
msgid "\"Active Congestion Control\" not enabled"
msgstr "“主动拥塞控制”未启用"
msgid ""
"<p>The active congestion control (ACC) observes your download activity and "
"automatically adjusts your download link limit to maintain proper QoS "
"performance. ACC automatically compensates for changes in your ISP's "
"download speed and the demand from your network adjusting the link speed to "
"the highest speed possible which will maintain proper QoS function. The "
"effective range of this control is between 15% and 100% of the total "
"download bandwidth you entered above.</p>"
msgstr ""
"<p>主动拥塞控制系统ACC观察你的下载活动并自动调整你的下载链接限制以保持适"
"当的 QoS 性能。ACC 自动调整 QoS 功能以补偿来自你 ISP 的下载速度变化及来自你网"
"络链接速度的调整需求,使速度最大化。这个控制的有效范围在你上面输入的下载总带"
"宽的 15% 至 100% 之间。</p>"
msgid ""
"<p>While ACC does not adjust your upload link speed you must enable and "
"properly configure your upload QoS for it to function properly.</p>"
msgstr ""
"<p>虽然 ACC 不调整你的上传链路速度,但你必须启用并正确配置你的上传 QoS 带宽以"
"使该功能正常工作。</p>"
msgid "All"
msgstr "全部"
msgid "Auto"
msgstr "自动"
msgid "Class Name"
msgstr "类型名称"
msgid "Classification Rules"
msgstr "分类规则"
msgid "Collecting data..."
msgstr "正在收集数据..."
msgid "Connection Bytes Reach"
msgstr "连接流量达到"
msgid "DPI Protocol"
msgstr "DPI 协议"
msgid "Default Service Class"
msgstr "默认服务类型"
msgid "Destination IP(s)"
msgstr "目标 IP"
msgid "Destination Port(s)"
msgstr "目标端口"
msgid "Disable QoS"
msgstr "禁用 QoS"
msgid "Download Settings"
msgstr "下载设置"
msgid ""
"Each service class is specified by four parameters: percent bandwidth at "
"capacity, realtime bandwidth and maximum bandwidth and the minimimze round "
"trip time flag."
msgstr ""
"每个下载服务类型由四个参数指定:带宽占用百分比、最小保证带宽、最大带宽和最小"
"往返延时标志。"
msgid ""
"Each upload service class is specified by three parameters: percent "
"bandwidth at capacity, minimum bandwidth and maximum bandwidth."
msgstr "每个上传服务类型由三个参数指定:带宽占用百分比、最小带宽和最大带宽。"
msgid "Edit Download Classification Rule"
msgstr "编辑下载分类规则"
msgid "Edit Download Service Class"
msgstr "编辑下载服务类型"
msgid "Edit Upload Classification Rule"
msgstr "编辑上传分类规则"
msgid "Edit Upload Service Class"
msgstr "编辑上传服务类型"
msgid "Enable Active Congestion Control"
msgstr "启用主动拥塞控制"
msgid "Enable QoS"
msgstr "启用 QoS"
msgid "Error collecting troubleshooting information"
msgstr "收集故障排查信息失败"
msgid "Gargoyle QoS"
msgstr "石像鬼 QoS"
msgid "Global Settings"
msgstr "全局设置"
msgid ""
"Indicates to the active congestion controller that you wish to minimize "
"round trip times (RTT) when this class is active. Use this setting for "
"online gaming or VoIP applications that need low round trip times (ping "
"times). Minimizing RTT comes at the expense of efficient WAN throughput so "
"while these class are active your WAN throughput will decline (usually "
"around 20%)."
msgstr ""
"告诉主动拥塞控制器你希望该服务类型启用时尽量减少往返延时RTT。该设置一般用"
"在 VoIP 或在线游戏这类需要低延时Ping 值的应用上。减小往返延时RTT会带"
"来WAN有效吞吐量的额外花销所以当这些服务类型启用时你的 WAN 吞吐量将下降(通"
"常在20%左右)"
msgid "Load"
msgstr "负载"
msgid "Loading"
msgstr "正在加载"
msgid "Manual Ping Limit"
msgstr "手动 Ping 限制"
msgid "Maximum Bandwidth"
msgstr "最大带宽"
msgid "Maximum Packet Length"
msgstr "最大数据包长度"
msgid "Minimize RTT"
msgstr "最小往返延时"
msgid "Minimum Bandwidth"
msgstr "最小带宽"
msgid "Minimum Packet Length"
msgstr "最小数据包长度"
msgid "No"
msgstr "否"
msgid "No data found"
msgstr "无数据"
msgid "None"
msgstr "无"
msgid "Not set"
msgstr "未设置"
msgid ""
"Packet's destination ip, can optionally have /[mask] after it (see -d option "
"in iptables man page)."
msgstr ""
"数据包的目标 IP可以在后面加子网掩码/[mask],请看 iptables 的 -d 参数说"
"明)"
msgid "Packet's destination port, can be a range (eg. 80-90)."
msgstr "数据包的目标端口可以是一个范围例如80-90"
msgid "Packet's maximum size (in bytes)."
msgstr "数据包的最大大小单位bytes"
msgid "Packet's minimum size (in bytes)."
msgstr "数据包的最小大小单位bytes"
msgid ""
"Packet's source ip, can optionally have /[mask] after it (see -s option in "
"iptables man page)."
msgstr ""
"数据包的源 IP可以在后面加子网掩码/[mask],请看 iptables 的 -s 参数说明)"
msgid "Packet's source port, can be a range (eg. 80-90)."
msgstr "数据包的源端口可以是一个范围例如80-90"
msgid ""
"Packets are tested against the rules in the order specified -- rules toward "
"the top have priority. As soon as a packet matches a rule it is classified, "
"and the rest of the rules are ignored. The order of the rules can be altered "
"using the arrow controls."
msgstr ""
"数据包将按规则中指定的顺序进行匹配 —— 靠上的规则优先进行匹配。一旦数据包匹配"
"一条规则那它将被归类,并且其余的规则将被忽略。使用上下箭头可调整规则的顺序。"
msgid "Percent Bandwidth At Capacity"
msgstr "带宽占用百分比"
msgid "QoS Switch"
msgstr "QoS 开关"
msgid ""
"Quality of Service (QoS) provides a way to control how available bandwidth "
"is allocated."
msgstr "QoS 可以用来分配和控制可用带宽。"
msgid ""
"Round trip ping times are compared against the ping limits. ACC controls the "
"link limit to maintain ping times under the appropriate limit. By default "
"ACC attempts to automatically select appropriate target ping limits for you "
"based on the link speeds you entered and the performance of your link it "
"measures during initialization. You cannot change the target ping time for "
"the minRTT mode but by entering a manual time you can control the target "
"ping time of the active mode. The time you enter becomes the increase in the "
"target ping time between minRTT and active mode. Leave empty to use the "
"default settings."
msgstr ""
"Ping 延时会与 Ping 限制进行比较。ACC 控制链路限制以保持 Ping 延时在适当范围。"
"默认情况下ACC 会自动根据你输入的链接适当为你选择适当的 Ping 限制。如果你想"
"尝试不同的 Ping 限制,你可以在这里输入一个时间值。输入高的时间值将导致更高的 "
"Ping 限制,低的时间值会有更低的限制。留空则将由 ACC 自动控制。"
msgid "Service Class"
msgstr "服务类型"
msgid "Service Class Name"
msgstr "服务类型名称"
msgid "Service Classes"
msgstr "服务类型"
msgid ""
"Should be set to around 98% of your available upload bandwidth. Entering a "
"number which is too high will result in QoS not meeting its class "
"requirements. Entering a number which is too low will needlessly penalize "
"your upload speed. You should use a speed test program (with QoS off) to "
"determine available upload bandwidth. Note that bandwidth is specified in "
"kbps, leave blank to disable update QoS. There are 8 kilobits per kilobyte."
msgstr ""
"应被设置为你可用上传带宽的 98% 左右。输入数值太高将导致 QoS 不能匹配服务类型"
"的要求。输入数值太低将造成不必要的上传速度限制。你应当在 QoS 关闭的情况下使用"
"测速程序以确定可用的上传带宽。带宽以 kbps 为单位,留空以禁用上传 QoS。"
"1KByte/s=8Kbps"
msgid "Source IP(s)"
msgstr "源 IP"
msgid "Source Port(s)"
msgstr "源端口"
msgid "Specifie how packets that do not match any rule should be classified."
msgstr "指定当数据包不匹配任何规则时将被如何归类。"
msgid ""
"Specifying correctly is crucial to making QoS work. Note that bandwidth is "
"specified in kbps, leave blank to disable download QoS. There are 8 kilobits "
"per kilobyte."
msgstr ""
"正确设置对于 QoS 的工作至关重要。带宽以 kbps 为单位,留空以禁用下载 QoS。"
"1KByte/s=8Kbps"
msgid ""
"The maximum amount of bandwidth this class will be allocated in kbit/s. Even "
"if unused bandwidth is available, this service class will never be permitted "
"to use more than this amount of bandwidth."
msgstr ""
"该服务类型可被分配的带宽最大值(以 kbit/s 为单位)。即使存在未使用带宽,该服"
"务类型也将永远不被允许使用超过此量的带宽。"
msgid ""
"The minimum service this class will be allocated when the link is at "
"capacity. Classes which specify minimum service are known as realtime "
"classes by the active congestion controller. Streaming video, VoIP and "
"interactive online gaming are all examples of applications that must have a "
"minimum bandwith to function. To determine what to enter use the application "
"on an unloaded LAN and observe how much bandwidth it uses. Then enter a "
"number only slightly higher than this into this field. QoS will satisfiy the "
"minimum service of all classes first before allocating to other waiting "
"classes so be careful to use minimum bandwidths sparingly."
msgstr ""
"将被分配用于该服务类型的最低链路带宽容量。指定了最小带宽的服务类型会被主动拥"
"塞控制器看作实时类应用。例如视频流、VoIP 和在线互动游戏都应该设置最小带宽。要"
"确定需要多少最小带宽,可以在一个没有负载的局域网中使用应用程序并观察它使用了"
"多少带宽。然后输入一个略高于这个值的数字到字段中。在分配剩余带宽到其它服务类"
"型前QoS 将优先满足所有服务类型的最小带宽要求,所以要谨慎地使用最小带宽。"
msgid ""
"The percentage of the total available bandwidth that should be allocated to "
"this class when all available bandwidth is being used. If unused bandwidth "
"is available, more can (and will) be allocated. The percentages can be "
"configured to equal more (or less) than 100, but when the settings are "
"applied the percentages will be adjusted proportionally so that they add to "
"100. This setting only comes into effect when the WAN link is saturated."
msgstr ""
"当所有可用带宽被占满后该服务类型占据总带宽的百分比。如果带宽未用完,该服务类"
"型会被分配更多带宽。该百分比值可被设置为等于、大于或小于 100但当设置被应用"
"时,百分比值将会被按比例调整以便使它们加起来等于 100。该设置只在 WAN 端链路饱"
"和时才生效。<em>PSWAN 端链路饱和即带宽被占用完)</em>"
msgid ""
"The segment of network between your router and the ping target is where "
"congestion is controlled. By monitoring the round trip ping times to the "
"target congestion is detected. By default ACC uses your WAN gateway as the "
"ping target. If you know that congestion on your link will occur in a "
"different segment then you can enter an alternate ping target. Leave empty "
"to use the default settings."
msgstr ""
"在路由器和 Ping 目标之间的网络部分是拥塞控制的地方。拥塞通过监视和目标间的 "
"Ping 延时来检测。默认情况下 ACC 使用你的 WAN 网关作为 Ping 的目标。假如你知道"
"拥塞会在你链路的不同段发生,你可用输入一个备用的 Ping 目标。留空则将由ACC 自"
"动控制。"
msgid ""
"The total size of data transmitted since the establishment of the link (in "
"kBytes)."
msgstr "自连接建立以来传输的数据总量单位kBytes"
msgid "Total Download Bandwidth"
msgstr "下载总带宽"
msgid "Total Upload Bandwidth"
msgstr "上传总带宽"
msgid "Transport Protocol"
msgstr "协议"
msgid "Troubleshooting"
msgstr "故障排除"
msgid "Troubleshooting Data"
msgstr "故障排除数据"
msgid "Unlimited"
msgstr "不限制"
msgid "Upload Settings"
msgstr "上传设置"
msgid "Use Non-standard Ping Target"
msgstr "使用自定义 Ping 目标"
msgid "Zero"
msgstr "零"

View File

@ -1,334 +0,0 @@
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8\n"
msgid "\"Active Congestion Control\" not enabled"
msgstr "“主动拥塞控制”未启用"
msgid ""
"<p>The active congestion control (ACC) observes your download activity and "
"automatically adjusts your download link limit to maintain proper QoS "
"performance. ACC automatically compensates for changes in your ISP's "
"download speed and the demand from your network adjusting the link speed to "
"the highest speed possible which will maintain proper QoS function. The "
"effective range of this control is between 15% and 100% of the total "
"download bandwidth you entered above.</p>"
msgstr ""
"<p>主动拥塞控制系统ACC观察你的下载活动并自动调整你的下载链接限制以保持适"
"当的 QoS 性能。ACC 自动调整 QoS 功能以补偿来自你 ISP 的下载速度变化及来自你网"
"络链接速度的调整需求,使速度最大化。这个控制的有效范围在你上面输入的下载总带"
"宽的 15% 至 100% 之间。</p>"
msgid ""
"<p>While ACC does not adjust your upload link speed you must enable and "
"properly configure your upload QoS for it to function properly.</p>"
msgstr ""
"<p>虽然 ACC 不调整你的上传链路速度,但你必须启用并正确配置你的上传 QoS 带宽以"
"使该功能正常工作。</p>"
msgid "All"
msgstr "全部"
msgid "Auto"
msgstr "自动"
msgid "Class Name"
msgstr "类型名称"
msgid "Classification Rules"
msgstr "分类规则"
msgid "Collecting data..."
msgstr "正在收集数据..."
msgid "Connection Bytes Reach"
msgstr "连接流量达到"
msgid "DPI Protocol"
msgstr "DPI 协议"
msgid "Default Service Class"
msgstr "默认服务类型"
msgid "Destination IP(s)"
msgstr "目标 IP"
msgid "Destination Port(s)"
msgstr "目标端口"
msgid "Disable QoS"
msgstr "禁用 QoS"
msgid "Download Settings"
msgstr "下载设置"
msgid ""
"Each service class is specified by four parameters: percent bandwidth at "
"capacity, realtime bandwidth and maximum bandwidth and the minimimze round "
"trip time flag."
msgstr ""
"每个下载服务类型由四个参数指定:带宽占用百分比、最小保证带宽、最大带宽和最小"
"往返延时标志。"
msgid ""
"Each upload service class is specified by three parameters: percent "
"bandwidth at capacity, minimum bandwidth and maximum bandwidth."
msgstr "每个上传服务类型由三个参数指定:带宽占用百分比、最小带宽和最大带宽。"
msgid "Edit Download Classification Rule"
msgstr "编辑下载分类规则"
msgid "Edit Download Service Class"
msgstr "编辑下载服务类型"
msgid "Edit Upload Classification Rule"
msgstr "编辑上传分类规则"
msgid "Edit Upload Service Class"
msgstr "编辑上传服务类型"
msgid "Enable Active Congestion Control"
msgstr "启用主动拥塞控制"
msgid "Enable QoS"
msgstr "启用 QoS"
msgid "Error collecting troubleshooting information"
msgstr "收集故障排查信息失败"
msgid "Gargoyle QoS"
msgstr "石像鬼 QoS"
msgid "Global Settings"
msgstr "全局设置"
msgid ""
"Indicates to the active congestion controller that you wish to minimize "
"round trip times (RTT) when this class is active. Use this setting for "
"online gaming or VoIP applications that need low round trip times (ping "
"times). Minimizing RTT comes at the expense of efficient WAN throughput so "
"while these class are active your WAN throughput will decline (usually "
"around 20%)."
msgstr ""
"告诉主动拥塞控制器你希望该服务类型启用时尽量减少往返延时RTT。该设置一般用"
"在 VoIP 或在线游戏这类需要低延时Ping 值的应用上。减小往返延时RTT会带"
"来WAN有效吞吐量的额外花销所以当这些服务类型启用时你的 WAN 吞吐量将下降(通"
"常在20%左右)"
msgid "Load"
msgstr "负载"
msgid "Loading"
msgstr "正在加载"
msgid "Manual Ping Limit"
msgstr "手动 Ping 限制"
msgid "Maximum Bandwidth"
msgstr "最大带宽"
msgid "Maximum Packet Length"
msgstr "最大数据包长度"
msgid "Minimize RTT"
msgstr "最小往返延时"
msgid "Minimum Bandwidth"
msgstr "最小带宽"
msgid "Minimum Packet Length"
msgstr "最小数据包长度"
msgid "No"
msgstr "否"
msgid "No data found"
msgstr "无数据"
msgid "None"
msgstr "无"
msgid "Not set"
msgstr "未设置"
msgid ""
"Packet's destination ip, can optionally have /[mask] after it (see -d option "
"in iptables man page)."
msgstr ""
"数据包的目标 IP可以在后面加子网掩码/[mask],请看 iptables 的 -d 参数说"
"明)"
msgid "Packet's destination port, can be a range (eg. 80-90)."
msgstr "数据包的目标端口可以是一个范围例如80-90"
msgid "Packet's maximum size (in bytes)."
msgstr "数据包的最大大小单位bytes"
msgid "Packet's minimum size (in bytes)."
msgstr "数据包的最小大小单位bytes"
msgid ""
"Packet's source ip, can optionally have /[mask] after it (see -s option in "
"iptables man page)."
msgstr ""
"数据包的源 IP可以在后面加子网掩码/[mask],请看 iptables 的 -s 参数说明)"
msgid "Packet's source port, can be a range (eg. 80-90)."
msgstr "数据包的源端口可以是一个范围例如80-90"
msgid ""
"Packets are tested against the rules in the order specified -- rules toward "
"the top have priority. As soon as a packet matches a rule it is classified, "
"and the rest of the rules are ignored. The order of the rules can be altered "
"using the arrow controls."
msgstr ""
"数据包将按规则中指定的顺序进行匹配 —— 靠上的规则优先进行匹配。一旦数据包匹配"
"一条规则那它将被归类,并且其余的规则将被忽略。使用上下箭头可调整规则的顺序。"
msgid "Percent Bandwidth At Capacity"
msgstr "带宽占用百分比"
msgid "QoS Switch"
msgstr "QoS 开关"
msgid ""
"Quality of Service (QoS) provides a way to control how available bandwidth "
"is allocated."
msgstr "QoS 可以用来分配和控制可用带宽。"
msgid ""
"Round trip ping times are compared against the ping limits. ACC controls the "
"link limit to maintain ping times under the appropriate limit. By default "
"ACC attempts to automatically select appropriate target ping limits for you "
"based on the link speeds you entered and the performance of your link it "
"measures during initialization. You cannot change the target ping time for "
"the minRTT mode but by entering a manual time you can control the target "
"ping time of the active mode. The time you enter becomes the increase in the "
"target ping time between minRTT and active mode. Leave empty to use the "
"default settings."
msgstr ""
"Ping 延时会与 Ping 限制进行比较。ACC 控制链路限制以保持 Ping 延时在适当范围。"
"默认情况下ACC 会自动根据你输入的链接适当为你选择适当的 Ping 限制。如果你想"
"尝试不同的 Ping 限制,你可以在这里输入一个时间值。输入高的时间值将导致更高的 "
"Ping 限制,低的时间值会有更低的限制。留空则将由 ACC 自动控制。"
msgid "Service Class"
msgstr "服务类型"
msgid "Service Class Name"
msgstr "服务类型名称"
msgid "Service Classes"
msgstr "服务类型"
msgid ""
"Should be set to around 98% of your available upload bandwidth. Entering a "
"number which is too high will result in QoS not meeting its class "
"requirements. Entering a number which is too low will needlessly penalize "
"your upload speed. You should use a speed test program (with QoS off) to "
"determine available upload bandwidth. Note that bandwidth is specified in "
"kbps, leave blank to disable update QoS. There are 8 kilobits per kilobyte."
msgstr ""
"应被设置为你可用上传带宽的 98% 左右。输入数值太高将导致 QoS 不能匹配服务类型"
"的要求。输入数值太低将造成不必要的上传速度限制。你应当在 QoS 关闭的情况下使用"
"测速程序以确定可用的上传带宽。带宽以 kbps 为单位,留空以禁用上传 QoS。"
"1KByte/s=8Kbps"
msgid "Source IP(s)"
msgstr "源 IP"
msgid "Source Port(s)"
msgstr "源端口"
msgid "Specifie how packets that do not match any rule should be classified."
msgstr "指定当数据包不匹配任何规则时将被如何归类。"
msgid ""
"Specifying correctly is crucial to making QoS work. Note that bandwidth is "
"specified in kbps, leave blank to disable download QoS. There are 8 kilobits "
"per kilobyte."
msgstr ""
"正确设置对于 QoS 的工作至关重要。带宽以 kbps 为单位,留空以禁用下载 QoS。"
"1KByte/s=8Kbps"
msgid ""
"The maximum amount of bandwidth this class will be allocated in kbit/s. Even "
"if unused bandwidth is available, this service class will never be permitted "
"to use more than this amount of bandwidth."
msgstr ""
"该服务类型可被分配的带宽最大值(以 kbit/s 为单位)。即使存在未使用带宽,该服"
"务类型也将永远不被允许使用超过此量的带宽。"
msgid ""
"The minimum service this class will be allocated when the link is at "
"capacity. Classes which specify minimum service are known as realtime "
"classes by the active congestion controller. Streaming video, VoIP and "
"interactive online gaming are all examples of applications that must have a "
"minimum bandwith to function. To determine what to enter use the application "
"on an unloaded LAN and observe how much bandwidth it uses. Then enter a "
"number only slightly higher than this into this field. QoS will satisfiy the "
"minimum service of all classes first before allocating to other waiting "
"classes so be careful to use minimum bandwidths sparingly."
msgstr ""
"将被分配用于该服务类型的最低链路带宽容量。指定了最小带宽的服务类型会被主动拥"
"塞控制器看作实时类应用。例如视频流、VoIP 和在线互动游戏都应该设置最小带宽。要"
"确定需要多少最小带宽,可以在一个没有负载的局域网中使用应用程序并观察它使用了"
"多少带宽。然后输入一个略高于这个值的数字到字段中。在分配剩余带宽到其它服务类"
"型前QoS 将优先满足所有服务类型的最小带宽要求,所以要谨慎地使用最小带宽。"
msgid ""
"The percentage of the total available bandwidth that should be allocated to "
"this class when all available bandwidth is being used. If unused bandwidth "
"is available, more can (and will) be allocated. The percentages can be "
"configured to equal more (or less) than 100, but when the settings are "
"applied the percentages will be adjusted proportionally so that they add to "
"100. This setting only comes into effect when the WAN link is saturated."
msgstr ""
"当所有可用带宽被占满后该服务类型占据总带宽的百分比。如果带宽未用完,该服务类"
"型会被分配更多带宽。该百分比值可被设置为等于、大于或小于 100但当设置被应用"
"时,百分比值将会被按比例调整以便使它们加起来等于 100。该设置只在 WAN 端链路饱"
"和时才生效。<em>PSWAN 端链路饱和即带宽被占用完)</em>"
msgid ""
"The segment of network between your router and the ping target is where "
"congestion is controlled. By monitoring the round trip ping times to the "
"target congestion is detected. By default ACC uses your WAN gateway as the "
"ping target. If you know that congestion on your link will occur in a "
"different segment then you can enter an alternate ping target. Leave empty "
"to use the default settings."
msgstr ""
"在路由器和 Ping 目标之间的网络部分是拥塞控制的地方。拥塞通过监视和目标间的 "
"Ping 延时来检测。默认情况下 ACC 使用你的 WAN 网关作为 Ping 的目标。假如你知道"
"拥塞会在你链路的不同段发生,你可用输入一个备用的 Ping 目标。留空则将由ACC 自"
"动控制。"
msgid ""
"The total size of data transmitted since the establishment of the link (in "
"kBytes)."
msgstr "自连接建立以来传输的数据总量单位kBytes"
msgid "Total Download Bandwidth"
msgstr "下载总带宽"
msgid "Total Upload Bandwidth"
msgstr "上传总带宽"
msgid "Transport Protocol"
msgstr "协议"
msgid "Troubleshooting"
msgstr "故障排除"
msgid "Troubleshooting Data"
msgstr "故障排除数据"
msgid "Unlimited"
msgstr "不限制"
msgid "Upload Settings"
msgstr "上传设置"
msgid "Use Non-standard Ping Target"
msgstr "使用自定义 Ping 目标"
msgid "Zero"
msgstr "零"

View File

@ -1,14 +0,0 @@
#!/bin/sh
uci -q batch <<-EOF >/dev/null
delete ucitrack.@qos_gargoyle[-1]
add ucitrack qos_gargoyle
set ucitrack.@qos_gargoyle[-1].init=qos_gargoyle
commit ucitrack
EOF
/etc/init.d/qos_gargoyle stop
/etc/init.d/qos_gargoyle disable
rm -rf /tmp/luci-modulecache /tmp/luci-indexcache
exit 0

View File

@ -1,6 +1,6 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=openwrt-dnsforwarder
PKG_NAME:=dnsforwarder
PKG_VERSION:=6
PKG_RELEASE:=3
@ -15,7 +15,7 @@ PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz
include $(INCLUDE_DIR)/package.mk
define Package/dnsforwarder
define Package/openwrt-dnsforwarder
SECTION:=net
CATEGORY:=Network
TITLE:=A simple DNS forwarder

View File

@ -1,66 +0,0 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=qos-gargoyle
PKG_VERSION:=1.0.1
PKG_RELEASE:=1
PKG_LICENSE:=GPL-3.0+
PKG_MAINTAINER:=Xingwang Liao <kuoruan@gmail.com>
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
include $(INCLUDE_DIR)/package.mk
define Package/qos-gargoyle
SECTION:=net
CATEGORY:=Gargoyle
SUBMENU:=Network
TITLE:=A set of QoS scripts
DEPENDS:=+tc +ip +kmod-sched +iptables-mod-filter +iptables-mod-ipopt +iptables-mod-imq
MAINTAINER:=Xingwang Liao <kuoruan@gmail.com>
PKGARCH:=all
endef
define Package/qos-gargoyle/description
A set of QoS scripts from Gargoyle
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
$(call Build/Prepare/Default)
endef
define Build/Configure
endef
define Build/Compile
-$(MAKE) -C $(PKG_BUILD_DIR) clean
$(MAKE) -C $(PKG_BUILD_DIR) \
$(TARGET_CONFIGURE_OPTS) \
CFLAGS="$(TARGET_CFLAGS) -I $(STAGING_DIR)/usr/include" \
LDFLAGS="$(TARGET_LDFLAGS) -L $(STAGING_DIR)/usr/lib" \
BUILD_DIR="$(BUILD_DIR)" \
all
endef
define Package/qos-gargoyle/install
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_DIR) $(1)/etc/hotplug.d/iface
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_BIN) ./files/qos_gargoyle.init $(1)/etc/init.d/qos_gargoyle
$(INSTALL_BIN) ./files/qos_gargoyle.hotplug $(1)/etc/hotplug.d/iface/23-qos_gargoyle
$(INSTALL_DATA) ./files/qos_gargoyle.conf $(1)/etc/config/qos_gargoyle
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/qosmon $(1)/usr/sbin/qosmon
endef
define Package/qos-gargoyle/prerm
#!/bin/sh
$${IPKG_INSTROOT}/etc/init.d/qos_gargoyle stop 2>/dev/null
$${IPKG_INSTROOT}/etc/init.d/qos_gargoyle disable 2>/dev/null
exit 0
endef
$(eval $(call BuildPackage,qos-gargoyle))

View File

@ -1,119 +0,0 @@
config global global
option mtu "1500" # the maximum allowed packet size (in bytes) on the interface being shaped
option network "wan" # the name of the network whose interface should have its outgoing traffic shaped
#option interface "eth1" # the name of the network interface which should have its outgoing traffic shaped
config upload upload
option total_bandwidth "600" # 600kbit/s
option default_class "uclass_2" # default traffic class, must be a section of type upload_class
config download download
option total_bandwidth "4000" # 4000kbit/s (500Kbyte/s)
option default_class "dclass_1" # default traffic class, must be a section of type download_class
#upload classes
config upload_class uclass_1
option percent_bandwidth "40" # percent of total bandwidth to use
config upload_class uclass_2
option percent_bandwidth "20" # percent of total bandwidth to use
option max_bandwidth "30" # max bandwidth useage in absolute speed (kbit/s)
config upload_class uclass_3
option percent_bandwidth "30" # percent of total bandwidth to use
option min_bandwidth "160" # min bandwidth to allocate to this class
config upload_class uclass_4
option percent_bandwidth "10" # percent of total bandwidth to use
#download classes
config download_class dclass_1
option percent_bandwidth "30" # percent of total bandwidth to use
option min_bandwidth "80" # min bandwidth to allocate to this class
config download_class dclass_2
option percent_bandwidth "60" # percent of total bandwidth to use
config download_class dclass_3
option percent_bandwidth "10" # percent of total bandwidth to use
option min_bandwidth "80" # min bandwidth to allocate to this class
# classification rules
#
# POSSIBLE OPTIONS:
# class name of bandwidth class to use if rule matches, this is required in each rule section
# test_order an integer that specifies the order in which the rule should be checked for a match (lower numbers are checked first)
# proto check that packet has this protocol (tcp, udp, both), if port is specified default is both
# source check that packet has this source ip, can optionally have /[mask] after it (see -s option in iptables man page)
# destination check that packet has this destination ip, can optionally have /[mask] after it (see -d option in iptables man page)
# dport check that packet has this destination port
# sport check that packet has this source port
# min_pkt_size check that packet is at least this size (in bytes)
# max_pkt_size check that packet is no larger than this size (in bytes)
# layer7 check whether packet matches layer7 specification
# ipp2p check wither packet matches ipp2p specification (used to recognize p2p protocols)
# "ipp2p" or "all" will match any of the specified p2p protocols, you can
# also specifically match any protocol listed in the documentation here:
# http://ipp2p.org/docu_en.html
#
# sytnax for upload rules and download rules is identical
config upload_rule
option class "uclass_4"
option test_order "1"
option destination "195.56.146.238"
config upload_rule
option class "uclass_3"
option test_order "2"
option proto "both"
option dstport "80-90"
config upload_rule
option class "uclass_1"
option test_order "3"
option dstport "22"
config upload_rule
option class "uclass_3"
option test_order "44"
option proto "udp"
option max_pkt_size "250"
config upload_rule
option class "uclass_3"
option test_order "5"
option proto "udp"
option max_pkt_size "250"
config upload_rule
option class "uclass_4"
option test_order "6"
option ipp2p "all"
config upload_rule
option class "uclass_3"
option test_order "7"
option layer7 "pop3"
#download rules
config download_rule
option class "dclass_2"
option test_order "1"
option dstport "80-90"
config download_rule
option class "dclass_3"
option test_order "2"
option ipp2p "all"

View File

@ -1,26 +0,0 @@
config upload 'upload'
option default_class 'uclass_2'
option total_bandwidth '1000'
config download 'download'
option qos_monenabled 'true'
option total_bandwidth '10000'
option default_class 'dclass_1'
config download_class 'dclass_1'
option name 'Normal'
option percent_bandwidth '100'
config upload_class 'uclass_1'
option name 'Fast'
option percent_bandwidth '90'
config upload_class 'uclass_2'
option name 'Normal'
option percent_bandwidth '10'
config upload_rule 'upload_rule_100'
option class 'uclass_1'
option max_pkt_size '128'

View File

@ -1,20 +0,0 @@
#!/bin/sh
[ "$ACTION" = "ifup" ] || [ "$ACTION" = "ifdown" ] || exit 0
. /lib/functions/network.sh
wan_net_name=
network_find_wan wan_net_name
if [ "$INTERFACE" = "$wan_net_name" ]; then
if [ -h /etc/rc.d/S50qos_gargoyle ]; then
if [ "$ACTION" = "ifup" ]; then
/etc/init.d/qos_gargoyle start $DEVICE
fi
fi
if [ "$ACTION" = "ifdown" ]; then
/etc/init.d/qos_gargoyle stop
fi
fi

View File

@ -1,771 +0,0 @@
#!/bin/sh /etc/rc.common
#
# Copyright (c) 2008 Eric Bishop
# Copyright (c) 2016 GuoGuo<gch981213@gmail.com>
# Copyright (c) 2017 Xingwang Liao<kuoruan@gmail.com>
# This is free software licensed under the terms of the GNU GPL v2.0
#
START=50
EXTRA_COMMANDS=show
EXTRA_HELP=" show Show current Qos configuration (if active)"
config_file_name="qos_gargoyle"
upload_mask="0x007F"
download_mask="0x7F00"
qos_mark_file="/etc/qos_class_marks"
# created while qos is being initialized so hotplug and init script don't
# both try to initialize qos at the same time
lock_file="/var/run/qos_updating"
load_all_config_options()
{
local config_name="$1"
local section_id="$2"
ALL_OPTION_VARIABLES=""
# this callback loads all the variables
# in the section_id section when we do
# config_load. We need to redefine
# the option_cb for different sections
# so that the active one isn't still active
# after we're done with it. For reference
# the $1 variable is the name of the option
# and $2 is the name of the section
config_cb()
{
if [ ."$2" = ."$section_id" ]; then
option_cb()
{
ALL_OPTION_VARIABLES="$ALL_OPTION_VARIABLES $1"
}
else
option_cb() { return 0; }
fi
}
config_load "$config_name"
for var in $ALL_OPTION_VARIABLES
do
config_get "$var" "$section_id" "$var"
done
}
load_all_config_sections()
{
local config_name="$1"
local section_type="$2"
all_config_sections=""
get_section_names()
{
all_config_sections="$all_config_sections $1"
}
config_load "$config_name"
config_foreach get_section_names "$section_type"
echo "$all_config_sections"
}
get_classname_mark()
{
local class="$1"
local class_mark_list="$2"
echo "$class_mark_list" | awk -v class="$class" '{ for (i = 1; i <= NF; i++){ if($i~class":"){ gsub(class":",""); print $i } }}'
}
apply_all_rules()
{
local rule_type="$1"
local class_mark_list="$2"
local chain="$3"
local table="$4"
local need_proto
local tmp_proto
# add filter rules
rule_list=$(load_all_config_sections "$config_file_name" "$rule_type")
for rule in $rule_list ; do
class=""
proto=""
min_pkt_size=""
max_pkt_size=""
match_str=""
need_proto=""
load_all_config_options "$config_file_name" "$rule"
for option in $ALL_OPTION_VARIABLES ; do
option_value=$(eval echo \$$option)
case "$option" in
source)
if [ "$3" = "qos_egress" ] ; then
if [ "$option_value" = "$local_ip" ] || [ "$option_value" = "$wan_ip" ]; then
option_value="$wan_ip"
fi
fi
match_str="$match_str -s $option_value"
;;
destination)
if [ "$3" = "qos_ingress" ] ; then
if [ "$option_value" = "$local_ip" ] || [ "$option_value" = "$wan_ip" ]; then
option_value="$wan_ip"
fi
fi
match_str="$match_str -d $option_value"
;;
srcport)
if [ -n "$(echo $option_value | grep '-')" ] ; then option_value="$(echo "$option_value" | sed -e 's,-,:,g')" ; fi
match_str="$match_str --sport $option_value"
need_proto="1"
;;
dstport)
if [ -n "$(echo $option_value | grep '-')" ] ; then option_value="$(echo "$option_value" | sed -e 's,-,:,g')" ; fi
match_str="$match_str --dport $option_value"
need_proto="1"
;;
ndpi)
match_str="$match_str -m ndpi --$option_value"
;;
connbytes_kb)
match_str="$match_str -m connbytes --connbytes $(($option_value*1024)): --connbytes-dir both --connbytes-mode bytes"
;;
esac
done
if [ -n "$min_pkt_size" ] || [ -n "$max_pkt_size" ] ; then
if [ -z "$min_pkt_size" ] ; then min_pkt_size=0 ; fi
if [ -z "$max_pkt_size" ] ; then max_pkt_size=1500 ; fi
match_str="$match_str -m length --length $min_pkt_size:$max_pkt_size"
fi
if [ -n "$class" ] ; then
if [ -n "$proto" ] || [ -n "$match_str" ] ; then
next_mark=$(get_classname_mark "$class" "$class_mark_list" )
# We need to specify both udp and tcp if the user indicated a port
# and he did not indiate a protocal.
if [ -z "$proto" ] && [ -n "$need_proto" ] ; then
$echo_on
iptables -t $table -I $chain -p tcp $match_str -j MARK --set-mark $next_mark
iptables -t $table -I $chain -p udp $match_str -j MARK --set-mark $next_mark
$echo_off
else
# Otherwise just specify what the user requested (or nothing)
if [ -n "$proto" ] ; then
tmp_proto="-p $proto"
else
tmp_proto=""
fi
$echo_on
iptables -t $table -I $chain $tmp_proto $match_str -j MARK --set-mark $next_mark
$echo_off
fi
fi
fi
done
}
update_markfile()
{
# initialize mark file in /tmp first, and test md5sum
# this should speed things up and prevent writing to flash unnecessarily (since /tmp is ramdisk)
tmp_qos_mark_file="/tmp/qos_marks.tmp.tmp"
rm -rf "$tmp_qos_mark_file"
# re-populate per the QoS setup.
if [ $total_upload_bandwidth -ge 0 ] ; then
upload_class_list=$(load_all_config_sections "$config_file_name" "upload_class")
next_class_index=2
for uclass_name in $upload_class_list ; do
printf "upload $uclass_name %d $upload_mask\n" $next_class_index >> "$tmp_qos_mark_file"
next_class_index=$(($next_class_index+1))
done
fi
if [ $total_download_bandwidth -ge 0 ] ; then
download_class_list=$(load_all_config_sections "$config_file_name" "download_class")
next_class_index=2
for dclass_name in $download_class_list ; do
printf "download $dclass_name %d $download_mask\n" $(($next_class_index << 8)) >> "$tmp_qos_mark_file"
next_class_index=$(($next_class_index+1))
done
fi
mark_files_match="0"
if [ -e "$qos_mark_file" ] ; then
new_md5=$(md5sum "$tmp_qos_mark_file" | awk '{ print $1 ; } ')
old_md5=$(md5sum "$qos_mark_file" | awk '{ print $1 ; } ')
if [ "$new_md5" = "$old_md5" ] ; then
mark_files_match="1"
fi
fi
if [ "$mark_files_match" = "0" ] ; then
mv "$tmp_qos_mark_file" "$qos_mark_file"
else
rm -rf "$tmp_qos_mark_file"
fi
}
module_installed()
{
lsmod | grep -wq "$1"
}
install_modules()
{
for m in "$@"; do
module_installed $m || insmod $m >&- 2>&-
done
}
initialize_qos()
{
# Now, load/insert necessary kernel modules
# The following packages are required for the modules:
module_installed imq && rmmod imq >&- 2>&-
insmod imq numdevs=1 hook_chains="INPUT,FORWARD" hook_tables="mangle,mangle" >&- 2>&-
ip link set dev imq0 mtu 1500
install_modules cls_fw cls_flow sch_hfsc sch_sfq
# Set the atm parameters if pppoe is being used.
wan_proto=
network_get_protocol wan_proto $wan_net_name
if [ "$wan_proto" = "pppoe" ] ; then
# On my PPPoE WAN link I see the following overhead
# MAC Header=14 & PPPoE=8 plus I read that an addition ATM=8 is added by the modem"
overhead="stab linklayer atm overhead 30 mtu 1492 tsize 128 "
# else
# Even for the ethernet cases I do not think the qdisc sees the MAC Header
# but for now leave this out.
# overhead="stab linklayer ethernet overhead 14 mpu 64"
fi
# On low memory routers we need to take it easy on how big the queues can get.
# When depth is limited to 32 maximum bandwidth through any class will be around 11Mbps.
# Otherwise it will be around 350Mbps.
total_mem="$(sed -e '/^MemTotal: /!d; s#MemTotal: *##; s# kB##g' /proc/meminfo)"
if [ "$total_mem" -lt 16000 ] ; then
sfq_depth="depth 32"
else
sfq_depth=""
fi
# load upload variables
load_all_config_options "$config_file_name" "upload"
if [ -n "$total_bandwidth" ] ; then
total_upload_bandwidth="$total_bandwidth"
else
total_upload_bandwidth=-1
fi
upload_default_class="$default_class"
if [ $total_upload_bandwidth -ge 0 ] ; then
# load upload classes
upload_class_list=$(load_all_config_sections "$config_file_name" "upload_class")
for uclass_name in $upload_class_list ; do
percent_bandwidth=""
min_bandwidth=""
max_bandwidth=""
load_all_config_options "$config_file_name" "$uclass_name"
if [ -z "$percent_bandwidth" ] ; then
percent_bandwidth="0"
fi
if [ -z "$min_bandwidth" ] ; then
min_bandwidth="-1"
fi
if [ -z "$max_bandwidth" ] ; then
max_bandwidth="-1"
fi
classdef="$percent_bandwidth $max_bandwidth $min_bandwidth"
eval $uclass_name=\"\$classdef\" #"#comment quote here so formatting in editor isn't FUBAR
done
# Attach egress queuing discipline to QoS interface, now with temperary default
$echo_on
tc qdisc add dev $qos_interface root handle 1:0 hfsc default 1
# For the root qdisc, only ul is relevant, since there is no link sharing, and rt only applies to leaf qdiscs
#
# A detailed explanation of how/why ls is being set is warranted here...
# This is being set to max bandwidth possible on a connection (right now that's about 1Gbit, we can increase later if necessary)
# Only ratio of child ls rates is important -- bounding is done by the ul parameter, since hfsc takes min of ls and ul
# This means we can just multiply percents by 10 in child nodes to get what ls curves for each should be
# Again, for ls the ratios matter, but the absolute values do not, provided that they are all < than the ul for either the
# child or any of the parent nodes
#
tc class add dev $qos_interface parent 1:0 classid 1:1 hfsc ls rate 1000Mbit ul rate ${total_upload_bandwidth}kbit
$echo_off
class_mark_list=""
upload_shift=0
next_class_index=2
next_classid=$(printf "0x%X" $(($next_class_index << $upload_shift)) )
def_upload_idx=$next_class_index
def_upload_class=$next_classid
for uclass_name in $upload_class_list ; do
class_mark_list="$class_mark_list$uclass_name:$next_classid "
uclass_def=$(eval echo "\$$uclass_name")
# % bandwidth at capacity for this class
m2=$(( 10 * $(echo $uclass_def | awk ' {print $1}' ) ))
# is there a minimum bandwidth specified in kbps?
min_bandwidth=$( echo $uclass_def | awk ' {print $3}' )
ll_str=""
if [ "$min_bandwidth" -gt 0 ] ; then
ll_str=" rt m1 $((2*$min_bandwidth))kbit d 20ms m2 ${min_bandwidth}kbit"
fi
# is there an upper limit specified in kbps?
max_bandwidth=$( echo $uclass_def | awk ' {print $2}' )
ul_str=""
if [ "$max_bandwidth" -ge 0 ] ; then
ul_str=" ul m2 ${max_bandwidth}kbit"
else
max_bandwidth="$total_upload_bandwidth"
fi
# tbw is the Delay x Bandwidth product in bytes per second. We do not actually know the packet
# delay so we make an estimate of 150ms here and hope for the best. max_bandwidth is in kbps
# we multiply 150ms by 1000 below so the units work out.
tbw=$(($max_bandwidth*150/8));
if [ "$tbw" -lt 3000 ] ; then
tbw=3000
fi
# We will use the SFQ qdisc with flow classifier. The limit on the depth of our qdisc depends on the upper limit
# of the bandwidth allocated to this class. To impliment per IP sharing of the class we use the flow classifier
# and the 'nfct-src' on the upload side and 'dst' on the download side. I found a nice man page here
# https://arndtroide.homelinux.org/cgi-bin/man/man2html?tc-sfq+8
$echo_on
# Add the leaf class
tc class add dev $qos_interface parent 1:1 classid 1:$next_class_index hfsc ls m2 ${m2}Mbit $ll_str $ul_str
# Add the qdisc to the leaf class, assuming average packet at 500 bytes.
tc qdisc add dev $qos_interface parent 1:$next_class_index handle $next_class_index:1 sfq headdrop limit $(($tbw/500)) $sfq_depth divisor 256
# Add a filter to the root class to direct packets to this leaf class according to the conntrack mark
tc filter add dev $qos_interface parent 1:0 protocol ip handle $next_classid fw flowid 1:$next_class_index
# Add a filter to the leaf class to define flows as being the source IP address.
tc filter add dev $qos_interface parent $next_class_index: handle 1 flow divisor 256 map key nfct-src and 0xff
$echo_off
if [ "$upload_default_class" = "$uclass_name" ] ; then
def_upload_idx=$next_class_index
def_upload_class=$next_classid
fi
next_class_index=$(($next_class_index+1))
next_classid=$(printf "0x%X" $(($next_class_index << $upload_shift)) )
done
$echo_on
# Go back and touch up the root qdisc to have the proper default class
tc qdisc change dev $qos_interface $overhead root handle 1:0 hfsc default $def_upload_idx
# Set up egress chain
iptables -t mangle -N qos_egress
iptables -t mangle -A POSTROUTING -o $qos_interface -j qos_egress
# Next the user entered rules.
$echo_off
apply_all_rules "upload_rule" "$class_mark_list" "qos_egress" "mangle"
$echo_on
# set default class mark first in case we don't match anything
iptables -t mangle -I qos_egress -j MARK --set-mark $def_upload_class
# if we already set a mark in quota chain, we need to save that mark to the connmark, then return so it doesn't get over-written
iptables -t mangle -I qos_egress -m mark ! --mark 0x0 -j RETURN
iptables -t mangle -I qos_egress -m mark ! --mark 0x0 -j CONNMARK --save-mark --mask $upload_mask
# save current mark to connmark at end of chain
iptables -t mangle -A qos_egress -j CONNMARK --save-mark --mask $upload_mask
$echo_off
fi
# load download variables
total_bandwidth=""
default_class=""
load_all_config_options "$config_file_name" "download"
if [ -n "$total_bandwidth" ] ; then
total_download_bandwidth="$total_bandwidth"
else
total_download_bandwidth=-1
fi
download_default_class="$default_class"
# Only if both upload and download QoS are enabled can we enable Gargoyle active QoS monitor
if [ $total_download_bandwidth -eq 0 ] || [ $total_upload_bandwidth -eq 0 ] ; then
qos_monenabled="false" ;
fi
if [ $total_download_bandwidth -ge 0 ] ; then
# Set up the InterMediate Queuing device (IMQ)
ip link set imq0 up
# Attach ingress queuing discipline to IMQ interface
tc qdisc add dev imq0 root handle 1:0 hfsc default 1
tc class add dev imq0 parent 1:0 classid 1:1 hfsc ls rate 1000Mbit ul rate ${total_download_bandwidth}kbit
# load download classes
download_class_list=$(load_all_config_sections "$config_file_name" "download_class")
for dclass_name in $download_class_list ; do
percent_bandwidth=""
min_bandwidth=""
max_bandwidth=""
minRTT=""
load_all_config_options "$config_file_name" "$dclass_name"
if [ -z "$percent_bandwidth" ] ; then
percent_bandwidth="0"
fi
if [ -z "$min_bandwidth" ] ; then
min_bandwidth="-1"
fi
if [ -z "$max_bandwidth" ] ; then
max_bandwidth="-1"
fi
classdef="$percent_bandwidth $max_bandwidth $min_bandwidth $minRTT"
eval $dclass_name=\"\$classdef\" #"#comment quote here so formatting in editor isn't FUBAR
done
class_mark_list=""
download_shift=8
next_class_index=2
next_classid=$(printf "0x%X" $(($next_class_index << $download_shift)) )
def_download_idx=$next_class_index
def_download_class=$next_classid
for dclass_name in $download_class_list ; do
class_mark_list="$class_mark_list$dclass_name:$next_classid "
dclass_def=$(eval echo "\$$dclass_name")
# bandwidth for this class
m2=$(( 10 * $(echo $dclass_def | awk ' {print $1}' ) ))
# The Gargoyle ACC switches from optimum WAN utilization mode to minimum RTT mode
# when it detects a class has become active that includes a two part service curve.
# So to trigger this behaviour we create two parts curves when minRTT is set.
minRTT=$( echo $dclass_def | awk ' {print $4}' )
if [ "$minRTT" = "Yes" ] ; then
ll_str=" ls m1 ${m2}Mbit d 20ms m2 ${m2}Mbit"
else
ll_str=" ls m2 ${m2}Mbit"
fi
# is there a minimum bandwidth specified?
min_bandwidth=$( echo $dclass_def | awk ' {print $3}' )
rt_str=""
if [ "$min_bandwidth" -gt 0 ] ; then
if [ "$minRTT" = "Yes" ] ; then
rt_str=" rt m1 $((2*$min_bandwidth))kbit d 20ms m2 ${min_bandwidth}kbit"
else
rt_str=" rt m2 ${min_bandwidth}kbit"
fi
fi
# is there an upper limit specified?
max_bandwidth=$( echo $dclass_def | awk ' {print $2}' )
ul_str=""
if [ "$max_bandwidth" -ge 0 ] ; then
ul_str=" ul m2 ${max_bandwidth}kbit"
else
max_bandwidth="$total_download_bandwidth"
fi
tbw=$(($max_bandwidth*150/8));
if [ "$tbw" -lt 5000 ] ; then
tbw=5000
fi
$echo_on
tc class add dev imq0 parent 1:1 classid 1:$next_class_index hfsc $rt_str $ll_str $ul_str
# Assume average download packet size is 1000 bytes.
tc qdisc add dev imq0 parent 1:$next_class_index handle $next_class_index:1 sfq headdrop limit $(($tbw/1000)) $sfq_depth divisor 256
tc filter add dev imq0 parent 1:0 prio $next_class_index protocol ip handle $next_classid fw flowid 1:$next_class_index
tc filter add dev imq0 parent $next_class_index: handle 1 flow divisor 256 map key dst and 0xff
$echo_off
if [ "$download_default_class" = "$dclass_name" ] ; then
def_download_idx=$next_class_index
def_download_class=$next_classid
fi
next_class_index=$(($next_class_index+1))
next_classid=$(printf "0x%X" $(($next_class_index << $download_shift)) )
done
$echo_on
# Go back and touch up the root qdisc to have the proper default class
tc qdisc change dev imq0 $overhead root handle 1:0 hfsc default $def_download_idx
# Create ingress chain
iptables -t mangle -N qos_ingress
# Mark ingress in FORWARD and INPUT chains to make sure any DNAT (virt. server) is taken into account
iptables -t mangle -A FORWARD -i $qos_interface -j qos_ingress
iptables -t mangle -A INPUT -i $qos_interface -j qos_ingress
# Now the rest of the user entered rules.
$echo_off
apply_all_rules "download_rule" "$class_mark_list" "qos_ingress" "mangle" "$download_mask"
$echo_on
# set default class mark first in case we don't match anything
iptables -t mangle -I qos_ingress -j MARK --set-mark $def_download_class
# if we already set a mark in quota chain, we need to save that mark to the connmark, then return so it doesn't get over-written
iptables -t mangle -I qos_ingress -m mark ! --mark 0x0 -j RETURN
iptables -t mangle -I qos_ingress -m mark ! --mark 0x0 -j CONNMARK --save-mark --mask $download_mask
# make sure all packets get sent through IMQ
iptables -t mangle -I qos_ingress -j IMQ --todev 0
# save current mark to connmark at end of chain
iptables -t mangle -A qos_ingress -j CONNMARK --save-mark --mask $download_mask
$echo_off
fi
# Enable Gargoyle active QoS monitor
if [ $total_upload_bandwidth -ge 0 ] && [ $total_download_bandwidth -ge 0 ] && [ "$qos_monenabled" = "true" ] ; then
$echo_on
# if the user specified a ping target then use that otherwise use the gateway.
if [ -z "$ptarget_ip" ] ; then
old_ifs="$IFS"
IFS=$(printf "\n\r")
targets=$(traceroute -n -I -w 1 -q 2 -m6 100.100.100.100 | grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}.*ms' | grep -v '8.8.8.8' | sed 's/ms//g')
ptarget_ip=""
for t in $targets ; do
if [ -z "$ptarget_ip" ] ; then
# ip of potential gateway
target=$(echo "$t" | awk '{ print $1 ; }')
target_is_local=$(echo "$target" | grep -E '^(192\.168|10\.|172\.1[6789]\.|172\.2[0-9]\.|172\.3[01]\.|0\.0\.0\.0|127\.|255\.)')
# round or rather ceil() time up to nearest millisecond since bash doesn't like working with decimals
time=$(echo "$t" | awk ' { print $3 ; }' | sed 's/\..*$//g')
time=$(( $time + 1 ))
if [ -z "$target_is_local" ] || [ "$time" -gt 5 ] ; then
ptarget_ip="$target"
fi
fi
done
IFS="$old_ifs"
# in case ping target is still not defined use default gateway
if [ -z "$ptarget_ip" ] ; then
network_get_gateway ptarget_ip $wan_net_name
fi
fi
# Ping responses for the ping target never go to the IMQ device.
iptables -t mangle -I qos_ingress -p icmp --icmp-type 0 -d $wan_ip -s $ptarget_ip -j RETURN
# Make a class to handle the outgoing ping requests from the router.
# These pings 84 bytes each for ethernet plus 22 more for PPPoE connections, allowing a maximum rate of 200ms we get 2.6kbps
tc class add dev $qos_interface parent 1:1 classid 1:127 hfsc rt umax 106 dmax 10ms rate 4kbit
tc qdisc add dev $qos_interface parent 1:127 pfifo
tc filter add dev $qos_interface parent 1:0 prio 1 protocol ip handle 127 fw flowid 1:127
# Mark all ping requests from the router to the ptarget to the above special class overriding any other mark.
iptables -t mangle -I qos_egress -p icmp --icmp-type 8 -s $wan_ip -d $ptarget_ip -j MARK --set-mark 127
# Start the monitor
if [ -n "$pinglimit" ] ; then
# In manual mode the user selects the active mode pinglimit indirectly. qosmon always measures the RTT of a ping on an unloaded link.
# This is called the ping entitlement. With a manual entry the minRTT ping limit is 110% of this measured ping entitlement
# and the active mode ping limit is the minRTT limit plus the user entered value. See the qosmon source code for more details.
# In summary manaully entered ping times only affect the active mode, not the minRTT mode ping time limits.
qosmon -a -b 800 $ptarget_ip $total_download_bandwidth $pinglimit
else
# In auto mode we calculate transmission delay based on our bandwidth and then ask qosmon
# to add this value to its measured ping entitlement to form the final ping limit.
pinglimit=$((1500*10*2/3/$total_download_bandwidth+1500*10/$total_upload_bandwidth+2))
qosmon -a -b 800 $ptarget_ip $total_download_bandwidth $pinglimit
fi
$echo_off
fi
update_markfile
}
define_interface()
{
. /lib/functions/network.sh
# Wait for up to 15 seconds for the wan interface to indicate it is up.
wan_net_name=""
network_find_wan wan_net_name
wait_sec=15
while [ -z "$wan_net_name" ] && [ $wait_sec -gt 0 ] ; do
sleep 1
wait_sec=$(($wait_sec - 1))
network_find_wan wan_net_name
done
if [ -z "$wan_net_name" ] ; then
echo "Network is not available."
exit 1
fi
qos_interface=""
wan_ip=""
local_ip=""
network_get_device qos_interface $wan_net_name
network_get_ipaddr wan_ip $wan_net_name
network_get_ipaddr local_ip lan
}
ipt_delete_rule()
{
local table=$1
local delete_chain=$2
local lines
lines=$(iptables -t $table -nL $delete_chain --line-numbers | grep $3 | awk '{print $1}')
for n in $lines ; do
iptables -t $table -D $delete_chain $n 2>/dev/null
done
}
stop()
{
# if already in process of being initialized, do not continue
# until that is finished, and then exit cleanly, without
# doing anything further
if [ -e "$lock_file" ] ; then
while [ -e "$lock_file" ] ; do
sleep 1
done
exit
fi
# Kill the qos monitor in case it is running
killall qosmon 2>/dev/null
$echo_on
for iface in $(tc qdisc show | grep hfsc | awk '{print $5}'); do
tc qdisc del dev "$iface" root
done
# eliminate existing rules in mangle table
ipt_delete_rule "mangle" "POSTROUTING" "qos_egress"
ipt_delete_rule "mangle" "FORWARD" "qos_ingress"
ipt_delete_rule "mangle" "INPUT" "qos_ingress"
iptables -t mangle -F qos_ingress 2>/dev/null
iptables -t mangle -F qos_egress 2>/dev/null
iptables -t mangle -X qos_ingress 2>/dev/null
iptables -t mangle -X qos_egress 2>/dev/null
$echo_off
}
start()
{
test_total_up=$(uci -q get qos_gargoyle.upload.total_bandwidth)
test_total_down=$(uci -q get qos_gargoyle.download.total_bandwidth)
if [ -z "$test_total_up" ] && [ -z "$test_total_down" ] ;then
disable
exit 0
fi
# This script is called by a hotplug event. If the WAN comes
# up fast we could end up trying to run qos_gargoyle while the
# boot is still in progress which causes issues in low memory
# routers. To avoid this we check to see if rcS is still running
# or not. If it is we wait until it completes or 60 seconds.
cnt=0
while ps | grep '[//]rcS S boot' >/dev/null
do
sleep 4
cnt=`expr $cnt + 1`
if [ $cnt -ge 15 ] ; then
break;
fi
done
stop
touch "$lock_file"
# load qos_interface from global variables
define_interface
if [ -n "$qos_interface" ] ; then
load_all_config_options "$config_file_name" "global"
initialize_qos
fi
rm -rf "$lock_file"
}
restart()
{
echo_on="set -x"
echo_off="set +x"
start
}
show()
{
# load global variables
load_all_config_options "$config_file_name" "global"
# load qos_interface from global variables
define_interface
echo "Egress configuration on $qos_interface"
iptables -t mangle -vnL qos_egress 2>/dev/null
tc -s qdisc show dev $qos_interface
tc -s class show dev $qos_interface
tc -s filter show dev $qos_interface
echo "Ingress configuration in imq0"
iptables -t mangle -vnL qos_ingress 2>/dev/null
tc -s qdisc show dev imq0
tc -s class show dev imq0
tc -s filter show dev imq0
tc -s filter show dev imq0 parent 2:
}
boot()
{
# Do nothing during init. Start is called by hotplug.
return 0
}

View File

@ -1,47 +0,0 @@
--- a/Makefile
+++ b/Makefile
@@ -21,7 +21,7 @@ qosmon: qosmon.o
$(CC) $(LDFLAGS) $^ $(TCOBJS) -o $@ $(LDLIBS)
qosmon.o: qosmon.c
- $(CC) -D ONLYBG $(CFLAGS) -I $(TCDIR)/include -I $(TCDIR)/tc -c $^ -o $@
+ $(CC) -D ONLYBG -D_GNU_SOURCE $(CFLAGS) -I $(TCDIR)/include -I $(TCDIR)/tc -c $^ -o $@
install: all uninstall
-mkdir -p $(BINDIR)
--- a/qosmon.c
+++ b/qosmon.c
@@ -34,6 +34,8 @@
#include <string.h>
#include <errno.h>
#include <time.h>
+#include <limits.h>
+#include <sys/param.h>
#include "utils.h"
#include "tc_util.h"
@@ -62,12 +64,13 @@
//after then and define dump_filter and talk accordingly.
#ifdef RTNL_FAMILY_MAX
#define dump_filter(a,b,c) rtnl_dump_filter(a,b,c)
-#define talk(a,b,c,d,e) rtnl_talk(a,b,c,d,e)
+#define talk(a,b,c) rtnl_talk(a,b,c)
#else
#define dump_filter(a,b,c) rtnl_dump_filter(a,b,c,NULL,NULL)
-#define talk(a,b,c,d,e) rtnl_talk(a,b,c,d,e,NULL,NULL)
+#define talk(a,b,c) rtnl_talk(a,b,c,NULL,NULL)
#endif
+#define __sighandler_t sighandler_t
/* use_names is required when linking to tc_util.o */
bool use_names = false;
@@ -630,7 +633,7 @@ int tc_class_modify(__u32 rate)
}
- if (talk(&rth, &req.n, 0, 0, NULL) < 0)
+ if (talk(&rth, &req.n, NULL) < 0)
return 2;
return 0;

View File

@ -1,35 +0,0 @@
BINDIR = /usr/sbin
#TCDIR:=$(BUILD_DIR)/iproute2-full/ip*
TCDIR:=$(shell if [ -e "${BUILD_DIR}/iproute2-full" ] ; then echo "${BUILD_DIR}/iproute2-full"/ip* ; else echo "${BUILD_DIR}/iproute2-tiny"/ip* ; fi ; )
#The ncurses library only needed if we remove the ONLYBG switch
#below. Mostly for debug
#LDLIBS += -lncurses
LDLIBS += -lm
TCOBJS := $(TCDIR)/tc/tc_util.o
TCOBJS += $(TCDIR)/tc/tc_core.o
TCOBJS += $(TCDIR)/tc/q_hfsc.o
TCOBJS += $(TCDIR)/lib/libnetlink.a
TCOBJS += $(TCDIR)/lib/libutil.a
LDFLAGS += -Wl,-export-dynamic
all: qosmon
qosmon: qosmon.o
$(CC) $(LDFLAGS) $^ $(TCOBJS) -o $@ $(LDLIBS)
qosmon.o: qosmon.c
$(CC) -D ONLYBG $(CFLAGS) -I $(TCDIR)/include -I $(TCDIR)/tc -c $^ -o $@
install: all uninstall
-mkdir -p $(BINDIR)
cp qosmon $(BINDIR)
uninstall:
rm -f $(BINDIR)/qosmon
clean:
rm -rf *.o *~ .*sw* qosmon

File diff suppressed because it is too large Load Diff