Re: [RFC] tcp: implement new per-interface sysctl "auto_dev_bind"
From: Eric Dumazet
Date: Fri Dec 13 2019 - 11:03:57 EST
On 12/13/19 2:07 AM, Willy Tarreau wrote:
> This sysctl, when set, makes sure that any TCP socket connecting through
> that interface or accepted from this interface will automatically be
> bound to this device so that the socket cannot migrate by accident to
> another interface if the current one goes down, and that incoming traffic
> from other interfaces may never reach the socket regardless of rp_filter.
> This can be useful for example, in order to protect connections made over
> a VPN interface, such as the attack described here:
>
> https://seclists.org/oss-sec/2019/q4/122.
>
> It might possibly have other use cases such as preventing traffic from
> leaking to the default route interface during a temporary outage of a
> tunnel interface, or sending traffic out of the host when a local
> address is removed.
>
> Only TCPv4 and TCPv6 are covered by this patch.
>
> Reported-by: "William J. Tolley" <william@xxxxxxxxxxxxxxxxxxxx>
> Cc: "Jason A. Donenfeld" <zx2c4@xxxxxxxxxx>
> Cc: Eric Dumazet <edumazet@xxxxxxxxxx>
> Signed-off-by: Willy Tarreau <w@xxxxxx>
>
> ---
>
> This issue was recently brought on the security list by William and was
> discussed with Eric and Jason. This patch is just a proposal to open
> the discussion around a clean solution to address the issue. It currently
> covers TCPv4 and TCPv6 (both tested). I have no idea whether this is
> the best way to proceed; I'm not sure whether we want to address other
> connected protocols (e.g. UDP can be "connected" but do we care?); and
> very likely the patch will need to be split in two for IPv4/IPv6 but
> I found it was more convenient for a review to have both parts together.
>
> --- test reports below
>
> IPv4: simple test over an ipip tunnel
>
> left (.236):
> ip tunnel add t4 mode ipip remote 192.168.0.176
> ip li set t4 up && ip a a 192.0.2.1/30 dev t4
>
> right (.176):
> ip tunnel add t4 mode ipip remote 192.168.0.236
> ip li set t4 up && ip a a 192.0.2.2/30 dev t4
>
> left:~# echo 0 > /proc/sys/net/ipv4/conf/t4/auto_dev_bind
> right:~# nc -lp4000
> left:~# telnet 192.0.2.2 4000 &
> left:~# netstat -atn|grep :4000
> tcp 0 0 192.0.2.1:19536 192.0.2.2:4000 ESTABLISHED
>
> attacker:~# nping --tcp --flags SA --source-ip 192.0.2.2 -g 4000 --dest-ip 192.0.2.1 -p 19536 --rate 3 -c 3 -e eth0 --dest-mac 18:66:c7:53:ae:87
>
> left:~# tcpdump -Sni t4
> 16:20:13.289142 IP 192.0.2.1.19536 > 192.0.2.2.4000: . ack 2220548823 win 507
> 16:20:13.955344 IP 192.0.2.1.19536 > 192.0.2.2.4000: . ack 2220548823 win 507
>
> left:~# echo 1 > /proc/sys/net/ipv4/conf/t4/auto_dev_bind
> left:~# telnet 192.0.2.2 4000 &
> left:~# netstat -atn|grep :4000
> tcp 0 0 192.0.2.1:19540 192.0.2.2:4000 ESTABLISHED
>
> attacker:~# nping --tcp --flags SA --source-ip 192.0.2.2 -g 4000 --dest-ip 192.0.2.1 -p 19540 --rate 3 -c 3 -e eth0 --dest-mac 18:66:c7:53:ae:87
>
> left:~# tcpdump -Sni t4
> 16:22:41.933842 IP 192.0.2.1.19540 > 192.0.2.2.4000: R 2405575235:2405575235(0) win 0
> 16:22:42.266897 IP 192.0.2.1.19540 > 192.0.2.2.4000: R 2405575235:2405575235(0) win 0
> 16:22:42.599940 IP 192.0.2.1.19540 > 192.0.2.2.4000: R 2405575235:2405575235(0) win 0
>
> IPv6: simple test over an sit tunnel
>
> left (.236):
> ip tunnel add t6 mode sit remote 192.168.0.176
> ip li set t6 up && ip -6 a a 2001:db8::1/64 dev t6
>
> right (.176):
> ip tunnel add t6 mode sit remote 192.168.0.236
> ip li set t6 up && ip -6 a a 2001:db8::2/64 dev t6
>
> left:~# echo 0 > /proc/sys/net/ipv4/conf/t4/auto_dev_bind
> right:~# nc6 -lp4000
> left:~# telnet -6 2001:db8::2 4000 &
> left:~# netstat -atn|grep :4000
> tcp 0 0 2001:db8::1:50636 2001:db8::2:4000 ESTABLISHED
> attacker:~# nping -6 --tcp --flags SA --source-ip 2001:db8::2 -g 4000 --dest-ip 2001:db8::1 -p 50636 --rate 3 -c 3 -e eth0 --dest-mac 18:66:c7:53:ae:87 --source-mac e8:b6:74:5d:19:ed
>
> left:~# tcpdump -Sni t6
> 16:29:19.842821 IP6 2001:db8::1.50636 > 2001:db8::2.4000: . ack 245909702 win 511
> 16:29:20.508811 IP6 2001:db8::1.50636 > 2001:db8::2.4000: . ack 245909702 win 511
>
> left:~# echo 1 > /proc/sys/net/ipv6/conf/t6/auto_dev_bind
> right:~# nc6 -lp4000
> left:~# telnet -6 2001:db8::2 4000 &
> left:~# netstat -atn|grep :4000
> tcp 0 0 2001:db8::1:56750 2001:db8::2:4000 ESTABLISHED
>
> attacker:~# nping -6 --tcp --flags SA --source-ip 2001:db8::2 -g 4000 --dest-ip 2001:db8::1 -p 56750 --rate 3 -c 3 -e eth0 --dest-mac 18:66:c7:53:ae:87 --source-mac e8:b6:74:5d:19:ed
>
> left:~# tcpdump -Sni t6
> 16:46:34.264607 IP6 2001:db8::1.56750 > 2001:db8::2.4000: R 3346985589:3346985589(0) win 0
> 16:46:34.597653 IP6 2001:db8::1.56750 > 2001:db8::2.4000: R 3346985589:3346985589(0) win 0
> 16:46:34.931292 IP6 2001:db8::1.56750 > 2001:db8::2.4000: R 3346985589:3346985589(0) win 0
>
> Test of incoming connection:
> right~# nc 2001:db8::1 22
> left:~# netstat -atn|grep :22
> tcp 0 0 2001:db8::1:22 2001:db8::2:35990 ESTABLISHED
>
> attacker:~# nping -6 --tcp --flags SA --source-ip 2001:db8::2 -g 35990 --dest-ip 2001:db8::1 -p 22 --rate 3 -c 3 -e eth0 --dest-mac 18:66:c7:53:ae:87 --source-mac e8:b6:74:5d:19:ed
>
> left:~# tcpdump -Sni t6
> 16:53:20.810751 IP6 2001:db8::1.22 > 2001:db8::2.35990: R 1630812853:1630812853(0) win 0
> 16:53:21.144036 IP6 2001:db8::1.22 > 2001:db8::2.35990: R 1630812853:1630812853(0) win 0
> 16:53:21.477052 IP6 2001:db8::1.22 > 2001:db8::2.35990: R 1630812853:1630812853(0) win 0
> ---
> include/linux/ipv6.h | 1 +
> include/uapi/linux/ip.h | 1 +
> include/uapi/linux/ipv6.h | 1 +
> net/ipv4/devinet.c | 1 +
> net/ipv4/tcp_ipv4.c | 11 +++++++++++
> net/ipv6/addrconf.c | 10 ++++++++++
> net/ipv6/tcp_ipv6.c | 13 +++++++++++++
> 7 files changed, 38 insertions(+)
>
Hi Willy, thanks for working on this.
Could you check if your patch works with syncookies mode ?
echo 2 >/proc/sys/net/ipv4/tcp_syncookies
I wonder if your patch could be simpler if you were plugging the logic for passive
flows in inet_request_bound_dev_if() ?