go.ligato.io/vpp-agent/v3@v3.5.0/tests/integration/vpp/110_srv6_test.go (about)

     1  // Copyright (c) 2019 Pantheon.tech
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at:
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package vpp
    16  
    17  import (
    18  	"fmt"
    19  	"net"
    20  	"testing"
    21  
    22  	. "github.com/onsi/gomega"
    23  	"go.ligato.io/cn-infra/v2/logging/logrus"
    24  	"google.golang.org/protobuf/proto"
    25  
    26  	netalloc_mock "go.ligato.io/vpp-agent/v3/plugins/netalloc/mock"
    27  	"go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/ifaceidx"
    28  	ifplugin_vppcalls "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/vppcalls"
    29  	l3plugin_vppcalls "go.ligato.io/vpp-agent/v3/plugins/vpp/l3plugin/vppcalls"
    30  	"go.ligato.io/vpp-agent/v3/plugins/vpp/l3plugin/vrfidx"
    31  	srv6_vppcalls "go.ligato.io/vpp-agent/v3/plugins/vpp/srplugin/vppcalls"
    32  	vpp_l3 "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/l3"
    33  	srv6 "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/srv6"
    34  
    35  	_ "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin"
    36  	_ "go.ligato.io/vpp-agent/v3/plugins/vpp/l3plugin"
    37  	_ "go.ligato.io/vpp-agent/v3/plugins/vpp/srplugin"
    38  )
    39  
    40  var (
    41  	sidA         = sid("A::")
    42  	nextHop      = net.ParseIP("B::").To16()
    43  	nextHop2     = net.ParseIP("C::").To16()
    44  	nextHopIPv4  = net.ParseIP("1.2.3.4").To4()
    45  	nextHop2IPv4 = net.ParseIP("1.2.3.5").To4()
    46  	vrfTables    = []*vpp_l3.VrfTable{
    47  		{
    48  			Id:       11,
    49  			Protocol: vpp_l3.VrfTable_IPV6,
    50  			Label:    "testIpv6Table1",
    51  		},
    52  		{
    53  			Id:       12,
    54  			Protocol: vpp_l3.VrfTable_IPV6,
    55  			Label:    "testIpv6Table2",
    56  		},
    57  		{
    58  			Id:       13,
    59  			Protocol: vpp_l3.VrfTable_IPV4,
    60  			Label:    "testIpv4Table1",
    61  		},
    62  		{
    63  			Id:       14,
    64  			Protocol: vpp_l3.VrfTable_IPV4,
    65  			Label:    "testIpv4Table2",
    66  		},
    67  	}
    68  )
    69  
    70  //TODO add CRUD tests for SR-proxy (there is no binary API for it -> dump for SR-proxy needs 1. CLI localsid dump
    71  // and 2. CLI fib table dump because CLI localsid dump does not include installation vrf for SR-proxy localsid)
    72  
    73  // TestLocalsidCRUD tests CRUD operations for Localsids
    74  func TestLocalsidCRUD(t *testing.T) {
    75  	ctx := setupVPP(t)
    76  	defer ctx.teardownVPP()
    77  
    78  	// create interfaces (for referencing from localsids)
    79  	ih := ifplugin_vppcalls.CompatibleInterfaceVppHandler(ctx.vppClient, logrus.NewLogger("test"))
    80  	const ifName = "loop1"
    81  	ifIdx, err := ih.AddLoopbackInterface(ifName)
    82  	Expect(err).To(BeNil(), fmt.Sprintf("fixture setup failed in creating of interface %v: %v", ifName, err))
    83  	t.Logf("interface created %v", ifIdx)
    84  	const ifName2 = "loop2"
    85  	ifIdx2, err := ih.AddLoopbackInterface(ifName2)
    86  	Expect(err).To(BeNil(), fmt.Sprintf("fixture setup failed in creating of interface %v: %v", ifName2, err))
    87  	t.Logf("interface created %v", ifIdx2)
    88  	ifIndexes := ifaceidx.NewIfaceIndex(logrus.NewLogger("test-idx"), "test-idx")
    89  	ifIndexes.Put(ifName, &ifaceidx.IfaceMetadata{SwIfIndex: ifIdx})
    90  	ifIndexes.Put(ifName2, &ifaceidx.IfaceMetadata{SwIfIndex: ifIdx2})
    91  
    92  	// create vrf tables (for referencing from localsids)
    93  	vrfIndexes := vrfidx.NewVRFIndex(logrus.NewLogger("test-vrf"), "test-vrf")
    94  	vrfIndexes.Put("vrf1-ipv4", &vrfidx.VRFMetadata{Index: 0, Protocol: vpp_l3.VrfTable_IPV4})
    95  	vrfIndexes.Put("vrf1-ipv6", &vrfidx.VRFMetadata{Index: 0, Protocol: vpp_l3.VrfTable_IPV6})
    96  	l3h := l3plugin_vppcalls.CompatibleL3VppHandler(ctx.vppClient, ifIndexes, vrfIndexes,
    97  		netalloc_mock.NewMockNetAlloc(), logrus.NewLogger("test-l3"))
    98  	for _, vrfTable := range vrfTables[:4] {
    99  		Expect(l3h.AddVrfTable(vrfTable)).Should(Succeed(), fmt.Sprintf("fixture setup failed "+
   100  			"in creating of vrf table with name %v due to: %v", vrfTable.Label, err))
   101  	}
   102  
   103  	// SRv6 handler
   104  	srh := srv6_vppcalls.CompatibleSRv6Handler(ctx.vppClient, ifIndexes, logrus.NewLogger("test"))
   105  
   106  	tests := []struct {
   107  		name                string
   108  		input               *srv6.LocalSID
   109  		expectedDump        *srv6.LocalSID
   110  		updatedInput        *srv6.LocalSID
   111  		updatedExpectedDump *srv6.LocalSID
   112  	}{
   113  		{
   114  			name: "base end",
   115  			input: &srv6.LocalSID{
   116  				Sid:               sidA.String(),
   117  				InstallationVrfId: 0,
   118  				EndFunction: &srv6.LocalSID_BaseEndFunction{
   119  					BaseEndFunction: &srv6.LocalSID_End{
   120  						Psp: true,
   121  					},
   122  				},
   123  			},
   124  			updatedInput: &srv6.LocalSID{
   125  				Sid:               sidA.String(),
   126  				InstallationVrfId: 0,
   127  				EndFunction: &srv6.LocalSID_BaseEndFunction{
   128  					BaseEndFunction: &srv6.LocalSID_End{
   129  						Psp: false,
   130  					},
   131  				},
   132  			},
   133  		},
   134  		{
   135  			name: "end.X",
   136  			input: &srv6.LocalSID{
   137  				Sid:               sidA.String(),
   138  				InstallationVrfId: 0,
   139  				EndFunction: &srv6.LocalSID_EndFunctionX{
   140  					EndFunctionX: &srv6.LocalSID_EndX{
   141  						Psp:               true,
   142  						NextHop:           nextHop.String(),
   143  						OutgoingInterface: ifName,
   144  					},
   145  				},
   146  			},
   147  			updatedInput: &srv6.LocalSID{
   148  				Sid:               sidA.String(),
   149  				InstallationVrfId: 0,
   150  				EndFunction: &srv6.LocalSID_EndFunctionX{
   151  					EndFunctionX: &srv6.LocalSID_EndX{
   152  						Psp:               true,
   153  						NextHop:           nextHop2.String(), // updated
   154  						OutgoingInterface: ifName,
   155  					},
   156  				},
   157  			},
   158  		},
   159  		{
   160  			name: "end.T",
   161  			input: &srv6.LocalSID{
   162  				Sid:               sidA.String(),
   163  				InstallationVrfId: 0,
   164  				EndFunction: &srv6.LocalSID_EndFunctionT{
   165  					EndFunctionT: &srv6.LocalSID_EndT{
   166  						Psp:   true,
   167  						VrfId: vrfTables[0].Id,
   168  					},
   169  				},
   170  			},
   171  			expectedDump: &srv6.LocalSID{
   172  				Sid:               sidA.String(),
   173  				InstallationVrfId: 0,
   174  				EndFunction: &srv6.LocalSID_EndFunctionT{
   175  					EndFunctionT: &srv6.LocalSID_EndT{
   176  						Psp:   true,
   177  						VrfId: 1, // bug in VPP, it should return client Table ID but it returns vpp-inner Table ID
   178  					},
   179  				},
   180  			},
   181  			updatedInput: &srv6.LocalSID{
   182  				Sid:               sidA.String(),
   183  				InstallationVrfId: 0,
   184  				EndFunction: &srv6.LocalSID_EndFunctionT{
   185  					EndFunctionT: &srv6.LocalSID_EndT{
   186  						Psp:   true,
   187  						VrfId: vrfTables[1].Id, // updated
   188  					},
   189  				},
   190  			},
   191  			updatedExpectedDump: &srv6.LocalSID{
   192  				Sid:               sidA.String(),
   193  				InstallationVrfId: 0,
   194  				EndFunction: &srv6.LocalSID_EndFunctionT{
   195  					EndFunctionT: &srv6.LocalSID_EndT{
   196  						Psp:   true,
   197  						VrfId: 2, // bug in VPP, it should return client Table ID but it returns vpp-inner Table ID
   198  					},
   199  				},
   200  			},
   201  		},
   202  		{
   203  			name: "end.DT4",
   204  			input: &srv6.LocalSID{
   205  				Sid:               sidA.String(),
   206  				InstallationVrfId: 0,
   207  				EndFunction: &srv6.LocalSID_EndFunctionDt4{
   208  					EndFunctionDt4: &srv6.LocalSID_EndDT4{
   209  						VrfId: vrfTables[2].Id,
   210  					},
   211  				},
   212  			},
   213  			expectedDump: &srv6.LocalSID{
   214  				Sid:               sidA.String(),
   215  				InstallationVrfId: 0,
   216  				EndFunction: &srv6.LocalSID_EndFunctionDt4{
   217  					EndFunctionDt4: &srv6.LocalSID_EndDT4{
   218  						VrfId: 1, // bug in VPP, it should return client Table ID but it returns vpp-inner Table ID
   219  					},
   220  				},
   221  			},
   222  			updatedInput: &srv6.LocalSID{
   223  				Sid:               sidA.String(),
   224  				InstallationVrfId: 0,
   225  				EndFunction: &srv6.LocalSID_EndFunctionDt4{
   226  					EndFunctionDt4: &srv6.LocalSID_EndDT4{
   227  						VrfId: vrfTables[3].Id, // updated
   228  					},
   229  				},
   230  			},
   231  			updatedExpectedDump: &srv6.LocalSID{
   232  				Sid:               sidA.String(),
   233  				InstallationVrfId: 0,
   234  				EndFunction: &srv6.LocalSID_EndFunctionDt4{
   235  					EndFunctionDt4: &srv6.LocalSID_EndDT4{
   236  						VrfId: 2, // bug in VPP, it should return client Table ID but it returns vpp-inner Table ID
   237  					},
   238  				},
   239  			},
   240  		},
   241  		{
   242  			name: "end.DT6",
   243  			input: &srv6.LocalSID{
   244  				Sid:               sidA.String(),
   245  				InstallationVrfId: 0,
   246  				EndFunction: &srv6.LocalSID_EndFunctionDt6{
   247  					EndFunctionDt6: &srv6.LocalSID_EndDT6{
   248  						VrfId: vrfTables[0].Id,
   249  					},
   250  				},
   251  			},
   252  			expectedDump: &srv6.LocalSID{
   253  				Sid:               sidA.String(),
   254  				InstallationVrfId: 0,
   255  				EndFunction: &srv6.LocalSID_EndFunctionDt6{
   256  					EndFunctionDt6: &srv6.LocalSID_EndDT6{
   257  						VrfId: 1, // bug in VPP, it should return client Table ID but it returns vpp-inner Table ID
   258  					},
   259  				},
   260  			},
   261  			updatedInput: &srv6.LocalSID{
   262  				Sid:               sidA.String(),
   263  				InstallationVrfId: 0,
   264  				EndFunction: &srv6.LocalSID_EndFunctionDt6{
   265  					EndFunctionDt6: &srv6.LocalSID_EndDT6{
   266  						VrfId: vrfTables[1].Id, // updated
   267  					},
   268  				},
   269  			},
   270  			updatedExpectedDump: &srv6.LocalSID{
   271  				Sid:               sidA.String(),
   272  				InstallationVrfId: 0,
   273  				EndFunction: &srv6.LocalSID_EndFunctionDt6{
   274  					EndFunctionDt6: &srv6.LocalSID_EndDT6{
   275  						VrfId: 2, // bug in VPP, it should return client Table ID but it returns vpp-inner Table ID
   276  					},
   277  				},
   278  			},
   279  		},
   280  		{
   281  			name: "end.DX2",
   282  			input: &srv6.LocalSID{
   283  				Sid:               sidA.String(),
   284  				InstallationVrfId: 0,
   285  				EndFunction: &srv6.LocalSID_EndFunctionDx2{
   286  					EndFunctionDx2: &srv6.LocalSID_EndDX2{
   287  						VlanTag:           0,
   288  						OutgoingInterface: ifName,
   289  					},
   290  				},
   291  			},
   292  			updatedInput: &srv6.LocalSID{
   293  				Sid:               sidA.String(),
   294  				InstallationVrfId: 0,
   295  				EndFunction: &srv6.LocalSID_EndFunctionDx2{
   296  					EndFunctionDx2: &srv6.LocalSID_EndDX2{
   297  						VlanTag:           0,
   298  						OutgoingInterface: ifName2,
   299  					},
   300  				},
   301  			},
   302  		},
   303  		{
   304  			name: "end.DX4",
   305  			input: &srv6.LocalSID{
   306  				Sid:               sidA.String(),
   307  				InstallationVrfId: 0,
   308  				EndFunction: &srv6.LocalSID_EndFunctionDx4{
   309  					EndFunctionDx4: &srv6.LocalSID_EndDX4{
   310  						NextHop:           nextHopIPv4.String(),
   311  						OutgoingInterface: ifName,
   312  					},
   313  				},
   314  			},
   315  			updatedInput: &srv6.LocalSID{
   316  				Sid:               sidA.String(),
   317  				InstallationVrfId: 0,
   318  				EndFunction: &srv6.LocalSID_EndFunctionDx4{
   319  					EndFunctionDx4: &srv6.LocalSID_EndDX4{
   320  						NextHop:           nextHop2IPv4.String(), // updated
   321  						OutgoingInterface: ifName,
   322  					},
   323  				},
   324  			},
   325  		},
   326  		{
   327  			name: "end.DX6",
   328  			input: &srv6.LocalSID{
   329  				Sid:               sidA.String(),
   330  				InstallationVrfId: 0,
   331  				EndFunction: &srv6.LocalSID_EndFunctionDx6{
   332  					EndFunctionDx6: &srv6.LocalSID_EndDX6{
   333  						NextHop:           nextHop.String(),
   334  						OutgoingInterface: ifName,
   335  					},
   336  				},
   337  			},
   338  			updatedInput: &srv6.LocalSID{
   339  				Sid:               sidA.String(),
   340  				InstallationVrfId: 0,
   341  				EndFunction: &srv6.LocalSID_EndFunctionDx6{
   342  					EndFunctionDx6: &srv6.LocalSID_EndDX6{
   343  						NextHop:           nextHop2.String(), // updated
   344  						OutgoingInterface: ifName,
   345  					},
   346  				},
   347  			},
   348  		},
   349  		{
   350  			name: "nondefault installation vrf table",
   351  			input: &srv6.LocalSID{
   352  				Sid:               sidA.String(),
   353  				InstallationVrfId: vrfTables[0].Id,
   354  				EndFunction: &srv6.LocalSID_BaseEndFunction{
   355  					BaseEndFunction: &srv6.LocalSID_End{
   356  						Psp: true,
   357  					},
   358  				},
   359  			},
   360  		},
   361  	}
   362  
   363  	for _, test := range tests {
   364  		t.Run(test.name, func(t *testing.T) {
   365  			// FIXME: fix expected VRF value for different VPP versions...
   366  			// 		a fix in VPP was merged into stable branch for 19.08 only...
   367  			// 		https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commit;h=4362672562f5b379361f147cbb7a9b72c3332c30
   368  			// 		this causes VPP 19.08 fail even though it actually is only versions it works okay in..
   369  			t.Skipf("FIXME: fix expected VRF value for different VPP versions")
   370  			if ctx.versionInfo.Release() == "20.05" {
   371  				// FIXME: following error occurs in VPP 20.05:
   372  				//  	can't properly handle end function of dumped localsid &{[0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0] true 0 0 0 {ADDRESS_IP4 {[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]}} 0} due to: localsid with unknown or unsupported behavior (0)
   373  				t.Skip("skipping for VPP 20.05, FIX NEEDED!")
   374  			}
   375  
   376  			// Create
   377  			Expect(srh.AddLocalSid(test.input)).Should(Succeed())
   378  
   379  			// Read
   380  			localsids, err := srh.DumpLocalSids()
   381  			t.Logf("received this localsids from dump: %v", localsids)
   382  			Expect(err).ShouldNot(HaveOccurred())
   383  			expected := test.input
   384  			if test.expectedDump != nil {
   385  				expected = test.expectedDump
   386  			}
   387  			Expect(localsids).To(HaveLen(1))
   388  			Expect(proto.Equal(localsids[0], expected)).To(BeTrue())
   389  
   390  			// Update (for localsids it means delete + create)
   391  			if test.updatedInput != nil {
   392  				Expect(srh.DeleteLocalSid(test.input)).Should(Succeed())
   393  				Expect(srh.AddLocalSid(test.updatedInput)).Should(Succeed())
   394  				localsids, err = srh.DumpLocalSids()
   395  				t.Logf("received this localsids from dump: %v", localsids)
   396  				Expect(err).ShouldNot(HaveOccurred())
   397  				expected := test.updatedInput
   398  				if test.updatedExpectedDump != nil {
   399  					expected = test.updatedExpectedDump
   400  				}
   401  				Expect(localsids).To(HaveLen(1))
   402  				Expect(proto.Equal(localsids[0], expected)).To(BeTrue())
   403  			}
   404  			// Delete
   405  			if test.updatedInput != nil {
   406  				Expect(srh.DeleteLocalSid(test.updatedInput)).Should(Succeed())
   407  			} else {
   408  				Expect(srh.DeleteLocalSid(test.input)).Should(Succeed())
   409  			}
   410  		})
   411  	}
   412  }
   413  
   414  // sid creates segment ID(=net.IP) from string
   415  func sid(str string) net.IP {
   416  	sid, err := parseIPv6(str)
   417  	if err != nil {
   418  		panic(fmt.Sprintf("can't parse %q into SRv6 SID (IPv6 address)", str))
   419  	}
   420  	return sid
   421  }
   422  
   423  // parseIPv6 parses string <str> to IPv6 address (including IPv4 address converted to IPv6 address)
   424  func parseIPv6(str string) (net.IP, error) {
   425  	ip := net.ParseIP(str)
   426  	if ip == nil {
   427  		return nil, fmt.Errorf(" %q is not ip address", str)
   428  	}
   429  	ipv6 := ip.To16()
   430  	if ipv6 == nil {
   431  		return nil, fmt.Errorf(" %q is not ipv6 address", str)
   432  	}
   433  	return ipv6, nil
   434  }