diff --git a/tools/xdp/BUILD b/tools/xdp/BUILD index f4c152170..28aa36833 100644 --- a/tools/xdp/BUILD +++ b/tools/xdp/BUILD @@ -8,30 +8,13 @@ package( go_binary( name = "xdp_loader", srcs = [ - "drop.go", "main.go", - "pass.go", - "redirect_host.go", - "tcpdump.go", - ], - embedsrcs = [ - "//tools/xdp/bpf:drop_ebpf.o", # keep - "//tools/xdp/bpf:pass_ebpf.o", # keep - "//tools/xdp/bpf:redirect_host_ebpf.o", # keep - "//tools/xdp/bpf:tcpdump_ebpf.o", # keep ], pure = True, visibility = ["//:sandbox"], deps = [ - "//pkg/buffer", - "//pkg/tcpip/header", - "//pkg/tcpip/link/sniffer", - "//pkg/tcpip/stack", - "//pkg/xdp", "//runsc/flag", - "@com_github_cilium_ebpf//:go_default_library", - "@com_github_cilium_ebpf//link:go_default_library", + "//tools/xdp/cmd", "@com_github_google_subcommands//:go_default_library", - "@org_golang_x_sys//unix:go_default_library", ], ) diff --git a/tools/xdp/cmd/BUILD b/tools/xdp/cmd/BUILD new file mode 100644 index 000000000..ba4e6b186 --- /dev/null +++ b/tools/xdp/cmd/BUILD @@ -0,0 +1,36 @@ +load("//tools:defs.bzl", "go_library") + +package( + default_applicable_licenses = ["//:license"], + licenses = ["notice"], +) + +go_library( + name = "cmd", + srcs = [ + "cmd.go", + "drop.go", + "pass.go", + "redirect_host.go", + "tcpdump.go", + ], + embedsrcs = [ + "//tools/xdp/cmd/bpf:drop_ebpf.o", # keep + "//tools/xdp/cmd/bpf:pass_ebpf.o", # keep + "//tools/xdp/cmd/bpf:redirect_host_ebpf.o", # keep + "//tools/xdp/cmd/bpf:tcpdump_ebpf.o", # keep + ], + visibility = ["//:sandbox"], + deps = [ + "//pkg/buffer", + "//pkg/tcpip/header", + "//pkg/tcpip/link/sniffer", + "//pkg/tcpip/stack", + "//pkg/xdp", + "//runsc/flag", + "@com_github_cilium_ebpf//:go_default_library", + "@com_github_cilium_ebpf//link:go_default_library", + "@com_github_google_subcommands//:go_default_library", + "@org_golang_x_sys//unix:go_default_library", + ], +) diff --git a/tools/xdp/bpf/BUILD b/tools/xdp/cmd/bpf/BUILD similarity index 100% rename from tools/xdp/bpf/BUILD rename to tools/xdp/cmd/bpf/BUILD diff --git a/tools/xdp/bpf/drop.ebpf.c b/tools/xdp/cmd/bpf/drop.ebpf.c similarity index 100% rename from tools/xdp/bpf/drop.ebpf.c rename to tools/xdp/cmd/bpf/drop.ebpf.c diff --git a/tools/xdp/bpf/pass.ebpf.c b/tools/xdp/cmd/bpf/pass.ebpf.c similarity index 100% rename from tools/xdp/bpf/pass.ebpf.c rename to tools/xdp/cmd/bpf/pass.ebpf.c diff --git a/tools/xdp/bpf/redirect_host.ebpf.c b/tools/xdp/cmd/bpf/redirect_host.ebpf.c similarity index 100% rename from tools/xdp/bpf/redirect_host.ebpf.c rename to tools/xdp/cmd/bpf/redirect_host.ebpf.c diff --git a/tools/xdp/bpf/tcpdump.ebpf.c b/tools/xdp/cmd/bpf/tcpdump.ebpf.c similarity index 100% rename from tools/xdp/bpf/tcpdump.ebpf.c rename to tools/xdp/cmd/bpf/tcpdump.ebpf.c diff --git a/tools/xdp/cmd/cmd.go b/tools/xdp/cmd/cmd.go new file mode 100644 index 000000000..c76bb4e55 --- /dev/null +++ b/tools/xdp/cmd/cmd.go @@ -0,0 +1,124 @@ +// Copyright 2022 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build amd64 || arm64 +// +build amd64 arm64 + +// Package cmd implements the subcommands of xdp_loader. +package cmd + +import ( + "bytes" + _ "embed" + "fmt" + "log" + "net" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/link" + "golang.org/x/sys/unix" +) + +func runBasicProgram(progData []byte, device string, deviceIndex int) error { + iface, err := getIface(device, deviceIndex) + if err != nil { + return fmt.Errorf("%v", err) + } + + // Load into the kernel. + spec, err := ebpf.LoadCollectionSpecFromReader(bytes.NewReader(progData)) + if err != nil { + return fmt.Errorf("failed to load spec: %v", err) + } + + var objects struct { + Program *ebpf.Program `ebpf:"xdp_prog"` + } + if err := spec.LoadAndAssign(&objects, nil); err != nil { + return fmt.Errorf("failed to load program: %v", err) + } + defer func() { + if err := objects.Program.Close(); err != nil { + fmt.Printf("failed to close program: %v", err) + } + }() + + _, cleanup, err := attach(objects.Program, iface) + if err != nil { + return fmt.Errorf("failed to attach: %v", err) + } + defer cleanup() + + waitForever() + return nil +} + +func getIface(device string, deviceIndex int) (*net.Interface, error) { + switch { + case device != "" && deviceIndex != 0: + return nil, fmt.Errorf("device specified twice") + case device != "": + iface, err := net.InterfaceByName(device) + if err != nil { + return nil, fmt.Errorf("unknown device %q: %v", device, err) + } + return iface, nil + case deviceIndex != 0: + iface, err := net.InterfaceByIndex(deviceIndex) + if err != nil { + return nil, fmt.Errorf("unknown device with index %d: %v", deviceIndex, err) + } + return iface, nil + default: + return nil, fmt.Errorf("no device specified") + } +} + +func attach(program *ebpf.Program, iface *net.Interface) (link.Link, func(), error) { + // Attach the program to the XDP hook on the device. Fallback from best + // to worst mode. + modes := []struct { + name string + flag link.XDPAttachFlags + }{ + {name: "offload", flag: link.XDPOffloadMode}, + {name: "driver", flag: link.XDPDriverMode}, + {name: "generic", flag: link.XDPGenericMode}, + } + var attached link.Link + var err error + for _, mode := range modes { + attached, err = link.AttachXDP(link.XDPOptions{ + Program: program, + Interface: iface.Index, + Flags: mode.flag, + }) + if err == nil { + log.Printf("attached with mode %q", mode.name) + break + } + log.Printf("failed to attach with mode %q: %v", mode.name, err) + } + if attached == nil { + return nil, nil, fmt.Errorf("failed to attach program") + } + return attached, func() { attached.Close() }, nil +} + +func waitForever() { + log.Printf("Successfully attached! Press CTRL-C to quit and remove the program from the device.") + for { + unix.Pause() + } +} diff --git a/tools/xdp/drop.go b/tools/xdp/cmd/drop.go similarity index 99% rename from tools/xdp/drop.go rename to tools/xdp/cmd/drop.go index 2d7c29f42..afa3f2b03 100644 --- a/tools/xdp/drop.go +++ b/tools/xdp/cmd/drop.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package cmd import ( "context" diff --git a/tools/xdp/pass.go b/tools/xdp/cmd/pass.go similarity index 99% rename from tools/xdp/pass.go rename to tools/xdp/cmd/pass.go index a47eedeb9..41c47b61b 100644 --- a/tools/xdp/pass.go +++ b/tools/xdp/cmd/pass.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package cmd import ( "context" diff --git a/tools/xdp/redirect_host.go b/tools/xdp/cmd/redirect_host.go similarity index 99% rename from tools/xdp/redirect_host.go rename to tools/xdp/cmd/redirect_host.go index 202847bf2..ffa876d47 100644 --- a/tools/xdp/redirect_host.go +++ b/tools/xdp/cmd/redirect_host.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package cmd import ( "bytes" diff --git a/tools/xdp/tcpdump.go b/tools/xdp/cmd/tcpdump.go similarity index 99% rename from tools/xdp/tcpdump.go rename to tools/xdp/cmd/tcpdump.go index 4d87dbec9..96988ef6c 100644 --- a/tools/xdp/tcpdump.go +++ b/tools/xdp/cmd/tcpdump.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package cmd import ( "bytes" diff --git a/tools/xdp/main.go b/tools/xdp/main.go index 429edd686..c3394e937 100644 --- a/tools/xdp/main.go +++ b/tools/xdp/main.go @@ -20,121 +20,22 @@ package main import ( - "bytes" "context" _ "embed" - "fmt" - "log" - "net" "os" - "github.com/cilium/ebpf" - "github.com/cilium/ebpf/link" "github.com/google/subcommands" - "golang.org/x/sys/unix" "gvisor.dev/gvisor/runsc/flag" + "gvisor.dev/gvisor/tools/xdp/cmd" ) func main() { - subcommands.Register(new(DropCommand), "") - subcommands.Register(new(PassCommand), "") - subcommands.Register(new(RedirectHostCommand), "") - subcommands.Register(new(TcpdumpCommand), "") + subcommands.Register(new(cmd.DropCommand), "") + subcommands.Register(new(cmd.PassCommand), "") + subcommands.Register(new(cmd.RedirectHostCommand), "") + subcommands.Register(new(cmd.TcpdumpCommand), "") flag.Parse() ctx := context.Background() os.Exit(int(subcommands.Execute(ctx))) } - -func runBasicProgram(progData []byte, device string, deviceIndex int) error { - iface, err := getIface(device, deviceIndex) - if err != nil { - return fmt.Errorf("%v", err) - } - - // Load into the kernel. - spec, err := ebpf.LoadCollectionSpecFromReader(bytes.NewReader(progData)) - if err != nil { - return fmt.Errorf("failed to load spec: %v", err) - } - - var objects struct { - Program *ebpf.Program `ebpf:"xdp_prog"` - } - if err := spec.LoadAndAssign(&objects, nil); err != nil { - return fmt.Errorf("failed to load program: %v", err) - } - defer func() { - if err := objects.Program.Close(); err != nil { - fmt.Printf("failed to close program: %v", err) - } - }() - - _, cleanup, err := attach(objects.Program, iface) - if err != nil { - return fmt.Errorf("failed to attach: %v", err) - } - defer cleanup() - - waitForever() - return nil -} - -func getIface(device string, deviceIndex int) (*net.Interface, error) { - switch { - case device != "" && deviceIndex != 0: - return nil, fmt.Errorf("device specified twice") - case device != "": - iface, err := net.InterfaceByName(device) - if err != nil { - return nil, fmt.Errorf("unknown device %q: %v", device, err) - } - return iface, nil - case deviceIndex != 0: - iface, err := net.InterfaceByIndex(deviceIndex) - if err != nil { - return nil, fmt.Errorf("unknown device with index %d: %v", deviceIndex, err) - } - return iface, nil - default: - return nil, fmt.Errorf("no device specified") - } -} - -func attach(program *ebpf.Program, iface *net.Interface) (link.Link, func(), error) { - // Attach the program to the XDP hook on the device. Fallback from best - // to worst mode. - modes := []struct { - name string - flag link.XDPAttachFlags - }{ - {name: "offload", flag: link.XDPOffloadMode}, - {name: "driver", flag: link.XDPDriverMode}, - {name: "generic", flag: link.XDPGenericMode}, - } - var attached link.Link - var err error - for _, mode := range modes { - attached, err = link.AttachXDP(link.XDPOptions{ - Program: program, - Interface: iface.Index, - Flags: mode.flag, - }) - if err == nil { - log.Printf("attached with mode %q", mode.name) - break - } - log.Printf("failed to attach with mode %q: %v", mode.name, err) - } - if attached == nil { - return nil, nil, fmt.Errorf("failed to attach program") - } - return attached, func() { attached.Close() }, nil -} - -func waitForever() { - log.Printf("Successfully attached! Press CTRL-C to quit and remove the program from the device.") - for { - unix.Pause() - } -}