Skip to content

Instantly share code, notes, and snippets.

@70p4z
Last active February 14, 2024 12:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 70p4z/78dceb382b7216f0e815e81da6bb7fcb to your computer and use it in GitHub Desktop.
Save 70p4z/78dceb382b7216f0e815e81da6bb7fcb to your computer and use it in GitHub Desktop.
DDWRT Parental Control Time Limit
#!/bin/sh
# This file is a Startup Script for DDWRT to enable setting timelimit for a given list of MAC address
# Sometimes soon, I'll add the modification to have it limiting all MAC by default, and add some exclusion for
# Specific MAC. Modification shall not be that complex though.
#
# How it works
# ============
# Basically, the script relies on a configuration file (which is defined within the script, because ddwrt not writable root FS).
# This file holds a MAC address and a list of 7 minute limit one for each of the week's day, starting on monday.
# On startup, and for each registered MAC address, the scripts registers an ACCEPT rule to monitor exchanged packets for the given MAC address.
# Once each ~ minute, the script looks at counter, and if the threshold is crossed, then account for a minute of time elapsed from the time limit.
# Once the time limit is reach, the rule is changed from an ACCEPT to a DROP.
# Time limits are accounted using epoch time, to make it easier to perform arithmetics.
#
#
# Changelog:
# 2024/02
# - rework session to account for remaining seconds available
# - implement temporary recharge using tmp file, to allow for dynamic tweak
# 2024/02
# - fix no rule for MAC implying invlaid counter checked
# - redesign to avoid ALLOW by default
# - support 0 minutes for a day, adjusting allow/deny rules accordingly
# 2024/01
# - allow MAC when IP timelimit file is destroyed (t make easier quota reset)
# 2023
# - Fix case matching of MAC when checking use
#
set -x
# <MAC> <MONDAY_MAX_MINUTES <TUESDAY_MAX_MINUTES> ... <SUNDAY_MAX_MINUTES>
#CONF=/etc/ip_timelimit_mac
# CUT after this line to set the ddwrt script, else it's too long (4k limit)
# ----8<----8<----8<----8<----8<----8<----8<----8<----8<----8<----8<----8<----
CONF=/tmp/ip_timelimit_mac
cat > $CONF <<EOF
#xx:xx:xx:xx:xx:xx 60 60 120 60 60 120 120 #machineX
EOF
FILTER_RULE=FORWARD
VALUE_THRS=100 # idle windows activity
value_off=3 # value to track is the byte count
LATENCY=60
# SESSDATE LASTTIME LASTTOTALPACKETS/BYTES REMAININGSECONDS
SESS_STEM=/tmp/iptl_
reset_mac() {
iptables -D ${FILTER_RULE} -m mac --mac-source $1 -j ACCEPT
iptables -D ${FILTER_RULE} -m mac --mac-source $1 -j DROP
}
allow_mac() {
reset_mac $1
iptables -I ${FILTER_RULE} -m mac --mac-source $1 -j ACCEPT
}
deny_mac() {
reset_mac $1
iptables -I ${FILTER_RULE} -m mac --mac-source $1 -j DROP
}
set_mac_rule_if_rem() {
if [ ! -z "$2" ] && [ $2 -gt 0 ]
then
allow_mac $1
else
deny_mac $1
fi
}
while read -r confline
do
if [ -z "$confline" ]
then
continue
fi
MAC=`echo $confline | cut -f1 -d' '`
reset_mac ${MAC}
done < $CONF
# run the timers
while [ true ]
do
while read -r confline
do
if [ -z "$confline" ]
then
continue
fi
MAC=`echo $confline | cut -f1 -d' '`
session_file=${SESS_STEM}${MAC}
# create session for the given MAC if no session file exists
today=`date +%F`
now=`date +%s`
day_idx=$(($(date +%u)+1))
limit_today=`echo $confline | cut -f${day_idx} -d' '`
if [ -z "${limit_today}" ]
then
limit_today=0
fi
if [ ! -f ${session_file} ]
then
# date lastcheck totalpackets/bytes remainingseconds
s_rem=$((${limit_today} * ${LATENCY} ))
s_date=${today}
echo "${s_date} ${now} 0 ${s_rem}" > ${session_file}
# if no file for that mac (likely removed) then allow it until limit are triggered
set_mac_rule_if_rem ${MAC} ${s_rem}
fi
read -r s_date s_last s_value s_rem < ${session_file}
#perform only if now is = than last time (minute)
if [ ${s_last} != ${now} ]
then
# recharge the remaining seconds and consume token
if [ -e "${session_file}_adj" ]
then
s_rem=$((${s_rem} + `cat "${session_file}_adj"` * ${LATENCY} ))
rm "${session_file}_adj"
set_mac_rule_if_rem ${MAC} ${s_rem}
fi
# reset limit if date NOW does not match session
if [ "${s_date}" != "${today}" ]
then
s_rem=$((${limit_today} * ${LATENCY} ))
s_date=${today}
# new day, reset quota
echo "${s_date} ${now} 0 ${s_rem}" > ${session_file}
set_mac_rule_if_rem ${MAC} ${s_rem}
fi
# if packet_count/bytes changed, if no rule then value is 0
value=`iptables -L ${FILTER_RULE} -nvx | grep -i ${MAC} | tr -s ' ' | cut -f${value_off} -d' '`
if [ -z "${value}" ]
then
value=0
# no rule yet defined, reset to the effective expected state
set_mac_rule_if_rem ${MAC} ${s_rem}
fi
if [ "${value}" -ge "$((${s_value}+${VALUE_THRS}))" ] && [ ${s_rem} -gt 0 ]
then
# traffic detected, account for elapsed minutes since last call
elapsed_seconds=$((${now} - ${s_last}))
s_rem=$((${s_rem} - ${elapsed_seconds}))
if [ ${s_rem} -gt 0 ]
then
# if we're having time extension, then modify rule, else don't disturb traffic counting
if [ ! -z "`iptables -L ${FILTER_RULE} -nvx | grep -i ${MAC} | grep DROP`" ]
then
allow_mac ${MAC}
fi
else
deny_mac ${MAC}
fi
fi
# update session (to account for blank period, we only account for packet exchange time)
echo "${s_date} ${now} ${value} ${s_rem}" > ${session_file}
fi
done < $CONF
sleep ${LATENCY}
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment