From 0f580cbbbd82a92519a8eb9dbaaef6a7ef4726d2 Mon Sep 17 00:00:00 2001 From: Kioubit Date: Mon, 27 Dec 2021 07:58:10 -0500 Subject: [PATCH] Small code optimizations --- pndp/NDPRequest.go | 14 +++--- pndp/interface.go | 58 +++++++++++++++++++++++++ pndp/{rawsocket.go => listener.go} | 66 +++------------------------- pndp/packet.go | 10 +---- pndp/process.go | 6 ++- pndp/responder.go | 69 ++++++++++++++++++------------ 6 files changed, 116 insertions(+), 107 deletions(-) create mode 100644 pndp/interface.go rename pndp/{rawsocket.go => listener.go} (66%) diff --git a/pndp/NDPRequest.go b/pndp/NDPRequest.go index 05f8c2c..7406aad 100644 --- a/pndp/NDPRequest.go +++ b/pndp/NDPRequest.go @@ -8,14 +8,12 @@ const ( ) type ndpRequest struct { - requestType ndpType - srcIP []byte - answeringForIP []byte - dstIP []byte - mac []byte - receivedIfaceMac []byte - sourceIface string - rawPacket []byte + requestType ndpType + srcIP []byte + answeringForIP []byte + dstIP []byte + sourceIface string + rawPacket []byte } type ndpQuestion struct { diff --git a/pndp/interface.go b/pndp/interface.go new file mode 100644 index 0000000..f0a2391 --- /dev/null +++ b/pndp/interface.go @@ -0,0 +1,58 @@ +package pndp + +import ( + "golang.org/x/net/bpf" + "golang.org/x/sys/unix" + "syscall" + "unsafe" +) + +// bpfFilter represents a classic BPF filter program that can be applied to a socket +type bpfFilter []bpf.Instruction + +// ApplyTo applies the current filter onto the provided file descriptor +func (filter bpfFilter) ApplyTo(fd int) (err error) { + var assembled []bpf.RawInstruction + if assembled, err = bpf.Assemble(filter); err != nil { + return err + } + + var program = unix.SockFprog{ + Len: uint16(len(assembled)), + Filter: (*unix.SockFilter)(unsafe.Pointer(&assembled[0])), + } + var b = (*[unix.SizeofSockFprog]byte)(unsafe.Pointer(&program))[:unix.SizeofSockFprog] + + if _, _, errno := syscall.Syscall6(syscall.SYS_SETSOCKOPT, + uintptr(fd), uintptr(syscall.SOL_SOCKET), uintptr(syscall.SO_ATTACH_FILTER), + uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), 0); errno != 0 { + return errno + } + + return nil +} + +type iflags struct { + name [syscall.IFNAMSIZ]byte + flags uint16 +} + +func setAllMulti(fd int, iface string, enable bool) { + var ifl iflags + copy(ifl.name[:], []byte(iface)) + _, _, ep := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.SIOCGIFFLAGS, uintptr(unsafe.Pointer(&ifl))) + if ep != 0 { + panic(ep) + } + + if enable { + ifl.flags |= uint16(syscall.IFF_ALLMULTI) + } else { + ifl.flags &^= uint16(syscall.IFF_ALLMULTI) + } + + _, _, ep = syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.SIOCSIFFLAGS, uintptr(unsafe.Pointer(&ifl))) + if ep != 0 { + panic(ep) + } +} diff --git a/pndp/rawsocket.go b/pndp/listener.go similarity index 66% rename from pndp/rawsocket.go rename to pndp/listener.go index 00bdb43..bd33219 100644 --- a/pndp/rawsocket.go +++ b/pndp/listener.go @@ -4,38 +4,11 @@ import ( "bytes" "fmt" "golang.org/x/net/bpf" - "golang.org/x/sys/unix" "net" "sync" "syscall" - "unsafe" ) -// bpfFilter represents a classic BPF filter program that can be applied to a socket -type bpfFilter []bpf.Instruction - -// ApplyTo applies the current filter onto the provided file descriptor -func (filter bpfFilter) ApplyTo(fd int) (err error) { - var assembled []bpf.RawInstruction - if assembled, err = bpf.Assemble(filter); err != nil { - return err - } - - var program = unix.SockFprog{ - Len: uint16(len(assembled)), - Filter: (*unix.SockFilter)(unsafe.Pointer(&assembled[0])), - } - var b = (*[unix.SizeofSockFprog]byte)(unsafe.Pointer(&program))[:unix.SizeofSockFprog] - - if _, _, errno := syscall.Syscall6(syscall.SYS_SETSOCKOPT, - uintptr(fd), uintptr(syscall.SOL_SOCKET), uintptr(syscall.SO_ATTACH_FILTER), - uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), 0); errno != 0 { - return errno - } - - return nil -} - // Htons Convert a uint16 to host byte order (big endian) func htons(v uint16) int { return int((v << 8) | (v >> 8)) @@ -155,39 +128,12 @@ func listen(iface string, responder chan *ndpRequest, requestType ndpType, stopW } responder <- &ndpRequest{ - requestType: requestType, - srcIP: buf[22:38], - dstIP: buf[38:54], - answeringForIP: buf[62:78], - mac: buf[80:86], - receivedIfaceMac: niface.HardwareAddr, - sourceIface: iface, - rawPacket: buf[:numRead], + requestType: requestType, + srcIP: buf[22:38], + dstIP: buf[38:54], + answeringForIP: buf[62:78], + sourceIface: iface, + rawPacket: buf[:numRead], } } } - -type iflags struct { - name [syscall.IFNAMSIZ]byte - flags uint16 -} - -func setAllMulti(fd int, iface string, enable bool) { - var ifl iflags - copy(ifl.name[:], []byte(iface)) - _, _, ep := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.SIOCGIFFLAGS, uintptr(unsafe.Pointer(&ifl))) - if ep != 0 { - panic(ep) - } - - if enable { - ifl.flags |= uint16(syscall.IFF_ALLMULTI) - } else { - ifl.flags &^= uint16(syscall.IFF_ALLMULTI) - } - - _, _, ep = syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.SIOCSIFFLAGS, uintptr(unsafe.Pointer(&ifl))) - if ep != 0 { - panic(ep) - } -} diff --git a/pndp/packet.go b/pndp/packet.go index 5854c2a..1a686cf 100644 --- a/pndp/packet.go +++ b/pndp/packet.go @@ -144,12 +144,7 @@ func checksumAddition(b []byte) uint32 { return sum } -func checkPacketChecksum(scrip, dstip, payload []byte) bool { - v6, err := newIpv6Header(scrip, dstip) - if err != nil { - return false - } - +func checkPacketChecksum(v6 *ipv6Header, payload []byte) bool { packetsum := make([]byte, 2) copy(packetsum, payload[2:4]) @@ -163,9 +158,6 @@ func checkPacketChecksum(scrip, dstip, payload []byte) bool { bChecksum := make([]byte, 2) binary.BigEndian.PutUint16(bChecksum, calculateChecksum(v6, payload)) if bytes.Equal(packetsum, bChecksum) { - if GlobalDebug { - fmt.Println("Verified received packet checksum") - } return true } else { if GlobalDebug { diff --git a/pndp/process.go b/pndp/process.go index 47a2dad..c6f057f 100644 --- a/pndp/process.go +++ b/pndp/process.go @@ -35,7 +35,9 @@ type ProxyObj struct { // With the optional autosenseInterface argument, the whitelist is configured based on the addresses assigned to the interface specified. This works even if the IP addresses change frequently. // Start() must be called on the object to actually start responding func NewResponder(iface string, filter []*net.IPNet, autosenseInterface string) *ResponderObj { - fmt.Println("WARNING: You should use a whitelist for the responder unless you really know what you are doing") + if filter == nil { + fmt.Println("WARNING: You should use a whitelist for the responder unless you really know what you are doing") + } var s sync.WaitGroup return &ResponderObj{ stopChan: make(chan struct{}), @@ -146,7 +148,7 @@ func (obj *ProxyObj) Stop() bool { } } -// ParseFilter Helper Function to Parse a string of CIDRs separated by a semicolon as a Whitelist for SimpleRespond +// ParseFilter Helper Function to Parse a string of CIDRs separated by a semicolon as a Whitelist func ParseFilter(f string) []*net.IPNet { if f == "" { return nil diff --git a/pndp/responder.go b/pndp/responder.go index ecf995d..e6e90e0 100644 --- a/pndp/responder.go +++ b/pndp/responder.go @@ -9,9 +9,13 @@ import ( ) func respond(iface string, requests chan *ndpRequest, respondType ndpType, ndpQuestionChan chan *ndpQuestion, filter []*net.IPNet, autoSense string, stopWG *sync.WaitGroup, stopChan chan struct{}) { - var ndpQuestionsList = make([]*ndpQuestion, 0, 40) stopWG.Add(1) defer stopWG.Done() + + var ndpQuestionsList = make([]*ndpQuestion, 0, 40) + var _, linkLocalSpace, _ = net.ParseCIDR("fe80::/10") + var _, ulaSpace, _ = net.ParseCIDR("fc00::/7") + fd, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_RAW, syscall.IPPROTO_RAW) if err != nil { panic(err) @@ -24,13 +28,13 @@ func respond(iface string, requests chan *ndpRequest, respondType ndpType, ndpQu panic(err) } - nIface, err := net.InterfaceByName(iface) + respondIface, err := net.InterfaceByName(iface) if err != nil { panic(err.Error()) } var result = emptyIpv6 - ifaceaddrs, err := nIface.Addrs() + ifaceaddrs, err := respondIface.Addrs() for _, n := range ifaceaddrs { tip, _, err := net.ParseCIDR(n.String()) @@ -42,8 +46,8 @@ func respond(iface string, requests chan *ndpRequest, respondType ndpType, ndpQu if tip.IsGlobalUnicast() { haveUla = true result = tip - _, tnet, _ := net.ParseCIDR("fc00::/7") - if !tnet.Contains(tip) { + + if !ulaSpace.Contains(tip) { break } } else if tip.IsLinkLocalUnicast() && !haveUla { @@ -53,15 +57,15 @@ func respond(iface string, requests chan *ndpRequest, respondType ndpType, ndpQu } for { - var n *ndpRequest + var req *ndpRequest if (ndpQuestionChan == nil && respondType == ndp_ADV) || (ndpQuestionChan != nil && respondType == ndp_SOL) { select { case <-stopChan: return - case n = <-requests: + case req = <-requests: } } else { - // THis is if ndpQuestionChan != nil && respondType == ndp_ADV + // This is if ndpQuestionChan != nil && respondType == ndp_ADV select { case <-stopChan: return @@ -69,33 +73,32 @@ func respond(iface string, requests chan *ndpRequest, respondType ndpType, ndpQu ndpQuestionsList = append(ndpQuestionsList, q) ndpQuestionsList = cleanupQuestionList(ndpQuestionsList) continue - case n = <-requests: + case req = <-requests: } } - var _, LinkLocalSpace, _ = net.ParseCIDR("fe80::/10") - if LinkLocalSpace.Contains(n.answeringForIP) { + if linkLocalSpace.Contains(req.answeringForIP) { if GlobalDebug { fmt.Println("Dropping packet asking for a link-local IP") } continue } - if n.requestType == ndp_ADV { - if (n.rawPacket[78] != 0x02) || (n.rawPacket[79] != 0x01) { + if req.requestType == ndp_ADV { + if (req.rawPacket[78] != 0x02) || (req.rawPacket[79] != 0x01) { if GlobalDebug { fmt.Println("Dropping Advertisement packet without target Source address set") } continue } - if n.rawPacket[58] == 0x0 { + if req.rawPacket[58] == 0x0 { if GlobalDebug { fmt.Println("Dropping Advertisement packet without any NDP flags set") } continue } } else { - if (n.rawPacket[78] != 0x01) || (n.rawPacket[79] != 0x01) { + if (req.rawPacket[78] != 0x01) || (req.rawPacket[79] != 0x01) { if GlobalDebug { fmt.Println("Dropping Solicitation packet without Source address set") } @@ -103,7 +106,11 @@ func respond(iface string, requests chan *ndpRequest, respondType ndpType, ndpQu } } - if !checkPacketChecksum(n.srcIP, n.dstIP, n.rawPacket[54:]) { + v6Header, err := newIpv6Header(req.srcIP, req.dstIP) + if err != nil { + continue + } + if !checkPacketChecksum(v6Header, req.rawPacket[54:]) { if GlobalDebug { fmt.Println("Dropping packet because of invalid checksum") } @@ -131,9 +138,9 @@ func respond(iface string, requests chan *ndpRequest, respondType ndpType, ndpQu if filter != nil { ok := false for _, i := range filter { - if i.Contains(n.answeringForIP) { + if i.Contains(req.answeringForIP) { if GlobalDebug { - fmt.Println("Responded for whitelisted IP", n.answeringForIP) + fmt.Println("Responded for whitelisted IP", req.answeringForIP) } ok = true break @@ -148,12 +155,12 @@ func respond(iface string, requests chan *ndpRequest, respondType ndpType, ndpQu fmt.Println("Getting ready to send packet of type", respondType, "out on interface", iface) } - if n.sourceIface == iface { - pkt(fd, result, n.srcIP, n.answeringForIP, nIface.HardwareAddr, respondType) + if req.sourceIface == iface { + pkt(fd, result, req.srcIP, req.answeringForIP, respondIface.HardwareAddr, respondType) } else { if respondType == ndp_ADV { success := false - n.dstIP, success = getAddressFromQuestionListRetry(n.answeringForIP, ndpQuestionChan, ndpQuestionsList) + req.dstIP, success = getAddressFromQuestionListRetry(req.answeringForIP, ndpQuestionChan, ndpQuestionsList) if !success { if GlobalDebug { fmt.Println("Nobody has asked for this IP") @@ -162,11 +169,11 @@ func respond(iface string, requests chan *ndpRequest, respondType ndpType, ndpQu } } else { ndpQuestionChan <- &ndpQuestion{ - targetIP: n.answeringForIP, - askedBy: n.srcIP, + targetIP: req.answeringForIP, + askedBy: req.srcIP, } } - pkt(fd, result, n.dstIP, n.answeringForIP, nIface.HardwareAddr, respondType) + pkt(fd, result, req.dstIP, req.answeringForIP, respondIface.HardwareAddr, respondType) } } } @@ -207,17 +214,23 @@ func getAddressFromQuestionListRetry(targetIP []byte, ndpQuestionChan chan *ndpQ if success { return result, true } -forloop: - for { + + hasBuffered := true + gotBuffered := false + for hasBuffered { select { case q := <-ndpQuestionChan: ndpQuestionsList = append(ndpQuestionsList, q) + gotBuffered = true default: - break forloop + hasBuffered = false } } - result, success = getAddressFromQuestionList(targetIP, ndpQuestionsList) + if gotBuffered { + result, success = getAddressFromQuestionList(targetIP, ndpQuestionsList) + } + return result, success }