gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/benchmarks/tcp/xdp.go (about) 1 // Copyright 2023 The gVisor Authors. 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 main 16 17 import ( 18 "bytes" 19 "fmt" 20 "net" 21 "runtime" 22 23 "github.com/cilium/ebpf" 24 "github.com/cilium/ebpf/link" 25 "golang.org/x/sys/unix" 26 "gvisor.dev/gvisor/pkg/tcpip" 27 "gvisor.dev/gvisor/pkg/tcpip/link/xdp" 28 "gvisor.dev/gvisor/pkg/tcpip/stack" 29 "gvisor.dev/gvisor/runsc/sandbox/bpf" 30 ) 31 32 func newXDPEndpoint(ifaceName string, mac net.HardwareAddr) (stack.LinkEndpoint, error) { 33 // Get all interfaces in the namespace. 34 ifaces, err := net.Interfaces() 35 if err != nil { 36 return nil, fmt.Errorf("querying interfaces: %v", err) 37 } 38 39 // Find our specific interface. 40 var iface net.Interface 41 for _, netif := range ifaces { 42 if netif.Name == ifaceName { 43 iface = netif 44 break 45 } 46 } 47 // Zero is never used as an Index. Use that to determine whether an 48 // interface was found. 49 if iface.Index == 0 { 50 return nil, fmt.Errorf("failed to find interface: %v", ifaceName) 51 } 52 53 // See sandbox.createSocketXDP. 54 55 // Create an XDP socket. Later we'll mmap memory for the various rings 56 // and bind to the device. 57 fd, err := unix.Socket(unix.AF_XDP, unix.SOCK_RAW, 0) 58 if err != nil { 59 return nil, fmt.Errorf("unable to create AF_XDP socket: %v", err) 60 } 61 62 // Attach a program to the device and insert our socket into its map. 63 64 // Load into the kernel. 65 spec, err := ebpf.LoadCollectionSpecFromReader(bytes.NewReader(bpf.AFXDPProgram)) 66 if err != nil { 67 return nil, fmt.Errorf("failed to load spec: %v", err) 68 } 69 70 var objects struct { 71 Program *ebpf.Program `ebpf:"xdp_prog"` 72 SockMap *ebpf.Map `ebpf:"sock_map"` 73 } 74 if err := spec.LoadAndAssign(&objects, nil); err != nil { 75 return nil, fmt.Errorf("failed to load program: %v", err) 76 } 77 78 rawLink, err := link.AttachRawLink(link.RawLinkOptions{ 79 Program: objects.Program, 80 Attach: ebpf.AttachXDP, 81 Target: iface.Index, 82 // By not setting the Flag field, the kernel will choose the 83 // fastest mode. In order those are: 84 // - Offloaded onto the NIC. 85 // - Running directly in the driver. 86 // - Generic mode, which works with any NIC/driver but lacks 87 // much of the XDP performance boost. 88 }) 89 if err != nil { 90 return nil, fmt.Errorf("failed to attach BPF program: %v", err) 91 } 92 93 // Insert our AF_XDP socket into the BPF map that dictates where 94 // packets are redirected to. 95 key := uint32(0) 96 val := uint32(fd) 97 if err := objects.SockMap.Update(&key, &val, 0 /* flags */); err != nil { 98 return nil, fmt.Errorf("failed to insert socket into BPF map: %v", err) 99 } 100 101 // Ensure that none of the XDP bits are removed by GC'ing the 102 // os.File-wrapped descriptors. 103 for i, fd := range []int{fd, objects.Program.FD(), objects.SockMap.FD(), rawLink.FD()} { 104 if _, err := unix.Dup(fd); err != nil { 105 return nil, fmt.Errorf("failed to dup fd with index %d (%d): %v", i, fd, err) 106 } 107 } 108 109 runtime.KeepAlive(objects) 110 runtime.KeepAlive(rawLink) 111 112 return xdp.New(&xdp.Options{ 113 FD: fd, 114 Address: tcpip.LinkAddress(mac), 115 TXChecksumOffload: false, 116 RXChecksumOffload: true, 117 InterfaceIndex: iface.Index, 118 Bind: true, 119 GRO: *gro, 120 }) 121 }