pax_global_header00006660000000000000000000000064143470612320014514gustar00rootroot0000000000000052 comment=4b89702f26d7cdcb0aa6c9617036bf1c924aba57 golang-github-mdlayher-raw-0.1.0/000077500000000000000000000000001434706123200166335ustar00rootroot00000000000000golang-github-mdlayher-raw-0.1.0/.github/000077500000000000000000000000001434706123200201735ustar00rootroot00000000000000golang-github-mdlayher-raw-0.1.0/.github/workflows/000077500000000000000000000000001434706123200222305ustar00rootroot00000000000000golang-github-mdlayher-raw-0.1.0/.github/workflows/linux-test.yml000066400000000000000000000014031434706123200250650ustar00rootroot00000000000000name: Linux Test on: push: branches: - '*' pull_request: branches: - '*' jobs: build: strategy: fail-fast: false matrix: go-version: [1.17] runs-on: ubuntu-latest steps: - name: Set up Go uses: actions/setup-go@v2 with: go-version: ${{ matrix.go-version }} id: go - name: Check out code into the Go module directory uses: actions/checkout@v2 - name: Build test binary run: go test -c -race # These tests run on Linux only. Apply capabilities and verify we can # actually use real AF_PACKET sockets. - name: Apply CAP_NET_RAW run: sudo setcap cap_net_raw+ep ./raw.test - name: Run tests with capabilities run: ./raw.test -test.v golang-github-mdlayher-raw-0.1.0/.github/workflows/static-analysis.yml000066400000000000000000000012741434706123200260670ustar00rootroot00000000000000name: Static Analysis on: push: branches: - '*' pull_request: branches: - '*' jobs: build: strategy: matrix: go-version: [1.17] runs-on: ubuntu-latest steps: - name: Set up Go uses: actions/setup-go@v2 with: go-version: ${{ matrix.go-version }} id: go - name: Check out code into the Go module directory uses: actions/checkout@v2 - name: Install staticcheck run: go install honnef.co/go/tools/cmd/staticcheck@latest - name: Print staticcheck version run: staticcheck -version - name: Run staticcheck run: staticcheck ./... - name: Run go vet run: go vet ./... golang-github-mdlayher-raw-0.1.0/.github/workflows/test.yml000066400000000000000000000012601434706123200237310ustar00rootroot00000000000000name: Test on: push: branches: - '*' pull_request: branches: - '*' jobs: build: strategy: fail-fast: false matrix: go-version: [1.12, 1.17] os: [ubuntu-latest, macos-latest] runs-on: ${{ matrix.os }} steps: - name: Set up Go uses: actions/setup-go@v2 with: go-version: ${{ matrix.go-version }} id: go - name: Check out code into the Go module directory uses: actions/checkout@v2 # Run basic tests, we just want to make sure there is parity on Linux and # macOS, and back to the oldest version of Go this library supports. - name: Run tests run: go test ./... golang-github-mdlayher-raw-0.1.0/.github/workflows/unsupported.yml000066400000000000000000000012211434706123200253370ustar00rootroot00000000000000name: Unsupported on: push: branches: - '*' pull_request: branches: - '*' jobs: build: strategy: fail-fast: false matrix: go-version: [1.17] runs-on: ubuntu-latest steps: - name: Set up Go uses: actions/setup-go@v2 with: go-version: ${{ matrix.go-version }} id: go - name: Check out code into the Go module directory uses: actions/checkout@v2 # Although this package doesn't support Windows, we want to verify that # everything builds properly. - name: Verify build for non-UNIX platforms run: go build env: GOOS: windows golang-github-mdlayher-raw-0.1.0/LICENSE.md000066400000000000000000000020631434706123200202400ustar00rootroot00000000000000# MIT License Copyright (C) 2015-2022 Matt Layher Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. golang-github-mdlayher-raw-0.1.0/README.md000066400000000000000000000027111434706123200201130ustar00rootroot00000000000000# raw [![Test Status](https://github.com/mdlayher/raw/workflows/Test/badge.svg)](https://github.com/mdlayher/raw/actions) [![Go Reference](https://pkg.go.dev/badge/github.com/mdlayher/raw.svg)](https://pkg.go.dev/github.com/mdlayher/raw) [![Go Report Card](https://goreportcard.com/badge/github.com/mdlayher/raw)](https://goreportcard.com/report/github.com/mdlayher/raw) Package `raw` enables reading and writing data at the device driver level for a network interface. MIT Licensed. Deprecated: use [`github.com/mdlayher/packet`](https://github.com/mdlayher/packet) on Linux instead. This package is unmaintained. For more information about using sockets with Ethernet frames in Go, check out my blog post: [Network Protocol Breakdown: Ethernet and Go](https://mdlayher.com/blog/network-protocol-breakdown-ethernet-and-go/). ## Unmaintained This repository was one of my first major Go networking libraries. Although I have updated it on Linux to incorporate modern Go best practices (asynchronous I/O, runtime network poller integration), the non-Linux platform code is effectively unmaintained and does not have the same level of functionality. I encourage all Linux users of this package to migrate to [`github.com/mdlayher/packet`](https://github.com/mdlayher/packet), which is a modern `AF_PACKET` library. The existing `*raw.Conn` APIs now call directly into the equivalent `*packet.Conn` APIs, and a level of indirection can be removed by migrating to that package. golang-github-mdlayher-raw-0.1.0/go.mod000066400000000000000000000007361434706123200177470ustar00rootroot00000000000000// Deprecated: use github.com/mdlayher/packet on Linux instead. This package is // unmaintained. module github.com/mdlayher/raw go 1.17 require ( github.com/google/go-cmp v0.5.6 github.com/mdlayher/packet v0.0.0-20220221164757-67998ac0ff93 golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c golang.org/x/sys v0.0.0-20220209214540-3681064d5158 ) require ( github.com/mdlayher/socket v0.2.1 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect ) golang-github-mdlayher-raw-0.1.0/go.sum000066400000000000000000000034171434706123200177730ustar00rootroot00000000000000github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/mdlayher/packet v0.0.0-20220221164757-67998ac0ff93 h1:elUwhY+HQaIV9kMgmsU9zOF413pDKoo2uFNypgP5SxM= github.com/mdlayher/packet v0.0.0-20220221164757-67998ac0ff93/go.mod h1:K9sWKMgN6wa78BbuJL+dT1ZZdiAfhkc2fb6XXLjHulk= github.com/mdlayher/socket v0.2.1 h1:F2aaOwb53VsBE+ebRS9bLd7yPOfYUMC8lOODdCBDY6w= github.com/mdlayher/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang-github-mdlayher-raw-0.1.0/raw.go000066400000000000000000000125151434706123200177570ustar00rootroot00000000000000// Package raw enables reading and writing data at the device driver level for a // network interface. // // Deprecated: use github.com/mdlayher/packet on Linux instead. This package is // unmaintained. // // // Unmaintained // // This repository was one of my first major Go networking libraries. Although I // have updated it on Linux to incorporate modern Go best practices // (asynchronous I/O, runtime network poller integration), the non-Linux // platform code is effectively unmaintained and does not have the same level of // functionality. // // I encourage all Linux users of this package to migrate to // https://github.com/mdlayher/packet, which is a modern AF_PACKET library. The // existing *raw.Conn APIs now call directly into the equivalent *packet.Conn // APIs, and a level of indirection can be removed by migrating to that package. package raw import ( "errors" "net" "time" "golang.org/x/net/bpf" ) // ErrNotImplemented is returned when certain functionality is not yet // implemented for the host operating system. var ErrNotImplemented = errors.New("raw: not implemented") var _ net.Addr = &Addr{} // Addr is a network address which can be used to contact other machines, using // their hardware addresses. type Addr struct { HardwareAddr net.HardwareAddr } // Network returns the address's network name, "raw". func (a *Addr) Network() string { return "raw" } // String returns the address's hardware address. func (a *Addr) String() string { return a.HardwareAddr.String() } var _ net.PacketConn = &Conn{} // Conn is an implementation of the net.PacketConn interface which can send // and receive data at the network interface device driver level. type Conn struct { // packetConn is the operating system-specific implementation of // a raw connection. p *packetConn } // ReadFrom implements the net.PacketConn ReadFrom method. func (c *Conn) ReadFrom(b []byte) (int, net.Addr, error) { return c.p.ReadFrom(b) } // WriteTo implements the net.PacketConn WriteTo method. func (c *Conn) WriteTo(b []byte, addr net.Addr) (int, error) { return c.p.WriteTo(b, addr) } // Close closes the connection. func (c *Conn) Close() error { return c.p.Close() } // LocalAddr returns the local network address. func (c *Conn) LocalAddr() net.Addr { return c.p.LocalAddr() } // SetDeadline implements the net.PacketConn SetDeadline method. func (c *Conn) SetDeadline(t time.Time) error { return c.p.SetDeadline(t) } // SetReadDeadline implements the net.PacketConn SetReadDeadline method. func (c *Conn) SetReadDeadline(t time.Time) error { return c.p.SetReadDeadline(t) } // SetWriteDeadline implements the net.PacketConn SetWriteDeadline method. func (c *Conn) SetWriteDeadline(t time.Time) error { return c.p.SetWriteDeadline(t) } var _ bpf.Setter = &Conn{} // SetBPF attaches an assembled BPF program to the connection. func (c *Conn) SetBPF(filter []bpf.RawInstruction) error { return c.p.SetBPF(filter) } // SetPromiscuous enables or disables promiscuous mode on the interface, allowing it // to receive traffic that is not addressed to the interface. func (c *Conn) SetPromiscuous(b bool) error { return c.p.SetPromiscuous(b) } // Stats contains statistics about a Conn. type Stats struct { // The total number of packets received. Packets uint64 // The number of packets dropped. Drops uint64 } // Stats retrieves statistics from the Conn. // // Only supported on Linux at this time. func (c *Conn) Stats() (*Stats, error) { return c.p.Stats() } // ListenPacket creates a net.PacketConn which can be used to send and receive // data at the network interface device driver level. // // ifi specifies the network interface which will be used to send and receive // data. // // proto specifies the protocol (usually the EtherType) which should be // captured and transmitted. proto, if needed, is automatically converted to // network byte order (big endian), akin to the htons() function in C. // // cfg specifies optional configuration which may be operating system-specific. // A nil Config is equivalent to the default configuration: send and receive // data at the network interface device driver level (usually raw Ethernet frames). func ListenPacket(ifi *net.Interface, proto uint16, cfg *Config) (*Conn, error) { // A nil config is an empty Config. if cfg == nil { cfg = &Config{} } p, err := listenPacket(ifi, proto, *cfg) if err != nil { return nil, err } return &Conn{ p: p, }, nil } // A Config can be used to specify additional options for a Conn. type Config struct { // Linux only: call socket(7) with SOCK_DGRAM instead of SOCK_RAW. // Has no effect on other operating systems. LinuxSockDGRAM bool // Linux only: do not accumulate packet socket statistic counters. Packet // socket statistics are reset on each call to retrieve them via getsockopt, // but this package's default behavior is to continue accumulating the // statistics internally per Conn. To use the Linux default behavior of // resetting statistics on each call to Stats, set this value to true. NoCumulativeStats bool // Linux only: initial filter to apply to the connection. This avoids // capturing random packets before SetBPF is called. Filter []bpf.RawInstruction // BSD only: configure the BPF direction flag to allow selection of inbound // only (0 - default) or bidirectional (1) packet processing. // Has no effect on other operating systems. BPFDirection int } golang-github-mdlayher-raw-0.1.0/raw_bsd.go000066400000000000000000000210311434706123200206000ustar00rootroot00000000000000//go:build darwin || dragonfly || freebsd || netbsd || openbsd // +build darwin dragonfly freebsd netbsd openbsd package raw import ( "errors" "fmt" "net" "os" "runtime" "sync" "syscall" "time" "unsafe" "golang.org/x/net/bpf" "golang.org/x/sys/unix" ) // osFreeBSD is the GOOS name for FreeBSD. const osFreeBSD = "freebsd" // bpfLen returns the length of the BPF header prepended to each incoming ethernet // frame. FreeBSD uses a slightly modified header from other BSD variants. func bpfLen() int { // Majority of BSD family systems use the bpf_hdr struct, but FreeBSD // has replaced this with bpf_xhdr, which is longer. const ( bpfHeaderLen = 18 bpfXHeaderLen = 26 ) if runtime.GOOS == osFreeBSD { return bpfXHeaderLen } return bpfHeaderLen } // Must implement net.PacketConn at compile-time. var _ net.PacketConn = &packetConn{} // packetConn is the Linux-specific implementation of net.PacketConn for this // package. type packetConn struct { proto uint16 ifi *net.Interface f *os.File fd int buflen int // Timeouts set via Set{Read,}Deadline, guarded by mutex timeoutMu sync.RWMutex rtimeout time.Time } // listenPacket creates a net.PacketConn which can be used to send and receive // data at the device driver level. func listenPacket(ifi *net.Interface, proto uint16, cfg Config) (*packetConn, error) { // TODO(mdlayher): consider porting NoTimeouts option to BSD if it pans out. var f *os.File var err error // Try to find an available BPF device for i := 0; i <= 255; i++ { bpfPath := fmt.Sprintf("/dev/bpf%d", i) f, err = os.OpenFile(bpfPath, os.O_RDWR, 0666) if err == nil { // Found a usable device break } // Device is busy, try the next one if perr, ok := err.(*os.PathError); ok { if perr.Err.(syscall.Errno) == syscall.EBUSY { continue } } return nil, err } if f == nil { return nil, errors.New("unable to open BPF device") } fd := int(f.Fd()) if fd == -1 { return nil, errors.New("unable to open BPF device") } // Configure BPF device to send and receive data buflen, err := configureBPF(fd, ifi, proto, cfg.BPFDirection) if err != nil { return nil, err } return &packetConn{ proto: proto, ifi: ifi, f: f, fd: fd, buflen: buflen, }, nil } // Maximum read timeout per syscall. // It is required because read/recvfrom won't be interrupted on closing of the file descriptor. const readTimeout = 200 * time.Millisecond // ReadFrom implements the net.PacketConn.ReadFrom method. func (p *packetConn) ReadFrom(b []byte) (int, net.Addr, error) { p.timeoutMu.Lock() deadline := p.rtimeout p.timeoutMu.Unlock() buf := make([]byte, p.buflen) var n int for { var timeout time.Duration if deadline.IsZero() { timeout = readTimeout } else { timeout = time.Until(deadline) if timeout > readTimeout { timeout = readTimeout } } tv := unix.NsecToTimeval(timeout.Nanoseconds()) if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(p.fd), syscall.BIOCSRTIMEOUT, uintptr(unsafe.Pointer(&tv))); err != 0 { return 0, nil, syscall.Errno(err) } // Attempt to receive on socket // The read sycall will NOT be interrupted by closing of the socket var err error n, err = syscall.Read(p.fd, buf) if err != nil { return n, nil, err } if n > 0 { break } } // TODO(mdlayher): consider parsing BPF header if it proves useful. // BPF header length depends on the platform this code is running on bpfl := bpfLen() // Retrieve source MAC address of ethernet header mac := make(net.HardwareAddr, 6) copy(mac, buf[bpfl+6:bpfl+12]) // Skip past BPF header to retrieve ethernet frame out := copy(b, buf[bpfl:bpfl+n]) return out, &Addr{ HardwareAddr: mac, }, nil } // WriteTo implements the net.PacketConn.WriteTo method. func (p *packetConn) WriteTo(b []byte, _ net.Addr) (int, error) { return syscall.Write(p.fd, b) } // Close closes the connection. func (p *packetConn) Close() error { return p.f.Close() } // LocalAddr returns the local network address. func (p *packetConn) LocalAddr() net.Addr { return &Addr{ HardwareAddr: p.ifi.HardwareAddr, } } // SetDeadline implements the net.PacketConn.SetDeadline method. func (p *packetConn) SetDeadline(t time.Time) error { return p.SetReadDeadline(t) } // SetReadDeadline implements the net.PacketConn.SetReadDeadline method. func (p *packetConn) SetReadDeadline(t time.Time) error { p.timeoutMu.Lock() p.rtimeout = t p.timeoutMu.Unlock() return nil } // SetWriteDeadline implements the net.PacketConn.SetWriteDeadline method. func (p *packetConn) SetWriteDeadline(t time.Time) error { return ErrNotImplemented } // SetBPF attaches an assembled BPF program to a raw net.PacketConn. func (p *packetConn) SetBPF(filter []bpf.RawInstruction) error { // Base filter filters traffic based on EtherType base, err := bpf.Assemble(baseFilter(p.proto)) if err != nil { return err } // Append user filter to base filter, translate to raw format, // and apply to BPF device return syscall.SetBpf(p.fd, assembleBpfInsn(append(base, filter...))) } // SetPromiscuous enables or disables promiscuous mode on the interface, allowing it // to receive traffic that is not addressed to the interface. func (p *packetConn) SetPromiscuous(b bool) error { m := 1 if !b { m = 0 } return syscall.SetBpfPromisc(p.fd, m) } // Stats retrieves statistics from the Conn. func (p *packetConn) Stats() (*Stats, error) { return nil, ErrNotImplemented } // configureBPF configures a BPF device with the specified file descriptor to // use the specified network and interface and protocol. func configureBPF(fd int, ifi *net.Interface, proto uint16, direction int) (int, error) { // Use specified interface with BPF device if err := syscall.SetBpfInterface(fd, ifi.Name); err != nil { return 0, err } // Inform BPF to send us its data immediately if err := syscall.SetBpfImmediate(fd, 1); err != nil { return 0, err } // Check buffer size of BPF device buflen, err := syscall.BpfBuflen(fd) if err != nil { return 0, err } // Do not automatically complete source address in ethernet headers if err := syscall.SetBpfHeadercmpl(fd, 1); err != nil { return 0, err } // Specify incoming only or bidirectional traffic using BPF device if err := setBPFDirection(fd, direction); err != nil { return 0, err } // Build and apply base BPF filter which checks for correct EtherType // on incoming packets prog, err := bpf.Assemble(baseInterfaceFilter(proto, ifi.MTU)) if err != nil { return 0, err } if err := syscall.SetBpf(fd, assembleBpfInsn(prog)); err != nil { return 0, err } // Flush any packets currently in the BPF device's buffer if err := syscall.FlushBpf(fd); err != nil { return 0, err } return buflen, nil } // assembleBpfInsn assembles a slice of bpf.RawInstructions to the format required by // package syscall. func assembleBpfInsn(filter []bpf.RawInstruction) []syscall.BpfInsn { // Copy each bpf.RawInstruction into syscall.BpfInsn. If needed, // the structures have the same memory layout and could probably be // unsafely cast to each other for speed. insns := make([]syscall.BpfInsn, 0, len(filter)) for _, ins := range filter { insns = append(insns, syscall.BpfInsn{ Code: ins.Op, Jt: ins.Jt, Jf: ins.Jf, K: ins.K, }) } return insns } // baseInterfaceFilter creates a base BPF filter which filters traffic based // on its EtherType and returns up to "mtu" bytes of data for processing. func baseInterfaceFilter(proto uint16, mtu int) []bpf.Instruction { return append( // Filter traffic based on EtherType baseFilter(proto), // Accept the packet bytes up to the interface's MTU bpf.RetConstant{ Val: uint32(mtu), }, ) } // baseFilter creates a base BPF filter which filters traffic based on its // EtherType. baseFilter can be prepended to other filters to handle common // filtering tasks. func baseFilter(proto uint16) []bpf.Instruction { // Offset | Length | Comment // ------------------------- // 00 | 06 | Ethernet destination MAC address // 06 | 06 | Ethernet source MAC address // 12 | 02 | Ethernet EtherType const ( etherTypeOffset = 12 etherTypeLength = 2 ) return []bpf.Instruction{ // Load EtherType value from Ethernet header bpf.LoadAbsolute{ Off: etherTypeOffset, Size: etherTypeLength, }, // If EtherType is equal to the protocol we are using, jump to instructions // added outside of this function. bpf.JumpIf{ Cond: bpf.JumpEqual, Val: uint32(proto), SkipTrue: 1, }, // EtherType does not match our protocol bpf.RetConstant{ Val: 0, }, } } golang-github-mdlayher-raw-0.1.0/raw_direction_bsd.go000066400000000000000000000012771434706123200226520ustar00rootroot00000000000000//go:build darwin || dragonfly || freebsd || netbsd // +build darwin dragonfly freebsd netbsd package raw import ( "syscall" "unsafe" ) // setBPFDirection enables filtering traffic traveling in a specific direction // using BPF, so that traffic sent by this package is not captured when reading // using this package. func setBPFDirection(fd int, direction int) error { _, _, err := syscall.Syscall( syscall.SYS_IOCTL, uintptr(fd), // Even though BIOCSDIRECTION is preferred on FreeBSD, BIOCSSEESENT continues // to work, and is required for other BSD platforms syscall.BIOCSSEESENT, uintptr(unsafe.Pointer(&direction)), ) if err != 0 { return syscall.Errno(err) } return nil } golang-github-mdlayher-raw-0.1.0/raw_direction_openbsd.go000066400000000000000000000012061434706123200235240ustar00rootroot00000000000000//go:build openbsd // +build openbsd package raw import ( "syscall" "unsafe" ) // setBPFDirection enables filtering traffic traveling in a specific direction // using BPF, so that traffic sent by this package is not captured when reading // using this package. func setBPFDirection(fd int, direction int) error { var dirfilt uint switch direction { case 0: // filter outbound dirfilt = syscall.BPF_DIRECTION_OUT default: // no filter } _, _, err := syscall.Syscall( syscall.SYS_IOCTL, uintptr(fd), syscall.BIOCSDIRFILT, uintptr(unsafe.Pointer(&dirfilt)), ) if err != 0 { return syscall.Errno(err) } return nil } golang-github-mdlayher-raw-0.1.0/raw_linux.go000066400000000000000000000071551434706123200212020ustar00rootroot00000000000000//go:build linux // +build linux package raw import ( "net" "sync/atomic" "time" "github.com/mdlayher/packet" "golang.org/x/net/bpf" "golang.org/x/sys/unix" ) // Must implement net.PacketConn at compile-time. var _ net.PacketConn = &packetConn{} // packetConn is the Linux-specific implementation of net.PacketConn for this // package. type packetConn struct { ifi *net.Interface c *packet.Conn // Should stats be accumulated instead of reset on each call? noCumulativeStats bool // Internal storage for cumulative stats. stats Stats } // listenPacket creates a net.PacketConn which can be used to send and receive // data at the device driver level. func listenPacket(ifi *net.Interface, proto uint16, cfg Config) (*packetConn, error) { typ := packet.Raw if cfg.LinuxSockDGRAM { typ = packet.Datagram } c, err := packet.Listen(ifi, typ, int(proto), &packet.Config{ // Propagate matching options. Filter: cfg.Filter, }) if err != nil { return nil, err } return &packetConn{ ifi: ifi, c: c, noCumulativeStats: cfg.NoCumulativeStats, }, nil } // ReadFrom implements the net.PacketConn.ReadFrom method. func (p *packetConn) ReadFrom(b []byte) (int, net.Addr, error) { n, addr, err := p.c.ReadFrom(b) if err != nil { return n, nil, err } raddr := &Addr{HardwareAddr: addr.(*packet.Addr).HardwareAddr} return n, raddr, nil } // WriteTo implements the net.PacketConn.WriteTo method. func (p *packetConn) WriteTo(b []byte, addr net.Addr) (int, error) { raddr, ok := addr.(*Addr) if !ok { return 0, unix.EINVAL } paddr := &packet.Addr{HardwareAddr: raddr.HardwareAddr} return p.c.WriteTo(b, paddr) } // Close closes the connection. func (p *packetConn) Close() error { return p.c.Close() } // LocalAddr returns the local network address. func (p *packetConn) LocalAddr() net.Addr { addr := p.c.LocalAddr().(*packet.Addr) return &Addr{ HardwareAddr: addr.HardwareAddr, } } // SetDeadline implements the net.PacketConn.SetDeadline method. func (p *packetConn) SetDeadline(t time.Time) error { return p.c.SetDeadline(t) } // SetReadDeadline implements the net.PacketConn.SetReadDeadline method. func (p *packetConn) SetReadDeadline(t time.Time) error { return p.c.SetReadDeadline(t) } // SetWriteDeadline implements the net.PacketConn.SetWriteDeadline method. func (p *packetConn) SetWriteDeadline(t time.Time) error { return p.c.SetWriteDeadline(t) } // SetBPF attaches an assembled BPF program to a raw net.PacketConn. func (p *packetConn) SetBPF(filter []bpf.RawInstruction) error { return p.c.SetBPF(filter) } // SetPromiscuous enables or disables promiscuous mode on the interface, allowing it // to receive traffic that is not addressed to the interface. func (p *packetConn) SetPromiscuous(enable bool) error { return p.c.SetPromiscuous(enable) } // Stats retrieves statistics from the Conn. func (p *packetConn) Stats() (*Stats, error) { stats, err := p.c.Stats() if err != nil { return nil, err } return p.handleStats(stats), nil } // handleStats handles creation of Stats structures from *packet.Stats. func (p *packetConn) handleStats(s *packet.Stats) *Stats { // Does the caller want instantaneous stats as provided by Linux? If so, // return the structure directly. if p.noCumulativeStats { return &Stats{ Packets: uint64(s.Packets), Drops: uint64(s.Drops), } } // The caller wants cumulative stats. Add stats with the internal stats // structure and return a copy of the resulting stats. packets := atomic.AddUint64(&p.stats.Packets, uint64(s.Packets)) drops := atomic.AddUint64(&p.stats.Drops, uint64(s.Drops)) return &Stats{ Packets: packets, Drops: drops, } } golang-github-mdlayher-raw-0.1.0/raw_linux_test.go000066400000000000000000000077121434706123200222400ustar00rootroot00000000000000//go:build go1.16 // +build go1.16 // Just because the library builds and runs on older versions of Go doesn't mean // we have to apply the same restrictions for tests. Go 1.16 is the oldest // upstream supported version of Go as of February 2022. package raw_test import ( "encoding/binary" "errors" "net" "os" "testing" "time" "github.com/mdlayher/raw" "golang.org/x/sys/unix" ) // These tests are effectively a copy/paste of // https://github.com/mdlayher/packet tests, because *raw.PacketConn is now // backed by *packet.Conn on Linux. // // This locks in the behaviors and verifies the same functionality for existing // users of package raw who may not ever migrate to the new package. func TestConnListenPacket(t *testing.T) { // Open a connection on an Ethernet interface and begin listening for // incoming Ethernet frames. We assume that this interface will receive some // sort of traffic in the next 30 seconds and we will capture that traffic // by looking for any EtherType value (ETH_P_ALL). c, ifi := testConn(t) t.Logf("interface: %q, MTU: %d", ifi.Name, ifi.MTU) if err := c.SetReadDeadline(time.Now().Add(30 * time.Second)); err != nil { t.Fatalf("failed to set read deadline: %v", err) } b := make([]byte, ifi.MTU) n, addr, err := c.ReadFrom(b) if err != nil { t.Fatalf("failed to read Ethernet frame: %v", err) } // Received some data, assume some Stats were populated. stats, err := c.Stats() if err != nil { t.Fatalf("failed to fetch stats: %v", err) } if stats.Packets == 0 { t.Fatal("stats indicated 0 received packets") } t.Logf(" - packets: %d, drops: %d", stats.Packets, stats.Drops) // TODO(mdlayher): we could import github.com/mdlayher/ethernet, but parsing // an Ethernet frame header is fairly easy and this keeps the go.mod tidy. // Need at least destination MAC, source MAC, and EtherType. const header = 6 + 6 + 2 if n < header { t.Fatalf("did not read a complete Ethernet frame from %v, only %d bytes read", addr, n) } // Parse the header to provide tidy log output. var ( dst = net.HardwareAddr(b[0:6]) src = net.HardwareAddr(b[6:12]) et = binary.BigEndian.Uint16(b[12:14]) ) // Check for the most likely EtherType values. var ets string switch et { case 0x0800: ets = "IPv4" case 0x0806: ets = "ARP" case 0x86dd: ets = "IPv6" default: ets = "unknown" } // And finally print what we found for the user. t.Log("Ethernet frame:") t.Logf(" - destination: %s", dst) t.Logf(" - source: %s", src) t.Logf(" - ethertype: %#04x (%s)", et, ets) t.Logf(" - payload: %d bytes", n-header) } // testConn produces a *raw.Conn bound to the returned *net.Interface. The // caller does not need to call Close on the *raw.Conn. func testConn(t *testing.T) (*raw.Conn, *net.Interface) { t.Helper() // TODO(mdlayher): probably parameterize the EtherType. ifi := testInterface(t) c, err := raw.ListenPacket(ifi, unix.ETH_P_ALL, nil) if err != nil { if errors.Is(err, os.ErrPermission) { t.Skipf("skipping, permission denied (try setting CAP_NET_RAW capability): %v", err) } t.Fatalf("failed to listen: %v", err) } t.Cleanup(func() { c.Close() }) return c, ifi } // testInterface looks for a suitable Ethernet interface to bind a *packet.Conn. func testInterface(t *testing.T) *net.Interface { ifis, err := net.Interfaces() if err != nil { t.Fatalf("failed to get network interfaces: %v", err) } if len(ifis) == 0 { t.Skip("skipping, no network interfaces found") } // Try to find a suitable network interface for tests. var tried []string for _, ifi := range ifis { tried = append(tried, ifi.Name) // true is used to line up other checks. ok := true && // Look for an Ethernet interface. len(ifi.HardwareAddr) == 6 && // Look for up, multicast, broadcast. ifi.Flags&(net.FlagUp|net.FlagMulticast|net.FlagBroadcast) != 0 if ok { return &ifi } } t.Skipf("skipping, could not find a usable network interface, tried: %s", tried) panic("unreachable") } golang-github-mdlayher-raw-0.1.0/raw_others.go000066400000000000000000000037601434706123200213450ustar00rootroot00000000000000//go:build !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd // +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd package raw import ( "net" "time" "golang.org/x/net/bpf" ) // Must implement net.PacketConn at compile-time. var _ net.PacketConn = &packetConn{} // packetConn is the generic implementation of net.PacketConn for this package. type packetConn struct{} // listenPacket is not currently implemented on this platform. func listenPacket(ifi *net.Interface, proto uint16, cfg Config) (*packetConn, error) { return nil, ErrNotImplemented } // ReadFrom is not currently implemented on this platform. func (p *packetConn) ReadFrom(b []byte) (int, net.Addr, error) { return 0, nil, ErrNotImplemented } // WriteTo is not currently implemented on this platform. func (p *packetConn) WriteTo(b []byte, addr net.Addr) (int, error) { return 0, ErrNotImplemented } // Close is not currently implemented on this platform. func (p *packetConn) Close() error { return ErrNotImplemented } // LocalAddr is not currently implemented on this platform. func (p *packetConn) LocalAddr() net.Addr { return nil } // SetDeadline is not currently implemented on this platform. func (p *packetConn) SetDeadline(t time.Time) error { return ErrNotImplemented } // SetReadDeadline is not currently implemented on this platform. func (p *packetConn) SetReadDeadline(t time.Time) error { return ErrNotImplemented } // SetWriteDeadline is not currently implemented on this platform. func (p *packetConn) SetWriteDeadline(t time.Time) error { return ErrNotImplemented } // SetBPF is not currently implemented on this platform. func (p *packetConn) SetBPF(filter []bpf.RawInstruction) error { return ErrNotImplemented } // SetPromisc is not currently implemented on this platform. func (p *packetConn) SetPromiscuous(enable bool) error { return ErrNotImplemented } // Stats is not currently implemented on this platform. func (p *packetConn) Stats() (*Stats, error) { return nil, ErrNotImplemented } golang-github-mdlayher-raw-0.1.0/staticcheck.conf000066400000000000000000000001351434706123200217660ustar00rootroot00000000000000# Do not flag deprecated syscall functions in BSD functionality. checks = ["all", "-SA1019"]