@@ -21,6 +21,7 @@ import (
21
21
"os/exec"
22
22
"strings"
23
23
"syscall"
24
+ "time"
24
25
25
26
. "github.com/onsi/ginkgo"
26
27
. "github.com/onsi/gomega"
@@ -38,6 +39,8 @@ import (
38
39
"github.com/projectcalico/vpp-dataplane/v3/config"
39
40
"github.com/projectcalico/vpp-dataplane/v3/vpplink"
40
41
"github.com/projectcalico/vpp-dataplane/v3/vpplink/types"
42
+
43
+ gomemif "go.fd.io/govpp/extras/gomemif/memif"
41
44
)
42
45
43
46
const (
@@ -245,6 +248,75 @@ var _ = Describe("Pod-related functionality of CNI", func() {
245
248
Expect (pblClientStr ).To (ContainSubstring (
246
249
fmt .Sprintf ("udp ports: %d-%d" , memifUDPPortStart , memifUDPPortEnd )),
247
250
"UDP port range is not correctly configured for memif interface" )
251
+
252
+ By ("Checking socket creation" )
253
+ memif_socket , err := gomemif .NewSocket ("gomemif_example" , "@vpp/memif-newInterface" )
254
+ Expect (err ).ToNot (HaveOccurred ())
255
+
256
+ By ("Checking slave connection to master" )
257
+ memifErrChan := make (chan error )
258
+ quitChan := make (chan int )
259
+ // Start master polling
260
+ go func () {
261
+ for {
262
+ select {
263
+ case <- quitChan :
264
+ return
265
+ default :
266
+ memif_socket .StartPolling (memifErrChan )
267
+ time .Sleep (100 * time .Millisecond )
268
+ }
269
+ }
270
+ }()
271
+
272
+ sockChannel := make (chan * gomemif.Interface , 1 )
273
+
274
+ slave := func () error {
275
+ args := & gomemif.Arguments {
276
+ IsMaster : false ,
277
+ Name : "memif" ,
278
+ ConnectedFunc : func (i * gomemif.Interface ) error { return nil },
279
+ DisconnectedFunc : func (i * gomemif.Interface ) error { return nil },
280
+ }
281
+
282
+ i , err := memif_socket .NewInterface (args )
283
+ if err != nil {
284
+ return err
285
+ }
286
+
287
+ retry := 5
288
+ for ! i .IsConnecting () {
289
+ err = i .RequestConnection ()
290
+ if err != nil {
291
+ retry --
292
+ }
293
+ if retry == 0 {
294
+ sockChannel <- nil
295
+ return err
296
+ }
297
+ time .Sleep (100 * time .Millisecond )
298
+ }
299
+ sockChannel <- i
300
+ return nil
301
+ }
302
+ // Create slave socket in container net space
303
+ err = cni .NetNsExec ("pid:" + containerPidStr , slave )
304
+ Expect (err ).ToNot (HaveOccurred ())
305
+ i := <- sockChannel
306
+
307
+ By ("Sending a ARP request" )
308
+ srcMac := net.HardwareAddr {0x01 , 0x66 , 0x38 , 0xa1 , 0x12 , 0x46 }
309
+ srcIp := net .IPv4 (1 , 2 , 3 , 4 )
310
+ dstIp := net .IPv4 (1 , 2 , 3 , 5 )
311
+
312
+ arp , err := cni .NewArpRequestPacket (srcMac , srcIp , dstIp )
313
+ Expect (err ).ToNot (HaveOccurred ())
314
+ txq , err := i .GetTxQueue (0 )
315
+ Expect (err ).ToNot (HaveOccurred ())
316
+
317
+ bytesWritten := txq .WritePacket (arp )
318
+ Expect (bytesWritten ).To (Equal (len (arp )))
319
+ quitChan <- 1
248
320
})
249
321
250
322
})
@@ -287,8 +359,256 @@ var _ = Describe("Pod-related functionality of CNI", func() {
287
359
pubSubHandlerMock .Start ()
288
360
})
289
361
290
- // TODO test multinet(additional network for pod) with MEMIF interface
362
+ Context ("With default memif interface configured for secondary(multinet) tunnel to pod" , func () {
363
+ BeforeEach (func () {
364
+ config .GetCalicoVppFeatureGates ().MemifEnabled = & config .True
365
+ })
366
+ It ("should have properly configured both interfaces tunnels to VPP" , func () {
367
+ const (
368
+ ipAddress = "1.2.3.44" // main TAP tunnel (=not multinet)
369
+ mainInterfaceName = "mainInterface" // name must be <=16 characters long due to tap name size on pod linux side
370
+ secondaryInterfaceName = "memif2nd" // name must be <=16 characters long due to tap name size on pod linux side
371
+ memifTCPPortStart = 2222
372
+ memifTCPPortEnd = 33333
373
+ memifUDPPortStart = 4444
374
+ memifUDPPortEnd = 55555
375
+ )
376
+
377
+ By ("Getting Pod mock container's PID" )
378
+ containerPidOutput , err := exec .Command ("docker" , "inspect" , "-f" , "{{.State.Pid}}" ,
379
+ PodMockContainerName ).Output ()
380
+ Expect (err ).Should (BeNil (), "Failed to get pod mock container's PID string" )
381
+ containerPidStr := strings .ReplaceAll (string (containerPidOutput ), "\n " , "" )
382
+
383
+ By ("Adding Pod to primary network using CNI server" )
384
+ newPodForPrimaryNetwork := & cniproto.AddRequest {
385
+ InterfaceName : mainInterfaceName ,
386
+ Netns : fmt .Sprintf ("/proc/%s/ns/net" , containerPidStr ), // expecting mount of "/proc" from host
387
+ ContainerIps : []* cniproto.IPConfig {{Address : ipAddress + "/24" }},
388
+ Workload : & cniproto.WorkloadIDs {},
389
+ }
390
+ common .VppManagerInfo = & config.VppManagerInfo {}
391
+ reply , err := cniServer .Add (context .Background (), newPodForPrimaryNetwork )
392
+ Expect (err ).ToNot (HaveOccurred (), "Pod addition to primary network failed" )
393
+ Expect (reply .Successful ).To (BeTrue (),
394
+ fmt .Sprintf ("Pod addition to primary network failed due to: %s" , reply .ErrorMessage ))
395
+
396
+ By ("Adding Pod to secondary(multinet) network using CNI server" )
397
+ secondaryIPAddress := test .FirstIPinIPRange (networkDefinition .Range ).String ()
398
+ newPodForSecondaryNetwork := & cniproto.AddRequest {
399
+ InterfaceName : secondaryInterfaceName ,
400
+ Netns : fmt .Sprintf ("/proc/%s/ns/net" , containerPidStr ), // expecting mount of "/proc" from host
401
+ ContainerIps : []* cniproto.IPConfig {{
402
+ Address : secondaryIPAddress + "/24" ,
403
+ }},
404
+ //Workload: &cniproto.WorkloadIDs{},
405
+ DataplaneOptions : map [string ]string {
406
+ test .DpoNetworkNameFieldName (): networkDefinition .Name ,
407
+ },
408
+ Workload : & cniproto.WorkloadIDs {
409
+ Annotations : map [string ]string {
410
+ // needed just for setting up steering of traffic to default Tun/Tap and to secondary Memif
411
+ cni .VppAnnotationPrefix + cni .MemifPortAnnotation : fmt .Sprintf ("tcp:%d-%d,udp:%d-%d" ,
412
+ memifTCPPortStart , memifTCPPortEnd , memifUDPPortStart , memifUDPPortEnd ),
413
+ },
414
+ },
415
+ }
416
+ reply , err = cniServer .Add (context .Background (), newPodForSecondaryNetwork )
417
+ Expect (err ).ToNot (HaveOccurred (), "Pod addition to secondary network failed" )
418
+ Expect (reply .Successful ).To (BeTrue (),
419
+ fmt .Sprintf ("Pod addition to secondary network failed due to: %s" , reply .ErrorMessage ))
420
+
421
+ By ("Checking existence of main tun interface tunnel to pod (at VPP's end)" )
422
+ mainSwIfIndex := test .AssertTunInterfaceExistence (vpp , newPodForPrimaryNetwork )
423
+
424
+ By ("Checking main tunnel's tun interface for common interface attributes" )
425
+ test .AssertTunnelInterfaceIPAddress (vpp , mainSwIfIndex , ipAddress )
426
+ test .AssertTunnelInterfaceMTU (vpp , mainSwIfIndex )
427
+
428
+ //By("Checking secondary tunnel's tun interface for existence")
429
+ //secondarySwIfIndex := test.AssertTunInterfaceExistence(vpp, newPodForSecondaryNetwork)
430
+ By ("Checking secondary tunnel's memif interface for existence" )
431
+ memifSwIfIndex , err := vpp .SearchInterfaceWithTag (
432
+ test .InterfaceTagForLocalMemifTunnel (newPodForSecondaryNetwork .InterfaceName , newPodForSecondaryNetwork .Netns ))
433
+ Expect (err ).ShouldNot (HaveOccurred (), "Failed to get memif interface at VPP's end" )
434
+
435
+ By ("Checking secondary tunnel's memif interface for common interface attributes" )
436
+ test .AssertTunnelInterfaceIPAddress (vpp , memifSwIfIndex , secondaryIPAddress )
437
+ test .AssertTunnelInterfaceMTU (vpp , memifSwIfIndex )
438
+ test .AssertInterfaceGSO (memifSwIfIndex , "secondary tunnel's memif interface" , vpp )
439
+
440
+ By ("Checking secondary tunnel's memif interface for memif attributes" )
441
+ memifs , err := vpp .ListMemifInterfaces ()
442
+ Expect (err ).ToNot (HaveOccurred (), "failed to get memif interfaces" )
443
+ Expect (memifs ).ToNot (BeEmpty (), "no memif interfaces retrieved" )
444
+ Expect (memifs [0 ].Role ).To (Equal (types .MemifMaster ))
445
+ Expect (memifs [0 ].Mode ).To (Equal (types .MemifModeEthernet ))
446
+ Expect (memifs [0 ].Flags & types .MemifAdminUp > 0 ).To (BeTrue ())
447
+ // Note: queues are allocated only when a client is listening
448
+ // Expect(memifs[0].QueueSize).To(Equal(config.GetCalicoVppInterfaces().DefaultPodIfSpec.RxQueueSize))
449
+ //Note:Memif.NumRxQueues and Memif.NumTxQueues is not dumped by VPP binary API dump -> can't test it
450
+
451
+ By ("Checking secondary tunnel's memif socket file" ) // checking only VPP setting, not file socket presence
452
+ socket , err := vpp .MemifsocketByID (memifs [0 ].SocketId )
453
+ Expect (err ).ToNot (HaveOccurred (), "failed to get memif socket" )
454
+ Expect (socket .SocketFilename ).To (Equal (
455
+ fmt .Sprintf ("@netns:%s@%s" , newPodForSecondaryNetwork .Netns , newPodForSecondaryNetwork .InterfaceName )),
456
+ "memif socket file is not configured correctly" )
457
+
458
+ test .RunInPod (newPodForSecondaryNetwork .Netns , func () {
459
+ By ("Checking main tunnel's tun interface on pod side" )
460
+ _ , err := netlink .LinkByName (mainInterfaceName )
461
+ Expect (err ).ToNot (HaveOccurred (), "can't find main interface in pod" )
462
+
463
+ By ("Checking secondary tunnel's tun interface on pod side" )
464
+ secTunLink , err := netlink .LinkByName (secondaryInterfaceName )
465
+ Expect (err ).ToNot (HaveOccurred (), "can't find secondary(multinet) interface in pod" )
466
+
467
+ By ("Checking multinet related routes on pod side" )
468
+ secTunLinkRoutes , err := netlink .RouteList (secTunLink , syscall .AF_INET ) // Ipv4 routes only
469
+ Expect (err ).ToNot (HaveOccurred (), "can't get routes from pod" )
470
+ Expect (secTunLinkRoutes ).To (ContainElements (
471
+ gs .MatchFields (gs .IgnoreExtras , gs.Fields {
472
+ "Dst" : gs .PointTo (Equal (* test .IpNet (networkDefinition .Range ))),
473
+ }),
474
+ ), "can't find route in pod that steers all multinet network " +
475
+ "traffic into multinet tunnel interface in pod" )
476
+ })
477
+
478
+ By ("checking pushing of LocalPodAddressAdded event for BGP pod network announcing" )
479
+ // Note: BGP is not tested here, only that event for it was sent
480
+ Expect (pubSubHandlerMock .ReceivedEvents ).To (ContainElements (
481
+ gs .MatchFields (gs .IgnoreExtras , gs.Fields {
482
+ "Type" : Equal (common .LocalPodAddressAdded ),
483
+ "New" : gs .MatchFields (gs .IgnoreExtras , gs.Fields {
484
+ "ContainerIP" : gs .PointTo (Equal (* test .IpNetWithIPInIPv6Format (ipAddress + "/32" ))),
485
+ }),
486
+ }),
487
+ gs .MatchFields (gs .IgnoreExtras , gs.Fields {
488
+ "Type" : Equal (common .LocalPodAddressAdded ),
489
+ "New" : gs .MatchFields (gs .IgnoreExtras , gs.Fields {
490
+ "ContainerIP" : gs .PointTo (Equal (* test .IpNetWithIPInIPv6Format (secondaryIPAddress + "/32" ))),
491
+ }),
492
+ }),
493
+ ))
291
494
495
+ By ("Checking default route from pod-specific VRF to multinet network-specific vrf" )
496
+ podVrf4ID , podVrf6ID , err := test .PodVRFs (secondaryInterfaceName , newPodForSecondaryNetwork .Netns , vpp )
497
+ Expect (err ).ToNot (HaveOccurred (), "can't find pod-specific VRFs" )
498
+ for idx , ipFamily := range vpplink .IpFamilies {
499
+ podVrfID := podVrf4ID
500
+ zeroIPNet := & net.IPNet {IP : net .IPv4zero .To4 (), Mask : net .IPMask (net .IPv4zero .To4 ())}
501
+ if ipFamily .IsIp6 {
502
+ podVrfID = podVrf6ID
503
+ zeroIPNet = & net.IPNet {IP : net .IPv6zero , Mask : net .IPMask (net .IPv6zero )}
504
+ }
505
+ routes , err := vpp .GetRoutes (podVrfID , ipFamily .IsIp6 )
506
+ Expect (err ).ToNot (HaveOccurred (),
507
+ fmt .Sprintf ("can't get %s routes in pod-specific VRF" , ipFamily .Str ))
508
+ Expect (routes ).To (ContainElements (
509
+ types.Route {
510
+ Paths : []types.RoutePath {{
511
+ Table : networkDefinition .VRF .Tables [idx ],
512
+ SwIfIndex : types .InvalidID ,
513
+ Gw : zeroIPNet .IP ,
514
+ }},
515
+ Dst : zeroIPNet ,
516
+ Table : podVrfID ,
517
+ },
518
+ ), "can't find default route from pod-specific VRF to multinet " +
519
+ "network-specific vrf" )
520
+ }
521
+
522
+ By ("Checking steering route in multinet network VRF leading to pod " +
523
+ "using multinet tunnel interface" )
524
+ // Note: should be checked for all container IPs of multinet, but we have only one
525
+ multinetVRFID := networkDefinition .VRF .Tables [test .IpFamilyIndex (vpplink .IpFamilyV4 )] // secondaryIPAddress is from IpFamilyV4
526
+ routes , err := vpp .GetRoutes (multinetVRFID , false )
527
+ Expect (err ).ToNot (HaveOccurred (),
528
+ "can't get ipv4 routes in multinet network-specific VRF" )
529
+ Expect (routes ).To (ContainElements (
530
+ types.Route {
531
+ Dst : test .IpNet (secondaryIPAddress + "/32" ),
532
+ Paths : []types.RoutePath {{
533
+ SwIfIndex : memifSwIfIndex ,
534
+ Gw : test .IpNet (secondaryIPAddress + "/32" ).IP ,
535
+ }},
536
+ Table : multinetVRFID ,
537
+ },
538
+ ), "can't find steering route in multinet network VRF leading " +
539
+ "to pod using multinet tunnel interface" )
540
+
541
+ By ("Checking socket creation" )
542
+ memif_socket , err := gomemif .NewSocket ("gomemif_example" , "@" + secondaryInterfaceName )
543
+ Expect (err ).ToNot (HaveOccurred ())
544
+
545
+ By ("Checking slave connection to master" )
546
+ memifErrChan := make (chan error )
547
+ quitChan := make (chan int )
548
+ // Start master polling
549
+ go func () {
550
+ for {
551
+ select {
552
+ case <- quitChan :
553
+ return
554
+ default :
555
+ memif_socket .StartPolling (memifErrChan )
556
+ time .Sleep (100 * time .Millisecond )
557
+ }
558
+ }
559
+ }()
560
+
561
+ sockChannel := make (chan * gomemif.Interface , 1 )
562
+
563
+ slave := func () error {
564
+ args := & gomemif.Arguments {
565
+ IsMaster : false ,
566
+ Name : "memif" ,
567
+ ConnectedFunc : func (i * gomemif.Interface ) error { return nil },
568
+ DisconnectedFunc : func (i * gomemif.Interface ) error { return nil },
569
+ }
570
+
571
+ i , err := memif_socket .NewInterface (args )
572
+ if err != nil {
573
+ return err
574
+ }
575
+
576
+ retry := 5
577
+ for ! i .IsConnecting () {
578
+ err = i .RequestConnection ()
579
+ if err != nil {
580
+ retry --
581
+ }
582
+ if retry == 0 {
583
+ sockChannel <- nil
584
+ return err
585
+ }
586
+ time .Sleep (100 * time .Millisecond )
587
+ }
588
+ sockChannel <- i
589
+ return nil
590
+ }
591
+ // Create slave socket in container net space
592
+ err = cni .NetNsExec ("pid:" + containerPidStr , slave )
593
+ Expect (err ).ToNot (HaveOccurred ())
594
+ i := <- sockChannel
595
+ Expect (i ).ToNot (BeNil ())
596
+
597
+ By ("Sending a ARP request" )
598
+ srcMac := net.HardwareAddr {0x01 , 0x66 , 0x38 , 0xa1 , 0x12 , 0x46 }
599
+ srcIp := net .IPv4 (1 , 2 , 3 , 4 )
600
+ dstIp := net .IPv4 (1 , 2 , 3 , 5 )
601
+
602
+ arp , err := cni .NewArpRequestPacket (srcMac , srcIp , dstIp )
603
+ Expect (err ).ToNot (HaveOccurred ())
604
+ txq , err := i .GetTxQueue (0 )
605
+ Expect (err ).ToNot (HaveOccurred ())
606
+
607
+ bytesWritten := txq .WritePacket (arp )
608
+ Expect (bytesWritten ).To (Equal (len (arp )))
609
+ quitChan <- 1
610
+ })
611
+ })
292
612
Context ("With default (TAP) interface configured for secondary(multinet) tunnel to pod" , func () {
293
613
It ("should have properly configured both TAP interface tunnels to VPP" , func () {
294
614
const (
0 commit comments