Skip to content

Commit

Permalink
mwan3: use helper library for mwan3track
Browse files Browse the repository at this point in the history
Rather than using a special mwan3 user to manage mwan3track's tracking
packets, this commit implements a small helper library to bind to
device and to set a fwmark so that the tracking packets can be routed
out of the correct interface.

This provides a consistent method for binding to a device rather than
relying on various packages potentially buggy implementations. For
example: openwrt#8139 and openwrt#12836

This helper issue also allows for more tracking methods to be added
even if they do not have a command line option to bind to device,
such as iperf3 (eg  openwrt#13050).

Signed-off-by: Aaron Goodman <aaronjg@stanford.edu>
  • Loading branch information
aaronjg committed Aug 28, 2020
1 parent 0190e13 commit f048fb4
Show file tree
Hide file tree
Showing 6 changed files with 347 additions and 104 deletions.
12 changes: 8 additions & 4 deletions net/mwan3/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
include $(TOPDIR)/rules.mk

PKG_NAME:=mwan3
PKG_VERSION:=2.9.0
PKG_VERSION:=2.10.0
PKG_RELEASE:=1
PKG_MAINTAINER:=Florian Eckert <fe@dev.tdt.de>
PKG_LICENSE:=GPL-2.0
Expand All @@ -25,9 +25,8 @@ define Package/mwan3
+iptables \
+iptables-mod-conntrack-extra \
+iptables-mod-ipopt \
+iptables-mod-extra \
+libcap-bin \
+jshn
PKG_CONFIG_DEPENDS:=IPV6
TITLE:=Multiwan hotplug script with connection tracking support
MAINTAINER:=Florian Eckert <fe@dev.tdt.de>
PKGARCH:=all
Expand Down Expand Up @@ -63,8 +62,13 @@ fi
exit 0
endef

define Build/Compile
$(TARGET_CC) $(CFLAGS) $(LDFLAGS) -fPIC -shared -o $(PKG_BUILD_DIR)/libwrap_mwan3_sockopt.so.1.0 $(if $(CONFIG_IPV6),-DCONFIG_IPV6) $(PKG_BUILD_DIR)/sockopt_wrap.c -ldl
endef

define Package/mwan3/install
$(CP) ./files/* $(1)
$(CP) ./files/* $(1)
$(CP) $(PKG_BUILD_DIR)/libwrap_mwan3_sockopt.so.1.0 $(1)/lib/mwan3/
endef

$(eval $(call BuildPackage,mwan3))
84 changes: 84 additions & 0 deletions net/mwan3/files/lib/mwan3/common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,19 @@ get_uptime() {
IP4="ip -4"
IP6="ip -6"
SCRIPTNAME="$(basename "$0")"

MWAN3_STATUS_DIR="/var/run/mwan3"
MWAN3TRACK_STATUS_DIR="/var/run/mwan3track"

MWAN3_INTERFACE_MAX=""

MMX_MASK=""
MMX_DEFAULT=""
MMX_BLACKHOLE=""
MM_BLACKHOLE=""

MMX_UNREACHABLE=""
MM_UNREACHABLE=""
MAX_SLEEP=$(((1<<31)-1))

LOG()
Expand Down Expand Up @@ -67,3 +78,76 @@ mwan3_get_src_ip()
fi
export "$1=$_src_ip"
}


mwan3_init()
{
local bitcnt
local mmdefault

[ -d $MWAN3_STATUS_DIR ] || mkdir -p $MWAN3_STATUS_DIR/iface_state

# mwan3's MARKing mask (at least 3 bits should be set)
if [ -e "${MWAN3_STATUS_DIR}/mmx_mask" ]; then
MMX_MASK=$(cat "${MWAN3_STATUS_DIR}/mmx_mask")
MWAN3_INTERFACE_MAX=$(uci_get_state mwan3 globals iface_max)
else
config_load mwan3
config_get MMX_MASK globals mmx_mask '0x3F00'
echo "$MMX_MASK"| tr 'A-F' 'a-f' > "${MWAN3_STATUS_DIR}/mmx_mask"
LOG debug "Using firewall mask ${MMX_MASK}"

bitcnt=$(mwan3_count_one_bits MMX_MASK)
mmdefault=$(((1<<bitcnt)-1))
MWAN3_INTERFACE_MAX=$(($mmdefault-3))
uci_toggle_state mwan3 globals iface_max "$MWAN3_INTERFACE_MAX"
LOG debug "Max interface count is ${MWAN3_INTERFACE_MAX}"
fi

# mark mask constants
bitcnt=$(mwan3_count_one_bits MMX_MASK)
mmdefault=$(((1<<bitcnt)-1))
MM_BLACKHOLE=$(($mmdefault-2))
MM_UNREACHABLE=$(($mmdefault-1))

# MMX_DEFAULT should equal MMX_MASK
MMX_DEFAULT=$(mwan3_id2mask mmdefault MMX_MASK)
MMX_BLACKHOLE=$(mwan3_id2mask MM_BLACKHOLE MMX_MASK)
MMX_UNREACHABLE=$(mwan3_id2mask MM_UNREACHABLE MMX_MASK)
}

# maps the 1st parameter so it only uses the bits allowed by the bitmask (2nd parameter)
# which means spreading the bits of the 1st parameter to only use the bits that are set to 1 in the 2nd parameter
# 0 0 0 0 0 1 0 1 (0x05) 1st parameter
# 1 0 1 0 1 0 1 0 (0xAA) 2nd parameter
# 1 0 1 result
mwan3_id2mask()
{
local bit_msk bit_val result
bit_val=0
result=0
for bit_msk in $(seq 0 31); do
if [ $((($2>>bit_msk)&1)) = "1" ]; then
if [ $((($1>>bit_val)&1)) = "1" ]; then
result=$((result|(1<<bit_msk)))
fi
bit_val=$((bit_val+1))
fi
done
printf "0x%x" $result
}


# counts how many bits are set to 1
# n&(n-1) clears the lowest bit set to 1
mwan3_count_one_bits()
{
local count n
count=0
n=$(($1))
while [ "$n" -gt "0" ]; do
n=$((n&(n-1)))
count=$((count+1))
done
echo $count
}
72 changes: 2 additions & 70 deletions net/mwan3/files/lib/mwan3/mwan3.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,8 @@ IPv6_REGEX="${IPv6_REGEX}fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|"
IPv6_REGEX="${IPv6_REGEX}::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|"
IPv6_REGEX="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])"

MWAN3_STATUS_DIR="/var/run/mwan3"
MWAN3_INTERFACE_MAX=""
DEFAULT_LOWEST_METRIC=256
MMX_MASK=""
MMX_DEFAULT=""
MMX_BLACKHOLE=""
MM_BLACKHOLE=""

MMX_UNREACHABLE=""
MM_UNREACHABLE=""
DEFAULT_LOWEST_METRIC=256

command -v ip6tables > /dev/null
NO_IPV6=$?
Expand Down Expand Up @@ -113,63 +105,6 @@ mwan3_count_one_bits()
echo $count
}

# maps the 1st parameter so it only uses the bits allowed by the bitmask (2nd parameter)
# which means spreading the bits of the 1st parameter to only use the bits that are set to 1 in the 2nd parameter
# 0 0 0 0 0 1 0 1 (0x05) 1st parameter
# 1 0 1 0 1 0 1 0 (0xAA) 2nd parameter
# 1 0 1 result
mwan3_id2mask()
{
local bit_msk bit_val result
bit_val=0
result=0
for bit_msk in $(seq 0 31); do
if [ $((($2>>bit_msk)&1)) = "1" ]; then
if [ $((($1>>bit_val)&1)) = "1" ]; then
result=$((result|(1<<bit_msk)))
fi
bit_val=$((bit_val+1))
fi
done
printf "0x%x" $result
}

mwan3_init()
{
local bitcnt
local mmdefault

[ -d $MWAN3_STATUS_DIR ] || mkdir -p $MWAN3_STATUS_DIR/iface_state

# mwan3's MARKing mask (at least 3 bits should be set)
if [ -e "${MWAN3_STATUS_DIR}/mmx_mask" ]; then
MMX_MASK=$(cat "${MWAN3_STATUS_DIR}/mmx_mask")
MWAN3_INTERFACE_MAX=$(uci_get_state mwan3 globals iface_max)
else
config_load mwan3
config_get MMX_MASK globals mmx_mask '0x3F00'
echo "$MMX_MASK"| tr 'A-F' 'a-f' > "${MWAN3_STATUS_DIR}/mmx_mask"
LOG debug "Using firewall mask ${MMX_MASK}"

bitcnt=$(mwan3_count_one_bits MMX_MASK)
mmdefault=$(((1<<bitcnt)-1))
MWAN3_INTERFACE_MAX=$(($mmdefault-3))
uci_toggle_state mwan3 globals iface_max "$MWAN3_INTERFACE_MAX"
LOG debug "Max interface count is ${MWAN3_INTERFACE_MAX}"
fi

# mark mask constants
bitcnt=$(mwan3_count_one_bits MMX_MASK)
mmdefault=$(((1<<bitcnt)-1))
MM_BLACKHOLE=$(($mmdefault-2))
MM_UNREACHABLE=$(($mmdefault-1))

# MMX_DEFAULT should equal MMX_MASK
MMX_DEFAULT=$(mwan3_id2mask mmdefault MMX_MASK)
MMX_BLACKHOLE=$(mwan3_id2mask MM_BLACKHOLE MMX_MASK)
MMX_UNREACHABLE=$(mwan3_id2mask MM_UNREACHABLE MMX_MASK)
}

mwan3_lock() {
lock /var/run/mwan3.lock
#LOG debug "$1 $2 (lock)"
Expand Down Expand Up @@ -361,6 +296,7 @@ mwan3_set_general_iptables()

fi
mwan3_push_update -A mwan3_hook \
-m mark --mark 0x0/$MMX_MASK \
-j CONNMARK --restore-mark --nfmask "$MMX_MASK" --ctmask "$MMX_MASK"
mwan3_push_update -A mwan3_hook \
-m mark --mark 0x0/$MMX_MASK \
Expand All @@ -382,10 +318,6 @@ mwan3_set_general_iptables()
mwan3_push_update -A PREROUTING -j mwan3_hook
fi
if [ -n "${current##*-A OUTPUT -j mwan3_hook*}" ]; then
mwan3_push_update -A OUTPUT \
-m owner \
--uid-owner $(id -u mwan3) \
-j RETURN
mwan3_push_update -A OUTPUT -j mwan3_hook
fi
mwan3_push_update COMMIT
Expand Down
1 change: 0 additions & 1 deletion net/mwan3/files/usr/sbin/mwan3
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,6 @@ start_()
local enabled hotplug_pids MWAN3_STARTUP
MWAN3_STARTUP=1
mwan3_lock "command" "mwan3"
user_exists mwan3 || user_add mwan3
uci_toggle_state mwan3 globals enabled "1"
config_load mwan3

Expand Down
48 changes: 19 additions & 29 deletions net/mwan3/files/usr/sbin/mwan3track
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ PING="/bin/ping"
IFDOWN_EVENT=0
IFUP_EVENT=0

CAPSH(){
capsh --caps="cap_net_raw+eip cap_setpcap,cap_setuid,cap_setgid+ep" --keep=1 --user=mwan3 --addamb=cap_net_raw -- -c "$*"
mwan3_init

WRAP(){
FAMILY=$FAMILY DEVICE=$DEVICE SRCIP=$SRC_IP FWMARK=$MMX_DEFAULT LD_PRELOAD=/lib/mwan3/libwrap_mwan3_sockopt.so.1.0 $*
}

clean_up() {
Expand Down Expand Up @@ -72,9 +74,9 @@ validate_track_method() {
esac
}

validate_capsh() {
command -v capsh &>/dev/null && return
LOG error "Missing capsh. Please install libcap-bin. Tracking disabled." &&
validate_wrap() {
[ -x /lib/mwan3/libwrap_mwan3_sockopt.so.1.0 ] && return
LOG error "Missing libwrap_mwan3_sockopt. Please reinstall mwan3." &&
exit 1
}

Expand Down Expand Up @@ -126,17 +128,6 @@ firstconnect() {

mwan3_get_src_ip SRC_IP $true_iface

# pinging IPv6 hosts with an interface is troublesome
# https://bugs.openwrt.org/index.php?do=details&task_id=2897
# https://bugs.openwrt.org/index.php?do=details&task_id=2167
# https://forum.openwrt.org/t/ping-and-traceroute-failing-for-eth0-3-on-ipv6/44680/11
# so use the IP address of the interface
if [ "$family" = "ipv6" ]; then
SOURCE="$SRC_IP"
else
SOURCE="$DEVICE"
fi

STARTED=1
if [ "$STATUS" = "offline" ]; then
disconnected 1
Expand All @@ -157,12 +148,12 @@ update_status() {
}

main() {
local reliability count timeout interval failure_interval family
local reliability count timeout interval failure_interval
local recovery_interval down up size
local keep_failure_interval check_quality failure_latency
local recovery_latency failure_loss recovery_loss

local max_ttl httping_ssl family track_ips
local max_ttl httping_ssl track_ips

INTERFACE=$1
STATUS=""
Expand All @@ -174,7 +165,7 @@ main() {
trap if_up USR2

config_load mwan3
config_get family $INTERFACE family ipv4
config_get FAMILY $INTERFACE family ipv4
config_get track_method $INTERFACE track_method ping
config_get_bool httping_ssl $INTERFACE httping_ssl 0
validate_track_method $track_method $SRC_IP || {
Expand Down Expand Up @@ -225,12 +216,11 @@ main() {
case "$track_method" in
ping)
if [ $check_quality -eq 0 ]; then
CAPSH $PING -${family#ipv} -I $SOURCE -c $count -W $timeout -s $size -t $max_ttl -q $track_ip &> /dev/null
WRAP $PING -${FAMILY#ipv} -c $count -W $timeout -s $size -t $max_ttl -q $track_ip &> /dev/null
result=$?
else
ping_result_raw="$(CAPSH $PING -${family#ipv} -I $SOURCE -c $count -W $timeout -s $size -t $max_ttl -q $track_ip 2>/dev/null)"
ping_result_raw="$(WRAP $PING -${FAMILY#ipv} -c $count -W $timeout -s $size -t $max_ttl -q $track_ip 2>/dev/null)"
ping_status=$?

ping_result=$(echo "$ping_result_raw" | tail -n2)
loss="$(echo "$ping_result" | grep "packet loss" | cut -d "," -f3 | awk '{print $1}' | sed -e 's/%//')"
if [ "$ping_status" -ne 0 ] || [ "$loss" -eq 100 ]; then
Expand All @@ -242,28 +232,28 @@ main() {
fi
;;
arping)
CAPSH arping -I $SOURCE -c $count -w $timeout -q $track_ip &> /dev/null
WRAP arping -I $DEVICE -c $count -w $timeout -q $track_ip &> /dev/null
result=$?
;;
httping)
if [ "$httping_ssl" -eq 1 ]; then
CAPSH httping -y $SRC_IP -c $count -t $timeout -q "https://$track_ip" &> /dev/null
WRAP httping -c $count -t $timeout -q "https://$track_ip" &> /dev/null
else
CAPSH httping -y $SRC_IP -c $count -t $timeout -q "http://$track_ip" &> /dev/null
WRAP httping -c $count -t $timeout -q "http://$track_ip" &> /dev/null
fi
result=$?
;;
nping-tcp)
result=$(CAPSH nping -e $SOURCE -c $count $track_ip --tcp | grep Lost | awk '{print $12}')
result=$(WRAP nping -c $count $track_ip --tcp | grep Lost | awk '{print $12}')
;;
nping-udp)
result=$(CAPSH nping -e $SOURCE -c $count $track_ip --udp | grep Lost | awk '{print $12}')
result=$(WRAP nping -c $count $track_ip --udp | grep Lost | awk '{print $12}')
;;
nping-icmp)
result=$(CAPSH nping -e $SOURCE -c $count $track_ip --icmp | grep Lost | awk '{print $12}')
result=$(WRAP nping -c $count $track_ip --icmp | grep Lost | awk '{print $12}')
;;
nping-arp)
result=$(CAPSH nping -e $SOURCE -c $count $track_ip --arp | grep Lost | awk '{print $12}')
result=$(WRAP nping -c $count $track_ip --arp | grep Lost | awk '{print $12}')
;;
esac
if [ $check_quality -eq 0 ]; then
Expand Down
Loading

0 comments on commit f048fb4

Please sign in to comment.