resolve package problems

This commit is contained in:
CN_SZTL 2019-07-02 18:53:28 +08:00
parent a64efe2d16
commit e04f9ff3cc
No known key found for this signature in database
GPG Key ID: 6850B6345C862176
45 changed files with 11185 additions and 3 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,410 @@
/* 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

@ -1 +0,0 @@
Subproject commit 91b4e235db8cbdfe1343e3ef011d36cb94fe463e

View File

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

View File

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

View File

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

View File

@ -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,175 @@
/* 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,37 @@
/* 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,184 @@
/* 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

@ -1,6 +1,6 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=dnsforwarder
PKG_NAME:=openwrt-dnsforwarder
PKG_VERSION:=6
PKG_RELEASE:=3

View File

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

View File

@ -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,26 @@
config upload 'upload'
option default_class 'uclass_2'
option total_bandwidth '1000'
config download 'download'
option qos_monenabled 'true'
option total_bandwidth '10000'
option default_class 'dclass_1'
config download_class 'dclass_1'
option name 'Normal'
option percent_bandwidth '100'
config upload_class 'uclass_1'
option name 'Fast'
option percent_bandwidth '90'
config upload_class 'uclass_2'
option name 'Normal'
option percent_bandwidth '10'
config upload_rule 'upload_rule_100'
option class 'uclass_1'
option max_pkt_size '128'

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -74,7 +74,7 @@ define Package/luci-app-ssr-plus
+PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Socks:shadowsocksr-libev-ssr-local \
+PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Socks:shadowsocks-libev-ss-local \
+PACKAGE_$(PKG_NAME)_INCLUDE_dnscrypt_proxy:dnscrypt-proxy-full \
+PACKAGE_$(PKG_NAME)_INCLUDE_dnsforwarder:dnsforwarder \
+PACKAGE_$(PKG_NAME)_INCLUDE_dnsforwarder:openwrt-dnsforwarder \
+PACKAGE_$(PKG_NAME)_INCLUDE_haproxy:haproxy
endef