google / gvisor

Application Kernel for Containers
https://gvisor.dev
Apache License 2.0
15.63k stars 1.29k forks source link

stack.AddAddressRange API deprecated - what is the alternative #3876

Closed majek closed 4 years ago

majek commented 4 years ago

Hi,

I'm maintaining slirpnetstack project, and I had used the AddAddressRange api: https://github.com/majek/slirpnetstack/blob/master/stack.go#L139-L140

s.AddAddressRange(nic, ipv4.ProtocolNumber, header.IPv4EmptySubnet)
s.AddAddressRange(nic, ipv6.ProtocolNumber, header.IPv6EmptySubnet)

The commit https://github.com/google/gvisor/commit/9a7b5830aa063895f67ca0fdf653a46906374613 removed that API. Is there a replacement?

Speciifcally, I used the thing to capture all traffic against netstack in my application, with SetTransportProtocolHandler https://github.com/majek/slirpnetstack/blob/1ae008981cfa90ac145d27ba842162b3aeee4559/main.go#L304-L306

    udpHandler := UdpRoutingHandler(s, &state)
    fwdUdp := udp.NewForwarder(s, udpHandler)
    s.SetTransportProtocolHandler(udp.ProtocolNumber, fwdUdp.HandlePacket)

To my understanding, SetTransportProtocolHandler only works after routing. So I need a way to route all the destnation IP's to the networking stack. This is somewhat equivalent to using Linux's AnyIP feature to capture 0.0.0.0/0 subnet.

Is there a way to achieve the same thing without AddAddressRange api?

majek commented 4 years ago

Tagging @ghanan94 @hbhasker

ghananigans commented 4 years ago

Looks like you want all incoming packets to be received by the netstack, right?

Can you set promiscuous mode to true (https://cs.opensource.google/gvisor/gvisor/+/master:pkg/tcpip/stack/stack.go;l=1381;drc=00479af515289f42a63ab14cec128ab030120b38;bpv=1;bpt=1)

majek commented 4 years ago

I want to accept the traffic with s.SetTransportProtocolHandler. Accept as for - get a connected endpoint instance for any connection. I don't want to sniff it. Will that work?

ghananigans commented 4 years ago

I believe it will. With promiscuous mode enabled, the NIC will receive all incoming IP packets. Can you give that a try and LMK if it works?

Note, you wouldn't need to do the work of pulling in the latest version of gvisor. You can test locally by removing the calls to AddAddressRange and adding a call to SetPromiscuousMode(nicID, true)

majek commented 4 years ago

while I'm pretty surprised - in linux routing != promiscous, two completely different layers, here it indeed seems to work. Thanks.

marek@mrnew:~//slirpnetstack$ git diff
diff --git a/stack.go b/stack.go
index 638587b..3e26d06 100644
--- a/stack.go
+++ b/stack.go
@@ -136,8 +136,10 @@ func createNIC(s *stack.Stack, nic tcpip.NICID, linkEP stack.LinkEndpoint) error
        // Assign L2 and L3 addresses
        s.AddAddress(nic, arp.ProtocolNumber, arp.ProtocolAddress)

-       s.AddAddressRange(nic, ipv4.ProtocolNumber, header.IPv4EmptySubnet)
-       s.AddAddressRange(nic, ipv6.ProtocolNumber, header.IPv6EmptySubnet)
+       s.SetPromiscuousMode(nic, true)
+
+       // s.AddAddressRange(nic, ipv4.ProtocolNumber, header.IPv4EmptySubnet)
+       // s.AddAddressRange(nic, ipv6.ProtocolNumber, header.IPv6EmptySubnet)

        return nil
 }
marek@mrnew:~//slirpnetstack$ make test
SLIRPNETSTACKBIN="./bin/slirpnetstack" \
PYTHONPATH=. \
PYTHONIOENCODING=utf-8 \
    unshare -Ur python3 -m tests.runner tests
...........................................................
----------------------------------------------------------------------
Ran 59 tests in 3.936s
hbhasker commented 4 years ago

Reopening to rollback the Address Range functions as we need them to support anyip feature in linux. See:

https://blog.widodh.nl/2016/04/anyip-bind-a-whole-subnet-to-your-linux-machine/#:~:text=The%20AnyIP%20feature%20of%20the%20Linux%20kernel%20allows%20you%20to,to%20bind%20a%20complete%20subnet.

majek commented 4 years ago

I wrote a bit about AnyIP in https://blog.cloudflare.com/how-we-built-spectrum/ , also you may consult Maciej Żenczykowski, I think he did some work there. Specific syntax:

ip route add local 192.0.2.0/24 dev lo
ip route add local 2001:db8::/64 dev lo
hbhasker commented 4 years ago

I was actually reading that up. Mostly it looks like inet_bind just skips the address check if the address being bound is In rtn_local route. There is a way to add an address range to a nic in Linux but it relies on IP aliasing which is old and I am not sure we want to support it.

I will open a separate bug to add support for different route types in gVisor today our route table is a very simple one and we don't really have anything similar to a node local route table etc.

hbhasker commented 4 years ago

I will talk with Maciej later today. But mostly it looks like we do things a bit differently than linux in Netstack. We for example don't really have a local routing table just the main routing table and this API sort of served as the equivalent of adding local routes. We will probably just add support for route types in our routing table and allow adding local routes and use them the same way linux does.

This will be needed anyway if we want to at some point support ip route ... commands in gVisor.

majek commented 4 years ago

Just to make it clear, I don't need it. The SetPromiscuousMode hack is working for me.

hbhasker commented 4 years ago

Ack. I am going to close this. The reason I want to add support for this is because the promiscous hack is an all or nothing. You can't for example forward a subset of IPs or a range of IPs.