Skip to content

Commit

Permalink
Merge pull request #1133 from henmohr/issue/1077
Browse files Browse the repository at this point in the history
Pirania New Release - Issue/1077
  • Loading branch information
ilario authored Nov 17, 2024
2 parents 08a3948 + 358c203 commit 06609f6
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 138 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ build
luacov.stats.out
tools/ansible/files/generic-rootfs.tar.gz
tools/ansible/files/ramfs.bzImage

deploy-pirania.sh
updatepkg.sh
# Vim text editor swap files
**/*~

Expand Down
2 changes: 1 addition & 1 deletion packages/pirania/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ This are the currently implemented features:
* Can be used without vouchers.
## Prerequisites

This software assumes that will be running on a OpenWRT/LEDE distribution (because uses uci for config). Needs `ip6tables-mod-nat` and `ipset` packages installed.
This software assumes that will be running on a OpenWRT/LEDE distribution (because uses uci for config). Needs `nftables` and `ipset` packages installed.

## Install

Expand Down
4 changes: 2 additions & 2 deletions packages/pirania/files/etc/config/pirania
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ config base_config 'base_config'
option url_fail '/portal/fail.html'
option db_path '/etc/pirania/vouchers/'
option hooks_path '/etc/pirania/hooks/'
option append_ipt_rules '0' # if set to 1, iptables rules will be Appended instead of Inserted
option append_nft_rules '0' # if set to 1, iptables rules will be Appended instead of Inserted
option with_vouchers '0'
list allowlist_ipv4 '10.0.0.0/8'
list allowlist_ipv4 '172.16.0.0/12'
list allowlist_ipv4 '192.168.0.0/16'
list allowlist_ipv6 'fc00::/7'
list allowlist_ipv6 'fe80::/64'
list allowlist_ipv6 '2a00:1508:0a00::/40'
# list catch_interfaces 'br-lan'
list catch_interfaces 'br-lan'
# list catch_interfaces 'anygw'
list catch_bridged_interfaces 'wlan0-ap'

Expand Down
221 changes: 90 additions & 131 deletions packages/pirania/files/usr/bin/captive-portal
Original file line number Diff line number Diff line change
@@ -1,164 +1,123 @@
#!/bin/sh
# requires ip6tables-mod-nat and ipset
# requires nftables, liblucihttp0, liblucihttp-lua, uhttpd, uhttpd-mod-lua, uhttpd-mod-ubus

clean_tables () {
echo "Cleaning captive-portal rules"
for iface in $(uci get pirania.base_config.catch_bridged_interfaces); do
ebtables -t nat -D PREROUTING -i $iface -j mark --mark-set 0x9124714
done

for ipvX in ipv4 ipv6 ; do
if [ "$ipvX" = "ipv4" ] ; then
iptables=iptables
family=inet
ipaddr=ipaddr
else
iptables=ip6tables
family=inet6
ipaddr=ip6addr
fi

$iptables -t mangle -D PREROUTING -m mark --mark 0x9124714 -j pirania

for interface in $(uci get pirania.base_config.catch_interfaces); do
$iptables -t mangle -D PREROUTING -i $interface -j pirania
done

$iptables -t nat -D PREROUTING -j pirania
$iptables -t filter -D FORWARD -j pirania
for table in mangle nat filter; do
$iptables -t $table -F pirania
$iptables -t $table -X pirania
done
done
}

clean_sets () {
ipset flush pirania-auth-macs
for ipvX in ipv4 ipv6 ; do
ipset flush pirania-allowlist-$ipvX
done
clean_tables () {
echo "Cleaning captive-portal rules if there's any"
if nft list tables inet | grep -q "pirania"; then
nft delete table inet pirania
fi

}

set_iptables () {
set_nftables () {
echo "Apply captive-portal rules"

append_ipt_rules=$(uci get pirania.base_config.append_ipt_rules 2> /dev/null)
if [ "$append_ipt_rules" = "1" ] ; then
AorI="A"
else
AorI="I"
fi

# Mark every packet from catch_bridged_interfaces to be handled later.
# bridged interfaces cant be handled by iptables.
for iface in $(uci get pirania.base_config.catch_bridged_interfaces); do
ebtables -t nat -$AorI PREROUTING -i $iface -j mark --mark-set 0x9124714
done

for ipvX in ipv4 ipv6 ; do
if [ "$ipvX" = "ipv4" ] ; then
iptables=iptables
family=inet
anygw=$(uci get network.lm_net_br_lan_anygw_if.ipaddr)
else
iptables=ip6tables
family=inet6
anygw=[$(uci get network.lan.ip6addr | cut -d/ -f1)]
fi

### Buildup: create a pirania chain in each table
for table in mangle nat filter; do
$iptables -t $table -N pirania
done

# Redirect to pirania chain every packet from catch_bridged_interfaces
if [ -n "$(uci get pirania.base_config.catch_bridged_interfaces)" ] ; then
$iptables -t mangle -$AorI PREROUTING -m mark --mark 0x9124714 -j pirania
fi

# Redirect to pirania chain every packet from catch_interfaces
for interface in $(uci get pirania.base_config.catch_interfaces); do
$iptables -t mangle -$AorI PREROUTING -i $interface -j pirania
done

# stop processing the chain for authorized macs and allowed ips (so they are accepted)
$iptables -t mangle -A pirania -m set --match-set pirania-auth-macs src -j RETURN
$iptables -t mangle -A pirania -m set --match-set pirania-allowlist-$ipvX dst -j RETURN

# mark other packages to be rejected later
$iptables -t mangle -A pirania -j MARK --set-mark 0x66/0xff
# except their dest port is 80, in this case mark to be redirected later
$iptables -t mangle -A pirania -p tcp -m tcp --dport 80 -j MARK --set-mark 0x80/0xff

# marked packages reach nat-prerouting table, send them to nat-pirania chain.
$iptables -t nat -$AorI PREROUTING --jump pirania
# Detect wheter add or insert rules
#append_nft_rules=$(uci get pirania.base_config.append_nft_rules 2> /dev/null)
#if [ "$append_nft_rules" = "1" ] ; then
# op="add rule"
#else
# op="insert rule"
#fi

# Create pirania tables
nft create table inet pirania
# Create default tables and chains
nft add table inet pirania
nft add chain inet pirania prerouting { type nat hook prerouting priority 0 \; }
nft add chain inet pirania input { type filter hook input priority 0 \; }
nft add chain inet pirania forward { type filter hook forward priority 0 \; }

# Add mac-adress set
nft add set inet pirania pirania-auth-macs { type ether_addr\; }

# Create ipv4 set on pirania table
nft add set inet pirania pirania-allowlist-ipv4 { type ipv4_addr \; flags interval \; comment \"allow ipv4 list\" \; }
# Create ipv6 set on pirania table
nft add set inet pirania pirania-allowlist-ipv6 { type ipv6_addr \; flags interval \; comment \"allow ipv6 list\" \; }

# Only accept packets from interfaces defined in catch_bridged_interfaces
catch_interfaces=$(uci get pirania.base_config.catch_bridged_interfaces | sed 's/ /,/g')

# stop processing the chain for authorized macs and allowed ips (so they are accepted)
nft add rule inet pirania prerouting ether saddr @pirania-auth-macs ct state new,established,related counter log prefix "ValidSMAC" accept
nft add rule inet pirania prerouting ip daddr @pirania-allowlist-ipv4 ct state new,established,related counter log prefix "ACCEPT-ipv4" accept
nft add rule inet pirania prerouting ip6 daddr @pirania-allowlist-ipv6 ct state new,established,related counter log prefix "ACCEPT-ipv6" accept

# send DNS requests, that are not from valid ips or macs, to our own captive portal DNS at 59053
nft add rule inet pirania prerouting meta l4proto udp udp dport 53 ether saddr != @pirania-auth-macs ct state new,established,related counter log prefix "SMACDNS" redirect to :59053
# redirect packets with dest port 80 to port 59080 of this host (the captive portal page).
nft add rule inet pirania prerouting meta l4proto tcp tcp dport 80 ether saddr != @pirania-auth-macs ct state new,established,related counter log prefix "SMACHTTP" redirect to :59080

#nft add rule inet pirania prerouting meta l4proto tcp tcp dport 80 ip saddr @pirania-allowlist-ipv4 ct state new,established,related counter log prefix "IPv4HTTP" redirect to :59080
#nft add rule inet pirania prerouting meta l4proto tcp tcp dport 80 ip6 saddr @pirania-allowlist-ipv6 ct state new,established,related counter log prefix "IPV6HTTP" redirect to :59080

#nft add rule inet pirania prerouting meta l4proto udp udp dport 53 ip saddr @pirania-allowlist-ipv4 ct state new,established,related counter redirect to :59053
#nft add rule inet pirania prerouting meta l4proto udp udp dport 53 ip6 saddr @pirania-allowlist-ipv6 ct state new,established,related counter redirect to :59053


# reject

# in nat-pirania chain do:
# send DNS requests, that are not from valid ips or macs, to our own captive portal DNS at 59053
$iptables -t nat -A pirania -p udp -m set ! --match-set pirania-allowlist-$ipvX src -m set ! --match-set pirania-auth-macs src --dport 53 -j DNAT --to-destination $anygw:59053
# redirect packets with dest port 80 to port 59080 of this host (the captive portal page).
$iptables -t nat -A pirania -p tcp -m tcp -m mark --mark 0x80/0xff -j REDIRECT --to-ports 59080

# Other packets, if intended to be forwarded will reach filter-forward chain, send them to filter-pirania chain.
$iptables -t filter -$AorI FORWARD --jump pirania
# And in there let's reject them with the best suited reject reason.
$iptables -t filter -A pirania -p tcp -m mark --mark 0x66/0xff -j REJECT --reject-with tcp-reset
$iptables -t filter -A pirania -m mark --mark 0x66/0xff -j REJECT
done
#nft add rule inet pirania prerouting drop
#nft add rule inet pirania forward meta mark 0x11/0x11 counter reject with tcp reset
#nft add rule inet pirania forward meta mark 0x11/0x11 counter reject

}

update_ipsets () {
# using temporary ipset sets and swaping them so the update
# implies minimal disturb to the network and a previous clean-up
# is not needed
ipset -exist create pirania-auth-macs hash:mac timeout 0
ipset -exist create pirania-auth-macs-tmp hash:mac timeout 0
for mac in $(pirania_authorized_macs) ; do
ipset -exist add pirania-auth-macs-tmp $mac
done
ipset swap pirania-auth-macs-tmp pirania-auth-macs
ipset destroy pirania-auth-macs-tmp

for ipvX in ipv4 ipv6 ; do
if [ "$ipvX" = "ipv4" ] ; then
family=inet
else
family=inet6
fi
ipset -exist create pirania-allowlist-${ipvX} hash:net family $family
ipset -exist create pirania-allowlist-${ipvX}-tmp hash:net family $family
for item in $(uci get pirania.base_config.allowlist_$ipvX); do
ipset -exist add pirania-allowlist-${ipvX}-tmp $item
done
ipset swap pirania-allowlist-${ipvX}-tmp pirania-allowlist-${ipvX}
ipset destroy pirania-allowlist-${ipvX}-tmp
done

# Create tables and sets
echo "Updating captive-portal rules"

# Add authorized MAC addresses
for mac in $(pirania_authorized_macs) ; do
nft add element inet pirania pirania-auth-macs {$mac}
echo "Adicionando enderecos:" $mac
done

# Update pirania-allowlist sets for ipv4 and ipv6
nft flush set inet pirania pirania-allowlist-ipv4
nft flush set inet pirania pirania-allowlist-ipv6

# Add allowed ip/prefixes
# Get values from allowlist_ipvX and add to pirania-allowlist-ipvX set
ipv4allowlist=$(uci get pirania.base_config.allowlist_ipv4 | sed 's/ /,/g')
nft add element inet pirania pirania-allowlist-ipv4 {$ipv4allowlist}

ipv6allowlist=$(uci get pirania.base_config.allowlist_ipv6 | sed 's/ /,/g')
nft add element inet pirania pirania-allowlist-ipv6 {$ipv6allowlist}
}

# check if captive-portal is enabled in /etc/config/pirania
enabled=$(uci get pirania.base_config.enabled)

if [ "$1" = "start" ]; then
echo "Running captive-portal"
/etc/init.d/pirania-dnsmasq start
/etc/init.d/pirania-uhttpd start
clean_tables
set_nftables
update_ipsets
set_iptables
exit
elif [ "$1" = "update" ] ; then
echo "Captive-portal updating rules"
update_ipsets
exit
elif [ "$1" = "clean" ] || [ "$1" = "stop" ] ; then
clean_tables
clean_sets
exit
elif [ "$enabled" = "1" ]; then
echo "Captive-portal already enabled, reloading rules"
clean_tables
# set_nftables
update_ipsets
set_iptables
exit
elif [ "$1" = "enabled" ]; then
uci set pirania.base_config.enabled='1'
# i/o error in my device - check later
#uci commit
echo "Captive-portal is now enabled"
else
echo "Pirania captive-portal is disabled. Try running captive-portal start"
exit
fi

fi
6 changes: 3 additions & 3 deletions packages/pirania/files/usr/lib/lua/voucher/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ function utils.getIpv4AndMac(ip_address)
res.mac = ipv4mac
return res
else
local ipv6macCommand = "ip neighbor | grep "..ip_address.." | awk -F ' ' '{print $5}' | head -n 1"
local ipv6macCommand = "ip neigh | grep "..ip_address.." | awk -F ' ' '{print $5}' | head -n 1"
fd6 = io.popen(ipv6macCommand, 'r')
ipv6mac = fd6:read('*l')
fd6:close()
local ipv4Command = "cat /proc/net/arp | grep "..ipv6mac.." | awk -F ' ' '{print $1}' | head -n 1"
fd4 = io.popen(ipv4Command, 'r')
local ipv4cCommand = "cat /proc/net/arp | grep "..ipv6mac.." | awk -F ' ' '{print $1}' | head -n 1"
fd4 = io.popen(ipv4cCommand, 'r')
ipv4 = fd4:read('*l')
fd4:close()
local res = {}
Expand Down

0 comments on commit 06609f6

Please sign in to comment.