2017-11-09 09:57:58 +01:00
|
|
|
#!/bin/sh
|
|
|
|
|
2020-08-13 02:38:56 +02:00
|
|
|
IP4="ip -4"
|
|
|
|
IP6="ip -6"
|
2020-07-26 23:21:50 +02:00
|
|
|
SCRIPTNAME="$(basename "$0")"
|
2020-10-12 00:37:25 +02:00
|
|
|
|
|
|
|
MWAN3_STATUS_DIR="/var/run/mwan3"
|
2022-03-08 12:52:11 +01:00
|
|
|
MWAN3_STATUS_IPTABLES_LOG_DIR="${MWAN3_STATUS_DIR}/iptables_log"
|
2020-08-31 21:49:17 +02:00
|
|
|
MWAN3TRACK_STATUS_DIR="/var/run/mwan3track"
|
2020-08-13 02:38:56 +02:00
|
|
|
|
2020-10-12 00:37:25 +02:00
|
|
|
MWAN3_INTERFACE_MAX=""
|
|
|
|
|
|
|
|
MMX_MASK=""
|
|
|
|
MMX_DEFAULT=""
|
|
|
|
MMX_BLACKHOLE=""
|
|
|
|
MM_BLACKHOLE=""
|
|
|
|
|
|
|
|
MMX_UNREACHABLE=""
|
|
|
|
MM_UNREACHABLE=""
|
mwan3: Fix packet routing when WAN interface is partially up
This introduces a new concept of "unknown_wan" to mwan3. The action for
this can be configured in the globals section the default of which is
'none'. This can be set to 'none', 'default', 'unreachable' or 'blacklist'
switching out the matching ip rule for this match. This assignment for
a connection is temporary and is re-resolved for each additional
original direction packet through the firewall allowing the unknown WAN
to start resolving once the ifup has finished for the given interface.
An example configuration:
config globals 'globals'
option unknown_wan_action 'unreachable'
Prior to this commit, mwan3 had multiple hit spots for packets in the
following order:
1. Packets are checked to see if they originate from known WAN interfaces
2. Packets are checked to see if they destined for ipsets defined
3. Packets are checked against default WAN policies
The WAN list is maintained via hotplug 'ifup'/'ifdown' events and the local
route ipset list is maintained via monitoring the routing table. This
means that while a WAN interface is brought up, the list for 2 is
updated before the list for 1, since an interface is fully brought up
before the ifup event is fired off. Additionally, we want to make sure we
don't apply a WAN policy for incoming packets from a WAN interface that
is in the process of being brought up.
We can identify packets that are presumably coming from a WAN interface
we don't recognize yet by eliminating all packets that the source comes
from networks we don't know about in the ipsets that mwan3 manages. We
have to be careful here to only match the original direction of the
packet flow (e.g. for instance with ICMP, the ping request is in the
ORIGINAL direction, and the response is in the REPLY direction) or else
we might match something we didn't intend to.
By modifying the rule set to the following:
1. Packets are checked to see if they are in a REPLY direction of flow
2. Packets are checked to see if they originate from known WAN interfaces
3. Packets are checked to see if they not sourced from ipsets defined
4. Packets are checked to see if they destined for ipsets defined
5. Packets are checked against default WAN policies
If a packet is in the REPLY direction of flow, we definitely don't want
to do any routing table assignments - we only want to do this for the
original direction of traffic flow. This reduces the amount of rules
parsed within mwan3.
If a packet is not sourced from a defined ipset, this should match any
packet originating from a "default route" upstream. We do this post the
known WAN interface check since we don't know what mask to apply to this
packet at this time until the 'ifup' has completed. It's also setup to
reevaluate this decision by clearing this specific mark when a new
packet comes in in the REPLY direction of flow before any subsequent
evaluations. This allows additional packets for the same connection to
eventually be assigned the appropriate mask once the 'ifup' has
finished.
One easy way to test this out before and after this change is to:
- Bring down wan (e.g. ifdown wan)
- Manually bring up WAN
- This mitigates the firewall rules being added for 1 above, but 2
is still added since this is monitoring the routing interface
- Ping the device from a non-local subnet via the WAN interface; leave
running
- Observe mark set to ICMP session via conntrack
- Bring up wan (e.g. ifup wan)
- Observe mark set to ICMP session from above
Signed-off-by: Tim Nordell <tnordell@airgain.com>
2023-06-26 17:24:27 +02:00
|
|
|
|
|
|
|
MMX_UNKNOWN_WAN=""
|
|
|
|
MM_UNKNOWN_WAN=""
|
2020-08-13 02:38:56 +02:00
|
|
|
MAX_SLEEP=$(((1<<31)-1))
|
|
|
|
|
2020-10-30 04:06:25 +01:00
|
|
|
command -v ip6tables > /dev/null
|
|
|
|
NO_IPV6=$?
|
|
|
|
|
2022-03-08 10:15:04 +01:00
|
|
|
IPS="ipset"
|
|
|
|
IPT4="iptables -t mangle -w"
|
|
|
|
IPT6="ip6tables -t mangle -w"
|
|
|
|
IPT4R="iptables-restore -T mangle -w -n"
|
|
|
|
IPT6R="ip6tables-restore -T mangle -w -n"
|
|
|
|
|
2020-07-26 23:21:50 +02:00
|
|
|
LOG()
|
|
|
|
{
|
|
|
|
local facility=$1; shift
|
|
|
|
# in development, we want to show 'debug' level logs
|
|
|
|
# when this release is out of beta, the comment in the line below
|
|
|
|
# should be removed
|
|
|
|
[ "$facility" = "debug" ] && return
|
2020-09-01 01:15:09 +02:00
|
|
|
logger -t "${SCRIPTNAME}[$$]" -p $facility "$*"
|
2020-07-26 23:21:50 +02:00
|
|
|
}
|
2020-10-12 00:37:25 +02:00
|
|
|
|
|
|
|
mwan3_get_true_iface()
|
|
|
|
{
|
|
|
|
local family V
|
|
|
|
_true_iface=$2
|
|
|
|
config_get family "$2" family ipv4
|
|
|
|
if [ "$family" = "ipv4" ]; then
|
|
|
|
V=4
|
|
|
|
elif [ "$family" = "ipv6" ]; then
|
|
|
|
V=6
|
|
|
|
fi
|
|
|
|
ubus call "network.interface.${2}_${V}" status &>/dev/null && _true_iface="${2}_${V}"
|
|
|
|
export "$1=$_true_iface"
|
|
|
|
}
|
|
|
|
|
2020-08-13 02:38:56 +02:00
|
|
|
mwan3_get_src_ip()
|
|
|
|
{
|
2020-08-26 00:16:07 +02:00
|
|
|
local family _src_ip interface true_iface device addr_cmd default_ip IP sed_str
|
|
|
|
interface=$2
|
|
|
|
mwan3_get_true_iface true_iface $interface
|
|
|
|
|
2020-08-13 02:38:56 +02:00
|
|
|
unset "$1"
|
2020-08-26 00:16:07 +02:00
|
|
|
config_get family "$interface" family ipv4
|
2020-08-13 02:38:56 +02:00
|
|
|
if [ "$family" = "ipv4" ]; then
|
2024-01-01 01:00:00 +01:00
|
|
|
addr_cmd_1='network_get_ipaddr'
|
|
|
|
addr_cmd_2='false'
|
2020-08-13 02:38:56 +02:00
|
|
|
default_ip="0.0.0.0"
|
|
|
|
sed_str='s/ *inet \([^ \/]*\).*/\1/;T; pq'
|
|
|
|
IP="$IP4"
|
|
|
|
elif [ "$family" = "ipv6" ]; then
|
2024-01-01 01:00:00 +01:00
|
|
|
addr_cmd_1='network_get_preferred_ipaddr6'
|
|
|
|
addr_cmd_2='network_get_ipaddr6'
|
2020-08-13 02:38:56 +02:00
|
|
|
default_ip="::"
|
|
|
|
sed_str='s/ *inet6 \([^ \/]*\).* scope.*/\1/;T; pq'
|
|
|
|
IP="$IP6"
|
|
|
|
fi
|
|
|
|
|
2024-01-01 01:00:00 +01:00
|
|
|
$addr_cmd_1 _src_ip "$true_iface" 2>&1 || $addr_cmd_2 _src_ip "$true_iface"
|
2020-08-13 02:38:56 +02:00
|
|
|
if [ -z "$_src_ip" ]; then
|
|
|
|
network_get_device device $true_iface
|
|
|
|
_src_ip=$($IP address ls dev $device 2>/dev/null | sed -ne "$sed_str")
|
|
|
|
if [ -n "$_src_ip" ]; then
|
|
|
|
LOG warn "no src $family address found from netifd for interface '$true_iface' dev '$device' guessing $_src_ip"
|
|
|
|
else
|
|
|
|
_src_ip="$default_ip"
|
|
|
|
LOG warn "no src $family address found for interface '$true_iface' dev '$device'"
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
export "$1=$_src_ip"
|
|
|
|
}
|
|
|
|
|
|
|
|
mwan3_get_mwan3track_status()
|
|
|
|
{
|
|
|
|
local track_ips pid
|
|
|
|
mwan3_list_track_ips()
|
|
|
|
{
|
|
|
|
track_ips="$1 $track_ips"
|
|
|
|
}
|
|
|
|
config_list_foreach "$1" track_ip mwan3_list_track_ips
|
|
|
|
|
|
|
|
if [ -n "$track_ips" ]; then
|
|
|
|
pid="$(pgrep -f "mwan3track $1$")"
|
|
|
|
if [ -n "$pid" ]; then
|
|
|
|
if [ "$(cat /proc/"$(pgrep -P $pid)"/cmdline)" = "sleep${MAX_SLEEP}" ]; then
|
|
|
|
tracking="paused"
|
|
|
|
else
|
|
|
|
tracking="active"
|
|
|
|
fi
|
|
|
|
else
|
|
|
|
tracking="down"
|
|
|
|
fi
|
|
|
|
else
|
2023-09-15 11:27:01 +02:00
|
|
|
tracking="disabled"
|
2020-08-13 02:38:56 +02:00
|
|
|
fi
|
|
|
|
echo "$tracking"
|
|
|
|
}
|
|
|
|
|
|
|
|
mwan3_init()
|
|
|
|
{
|
2020-12-29 02:09:08 +01:00
|
|
|
local bitcnt mmdefault source_routing
|
|
|
|
|
|
|
|
config_load mwan3
|
2020-08-13 02:38:56 +02:00
|
|
|
|
|
|
|
[ -d $MWAN3_STATUS_DIR ] || mkdir -p $MWAN3_STATUS_DIR/iface_state
|
2022-03-08 12:52:11 +01:00
|
|
|
[ -d "$MWAN3_STATUS_IPTABLES_LOG_DIR" ] || mkdir -p "$MWAN3_STATUS_IPTABLES_LOG_DIR"
|
2020-08-13 02:38:56 +02:00
|
|
|
|
|
|
|
# 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_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
|
|
|
|
|
2020-12-12 14:45:53 +01:00
|
|
|
# remove "linkdown", expiry and source based routing modifiers from route lines
|
2020-12-29 02:09:08 +01:00
|
|
|
config_get_bool source_routing globals source_routing 0
|
|
|
|
[ $source_routing -eq 1 ] && unset source_routing
|
2022-10-04 09:27:16 +02:00
|
|
|
MWAN3_ROUTE_LINE_EXP="s/offload//; s/linkdown //; s/expires [0-9]\+sec//; s/error [0-9]\+//; ${source_routing:+s/default\(.*\) from [^ ]*/default\1/;} p"
|
2020-12-12 14:45:53 +01:00
|
|
|
|
2020-08-13 02:38:56 +02:00
|
|
|
# mark mask constants
|
|
|
|
bitcnt=$(mwan3_count_one_bits MMX_MASK)
|
|
|
|
mmdefault=$(((1<<bitcnt)-1))
|
|
|
|
MM_BLACKHOLE=$((mmdefault-2))
|
|
|
|
MM_UNREACHABLE=$((mmdefault-1))
|
mwan3: Fix packet routing when WAN interface is partially up
This introduces a new concept of "unknown_wan" to mwan3. The action for
this can be configured in the globals section the default of which is
'none'. This can be set to 'none', 'default', 'unreachable' or 'blacklist'
switching out the matching ip rule for this match. This assignment for
a connection is temporary and is re-resolved for each additional
original direction packet through the firewall allowing the unknown WAN
to start resolving once the ifup has finished for the given interface.
An example configuration:
config globals 'globals'
option unknown_wan_action 'unreachable'
Prior to this commit, mwan3 had multiple hit spots for packets in the
following order:
1. Packets are checked to see if they originate from known WAN interfaces
2. Packets are checked to see if they destined for ipsets defined
3. Packets are checked against default WAN policies
The WAN list is maintained via hotplug 'ifup'/'ifdown' events and the local
route ipset list is maintained via monitoring the routing table. This
means that while a WAN interface is brought up, the list for 2 is
updated before the list for 1, since an interface is fully brought up
before the ifup event is fired off. Additionally, we want to make sure we
don't apply a WAN policy for incoming packets from a WAN interface that
is in the process of being brought up.
We can identify packets that are presumably coming from a WAN interface
we don't recognize yet by eliminating all packets that the source comes
from networks we don't know about in the ipsets that mwan3 manages. We
have to be careful here to only match the original direction of the
packet flow (e.g. for instance with ICMP, the ping request is in the
ORIGINAL direction, and the response is in the REPLY direction) or else
we might match something we didn't intend to.
By modifying the rule set to the following:
1. Packets are checked to see if they are in a REPLY direction of flow
2. Packets are checked to see if they originate from known WAN interfaces
3. Packets are checked to see if they not sourced from ipsets defined
4. Packets are checked to see if they destined for ipsets defined
5. Packets are checked against default WAN policies
If a packet is in the REPLY direction of flow, we definitely don't want
to do any routing table assignments - we only want to do this for the
original direction of traffic flow. This reduces the amount of rules
parsed within mwan3.
If a packet is not sourced from a defined ipset, this should match any
packet originating from a "default route" upstream. We do this post the
known WAN interface check since we don't know what mask to apply to this
packet at this time until the 'ifup' has completed. It's also setup to
reevaluate this decision by clearing this specific mark when a new
packet comes in in the REPLY direction of flow before any subsequent
evaluations. This allows additional packets for the same connection to
eventually be assigned the appropriate mask once the 'ifup' has
finished.
One easy way to test this out before and after this change is to:
- Bring down wan (e.g. ifdown wan)
- Manually bring up WAN
- This mitigates the firewall rules being added for 1 above, but 2
is still added since this is monitoring the routing interface
- Ping the device from a non-local subnet via the WAN interface; leave
running
- Observe mark set to ICMP session via conntrack
- Bring up wan (e.g. ifup wan)
- Observe mark set to ICMP session from above
Signed-off-by: Tim Nordell <tnordell@airgain.com>
2023-06-26 17:24:27 +02:00
|
|
|
MM_UNKNOWN_WAN=$((mmdefault-3))
|
2020-08-13 02:38:56 +02:00
|
|
|
|
|
|
|
# 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: Fix packet routing when WAN interface is partially up
This introduces a new concept of "unknown_wan" to mwan3. The action for
this can be configured in the globals section the default of which is
'none'. This can be set to 'none', 'default', 'unreachable' or 'blacklist'
switching out the matching ip rule for this match. This assignment for
a connection is temporary and is re-resolved for each additional
original direction packet through the firewall allowing the unknown WAN
to start resolving once the ifup has finished for the given interface.
An example configuration:
config globals 'globals'
option unknown_wan_action 'unreachable'
Prior to this commit, mwan3 had multiple hit spots for packets in the
following order:
1. Packets are checked to see if they originate from known WAN interfaces
2. Packets are checked to see if they destined for ipsets defined
3. Packets are checked against default WAN policies
The WAN list is maintained via hotplug 'ifup'/'ifdown' events and the local
route ipset list is maintained via monitoring the routing table. This
means that while a WAN interface is brought up, the list for 2 is
updated before the list for 1, since an interface is fully brought up
before the ifup event is fired off. Additionally, we want to make sure we
don't apply a WAN policy for incoming packets from a WAN interface that
is in the process of being brought up.
We can identify packets that are presumably coming from a WAN interface
we don't recognize yet by eliminating all packets that the source comes
from networks we don't know about in the ipsets that mwan3 manages. We
have to be careful here to only match the original direction of the
packet flow (e.g. for instance with ICMP, the ping request is in the
ORIGINAL direction, and the response is in the REPLY direction) or else
we might match something we didn't intend to.
By modifying the rule set to the following:
1. Packets are checked to see if they are in a REPLY direction of flow
2. Packets are checked to see if they originate from known WAN interfaces
3. Packets are checked to see if they not sourced from ipsets defined
4. Packets are checked to see if they destined for ipsets defined
5. Packets are checked against default WAN policies
If a packet is in the REPLY direction of flow, we definitely don't want
to do any routing table assignments - we only want to do this for the
original direction of traffic flow. This reduces the amount of rules
parsed within mwan3.
If a packet is not sourced from a defined ipset, this should match any
packet originating from a "default route" upstream. We do this post the
known WAN interface check since we don't know what mask to apply to this
packet at this time until the 'ifup' has completed. It's also setup to
reevaluate this decision by clearing this specific mark when a new
packet comes in in the REPLY direction of flow before any subsequent
evaluations. This allows additional packets for the same connection to
eventually be assigned the appropriate mask once the 'ifup' has
finished.
One easy way to test this out before and after this change is to:
- Bring down wan (e.g. ifdown wan)
- Manually bring up WAN
- This mitigates the firewall rules being added for 1 above, but 2
is still added since this is monitoring the routing interface
- Ping the device from a non-local subnet via the WAN interface; leave
running
- Observe mark set to ICMP session via conntrack
- Bring up wan (e.g. ifup wan)
- Observe mark set to ICMP session from above
Signed-off-by: Tim Nordell <tnordell@airgain.com>
2023-06-26 17:24:27 +02:00
|
|
|
MMX_UNKNOWN_WAN=$(mwan3_id2mask MM_UNKNOWN_WAN MMX_MASK)
|
2020-08-13 02:38:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
# 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
|
|
|
|
}
|
2020-11-13 01:21:04 +01:00
|
|
|
|
|
|
|
get_uptime() {
|
|
|
|
local uptime=$(cat /proc/uptime)
|
|
|
|
echo "${uptime%%.*}"
|
|
|
|
}
|
|
|
|
|
|
|
|
get_online_time() {
|
|
|
|
local time_n time_u iface
|
|
|
|
iface="$1"
|
|
|
|
time_u="$(cat "$MWAN3TRACK_STATUS_DIR/${iface}/ONLINE" 2>/dev/null)"
|
|
|
|
[ -z "${time_u}" ] || [ "${time_u}" = "0" ] || {
|
|
|
|
time_n="$(get_uptime)"
|
|
|
|
echo $((time_n-time_u))
|
|
|
|
}
|
|
|
|
}
|