mirror of
https://github.com/hanwckf/immortalwrt-mt798x.git
synced 2025-01-09 02:43:53 +08:00
gargoyle-qos: add packages
This commit is contained in:
parent
a4cc047af5
commit
1919abdea1
@ -34,7 +34,9 @@ NanoPi R1s(h5) support: [jerrykuku/openwrt-nanopi-r1s-h5](https://github.com/jer
|
||||
NanoPi support: [speedyworldclub/nlede](https://github.com/speedyworldclub/nlede).<br/>
|
||||
qntfs-driver source: [lbbboy/qntfs-driver](https://github.com/lbbboy/qntfs-driver).<br/>
|
||||
lua-maxminddb source: [jerrykuku/lua-maxminddb](https://github.com/jerrykuku/lua-maxminddb).<br/>
|
||||
package openwrt-mwol: [Mleaf/openwrt-mwol](https://github.com/Mleaf/openwrt-mwol).
|
||||
package openwrt-mwol: [Mleaf/openwrt-mwol](https://github.com/Mleaf/openwrt-mwol).<br/>
|
||||
package gargoyle: [ericpaulbishop/gargoyle](https://github.com/ericpaulbishop/gargoyle).<br/>
|
||||
luci-app-qos-gargoyle source: [kuoruan/luci-app-qos-gargoyle](https://github.com/kuoruan/luci-app-qos-gargoyle).
|
||||
|
||||
## License
|
||||
### Depending on their own License.
|
||||
|
72
package/ctcgfw/gargoyle-firewall-util/Makefile
Normal file
72
package/ctcgfw/gargoyle-firewall-util/Makefile
Normal file
@ -0,0 +1,72 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=gargoyle-firewall-util
|
||||
PKG_VERSION:=$(GARGOYLE_VERSION)
|
||||
ifeq ($(GARGOYLE_VERSION),)
|
||||
PKG_VERSION:=1.0.0
|
||||
endif
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/gargoyle-firewall-util
|
||||
SECTION:=net
|
||||
CATEGORY:=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
|
||||
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))
|
2
package/ctcgfw/gargoyle-firewall-util/files/gargoyle_additions.firewall
Executable file
2
package/ctcgfw/gargoyle-firewall-util/files/gargoyle_additions.firewall
Executable file
@ -0,0 +1,2 @@
|
||||
. /usr/lib/gargoyle_firewall_util/gargoyle_firewall_util.sh
|
||||
initialize_firewall
|
@ -0,0 +1,40 @@
|
||||
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 get network.wan 2>/dev/null)
|
||||
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
|
@ -0,0 +1,639 @@
|
||||
# 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 get dropbear.@dropbear[0].max_remote_attempts 2>/dev/null)
|
||||
ssh_port=$(uci 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"
|
||||
}
|
||||
|
||||
# creates a chain that sets third byte of connmark to a value that denotes what l7 proto
|
||||
# is associated with connection. This only sets the connmark, it does not save it to mark
|
||||
create_l7marker_chain()
|
||||
{
|
||||
# eliminate chain if it exists
|
||||
delete_chain_from_table "mangle" "l7marker"
|
||||
|
||||
app_proto_num=1
|
||||
app_proto_shift=16
|
||||
app_proto_mask="0xFF0000"
|
||||
|
||||
all_prots=$(ls /etc/l7-protocols/* | sed 's/^.*\///' | sed 's/\.pat$//' )
|
||||
qos_active=$(ls /etc/rc.d/*qos_gargoyle* 2>/dev/null)
|
||||
if [ -n "$qos_active" ] ; then
|
||||
qos_l7=$(uci show qos_gargoyle | sed '/layer7=/!d; s/^.*=//g')
|
||||
fi
|
||||
fw_l7=$(uci show firewall | sed '/app_proto/!d; s/^.*=//g')
|
||||
all_used="$fw_l7 $qos_l7"
|
||||
|
||||
if [ "$all_used" != " " ] ; then
|
||||
iptables -t mangle -N l7marker
|
||||
iptables -t mangle -I PREROUTING -m connbytes --connbytes 0:20 --connbytes-dir both --connbytes-mode packets -m connmark --mark 0x0/$app_proto_mask -j l7marker
|
||||
iptables -t mangle -I POSTROUTING -m connbytes --connbytes 0:20 --connbytes-dir both --connbytes-mode packets -m connmark --mark 0x0/$app_proto_mask -j l7marker
|
||||
|
||||
for proto in $all_prots ; do
|
||||
proto_is_used=$(echo "$all_used" | grep "$proto")
|
||||
if [ -n "$proto_is_used" ] ; then
|
||||
app_proto_mark=$(printf "0x%X" $(($app_proto_num << $app_proto_shift)) )
|
||||
iptables -t mangle -A l7marker -m connmark --mark 0x0/$app_proto_mask -m layer7 --l7proto $proto -j CONNMARK --set-mark $app_proto_mark/$app_proto_mask
|
||||
echo "$proto $app_proto_mark $app_proto_mask" >> /tmp/l7marker.marks.tmp
|
||||
app_proto_num=$((app_proto_num + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
copy_file="y"
|
||||
if [ -e /etc/md5/layer7.md5 ] ; then
|
||||
old_md5=$(cat /etc/md5/layer7.md5)
|
||||
current_md5=$(md5sum /tmp/l7marker.marks.tmp | awk ' { print $1 ; } ' )
|
||||
if [ "$current_md5" = "$old_md5" ] ; then
|
||||
copy_file="n"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$copy_file" = "y" ] ; then
|
||||
mv /tmp/l7marker.marks.tmp /etc/l7marker.marks
|
||||
mkdir -p /etc/md5
|
||||
md5sum /etc/l7marker.marks | awk ' { print $1 ; }' > /etc/md5/layer7.md5
|
||||
else
|
||||
rm /tmp/l7marker.marks.tmp
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
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 get firewall.@defaults[0].enforce_dhcp_assignments 2> /dev/null)
|
||||
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
|
||||
create_l7marker_chain
|
||||
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
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
#!/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
|
21
package/ctcgfw/gargoyle-firewall-util/files/set_kernel_timezone.init
Executable file
21
package/ctcgfw/gargoyle-firewall-util/files/set_kernel_timezone.init
Executable file
@ -0,0 +1,21 @@
|
||||
#!/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
|
||||
}
|
||||
|
19
package/ctcgfw/gargoyle-firewall-util/src/Makefile
Normal file
19
package/ctcgfw/gargoyle-firewall-util/src/Makefile
Normal file
@ -0,0 +1,19 @@
|
||||
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*
|
228
package/ctcgfw/gargoyle-firewall-util/src/backup_quotas.c
Normal file
228
package/ctcgfw/gargoyle-firewall-util/src/backup_quotas.c
Normal file
@ -0,0 +1,228 @@
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,79 @@
|
||||
#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;
|
||||
}
|
||||
|
1040
package/ctcgfw/gargoyle-firewall-util/src/make_iptables_rules.c
Normal file
1040
package/ctcgfw/gargoyle-firewall-util/src/make_iptables_rules.c
Normal file
File diff suppressed because it is too large
Load Diff
429
package/ctcgfw/gargoyle-firewall-util/src/print_quotas.c
Normal file
429
package/ctcgfw/gargoyle-firewall-util/src/print_quotas.c
Normal file
@ -0,0 +1,429 @@
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
1105
package/ctcgfw/gargoyle-firewall-util/src/restore_quotas.c
Normal file
1105
package/ctcgfw/gargoyle-firewall-util/src/restore_quotas.c
Normal file
File diff suppressed because it is too large
Load Diff
59
package/ctcgfw/libericstools/Makefile
Normal file
59
package/ctcgfw/libericstools/Makefile
Normal file
@ -0,0 +1,59 @@
|
||||
#
|
||||
# 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:=$(GARGOYLE_VERSION)
|
||||
ifeq ($(GARGOYLE_VERSION),)
|
||||
PKG_VERSION:=1.0.0
|
||||
endif
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/libericstools
|
||||
SECTION:=libs
|
||||
CATEGORY:=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))
|
92
package/ctcgfw/libericstools/src/Makefile
Normal file
92
package/ctcgfw/libericstools/src/Makefile
Normal file
@ -0,0 +1,92 @@
|
||||
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
|
309
package/ctcgfw/libericstools/src/erics_tools.h
Normal file
309
package/ctcgfw/libericstools/src/erics_tools.h
Normal file
@ -0,0 +1,309 @@
|
||||
/*
|
||||
* 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 */
|
195
package/ctcgfw/libericstools/src/file_util.c
Normal file
195
package/ctcgfw/libericstools/src/file_util.c
Normal file
@ -0,0 +1,195 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
280
package/ctcgfw/libericstools/src/list.c
Normal file
280
package/ctcgfw/libericstools/src/list.c
Normal file
@ -0,0 +1,280 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
360
package/ctcgfw/libericstools/src/priority_queue.c
Normal file
360
package/ctcgfw/libericstools/src/priority_queue.c
Normal file
@ -0,0 +1,360 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
80
package/ctcgfw/libericstools/src/safe_malloc.c
Normal file
80
package/ctcgfw/libericstools/src/safe_malloc.c
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
683
package/ctcgfw/libericstools/src/string_util.c
Normal file
683
package/ctcgfw/libericstools/src/string_util.c
Normal file
@ -0,0 +1,683 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
91
package/ctcgfw/libericstools/src/test_list_and_queue.c
Normal file
91
package/ctcgfw/libericstools/src/test_list_and_queue.c
Normal file
@ -0,0 +1,91 @@
|
||||
#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;
|
||||
}
|
350
package/ctcgfw/libericstools/src/test_map.c
Normal file
350
package/ctcgfw/libericstools/src/test_map.c
Normal file
@ -0,0 +1,350 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
26
package/ctcgfw/libericstools/src/test_string.c
Normal file
26
package/ctcgfw/libericstools/src/test_string.c
Normal file
@ -0,0 +1,26 @@
|
||||
#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;
|
||||
}
|
911
package/ctcgfw/libericstools/src/tree_map.c
Normal file
911
package/ctcgfw/libericstools/src/tree_map.c
Normal file
@ -0,0 +1,911 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
83
package/ctcgfw/libiptbwctl/Makefile
Normal file
83
package/ctcgfw/libiptbwctl/Makefile
Normal file
@ -0,0 +1,83 @@
|
||||
#
|
||||
# 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:=$(GARGOYLE_VERSION)
|
||||
ifeq ($(GARGOYLE_VERSION),)
|
||||
PKG_VERSION:=1.0.0
|
||||
endif
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/libiptbwctl
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/libiptbwctl
|
||||
SECTION:=net
|
||||
CATEGORY:=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))
|
||||
|
70
package/ctcgfw/libiptbwctl/src/Makefile
Normal file
70
package/ctcgfw/libiptbwctl/src/Makefile
Normal file
@ -0,0 +1,70 @@
|
||||
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*
|
1349
package/ctcgfw/libiptbwctl/src/ipt_bwctl.c
Normal file
1349
package/ctcgfw/libiptbwctl/src/ipt_bwctl.c
Normal file
File diff suppressed because it is too large
Load Diff
161
package/ctcgfw/libiptbwctl/src/ipt_bwctl.h
Normal file
161
package/ctcgfw/libiptbwctl/src/ipt_bwctl.h
Normal file
@ -0,0 +1,161 @@
|
||||
/* 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>
|
||||
#include <sys/syscall.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);
|
23
package/ctcgfw/libiptbwctl/src/ipt_bwctl_safe_malloc.c
Normal file
23
package/ctcgfw/libiptbwctl/src/ipt_bwctl_safe_malloc.c
Normal file
@ -0,0 +1,23 @@
|
||||
#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;
|
||||
}
|
12
package/ctcgfw/libiptbwctl/src/utils/Makefile
Normal file
12
package/ctcgfw/libiptbwctl/src/utils/Makefile
Normal file
@ -0,0 +1,12 @@
|
||||
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*
|
177
package/ctcgfw/libiptbwctl/src/utils/bw_get.c
Normal file
177
package/ctcgfw/libiptbwctl/src/utils/bw_get.c
Normal file
@ -0,0 +1,177 @@
|
||||
/* 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;
|
||||
}
|
39
package/ctcgfw/libiptbwctl/src/utils/bw_print_history_file.c
Normal file
39
package/ctcgfw/libiptbwctl/src/utils/bw_print_history_file.c
Normal file
@ -0,0 +1,39 @@
|
||||
/* 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;
|
||||
}
|
186
package/ctcgfw/libiptbwctl/src/utils/bw_set.c
Normal file
186
package/ctcgfw/libiptbwctl/src/utils/bw_set.c
Normal file
@ -0,0 +1,186 @@
|
||||
/* 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;
|
||||
}
|
||||
|
27
package/ctcgfw/libiptbwctl/src/utils/set_kernel_timezone.c
Normal file
27
package/ctcgfw/libiptbwctl/src/utils/set_kernel_timezone.c
Normal file
@ -0,0 +1,27 @@
|
||||
/* 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;
|
||||
}
|
22
package/ctcgfw/luci-app-qos-gargoyle/Makefile
Normal file
22
package/ctcgfw/luci-app-qos-gargoyle/Makefile
Normal file
@ -0,0 +1,22 @@
|
||||
#
|
||||
# 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.6
|
||||
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
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
@ -0,0 +1,100 @@
|
||||
-- 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
|
@ -0,0 +1,166 @@
|
||||
-- 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
|
@ -0,0 +1,61 @@
|
||||
-- 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
|
@ -0,0 +1,87 @@
|
||||
-- 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
|
@ -0,0 +1,123 @@
|
||||
-- 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(SimpleSection, 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
|
@ -0,0 +1,160 @@
|
||||
-- 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
|
@ -0,0 +1,52 @@
|
||||
-- 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
|
@ -0,0 +1,87 @@
|
||||
-- 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
|
@ -0,0 +1,31 @@
|
||||
-- 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
|
@ -0,0 +1,97 @@
|
||||
<%#
|
||||
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>
|
@ -0,0 +1,52 @@
|
||||
<%#
|
||||
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 "/etc/init.d/qos_gargoyle show" : </span><br /><br />',
|
||||
data.show,
|
||||
'<br /><br /><span class="description">Output of "cat /tmp/qosmon.status" : </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%>
|
334
package/ctcgfw/luci-app-qos-gargoyle/po/zh-cn/qos-gargoyle.po
Normal file
334
package/ctcgfw/luci-app-qos-gargoyle/po/zh-cn/qos-gargoyle.po
Normal file
@ -0,0 +1,334 @@
|
||||
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>(PS:WAN 端链路饱和即带宽被占用完)</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 "零"
|
@ -0,0 +1,11 @@
|
||||
#!/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
|
||||
|
||||
rm -rf /tmp/luci-modulecache /tmp/luci-indexcache
|
||||
exit 0
|
68
package/ctcgfw/qos-gargoyle/Makefile
Normal file
68
package/ctcgfw/qos-gargoyle/Makefile
Normal file
@ -0,0 +1,68 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=qos-gargoyle
|
||||
PKG_VERSION:=$(GARGOYLE_VERSION)
|
||||
ifeq ($(GARGOYLE_VERSION),)
|
||||
PKG_VERSION:=1.0.0
|
||||
endif
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(INCLUDE_DIR)/kernel.mk
|
||||
|
||||
define Package/qos-gargoyle
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
TITLE:=A set of QoS scripts designed for use with Gargoyle Web Interface
|
||||
DEPENDS:=+tc +ip +kmod-sched +iptables-mod-filter +iptables-mod-ipopt +iptables-mod-imq +gargoyle-firewall-util
|
||||
MAINTAINER:=Eric Bishop <eric@gargoyle-router.com>
|
||||
endef
|
||||
|
||||
define Package/qos-gargoyle/description
|
||||
A set of QoS scripts designed for use with Gargoyle Web Interface
|
||||
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) \
|
||||
CFLAGS="$(TARGET_CFLAGS) -I $(STAGING_DIR)/usr/include" \
|
||||
LDFLAGS="$(TARGET_LDFLAGS) -L $(STAGING_DIR)/usr/lib" \
|
||||
BUILD_DIR="$(KERNEL_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
|
||||
$${IPKG_INSTROOT}/etc/init.d/qos_gargoyle stop 2>/dev/null
|
||||
$${IPKG_INSTROOT}/etc/init.d/qos_gargoyle disable 2>/dev/null
|
||||
ls >/dev/null 2>&1
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,qos-gargoyle))
|
119
package/ctcgfw/qos-gargoyle/files/example-qos-config2.conf
Normal file
119
package/ctcgfw/qos-gargoyle/files/example-qos-config2.conf
Normal file
@ -0,0 +1,119 @@
|
||||
|
||||
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"
|
||||
|
25
package/ctcgfw/qos-gargoyle/files/qos_gargoyle.conf
Normal file
25
package/ctcgfw/qos-gargoyle/files/qos_gargoyle.conf
Normal file
@ -0,0 +1,25 @@
|
||||
|
||||
config upload 'upload'
|
||||
option default_class 'uclass_2'
|
||||
|
||||
config download 'download'
|
||||
option qos_monenabled 'true'
|
||||
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 test_order '100'
|
||||
option max_pkt_size '128'
|
||||
|
12
package/ctcgfw/qos-gargoyle/files/qos_gargoyle.hotplug
Normal file
12
package/ctcgfw/qos-gargoyle/files/qos_gargoyle.hotplug
Normal file
@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
if [ "$INTERFACE" = "wan" ]; 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
|
868
package/ctcgfw/qos-gargoyle/files/qos_gargoyle.init
Executable file
868
package/ctcgfw/qos-gargoyle/files/qos_gargoyle.init
Executable file
@ -0,0 +1,868 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
#
|
||||
# Copyright Eric Bishop, 2008
|
||||
# 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)"
|
||||
|
||||
include /lib/network
|
||||
include /usr/lib/gargoyle_firewall_util
|
||||
|
||||
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=""
|
||||
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"
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
load_and_sort_all_config_sections()
|
||||
{
|
||||
local config_name="$1"
|
||||
local section_type="$2"
|
||||
local sort_variable="$3"
|
||||
|
||||
all_config_sections=""
|
||||
defined_option_cb()
|
||||
{
|
||||
if [ "$1" = "$sort_variable" ]; then
|
||||
all_config_sections=" $2:$all_config_sections"
|
||||
fi
|
||||
}
|
||||
|
||||
config_cb()
|
||||
{
|
||||
if [ -n "$2" ] || [ -n "$1" ] ; then
|
||||
if [ -n "$section_type" ] ; then
|
||||
if [ "$1" = "$section_type" ] ; then
|
||||
all_config_sections="$2 $all_config_sections"
|
||||
option_cb() { defined_option_cb $1 $2 ; }
|
||||
else
|
||||
option_cb() { return 0; }
|
||||
fi
|
||||
else
|
||||
all_config_sections="$2 $all_config_sections"
|
||||
option_cb(){ defined_option_cb $1 $2 ; }
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
config_load "$config_name"
|
||||
|
||||
echo "$all_config_sections" | awk ' {for(i=1; i <= NF; i++){ print $i }}' | sort -n -t ":" | awk 'BEGIN {FS=":"}; {print $2}'
|
||||
}
|
||||
|
||||
|
||||
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_and_sort_all_config_sections "$config_file_name" "$rule_type" "test_order")
|
||||
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"
|
||||
|
||||
;;
|
||||
layer7)
|
||||
layer7_connmark=$(cat /etc/l7marker.marks 2>/dev/null | grep "$option_value" | awk '{ print $2 }')
|
||||
layer7_mask=$(cat /etc/l7marker.marks 2>/dev/null | grep "$option_value" | awk '{ print $3 }')
|
||||
if [ -n "$layer7_connmark" ] ; then
|
||||
match_str="$match_str -m connmark --mark $layer7_connmark/$layer7_mask "
|
||||
else
|
||||
match_str="$match_str -m layer7 --l7proto $option_value"
|
||||
fi
|
||||
;;
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
initialize_qos()
|
||||
{
|
||||
#initialize layer7_marker if necessary
|
||||
create_l7marker_chain
|
||||
|
||||
# Now, load/insert necessary kernel modules
|
||||
# The following packages are required for the modules:
|
||||
rmmod imq >&- 2>&-
|
||||
insmod imq numdevs=1 hook_chains="INPUT,FORWARD" hook_tables="mangle,mangle" >&- 2>&-
|
||||
ip link set dev imq0 mtu 1500
|
||||
|
||||
# Make sure the other kernel modules we need are loaded
|
||||
insmod cls_fw >&- 2>&-
|
||||
insmod cls_flow >&- 2>&-
|
||||
insmod sch_hfsc >&- 2>&-
|
||||
insmod sch_sfq >&- 2>&-
|
||||
|
||||
#Deciding how to set sfq_depth is not straight forward. Too high and we burn up RAM
|
||||
#needlessly on low memory routers. Too low and our maximum bandwidth gets limited.
|
||||
#
|
||||
#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
|
||||
|
||||
$echo_off
|
||||
#load upload variables
|
||||
load_all_config_options "$config_file_name" "upload"
|
||||
$echo_on
|
||||
if [ -n "$total_bandwidth" ] ; then
|
||||
total_upload_bandwidth="$total_bandwidth"
|
||||
else
|
||||
total_upload_bandwidth=-1
|
||||
fi
|
||||
upload_default_class="$default_class"
|
||||
|
||||
#load download variables
|
||||
total_bandwidth=""
|
||||
default_class=""
|
||||
$echo_off
|
||||
load_all_config_options "$config_file_name" "download"
|
||||
$echo_on
|
||||
if [ -n "$total_bandwidth" ] ; then
|
||||
total_download_bandwidth="$total_bandwidth"
|
||||
else
|
||||
total_download_bandwidth=-1
|
||||
fi
|
||||
download_default_class="$default_class"
|
||||
|
||||
#Since the introduction of ADSL IP Extensions its not so easy to tell if we have a DSL connection
|
||||
#or not making it hard to set the stab parameters correctly.
|
||||
#Stab parameters here are derived from tc-stab(8).html
|
||||
overhead="stab linklayer atm overhead 32 mtu 2048 "
|
||||
|
||||
#It is now often difficult to tell if the overhead should be used or not because even DSL modems use DHCP or may
|
||||
#not be in bridge mode. So I make the assumption that all connections with less than 3Mbps down or 1Mbps up
|
||||
#are DSL regardless of the protocol selection type.
|
||||
wan_proto=$(uci get network.wan.proto 2>/dev/null)
|
||||
if [ "$wan_proto" != "pppoe" ] && [ "$total_upload_bandwidth" -ge 1100 ] && [ "$total_download_bandwidth" -ge 3100 ] ; then
|
||||
overhead=""
|
||||
fi
|
||||
|
||||
$echo_off
|
||||
|
||||
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 and ls are relevant since rt only applies to leaf qdiscs
|
||||
#
|
||||
# A detailed explanation of how/why/what is being set is warranted here...
|
||||
# Link Share bandwidths of the leaf nodes are all relative to their parents link share parameter and those of their
|
||||
# fellow leaf nodes competing for shares. We set the root class link share to 1000Mbit as per unit bandwidth.
|
||||
# Leaf nodes are then set with the respective percent of this per unit number.
|
||||
#
|
||||
# The actual maximum link speed is the lower of the ul and the ls and this is going to always be the ul parameter in our
|
||||
# design.
|
||||
#
|
||||
# Again, for ls only the ratios matter, the absolute values do not.
|
||||
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 "
|
||||
|
||||
$echo_on
|
||||
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}' )
|
||||
if [ "$min_bandwidth" -gt 0 ] ; then
|
||||
ll_str=" rt m1 $((2*$min_bandwidth))kbit d 2ms m2 ${min_bandwidth}kbit"
|
||||
else
|
||||
ll_str=""
|
||||
fi
|
||||
|
||||
#is there an upper limit specified in kbps?
|
||||
max_bandwidth=$( echo $uclass_def | awk ' {print $2}' )
|
||||
if [ "$max_bandwidth" -ge 0 ] ; then
|
||||
ul_str=" ul m2 ${max_bandwidth}kbit"
|
||||
else
|
||||
#Calculate from the class link share.
|
||||
max_bandwidth=$(($m2*$total_upload_bandwidth/1000000))
|
||||
ul_str=""
|
||||
fi
|
||||
|
||||
|
||||
# For leaf nodes in HFSC we calculate the latency as the time spent waiting to earn enough credit (credit_t) to
|
||||
#get selected to send plus the time to transmit (tts). The maximum latency can be calculated as shown
|
||||
#below assuming an MTU of 1500bytes and 8.2bits/byte including overhead.
|
||||
#credit_t = $((1500*82/$max_bandwidth/10));
|
||||
#tts = $((1500*82/$total_upload_bandwidth/10));
|
||||
|
||||
#tbw is the Delay x Bandwidth product in bytes. 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 100ms by 1000 below so the units work out.
|
||||
tbw=$(($max_bandwidth*100/8));
|
||||
if [ "$tbw" -lt 6000 ] ; then
|
||||
tbw=6000
|
||||
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
|
||||
|
||||
#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 250 bytes.
|
||||
tc qdisc add dev $qos_interface parent 1:$next_class_index handle $next_class_index:1 sfq headdrop limit $(($tbw/250)) $sfq_depth divisor 256
|
||||
|
||||
#
|
||||
#Folks interested in experimenting with CoDEL can comment out the preceeding line and uncomment hte next line.
|
||||
#This will work for the upload direction. Left to the student how to mod the download.
|
||||
#As of Chaos Calmer there does not seem to be any benefit in changing SFQ to FQ_CODEL and some stability
|
||||
#issues as well. Will re-evaluate once Openwrt 18.06 based Gargoyle is released.
|
||||
#fq_codel parameters
|
||||
# limit - SFQ used 127 so i suggest the same here.
|
||||
# target - At and above 6Mbps = 5ms (min). At 100kbps = 100ms (max). Select accordingly
|
||||
# interval - 100ms, This is the default
|
||||
# flows - 255 (One for each IP)
|
||||
#
|
||||
#tc qdisc add dev $qos_interface parent 1:$next_class_index handle $next_class_index:1 fq_codel limit 127 target 5ms interval 100ms flows 255 quantum 1514
|
||||
|
||||
#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
|
||||
fi
|
||||
|
||||
|
||||
|
||||
#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 (IMQ0) for ingress
|
||||
ip link set imq0 up
|
||||
|
||||
# Attach ingress queuing discipline to IMQ0 with temporary default
|
||||
tc qdisc add dev imq0 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
|
||||
tc class add dev imq0 parent 1:0 classid 1:1 hfsc ls rate 1000Mbit ul m2 ${total_download_bandwidth}kbit
|
||||
|
||||
#load download classes
|
||||
$echo_off
|
||||
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
|
||||
$echo_on
|
||||
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.
|
||||
|
||||
#Calculations for the delay parameter. Assuming PKT is the packet size the best we
|
||||
#could possibly do is PKT/total_download_bandwidth. If we do nothing we get the
|
||||
#worst delay of PKT/$m2 (the class bandwidth). Since this class has minRTT set we
|
||||
#are going to select the best case and allow up to one max size packet to go at the highest
|
||||
#speed.
|
||||
|
||||
#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
|
||||
|
||||
#How about a link share in percent?
|
||||
minRTT=$( echo $dclass_def | awk ' {print $4}' )
|
||||
if [ "$minRTT" = "Yes" ] ; then
|
||||
d1=$((15000/$max_bandwidth+1))
|
||||
ll_str=" ls m1 $((2*$m2))Mbit d ${d1}ms 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
|
||||
d2=$((15000/$max_bandwidth+1))
|
||||
rt_str=" rt m1 $(($max_bandwidth))kbit d ${d2}ms m2 ${min_bandwidth}kbit"
|
||||
else
|
||||
rt_str=" rt m2 ${min_bandwidth}kbit"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
tbw=$(($max_bandwidth*100/8));
|
||||
if [ "$tbw" -lt 10000 ] ; then
|
||||
tbw=10000
|
||||
fi
|
||||
|
||||
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 250 bytes.
|
||||
tc qdisc add dev imq0 parent 1:$next_class_index handle $next_class_index:1 sfq headdrop limit $(($tbw/250)) $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 8.8.8.8 | 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
|
||||
ptarget_ip=$(gargoyle_header_footer -i gargoyle | sed -n 's/.*currentWanGateway.*"\(.*\)".*/\1/p')
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
#Ping responses from the ping target never go to ingress QoS (IMQ0 device).
|
||||
iptables -t mangle -I qos_ingress -p icmp --icmp-type 0 -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()
|
||||
{
|
||||
$echo_on
|
||||
#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 wan 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
|
||||
|
||||
qos_interface=$(uci -P /var/state get network.wan.ifname 2>/dev/null)
|
||||
if [ -z $qos_interface ] ; then
|
||||
qos_interface=$(uci -P /var/state get network.wan.device 2>/dev/null)
|
||||
fi
|
||||
|
||||
#Determine our wan ip from the wan interface we detected.
|
||||
wan_ip=$(ifconfig $qos_interface | sed -n 's/.*inet addr:\([0-9/.]*\).*/\1/p')
|
||||
local_ip=$(ifconfig br-lan | sed -n 's/.*inet addr:\([0-9/.]*\).*/\1/p')
|
||||
|
||||
#Determine the VPN interface if there is one.
|
||||
vpn_interface=$(uci -P /var/state get firewall.vpn_zone.device 2>/dev/null)
|
||||
$echo_off
|
||||
}
|
||||
|
||||
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
|
||||
#Delete the qdiscs we hung on the devices
|
||||
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
|
||||
delete_chain_from_table "mangle" "qos_egress"
|
||||
delete_chain_from_table "mangle" "qos_ingress"
|
||||
$echo_off
|
||||
|
||||
}
|
||||
|
||||
|
||||
start()
|
||||
{
|
||||
test_total_up=$(uci get qos_gargoyle.upload.total_bandwidth 2>/dev/null)
|
||||
test_total_down=$(uci get qos_gargoyle.download.total_bandwidth 2>/dev/null)
|
||||
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 "$1"
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
35
package/ctcgfw/qos-gargoyle/src/Makefile
Normal file
35
package/ctcgfw/qos-gargoyle/src/Makefile
Normal file
@ -0,0 +1,35 @@
|
||||
BINDIR = /usr/sbin
|
||||
#TCDIR:=$(BUILD_DIR)/iproute2*/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
|
||||
|
1256
package/ctcgfw/qos-gargoyle/src/qosmon.c
Normal file
1256
package/ctcgfw/qos-gargoyle/src/qosmon.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -187,6 +187,14 @@ $(call Package/iptables/Module, +kmod-ipt-imq)
|
||||
TITLE:=IMQ support
|
||||
endef
|
||||
|
||||
define Package/iptables-mod-imq/description
|
||||
iptables extension for IMQ support.
|
||||
|
||||
Targets:
|
||||
- IMQ
|
||||
|
||||
endef
|
||||
|
||||
define Package/iptables-mod-bandwidth
|
||||
$(call Package/iptables/Module, +kmod-ipt-bandwidth)
|
||||
TITLE:=bandwidth option extensions
|
||||
@ -207,14 +215,6 @@ $(call Package/iptables/Module, +kmod-ipt-weburl)
|
||||
TITLE:=weburl option extensions
|
||||
endef
|
||||
|
||||
define Package/iptables-mod-imq/description
|
||||
iptables extension for IMQ support.
|
||||
|
||||
Targets:
|
||||
- IMQ
|
||||
|
||||
endef
|
||||
|
||||
define Package/iptables-mod-ipopt
|
||||
$(call Package/iptables/Module, +kmod-ipt-ipopt)
|
||||
TITLE:=IP/Packet option extensions
|
||||
|
Loading…
x
Reference in New Issue
Block a user