gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/tools/xdp/cmd/cmd.go (about) 1 // Copyright 2022 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 //go:build amd64 || arm64 16 // +build amd64 arm64 17 18 // Package cmd implements the subcommands of xdp_loader. 19 package cmd 20 21 import ( 22 "bytes" 23 _ "embed" 24 "fmt" 25 "log" 26 "net" 27 28 "github.com/cilium/ebpf" 29 "github.com/cilium/ebpf/link" 30 "golang.org/x/sys/unix" 31 ) 32 33 func runBasicProgram(progData []byte, device string, deviceIndex int) error { 34 iface, err := getIface(device, deviceIndex) 35 if err != nil { 36 return fmt.Errorf("%v", err) 37 } 38 39 // Load into the kernel. 40 spec, err := ebpf.LoadCollectionSpecFromReader(bytes.NewReader(progData)) 41 if err != nil { 42 return fmt.Errorf("failed to load spec: %v", err) 43 } 44 45 var objects struct { 46 Program *ebpf.Program `ebpf:"xdp_prog"` 47 } 48 if err := spec.LoadAndAssign(&objects, nil); err != nil { 49 return fmt.Errorf("failed to load program: %v", err) 50 } 51 defer func() { 52 if err := objects.Program.Close(); err != nil { 53 fmt.Printf("failed to close program: %v", err) 54 } 55 }() 56 57 _, cleanup, err := attach(objects.Program, iface) 58 if err != nil { 59 return fmt.Errorf("failed to attach: %v", err) 60 } 61 defer cleanup() 62 63 waitForever() 64 return nil 65 } 66 67 func getIface(device string, deviceIndex int) (*net.Interface, error) { 68 switch { 69 case device != "" && deviceIndex != 0: 70 return nil, fmt.Errorf("device specified twice") 71 case device != "": 72 iface, err := net.InterfaceByName(device) 73 if err != nil { 74 return nil, fmt.Errorf("unknown device %q: %v", device, err) 75 } 76 return iface, nil 77 case deviceIndex != 0: 78 iface, err := net.InterfaceByIndex(deviceIndex) 79 if err != nil { 80 return nil, fmt.Errorf("unknown device with index %d: %v", deviceIndex, err) 81 } 82 return iface, nil 83 default: 84 return nil, fmt.Errorf("no device specified") 85 } 86 } 87 88 func attach(program *ebpf.Program, iface *net.Interface) (link.Link, func(), error) { 89 // Attach the program to the XDP hook on the device. Fallback from best 90 // to worst mode. 91 modes := []struct { 92 name string 93 flag link.XDPAttachFlags 94 }{ 95 {name: "offload", flag: link.XDPOffloadMode}, 96 {name: "driver", flag: link.XDPDriverMode}, 97 {name: "generic", flag: link.XDPGenericMode}, 98 } 99 var attached link.Link 100 var err error 101 for _, mode := range modes { 102 attached, err = link.AttachXDP(link.XDPOptions{ 103 Program: program, 104 Interface: iface.Index, 105 Flags: mode.flag, 106 }) 107 if err == nil { 108 log.Printf("attached with mode %q", mode.name) 109 break 110 } 111 log.Printf("failed to attach with mode %q: %v", mode.name, err) 112 } 113 if attached == nil { 114 return nil, nil, fmt.Errorf("failed to attach program") 115 } 116 return attached, func() { attached.Close() }, nil 117 } 118 119 func waitForever() { 120 log.Printf("Successfully attached! Press CTRL-C to quit and remove the program from the device.") 121 for { 122 unix.Pause() 123 } 124 }