Small code optimizations

This commit is contained in:
Kioubit 2021-12-27 07:58:10 -05:00
parent 5a901eada3
commit 0f580cbbbd
6 changed files with 116 additions and 107 deletions

View File

@ -8,14 +8,12 @@ const (
) )
type ndpRequest struct { type ndpRequest struct {
requestType ndpType requestType ndpType
srcIP []byte srcIP []byte
answeringForIP []byte answeringForIP []byte
dstIP []byte dstIP []byte
mac []byte sourceIface string
receivedIfaceMac []byte rawPacket []byte
sourceIface string
rawPacket []byte
} }
type ndpQuestion struct { type ndpQuestion struct {

58
pndp/interface.go Normal file
View File

@ -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)
}
}

View File

@ -4,38 +4,11 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"golang.org/x/net/bpf" "golang.org/x/net/bpf"
"golang.org/x/sys/unix"
"net" "net"
"sync" "sync"
"syscall" "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) // Htons Convert a uint16 to host byte order (big endian)
func htons(v uint16) int { func htons(v uint16) int {
return int((v << 8) | (v >> 8)) return int((v << 8) | (v >> 8))
@ -155,39 +128,12 @@ func listen(iface string, responder chan *ndpRequest, requestType ndpType, stopW
} }
responder <- &ndpRequest{ responder <- &ndpRequest{
requestType: requestType, requestType: requestType,
srcIP: buf[22:38], srcIP: buf[22:38],
dstIP: buf[38:54], dstIP: buf[38:54],
answeringForIP: buf[62:78], answeringForIP: buf[62:78],
mac: buf[80:86], sourceIface: iface,
receivedIfaceMac: niface.HardwareAddr, rawPacket: buf[:numRead],
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)
}
}

View File

@ -144,12 +144,7 @@ func checksumAddition(b []byte) uint32 {
return sum return sum
} }
func checkPacketChecksum(scrip, dstip, payload []byte) bool { func checkPacketChecksum(v6 *ipv6Header, payload []byte) bool {
v6, err := newIpv6Header(scrip, dstip)
if err != nil {
return false
}
packetsum := make([]byte, 2) packetsum := make([]byte, 2)
copy(packetsum, payload[2:4]) copy(packetsum, payload[2:4])
@ -163,9 +158,6 @@ func checkPacketChecksum(scrip, dstip, payload []byte) bool {
bChecksum := make([]byte, 2) bChecksum := make([]byte, 2)
binary.BigEndian.PutUint16(bChecksum, calculateChecksum(v6, payload)) binary.BigEndian.PutUint16(bChecksum, calculateChecksum(v6, payload))
if bytes.Equal(packetsum, bChecksum) { if bytes.Equal(packetsum, bChecksum) {
if GlobalDebug {
fmt.Println("Verified received packet checksum")
}
return true return true
} else { } else {
if GlobalDebug { if GlobalDebug {

View File

@ -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. // 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 // Start() must be called on the object to actually start responding
func NewResponder(iface string, filter []*net.IPNet, autosenseInterface string) *ResponderObj { 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 var s sync.WaitGroup
return &ResponderObj{ return &ResponderObj{
stopChan: make(chan struct{}), 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 { func ParseFilter(f string) []*net.IPNet {
if f == "" { if f == "" {
return nil return nil

View File

@ -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{}) { 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) stopWG.Add(1)
defer stopWG.Done() 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) fd, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_RAW, syscall.IPPROTO_RAW)
if err != nil { if err != nil {
panic(err) panic(err)
@ -24,13 +28,13 @@ func respond(iface string, requests chan *ndpRequest, respondType ndpType, ndpQu
panic(err) panic(err)
} }
nIface, err := net.InterfaceByName(iface) respondIface, err := net.InterfaceByName(iface)
if err != nil { if err != nil {
panic(err.Error()) panic(err.Error())
} }
var result = emptyIpv6 var result = emptyIpv6
ifaceaddrs, err := nIface.Addrs() ifaceaddrs, err := respondIface.Addrs()
for _, n := range ifaceaddrs { for _, n := range ifaceaddrs {
tip, _, err := net.ParseCIDR(n.String()) tip, _, err := net.ParseCIDR(n.String())
@ -42,8 +46,8 @@ func respond(iface string, requests chan *ndpRequest, respondType ndpType, ndpQu
if tip.IsGlobalUnicast() { if tip.IsGlobalUnicast() {
haveUla = true haveUla = true
result = tip result = tip
_, tnet, _ := net.ParseCIDR("fc00::/7")
if !tnet.Contains(tip) { if !ulaSpace.Contains(tip) {
break break
} }
} else if tip.IsLinkLocalUnicast() && !haveUla { } else if tip.IsLinkLocalUnicast() && !haveUla {
@ -53,15 +57,15 @@ func respond(iface string, requests chan *ndpRequest, respondType ndpType, ndpQu
} }
for { for {
var n *ndpRequest var req *ndpRequest
if (ndpQuestionChan == nil && respondType == ndp_ADV) || (ndpQuestionChan != nil && respondType == ndp_SOL) { if (ndpQuestionChan == nil && respondType == ndp_ADV) || (ndpQuestionChan != nil && respondType == ndp_SOL) {
select { select {
case <-stopChan: case <-stopChan:
return return
case n = <-requests: case req = <-requests:
} }
} else { } else {
// THis is if ndpQuestionChan != nil && respondType == ndp_ADV // This is if ndpQuestionChan != nil && respondType == ndp_ADV
select { select {
case <-stopChan: case <-stopChan:
return return
@ -69,33 +73,32 @@ func respond(iface string, requests chan *ndpRequest, respondType ndpType, ndpQu
ndpQuestionsList = append(ndpQuestionsList, q) ndpQuestionsList = append(ndpQuestionsList, q)
ndpQuestionsList = cleanupQuestionList(ndpQuestionsList) ndpQuestionsList = cleanupQuestionList(ndpQuestionsList)
continue continue
case n = <-requests: case req = <-requests:
} }
} }
var _, LinkLocalSpace, _ = net.ParseCIDR("fe80::/10") if linkLocalSpace.Contains(req.answeringForIP) {
if LinkLocalSpace.Contains(n.answeringForIP) {
if GlobalDebug { if GlobalDebug {
fmt.Println("Dropping packet asking for a link-local IP") fmt.Println("Dropping packet asking for a link-local IP")
} }
continue continue
} }
if n.requestType == ndp_ADV { if req.requestType == ndp_ADV {
if (n.rawPacket[78] != 0x02) || (n.rawPacket[79] != 0x01) { if (req.rawPacket[78] != 0x02) || (req.rawPacket[79] != 0x01) {
if GlobalDebug { if GlobalDebug {
fmt.Println("Dropping Advertisement packet without target Source address set") fmt.Println("Dropping Advertisement packet without target Source address set")
} }
continue continue
} }
if n.rawPacket[58] == 0x0 { if req.rawPacket[58] == 0x0 {
if GlobalDebug { if GlobalDebug {
fmt.Println("Dropping Advertisement packet without any NDP flags set") fmt.Println("Dropping Advertisement packet without any NDP flags set")
} }
continue continue
} }
} else { } else {
if (n.rawPacket[78] != 0x01) || (n.rawPacket[79] != 0x01) { if (req.rawPacket[78] != 0x01) || (req.rawPacket[79] != 0x01) {
if GlobalDebug { if GlobalDebug {
fmt.Println("Dropping Solicitation packet without Source address set") 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 { if GlobalDebug {
fmt.Println("Dropping packet because of invalid checksum") 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 { if filter != nil {
ok := false ok := false
for _, i := range filter { for _, i := range filter {
if i.Contains(n.answeringForIP) { if i.Contains(req.answeringForIP) {
if GlobalDebug { if GlobalDebug {
fmt.Println("Responded for whitelisted IP", n.answeringForIP) fmt.Println("Responded for whitelisted IP", req.answeringForIP)
} }
ok = true ok = true
break 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) fmt.Println("Getting ready to send packet of type", respondType, "out on interface", iface)
} }
if n.sourceIface == iface { if req.sourceIface == iface {
pkt(fd, result, n.srcIP, n.answeringForIP, nIface.HardwareAddr, respondType) pkt(fd, result, req.srcIP, req.answeringForIP, respondIface.HardwareAddr, respondType)
} else { } else {
if respondType == ndp_ADV { if respondType == ndp_ADV {
success := false success := false
n.dstIP, success = getAddressFromQuestionListRetry(n.answeringForIP, ndpQuestionChan, ndpQuestionsList) req.dstIP, success = getAddressFromQuestionListRetry(req.answeringForIP, ndpQuestionChan, ndpQuestionsList)
if !success { if !success {
if GlobalDebug { if GlobalDebug {
fmt.Println("Nobody has asked for this IP") fmt.Println("Nobody has asked for this IP")
@ -162,11 +169,11 @@ func respond(iface string, requests chan *ndpRequest, respondType ndpType, ndpQu
} }
} else { } else {
ndpQuestionChan <- &ndpQuestion{ ndpQuestionChan <- &ndpQuestion{
targetIP: n.answeringForIP, targetIP: req.answeringForIP,
askedBy: n.srcIP, 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 { if success {
return result, true return result, true
} }
forloop:
for { hasBuffered := true
gotBuffered := false
for hasBuffered {
select { select {
case q := <-ndpQuestionChan: case q := <-ndpQuestionChan:
ndpQuestionsList = append(ndpQuestionsList, q) ndpQuestionsList = append(ndpQuestionsList, q)
gotBuffered = true
default: default:
break forloop hasBuffered = false
} }
} }
result, success = getAddressFromQuestionList(targetIP, ndpQuestionsList) if gotBuffered {
result, success = getAddressFromQuestionList(targetIP, ndpQuestionsList)
}
return result, success return result, success
} }