From 35c1a1f38d6b28d51f00fd5dbf7777475c89aa54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20Soul=C3=A9?= Date: Fri, 26 Apr 2024 16:59:20 +0200 Subject: [PATCH] Add (*afpacket.TPacket).SetPromiscuous MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #564. Signed-off-by: Maxime Soulé --- afpacket/afpacket.go | 45 ++++++++++++++++++++++++++++++++------------ afpacket/options.go | 2 ++ 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/afpacket/afpacket.go b/afpacket/afpacket.go index 6600ae25e..f7921dd66 100644 --- a/afpacket/afpacket.go +++ b/afpacket/afpacket.go @@ -135,20 +135,20 @@ type TPacket struct { var _ gopacket.ZeroCopyPacketDataSource = &TPacket{} -// bindToInterface binds the TPacket socket to a particular named interface. -func (h *TPacket) bindToInterface(ifaceName string) error { - ifIndex := 0 +// bindToInterface binds the TPacket socket to the configured interface. +func (h *TPacket) bindToInterface() error { + h.opts.ifaceIdx = 0 // An empty string here means to listen to all interfaces - if ifaceName != "" { - iface, err := net.InterfaceByName(ifaceName) + if h.opts.iface != "" { + iface, err := net.InterfaceByName(h.opts.iface) if err != nil { return fmt.Errorf("InterfaceByName: %v", err) } - ifIndex = iface.Index + h.opts.ifaceIdx = iface.Index } s := &unix.SockaddrLinklayer{ Protocol: htons(uint16(unix.ETH_P_ALL)), - Ifindex: ifIndex, + Ifindex: h.opts.ifaceIdx, } return unix.Bind(h.fd, s) } @@ -242,7 +242,7 @@ func NewTPacket(opts ...interface{}) (h *TPacket, err error) { return nil, err } h.fd = fd - if err = h.bindToInterface(h.opts.iface); err != nil { + if err = h.bindToInterface(); err != nil { goto errlbl } if err = h.setRequestedTPacketVersion(); err != nil { @@ -279,6 +279,26 @@ func (h *TPacket) SetEBPF(progFd int32) error { return setsockopt(h.fd, unix.SOL_SOCKET, unix.SO_ATTACH_BPF, unsafe.Pointer(&progFd), 4) } +// SetPromiscuous sets promiscous mode to the required value. If it is +// enabled, traffic not destined for the interface will also be +// captured. +// +// Non empty OptInterface options is required, otherwise an error is returned. +func (h *TPacket) SetPromiscuous(on bool) error { + if h.opts.ifaceIdx == 0 { + return errors.New("SetPromiscuous needs non empty OptInterface option") + } + mreq := unix.PacketMreq{ + Ifindex: int32(h.opts.ifaceIdx), + Type: unix.PACKET_MR_PROMISC, + } + opt := unix.PACKET_DROP_MEMBERSHIP + if on { + opt = unix.PACKET_ADD_MEMBERSHIP + } + return unix.SetsockoptPacketMreq(h.fd, unix.SOL_PACKET, opt, &mreq) +} + func (h *TPacket) releaseCurrentPacket() error { h.current.clearStatus() h.offset++ @@ -293,10 +313,11 @@ func (h *TPacket) releaseCurrentPacket() error { // to old bytes when using ZeroCopyReadPacketData... if you need to keep data past // the next time you call ZeroCopyReadPacketData, use ReadPacketData, which copies // the bytes into a new buffer for you. -// tp, _ := NewTPacket(...) -// data1, _, _ := tp.ZeroCopyReadPacketData() -// // do everything you want with data1 here, copying bytes out of it if you'd like to keep them around. -// data2, _, _ := tp.ZeroCopyReadPacketData() // invalidates bytes in data1 +// +// tp, _ := NewTPacket(...) +// data1, _, _ := tp.ZeroCopyReadPacketData() +// // do everything you want with data1 here, copying bytes out of it if you'd like to keep them around. +// data2, _, _ := tp.ZeroCopyReadPacketData() // invalidates bytes in data1 func (h *TPacket) ZeroCopyReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) { h.mu.Lock() retry: diff --git a/afpacket/options.go b/afpacket/options.go index 3e305c4d6..44cea8db3 100644 --- a/afpacket/options.go +++ b/afpacket/options.go @@ -4,6 +4,7 @@ // that can be found in the LICENSE file in the root of the source // tree. +//go:build linux // +build linux package afpacket @@ -126,6 +127,7 @@ type options struct { version OptTPacketVersion socktype OptSocketType iface string + ifaceIdx int } var defaultOpts = options{