Last active
February 14, 2024 12:50
-
-
Save 70p4z/78dceb382b7216f0e815e81da6bb7fcb to your computer and use it in GitHub Desktop.
DDWRT Parental Control Time Limit
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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