// Copyright (c) Tailscale Inc & AUTHORS // SPDX-License-Identifier: BSD-3-Clause package appc import ( "net/netip" "testing" "tailscale.com/tailcfg" ) // TestHandleConnectorTransitIPRequestZeroLength tests that if sent a // ConnectorTransitIPRequest with 0 TransitIPRequests, we respond with a // ConnectorTransitIPResponse with 0 TransitIPResponses. func TestHandleConnectorTransitIPRequestZeroLength(t *testing.T) { c := &Conn25{} req := ConnectorTransitIPRequest{} nid := tailcfg.NodeID(1) resp := c.HandleConnectorTransitIPRequest(nid, req) if len(resp.TransitIPs) != 0 { t.Fatalf("n TransitIPs in response: %d, want 0", len(resp.TransitIPs)) } } // TestHandleConnectorTransitIPRequestStoresAddr tests that if sent a // request with a transit addr and a destination addr we store that mapping // and can retrieve it. If sent another req with a different dst for that transit addr // we store that instead. func TestHandleConnectorTransitIPRequestStoresAddr(t *testing.T) { c := &Conn25{} nid := tailcfg.NodeID(1) tip := netip.MustParseAddr("0.0.0.1") dip := netip.MustParseAddr("1.2.3.4") dip2 := netip.MustParseAddr("1.2.3.5") mr := func(t, d netip.Addr) ConnectorTransitIPRequest { return ConnectorTransitIPRequest{ TransitIPs: []TransitIPRequest{ {TransitIP: t, DestinationIP: d}, }, } } resp := c.HandleConnectorTransitIPRequest(nid, mr(tip, dip)) if len(resp.TransitIPs) != 1 { t.Fatalf("n TransitIPs in response: %d, want 1", len(resp.TransitIPs)) } got := resp.TransitIPs[0].Code if got != TransitIPResponseCode(0) { t.Fatalf("TransitIP Code: %d, want 0", got) } gotAddr := c.getDstAddr(nid, tip) if gotAddr != dip { t.Fatalf("Connector stored destination for tip: %v, want %v", gotAddr, dip) } // mapping can be overwritten resp2 := c.HandleConnectorTransitIPRequest(nid, mr(tip, dip2)) if len(resp2.TransitIPs) != 1 { t.Fatalf("n TransitIPs in response: %d, want 1", len(resp2.TransitIPs)) } got2 := resp.TransitIPs[0].Code if got2 != TransitIPResponseCode(0) { t.Fatalf("TransitIP Code: %d, want 0", got2) } gotAddr2 := c.getDstAddr(nid, tip) if gotAddr2 != dip2 { t.Fatalf("Connector stored destination for tip: %v, want %v", gotAddr, dip2) } } // TestHandleConnectorTransitIPRequestMultipleTIP tests that we can // get a req with multiple mappings and we store them all. Including // multiple transit addrs for the same destination. func TestHandleConnectorTransitIPRequestMultipleTIP(t *testing.T) { c := &Conn25{} nid := tailcfg.NodeID(1) tip := netip.MustParseAddr("0.0.0.1") tip2 := netip.MustParseAddr("0.0.0.2") tip3 := netip.MustParseAddr("0.0.0.3") dip := netip.MustParseAddr("1.2.3.4") dip2 := netip.MustParseAddr("1.2.3.5") req := ConnectorTransitIPRequest{ TransitIPs: []TransitIPRequest{ {TransitIP: tip, DestinationIP: dip}, {TransitIP: tip2, DestinationIP: dip2}, // can store same dst addr for multiple transit addrs {TransitIP: tip3, DestinationIP: dip}, }, } resp := c.HandleConnectorTransitIPRequest(nid, req) if len(resp.TransitIPs) != 3 { t.Fatalf("n TransitIPs in response: %d, want 3", len(resp.TransitIPs)) } for i := 0; i < 3; i++ { got := resp.TransitIPs[i].Code if got != TransitIPResponseCode(0) { t.Fatalf("i=%d TransitIP Code: %d, want 0", i, got) } } gotAddr1 := c.getDstAddr(nid, tip) if gotAddr1 != dip { t.Fatalf("Connector stored destination for tip(%v): %v, want %v", tip, gotAddr1, dip) } gotAddr2 := c.getDstAddr(nid, tip2) if gotAddr2 != dip2 { t.Fatalf("Connector stored destination for tip(%v): %v, want %v", tip2, gotAddr2, dip2) } gotAddr3 := c.getDstAddr(nid, tip3) if gotAddr3 != dip { t.Fatalf("Connector stored destination for tip(%v): %v, want %v", tip3, gotAddr3, dip) } } // TestHandleConnectorTransitIPRequestSameTIP tests that if we get // a req that has more than one TransitIPRequest for the same transit addr // only the first is stored, and the subsequent ones get an error code and // message in the response. func TestHandleConnectorTransitIPRequestSameTIP(t *testing.T) { c := &Conn25{} nid := tailcfg.NodeID(1) tip := netip.MustParseAddr("0.0.0.1") tip2 := netip.MustParseAddr("0.0.0.2") dip := netip.MustParseAddr("1.2.3.4") dip2 := netip.MustParseAddr("1.2.3.5") dip3 := netip.MustParseAddr("1.2.3.6") req := ConnectorTransitIPRequest{ TransitIPs: []TransitIPRequest{ {TransitIP: tip, DestinationIP: dip}, // cannot have dupe TransitIPs in one ConnectorTransitIPRequest {TransitIP: tip, DestinationIP: dip2}, {TransitIP: tip2, DestinationIP: dip3}, }, } resp := c.HandleConnectorTransitIPRequest(nid, req) if len(resp.TransitIPs) != 3 { t.Fatalf("n TransitIPs in response: %d, want 3", len(resp.TransitIPs)) } got := resp.TransitIPs[0].Code if got != TransitIPResponseCode(0) { t.Fatalf("i=0 TransitIP Code: %d, want 0", got) } msg := resp.TransitIPs[0].Message if msg != "" { t.Fatalf("i=0 TransitIP Message: \"%s\", want \"%s\"", msg, "") } got1 := resp.TransitIPs[1].Code if got1 != TransitIPResponseCode(1) { t.Fatalf("i=1 TransitIP Code: %d, want 1", got1) } msg1 := resp.TransitIPs[1].Message if msg1 != dupeTransitIPMessage { t.Fatalf("i=1 TransitIP Message: \"%s\", want \"%s\"", msg1, dupeTransitIPMessage) } got2 := resp.TransitIPs[2].Code if got2 != TransitIPResponseCode(0) { t.Fatalf("i=2 TransitIP Code: %d, want 0", got2) } msg2 := resp.TransitIPs[2].Message if msg2 != "" { t.Fatalf("i=2 TransitIP Message: \"%s\", want \"%s\"", msg, "") } gotAddr1 := c.getDstAddr(nid, tip) if gotAddr1 != dip { t.Fatalf("Connector stored destination for tip(%v): %v, want %v", tip, gotAddr1, dip) } gotAddr2 := c.getDstAddr(nid, tip2) if gotAddr2 != dip3 { t.Fatalf("Connector stored destination for tip(%v): %v, want %v", tip2, gotAddr2, dip3) } }