gargoyle-qos: add packages

This commit is contained in:
CN_SZTL 2020-02-25 23:53:23 +08:00
parent a4cc047af5
commit 1919abdea1
No known key found for this signature in database
GPG Key ID: 6850B6345C862176
57 changed files with 13063 additions and 9 deletions

View File

@ -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.

View 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))

View File

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

View File

@ -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

View File

@ -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
}

View File

@ -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

View 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
}

View 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*

View 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;
}

View File

@ -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;
}

File diff suppressed because it is too large Load Diff

View 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;
}

File diff suppressed because it is too large Load Diff

View 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))

View 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

View 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 */

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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;
}

View 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);
}

View 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;
}

View 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;
}

View 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))

View 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*

File diff suppressed because it is too large Load Diff

View 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);

View 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;
}

View 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*

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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 &#34;/etc/init.d/qos_gargoyle show&#34; : </span><br /><br />',
data.show,
'<br /><br /><span class="description">Output of &#34;cat /tmp/qosmon.status&#34; : </span><br /><br />',
data.mon
);
} else {
tshoot.innerHTML = '<strong><%:Error collecting troubleshooting information%></strong>';
}
}
);
//]]></script>
<div id="troubleshoot">
<fieldset class="cbi-section">
<legend><%:Troubleshooting Data%></legend>
<div id="troubleshoot_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /><%:Collecting data...%></div>
</fieldset>
</div>
<%+footer%>

View File

@ -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>PSWAN 端链路饱和即带宽被占用完)</em>"
msgid ""
"The segment of network between your router and the ping target is where "
"congestion is controlled. By monitoring the round trip ping times to the "
"target congestion is detected. By default ACC uses your WAN gateway as the "
"ping target. If you know that congestion on your link will occur in a "
"different segment then you can enter an alternate ping target. Leave empty "
"to use the default settings."
msgstr ""
"在路由器和 Ping 目标之间的网络部分是拥塞控制的地方。拥塞通过监视和目标间的 "
"Ping 延时来检测。默认情况下 ACC 使用你的 WAN 网关作为 Ping 的目标。假如你知道"
"拥塞会在你链路的不同段发生,你可用输入一个备用的 Ping 目标。留空则将由ACC 自"
"动控制。"
msgid ""
"The total size of data transmitted since the establishment of the link (in "
"kBytes)."
msgstr "自连接建立以来传输的数据总量单位kBytes"
msgid "Total Download Bandwidth"
msgstr "下载总带宽"
msgid "Total Upload Bandwidth"
msgstr "上传总带宽"
msgid "Transport Protocol"
msgstr "协议"
msgid "Troubleshooting"
msgstr "故障排除"
msgid "Troubleshooting Data"
msgstr "故障排除数据"
msgid "Unlimited"
msgstr "不限制"
msgid "Upload Settings"
msgstr "上传设置"
msgid "Use Non-standard Ping Target"
msgstr "使用自定义 Ping 目标"
msgid "Zero"
msgstr "零"

View File

@ -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

View 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))

View 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"

View 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'

View 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

View 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
}

View 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

File diff suppressed because it is too large Load Diff

View File

@ -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