pax_global_header00006660000000000000000000000064136600212210014503gustar00rootroot0000000000000052 comment=d06efe66126b9bef93535867245ba9e4f40bad27 golang-github-mdlayher-raw-0.0~git20191009.50f2db8/000077500000000000000000000000001366002122100212645ustar00rootroot00000000000000golang-github-mdlayher-raw-0.0~git20191009.50f2db8/.builds/000077500000000000000000000000001366002122100226245ustar00rootroot00000000000000golang-github-mdlayher-raw-0.0~git20191009.50f2db8/.builds/freebsd.yml000066400000000000000000000005751366002122100247700ustar00rootroot00000000000000image: freebsd/latest packages: - go sources: - https://github.com/mdlayher/raw environment: GO111MODULE: "on" tasks: - build: | go version go get golang.org/x/lint/golint go get honnef.co/go/tools/cmd/staticcheck cd raw/ /home/build/go/bin/staticcheck ./... /home/build/go/bin/golint -set_exit_status ./... go test -v -race ./... golang-github-mdlayher-raw-0.0~git20191009.50f2db8/.builds/linux.yml000066400000000000000000000005701366002122100245100ustar00rootroot00000000000000image: archlinux packages: - go sources: - https://github.com/mdlayher/raw environment: GO111MODULE: "on" tasks: - build: | go version go get golang.org/x/lint/golint go get honnef.co/go/tools/cmd/staticcheck cd raw/ /home/build/go/bin/staticcheck ./... /home/build/go/bin/golint -set_exit_status ./... go test -v -race ./... golang-github-mdlayher-raw-0.0~git20191009.50f2db8/.builds/openbsd.yml000066400000000000000000000005641366002122100250060ustar00rootroot00000000000000image: openbsd/6.5 packages: - go sources: - https://github.com/mdlayher/raw environment: GO111MODULE: "on" tasks: - build: | go version go get golang.org/x/lint/golint go get honnef.co/go/tools/cmd/staticcheck cd raw/ /home/build/go/bin/staticcheck ./... /home/build/go/bin/golint -set_exit_status ./... go test -v ./... golang-github-mdlayher-raw-0.0~git20191009.50f2db8/LICENSE.md000066400000000000000000000020701366002122100226670ustar00rootroot00000000000000MIT License =========== Copyright (C) 2015 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.0~git20191009.50f2db8/README.md000066400000000000000000000027001366002122100225420ustar00rootroot00000000000000# raw [![builds.sr.ht status](https://builds.sr.ht/~mdlayher/raw.svg)](https://builds.sr.ht/~mdlayher/raw?) [![GoDoc](https://godoc.org/github.com/mdlayher/raw?status.svg)](https://godoc.org/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. For more information about using raw sockets with Ethernet frames in Go, check out my blog post: [Network Protocol Breakdown: Ethernet and Go](https://medium.com/@mdlayher/network-protocol-breakdown-ethernet-and-go-de985d726cc1). Portions of this code are taken from the Go standard library. The Go standard library is Copyright (c) 2012 The Go Authors. All rights reserved. The Go license can be found at https://golang.org/LICENSE. ## Stability At this time, package `raw` is in a pre-v1.0.0 state. Changes are being made which may impact the exported API of this package and others in its ecosystem. The general policy of this package is to only support the latest, stable version of Go. Compatibility shims may be added for prior versions of Go on an as-needed basis. If you would like to raise a concern, please [file an issue](https://github.com/mdlayher/raw/issues/new). **If you depend on this package in your applications, please vendor it or use Go modules when building your application.** golang-github-mdlayher-raw-0.0~git20191009.50f2db8/go.mod000066400000000000000000000003001366002122100223630ustar00rootroot00000000000000module github.com/mdlayher/raw go 1.12 require ( github.com/google/go-cmp v0.2.0 golang.org/x/net v0.0.0-20190419010253-1f3472d942ba golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be ) golang-github-mdlayher-raw-0.0~git20191009.50f2db8/go.sum000066400000000000000000000015561366002122100224260ustar00rootroot00000000000000github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20190419010253-1f3472d942ba h1:h0zCzEL5UW1mERvwTN6AXcc75PpLkY6OcReia6Dq1BM= golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be h1:mI+jhqkn68ybP0ORJqunXn+fq+Eeb4hHKqLQcFICjAc= golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang-github-mdlayher-raw-0.0~git20191009.50f2db8/raw.go000066400000000000000000000111401366002122100224010ustar00rootroot00000000000000// Package raw enables reading and writing data at the device driver level for // a network interface. 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.0~git20191009.50f2db8/raw_bsd.go000066400000000000000000000207321366002122100232400ustar00rootroot00000000000000// +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.0~git20191009.50f2db8/raw_direction_bsd.go000066400000000000000000000012131366002122100252710ustar00rootroot00000000000000// +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.0~git20191009.50f2db8/raw_direction_openbsd.go000066400000000000000000000011631366002122100261570ustar00rootroot00000000000000// +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.0~git20191009.50f2db8/raw_linux.go000066400000000000000000000242111366002122100236230ustar00rootroot00000000000000// +build linux package raw import ( "net" "os" "sync/atomic" "syscall" "time" "unsafe" "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 s socket pbe uint16 // Should stats be accumulated instead of reset on each call? noCumulativeStats bool // Internal storage for cumulative stats. stats Stats } // socket is an interface which enables swapping out socket syscalls for // testing. type socket interface { Bind(unix.Sockaddr) error Close() error GetSockoptTpacketStats(level, name int) (*unix.TpacketStats, error) Recvfrom([]byte, int) (int, unix.Sockaddr, error) Sendto([]byte, int, unix.Sockaddr) error SetSockoptPacketMreq(level, name int, mreq *unix.PacketMreq) error SetSockoptSockFprog(level, name int, fprog *unix.SockFprog) error SetDeadline(time.Time) error SetReadDeadline(time.Time) error SetWriteDeadline(time.Time) error } // htons converts a short (uint16) from host-to-network byte order. // Thanks to mikioh for this neat trick: // https://github.com/mikioh/-stdyng/blob/master/afpacket.go func htons(i uint16) uint16 { return (i<<8)&0xff00 | i>>8 } // 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) { filename := "eth-packet-socket" // Enabling overriding the socket type via config. typ := unix.SOCK_RAW if cfg.LinuxSockDGRAM { filename = "packet-socket" typ = unix.SOCK_DGRAM } // Open a packet socket using specified socket type. Do not specify // a protocol to avoid capturing packets which to not match cfg.Filter. // The later call to bind() will set up the correct protocol for us. sock, err := unix.Socket(unix.AF_PACKET, typ, 0) if err != nil { return nil, err } if err := unix.SetNonblock(sock, true); err != nil { return nil, err } // When using Go 1.12+, the SetNonblock call we just did puts the file // descriptor into non-blocking mode. In that case, os.NewFile // registers the file descriptor with the runtime poller, which is then // used for all subsequent operations. // // See also: https://golang.org/pkg/os/#NewFile f := os.NewFile(uintptr(sock), filename) sc, err := f.SyscallConn() if err != nil { return nil, err } // Wrap raw socket in socket interface. pc, err := newPacketConn(ifi, &sysSocket{f: f, rc: sc}, htons(proto), cfg.Filter) if err != nil { return nil, err } pc.noCumulativeStats = cfg.NoCumulativeStats return pc, nil } // newPacketConn creates a net.PacketConn using the specified network // interface, wrapped socket and big endian protocol number. // // It is the entry point for tests in this package. func newPacketConn(ifi *net.Interface, s socket, pbe uint16, filter []bpf.RawInstruction) (*packetConn, error) { pc := &packetConn{ ifi: ifi, s: s, pbe: pbe, } if len(filter) > 0 { if err := pc.SetBPF(filter); err != nil { return nil, err } } // Bind the packet socket to the interface specified by ifi // packet(7): // Only the sll_protocol and the sll_ifindex address fields are used for // purposes of binding. // This overrides the protocol given to socket(AF_PACKET). err := s.Bind(&unix.SockaddrLinklayer{ Protocol: pc.pbe, Ifindex: ifi.Index, }) if err != nil { return nil, err } return pc, nil } // ReadFrom implements the net.PacketConn.ReadFrom method. func (p *packetConn) ReadFrom(b []byte) (int, net.Addr, error) { // Attempt to receive on socket n, addr, err := p.s.Recvfrom(b, 0) if err != nil { return n, nil, err } // Retrieve hardware address and other information from addr. sa, ok := addr.(*unix.SockaddrLinklayer) if !ok { return n, nil, unix.EINVAL } // Use length specified to convert byte array into a hardware address slice. mac := make(net.HardwareAddr, sa.Halen) copy(mac, sa.Addr[:]) // packet(7): // sll_hatype and sll_pkttype are set on received packets for your // information. // TODO(mdlayher): determine if similar fields exist and are useful on // non-Linux platforms return n, &Addr{ HardwareAddr: mac, }, nil } // WriteTo implements the net.PacketConn.WriteTo method. func (p *packetConn) WriteTo(b []byte, addr net.Addr) (int, error) { // Ensure correct Addr type. a, ok := addr.(*Addr) if !ok || a.HardwareAddr == nil { return 0, unix.EINVAL } // Convert hardware address back to byte array form. var baddr [8]byte copy(baddr[:], a.HardwareAddr) // Send message on socket to the specified hardware address from addr // packet(7): // When you send packets it is enough to specify sll_family, sll_addr, // sll_halen, sll_ifindex, and sll_protocol. The other fields should // be 0. // In this case, sll_family is taken care of automatically by unix. err := p.s.Sendto(b, 0, &unix.SockaddrLinklayer{ Ifindex: p.ifi.Index, Halen: uint8(len(a.HardwareAddr)), Addr: baddr, Protocol: p.pbe, }) return len(b), err } // Close closes the connection. func (p *packetConn) Close() error { return p.s.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.s.SetDeadline(t) } // SetReadDeadline implements the net.PacketConn.SetReadDeadline method. func (p *packetConn) SetReadDeadline(t time.Time) error { return p.s.SetReadDeadline(t) } // SetWriteDeadline implements the net.PacketConn.SetWriteDeadline method. func (p *packetConn) SetWriteDeadline(t time.Time) error { return p.s.SetWriteDeadline(t) } // SetBPF attaches an assembled BPF program to a raw net.PacketConn. func (p *packetConn) SetBPF(filter []bpf.RawInstruction) error { prog := unix.SockFprog{ Len: uint16(len(filter)), Filter: (*unix.SockFilter)(unsafe.Pointer(&filter[0])), } err := p.s.SetSockoptSockFprog( unix.SOL_SOCKET, unix.SO_ATTACH_FILTER, &prog, ) if err != nil { return os.NewSyscallError("setsockopt", err) } return nil } // 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 { mreq := unix.PacketMreq{ Ifindex: int32(p.ifi.Index), Type: unix.PACKET_MR_PROMISC, } membership := unix.PACKET_ADD_MEMBERSHIP if !b { membership = unix.PACKET_DROP_MEMBERSHIP } return p.s.SetSockoptPacketMreq(unix.SOL_PACKET, membership, &mreq) } // Stats retrieves statistics from the Conn. func (p *packetConn) Stats() (*Stats, error) { stats, err := p.s.GetSockoptTpacketStats(unix.SOL_PACKET, unix.PACKET_STATISTICS) if err != nil { return nil, err } return p.handleStats(stats), nil } // handleStats handles creation of Stats structures from raw packet socket stats. func (p *packetConn) handleStats(s *unix.TpacketStats) *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, } } // sysSocket is the default socket implementation. It makes use of // Linux-specific system calls to handle raw socket functionality. type sysSocket struct { f *os.File rc syscall.RawConn } func (s *sysSocket) SetDeadline(t time.Time) error { return s.f.SetDeadline(t) } func (s *sysSocket) SetReadDeadline(t time.Time) error { return s.f.SetReadDeadline(t) } func (s *sysSocket) SetWriteDeadline(t time.Time) error { return s.f.SetWriteDeadline(t) } func (s *sysSocket) Bind(sa unix.Sockaddr) error { var err error cerr := s.rc.Control(func(fd uintptr) { err = unix.Bind(int(fd), sa) }) if err != nil { return err } return cerr } func (s *sysSocket) Close() error { return s.f.Close() } func (s *sysSocket) GetSockoptTpacketStats(level, name int) (*unix.TpacketStats, error) { var stats *unix.TpacketStats var err error cerr := s.rc.Control(func(fd uintptr) { s, errno := unix.GetsockoptTpacketStats(int(fd), level, name) stats = s if errno != nil { err = os.NewSyscallError("getsockopt", errno) } }) if err != nil { return stats, err } return stats, cerr } func (s *sysSocket) Recvfrom(p []byte, flags int) (n int, addr unix.Sockaddr, err error) { cerr := s.rc.Read(func(fd uintptr) bool { n, addr, err = unix.Recvfrom(int(fd), p, flags) // When the socket is in non-blocking mode, we might see EAGAIN // and end up here. In that case, return false to let the // poller wait for readiness. See the source code for // internal/poll.FD.RawRead for more details. // // If the socket is in blocking mode, EAGAIN should never occur. return err != unix.EAGAIN }) if err != nil { return n, addr, err } return n, addr, cerr } func (s *sysSocket) Sendto(p []byte, flags int, to unix.Sockaddr) error { var err error cerr := s.rc.Write(func(fd uintptr) bool { err = unix.Sendto(int(fd), p, flags, to) // See comment in Recvfrom. return err != unix.EAGAIN }) if err != nil { return err } return cerr } func (s *sysSocket) SetSockoptSockFprog(level, name int, fprog *unix.SockFprog) error { var err error cerr := s.rc.Control(func(fd uintptr) { errno := unix.SetsockoptSockFprog(int(fd), level, name, fprog) if errno != nil { err = os.NewSyscallError("setsockopt", errno) } }) if err != nil { return err } return cerr } func (s *sysSocket) SetSockoptPacketMreq(level, name int, mreq *unix.PacketMreq) error { var err error cerr := s.rc.Control(func(fd uintptr) { errno := unix.SetsockoptPacketMreq(int(fd), level, name, mreq) if errno != nil { err = os.NewSyscallError("setsockopt", errno) } }) if err != nil { return err } return cerr } golang-github-mdlayher-raw-0.0~git20191009.50f2db8/raw_linux_test.go000066400000000000000000000240151366002122100246640ustar00rootroot00000000000000// +build linux package raw import ( "bytes" "net" "testing" "time" "github.com/google/go-cmp/cmp" "golang.org/x/net/bpf" "golang.org/x/sys/unix" ) // Test to ensure that socket is bound with correct sockaddr_ll information type bindSocket struct { bind unix.Sockaddr noopSocket } func (s *bindSocket) Bind(sa unix.Sockaddr) error { s.bind = sa return nil } func Test_newPacketConnBind(t *testing.T) { s := &bindSocket{} ifIndex := 1 protocol := uint16(1) _, err := newPacketConn( &net.Interface{ Index: ifIndex, }, s, protocol, nil, ) if err != nil { t.Fatal(err) } sall, ok := s.bind.(*unix.SockaddrLinklayer) if !ok { t.Fatalf("bind sockaddr has incorrect type: %T", s.bind) } if want, got := ifIndex, sall.Ifindex; want != got { t.Fatalf("unexpected network interface index:\n- want: %v\n- got: %v", want, got) } if want, got := protocol, sall.Protocol; want != got { t.Fatalf("unexpected protocol:\n- want: %v\n- got: %v", want, got) } } // Test for incorrect sockaddr type after recvfrom on a socket. type addrRecvfromSocket struct { addr unix.Sockaddr noopSocket } func (s *addrRecvfromSocket) Recvfrom(p []byte, flags int) (int, unix.Sockaddr, error) { return 0, s.addr, nil } func Test_packetConnReadFromRecvfromInvalidSockaddr(t *testing.T) { p, err := newPacketConn( &net.Interface{}, &addrRecvfromSocket{ addr: &unix.SockaddrInet4{}, }, 0, nil, ) if err != nil { t.Fatal(err) } _, _, err = p.ReadFrom(nil) if want, got := unix.EINVAL, err; want != got { t.Fatalf("unexpected error:\n- want: %v\n- got: %v", want, got) } } // Test for malformed hardware address after recvfrom on a socket func Test_packetConnReadFromRecvfromInvalidHardwareAddr(t *testing.T) { p, err := newPacketConn( &net.Interface{}, &addrRecvfromSocket{ addr: nil, }, 0, nil, ) if err != nil { t.Fatal(err) } _, _, err = p.ReadFrom(nil) if want, got := unix.EINVAL, err; want != got { t.Fatalf("unexpected error:\n- want: %v\n- got: %v", want, got) } } // Test for a correct ReadFrom with data and address. type recvfromSocket struct { p []byte flags int addr unix.Sockaddr noopSocket } func (s *recvfromSocket) Recvfrom(p []byte, flags int) (int, unix.Sockaddr, error) { copy(p, s.p) s.flags = flags return len(s.p), s.addr, nil } func Test_packetConnReadFromRecvfromOK(t *testing.T) { const wantN = 4 data := []byte{0, 1, 2, 3} deadbeefHW := net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad} s := &recvfromSocket{ p: data, addr: &unix.SockaddrLinklayer{ Halen: 6, Addr: [8]byte{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0x00, 0x00}, }, } p, err := newPacketConn( &net.Interface{}, s, 0, nil, ) if err != nil { t.Fatal(err) } buf := make([]byte, 8) n, addr, err := p.ReadFrom(buf) if err != nil { t.Fatal(err) } if want, got := 0, s.flags; want != got { t.Fatalf("unexpected flags:\n- want: %v\n- got: %v", want, got) } raddr, ok := addr.(*Addr) if !ok { t.Fatalf("read sockaddr has incorrect type: %T", addr) } if want, got := deadbeefHW, raddr.HardwareAddr; !bytes.Equal(want, got) { t.Fatalf("unexpected hardware address:\n- want: %v\n- got: %v", want, got) } if want, got := wantN, n; want != got { t.Fatalf("unexpected data length:\n- want: %v\n- got: %v", want, got) } if want, got := data, buf[:n]; !bytes.Equal(want, got) { t.Fatalf("unexpected data:\n- want: %v\n- got: %v", want, got) } } // Test for incorrect sockaddr type for WriteTo. func Test_packetConnWriteToInvalidSockaddr(t *testing.T) { _, err := (&packetConn{}).WriteTo(nil, &net.IPAddr{}) if want, got := unix.EINVAL, err; want != got { t.Fatalf("unexpected error:\n- want: %v\n- got: %v", want, got) } } // Test for malformed hardware address with WriteTo. func Test_packetConnWriteToInvalidHardwareAddr(t *testing.T) { addrs := []net.HardwareAddr{ // Explicitly nil. nil, } for _, addr := range addrs { _, err := (&packetConn{}).WriteTo(nil, &Addr{ HardwareAddr: addr, }) if want, got := unix.EINVAL, err; want != got { t.Fatalf("unexpected error:\n- want: %v\n- got: %v", want, got) } } } // Test for a correct WriteTo with data and address. type sendtoSocket struct { p []byte flags int addr unix.Sockaddr noopSocket } func (s *sendtoSocket) Sendto(p []byte, flags int, to unix.Sockaddr) error { copy(s.p, p) s.flags = flags s.addr = to return nil } func Test_packetConnWriteToSendtoOK(t *testing.T) { const wantN = 4 data := []byte{0, 1, 2, 3} deadbeefHW := net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad} s := &sendtoSocket{ p: make([]byte, wantN), } p, err := newPacketConn( &net.Interface{}, s, 0, nil, ) if err != nil { t.Fatal(err) } n, err := p.WriteTo(data, &Addr{ HardwareAddr: deadbeefHW, }) if err != nil { t.Fatal(err) } if want, got := 0, s.flags; want != got { t.Fatalf("unexpected flags:\n- want: %v\n- got: %v", want, got) } if want, got := wantN, n; want != got { t.Fatalf("unexpected data length:\n- want: %v\n- got: %v", want, got) } if want, got := data, s.p; !bytes.Equal(want, got) { t.Fatalf("unexpected data:\n- want: %v\n- got: %v", want, got) } sall, ok := s.addr.(*unix.SockaddrLinklayer) if !ok { t.Fatalf("write sockaddr has incorrect type: %T", s.addr) } if want, got := deadbeefHW, sall.Addr[:][:sall.Halen]; !bytes.Equal(want, got) { t.Fatalf("unexpected hardware address:\n- want: %v\n- got: %v", want, got) } } // Test that socket close functions as intended. type captureCloseSocket struct { closed bool noopSocket } func (s *captureCloseSocket) Close() error { s.closed = true return nil } func Test_packetConnClose(t *testing.T) { s := &captureCloseSocket{} p := &packetConn{ s: s, } if err := p.Close(); err != nil { t.Fatal(err) } if !s.closed { t.Fatalf("socket should be closed, but is not") } } // Test that LocalAddr returns the hardware address of the network interface // which is being used by the socket. func Test_packetConnLocalAddr(t *testing.T) { deadbeefHW := net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad} p := &packetConn{ ifi: &net.Interface{ HardwareAddr: deadbeefHW, }, } if want, got := deadbeefHW, p.LocalAddr().(*Addr).HardwareAddr; !bytes.Equal(want, got) { t.Fatalf("unexpected hardware address:\n- want: %v\n- got: %v", want, got) } } // Test that BPF filter attachment works as intended. type setSockoptSocket struct { setsockoptSockFprog func(level, name int, fprog *unix.SockFprog) error noopSocket } func (s *setSockoptSocket) SetSockoptSockFprog(level, name int, fprog *unix.SockFprog) error { return s.setsockoptSockFprog(level, name, fprog) } func Test_packetConnSetBPF(t *testing.T) { filter, err := bpf.Assemble([]bpf.Instruction{ bpf.RetConstant{Val: 0}, }) if err != nil { t.Fatalf("failed to assemble filter: %v", err) } count := 0 fn := func(level, name int, _ *unix.SockFprog) error { // Though we can't check the filter itself, we can check the setsockopt // level and name for correctness. if want, got := unix.SOL_SOCKET, level; want != got { t.Fatalf("unexpected setsockopt level:\n- want: %v\n- got: %v", want, got) } if want, got := unix.SO_ATTACH_FILTER, name; want != got { t.Fatalf("unexpected setsockopt name:\n- want: %v\n- got: %v", want, got) } count++ return nil } s := &setSockoptSocket{ setsockoptSockFprog: fn, } p, err := newPacketConn(&net.Interface{}, s, 1, filter) if err != nil { t.Fatalf("failed to create connection with filter: %v", err) } if count != 1 { t.Fatal("creating a socket with filter didn't install it") } if err := p.SetBPF(filter); err != nil { t.Fatalf("failed to attach filter: %v", err) } if count != 2 { t.Fatal("creating a socket with filter didn't install it") } } func Test_packetConn_handleStats(t *testing.T) { tests := []struct { name string noCumulative bool stats []unix.TpacketStats out []Stats }{ { name: "no cumulative", noCumulative: true, stats: []unix.TpacketStats{ // Expect these exact outputs. {Packets: 1, Drops: 1}, {Packets: 2, Drops: 2}, }, out: []Stats{ {Packets: 1, Drops: 1}, {Packets: 2, Drops: 2}, }, }, { name: "cumulative", stats: []unix.TpacketStats{ // Expect accumulation of structures. {Packets: 1, Drops: 1}, {Packets: 2, Drops: 2}, }, out: []Stats{ {Packets: 1, Drops: 1}, {Packets: 3, Drops: 3}, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { p := &packetConn{noCumulativeStats: tt.noCumulative} if diff := cmp.Diff(len(tt.stats), len(tt.out)); diff != "" { t.Fatalf("unexpected number of test cases (-want +got):\n%s", diff) } for i := 0; i < len(tt.stats); i++ { out := *p.handleStats(&tt.stats[i]) if diff := cmp.Diff(tt.out[i], out); diff != "" { t.Fatalf("unexpected Stats[%02d] (-want +got):\n%s", i, diff) } } }) } } // noopSocket is a socket implementation which noops every operation. It is // the basis for more specific socket implementations. type noopSocket struct{} func (noopSocket) Bind(sa unix.Sockaddr) error { return nil } func (noopSocket) Close() error { return nil } func (noopSocket) GetSockoptTpacketStats(level, name int) (*unix.TpacketStats, error) { return nil, nil } func (noopSocket) Recvfrom(p []byte, flags int) (int, unix.Sockaddr, error) { return 0, nil, nil } func (noopSocket) Sendto(p []byte, flags int, to unix.Sockaddr) error { return nil } func (noopSocket) SetSockoptPacketMreq(level, name int, mreq *unix.PacketMreq) error { return nil } func (noopSocket) SetSockoptSockFprog(level, name int, fprog *unix.SockFprog) error { return nil } func (noopSocket) SetDeadline(timeout time.Time) error { return nil } func (noopSocket) SetReadDeadline(timeout time.Time) error { return nil } func (noopSocket) SetWriteDeadline(timeout time.Time) error { return nil } golang-github-mdlayher-raw-0.0~git20191009.50f2db8/raw_others.go000066400000000000000000000036351366002122100237770ustar00rootroot00000000000000// +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(b 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.0~git20191009.50f2db8/staticcheck.conf000066400000000000000000000001351366002122100244170ustar00rootroot00000000000000# Do not flag deprecated syscall functions in BSD functionality. checks = ["all", "-SA1019"]