More Fixes and optimization

This commit is contained in:
Kioubit 2021-12-25 08:21:07 -05:00
parent ce659c31d8
commit d3a5059aa2
8 changed files with 151 additions and 73 deletions

View File

@ -32,7 +32,9 @@ func readConfig(dest string) {
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
defer file.Close() defer func(file *os.File) {
_ = file.Close()
}(file)
scanner := bufio.NewScanner(file) scanner := bufio.NewScanner(file)

View File

@ -17,7 +17,7 @@ func WaitForSignal() {
} }
func main() { func main() {
fmt.Println("PNDPD Version 0.9 - Kioubit 2021") fmt.Println("PNDPD Version 1.0 - Kioubit 2021")
if len(os.Args) <= 2 { if len(os.Args) <= 2 {
printUsage() printUsage()
@ -31,7 +31,6 @@ func main() {
r.Start() r.Start()
} else { } else {
r = pndp.NewResponder(os.Args[2], nil, "") r = pndp.NewResponder(os.Args[2], nil, "")
fmt.Println("WARNING: You should use a whitelist unless you know what you are doing")
r.Start() r.Start()
} }
WaitForSignal() WaitForSignal()

View File

@ -15,6 +15,7 @@ type ndpRequest struct {
mac []byte mac []byte
receivedIfaceMac []byte receivedIfaceMac []byte
sourceIface string sourceIface string
rawPacket []byte
} }
type ndpQuestion struct { type ndpQuestion struct {

View File

@ -1,14 +1,14 @@
package pndp package pndp
import ( import (
"bytes"
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt"
"net" "net"
"strings"
) )
var emptyIpv6 = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} var emptyIpv6 = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
var allNodesIpv6 = []byte{0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}
type payload interface { type payload interface {
constructPacket() ([]byte, int) constructPacket() ([]byte, int)
@ -115,6 +115,11 @@ func (p *ndpPayload) constructPacket() ([]byte, int) {
} }
func calculateChecksum(h *ipv6Header, payload []byte) uint16 { func calculateChecksum(h *ipv6Header, payload []byte) uint16 {
if payload == nil {
return 0x0000
} else if len(payload) == 0 {
return 0x0000
}
sumPseudoHeader := checksumAddition(h.srcIP) + checksumAddition(h.dstIP) + checksumAddition([]byte{0x00, h.protocol}) + checksumAddition(h.payloadLen) sumPseudoHeader := checksumAddition(h.srcIP) + checksumAddition(h.dstIP) + checksumAddition([]byte{0x00, h.protocol}) + checksumAddition(h.payloadLen)
sumPayload := checksumAddition(payload) sumPayload := checksumAddition(payload)
sumTotal := sumPayload + sumPseudoHeader sumTotal := sumPayload + sumPseudoHeader
@ -128,7 +133,7 @@ func checksumAddition(b []byte) uint32 {
var sum uint32 = 0 var sum uint32 = 0
for i := 0; i < len(b); i++ { for i := 0; i < len(b); i++ {
if i%2 == 0 { if i%2 == 0 {
if len(b) <= i-1 { if len(b)-1 == i {
sum += uint32(uint16(b[i])<<8 | uint16(0x0)) sum += uint32(uint16(b[i])<<8 | uint16(0x0))
} else { } else {
sum += uint32(uint16(b[i])<<8 | uint16(b[i+1])) sum += uint32(uint16(b[i])<<8 | uint16(b[i+1]))
@ -138,7 +143,41 @@ func checksumAddition(b []byte) uint32 {
return sum return sum
} }
func checkPacketChecksum(scrip, dstip, payload []byte) bool {
v6, err := newIpv6Header(scrip, dstip)
if err != nil {
return false
}
packetsum := make([]byte, 2)
copy(packetsum, payload[2:4])
bPayloadLen := make([]byte, 2)
binary.BigEndian.PutUint16(bPayloadLen, uint16(len(payload)))
v6.payloadLen = bPayloadLen
payload[2] = 0x0
payload[3] = 0x0
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 {
fmt.Println("Received packet checksum validation failed")
}
return false
}
}
func isIpv6(ip string) bool { func isIpv6(ip string) bool {
rip := net.ParseIP(ip) rip := net.ParseIP(ip)
return rip != nil && strings.Contains(ip, ":") if rip.To16() == nil {
return false
}
return true
} }

View File

@ -35,6 +35,7 @@ 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")
var s sync.WaitGroup var s sync.WaitGroup
return &ResponderObj{ return &ResponderObj{
stopChan: make(chan struct{}), stopChan: make(chan struct{}),
@ -61,7 +62,7 @@ func (obj *ResponderObj) start() {
} }
//Stop a running Responder instance //Stop a running Responder instance
// Returns false on success // Returns false on error
func (obj *ResponderObj) Stop() bool { func (obj *ResponderObj) Stop() bool {
close(obj.stopChan) close(obj.stopChan)
fmt.Println("Shutting down responder instance..") fmt.Println("Shutting down responder instance..")

View File

@ -2,7 +2,6 @@ package pndp
import ( import (
"bytes" "bytes"
"encoding/binary"
"fmt" "fmt"
"golang.org/x/net/bpf" "golang.org/x/net/bpf"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
@ -46,6 +45,7 @@ func htons16(v uint16) uint16 { return v<<8 | v>>8 }
func listen(iface string, responder chan *ndpRequest, requestType ndpType, stopWG *sync.WaitGroup, stopChan chan struct{}) { func listen(iface string, responder chan *ndpRequest, requestType ndpType, stopWG *sync.WaitGroup, stopChan chan struct{}) {
stopWG.Add(1) stopWG.Add(1)
defer stopWG.Done() defer stopWG.Done()
niface, err := net.InterfaceByName(iface) niface, err := net.InterfaceByName(iface)
if err != nil { if err != nil {
panic(err.Error()) panic(err.Error())
@ -61,7 +61,7 @@ func listen(iface string, responder chan *ndpRequest, requestType ndpType, stopW
} }
go func() { go func() {
<-stopChan <-stopChan
syscall.Close(fd) _ = syscall.Close(fd)
stopWG.Done() // syscall.read does not release when the file descriptor is closed stopWG.Done() // syscall.read does not release when the file descriptor is closed
}() }()
if GlobalDebug { if GlobalDebug {
@ -99,9 +99,9 @@ func listen(iface string, responder chan *ndpRequest, requestType ndpType, stopW
bpf.LoadAbsolute{Off: 54, Size: 1}, bpf.LoadAbsolute{Off: 54, Size: 1},
// Jump to the drop packet instruction if Type is not Neighbor Solicitation / Advertisement. // Jump to the drop packet instruction if Type is not Neighbor Solicitation / Advertisement.
bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: protocolNo, SkipTrue: 1}, bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: protocolNo, SkipTrue: 1},
// Verdict is "send up to 4k of the packet to userspace."buf // Verdict is: send up to 4096 bytes of the packet to userspace.
bpf.RetConstant{Val: 4096}, bpf.RetConstant{Val: 4096},
// Verdict is "ignore packet." // Verdict is: "ignore packet."
bpf.RetConstant{Val: 0}, bpf.RetConstant{Val: 0},
} }
@ -118,7 +118,6 @@ func listen(iface string, responder chan *ndpRequest, requestType ndpType, stopW
} }
if numRead < 86 { if numRead < 86 {
if GlobalDebug { if GlobalDebug {
fmt.Println("Dropping packet since it does not meet the minimum length requirement") fmt.Println("Dropping packet since it does not meet the minimum length requirement")
fmt.Printf("% X\n", buf[:numRead]) fmt.Printf("% X\n", buf[:numRead])
} }
@ -128,72 +127,39 @@ func listen(iface string, responder chan *ndpRequest, requestType ndpType, stopW
fmt.Println("Got packet on", iface, "of type", requestType) fmt.Println("Got packet on", iface, "of type", requestType)
fmt.Printf("% X\n", buf[:numRead]) fmt.Printf("% X\n", buf[:numRead])
fmt.Println("Source MAC ETHER") fmt.Println("Source mac on ethernet layer:")
fmt.Printf("% X\n", buf[:numRead][6:12]) fmt.Printf("% X\n", buf[6:12])
fmt.Println("Source IP:") fmt.Println("Source IP:")
fmt.Printf("% X\n", buf[:numRead][22:38]) fmt.Printf("% X\n", buf[22:38])
fmt.Println("Destination IP:") fmt.Println("Destination IP:")
fmt.Printf("% X\n", buf[:numRead][38:54]) fmt.Printf("% X\n", buf[38:54])
fmt.Println("Requested IP:") fmt.Println("Requested IP:")
fmt.Printf("% X\n", buf[:numRead][62:78]) fmt.Printf("% X\n", buf[62:78])
fmt.Println("Source MAC") if requestType == ndp_ADV {
fmt.Printf("% X\n", buf[:numRead][80:86]) fmt.Println("NDP Flags")
fmt.Printf("% X\n", buf[58])
}
fmt.Println("NDP MAC:")
fmt.Printf("% X\n", buf[80:86])
fmt.Println() fmt.Println()
} }
if bytes.Equal(buf[:numRead][6:12], niface.HardwareAddr) { if bytes.Equal(buf[6:12], niface.HardwareAddr) {
if GlobalDebug { if GlobalDebug {
fmt.Println("Dropping packet from ourselves") fmt.Println("Dropping packet from ourselves")
} }
continue continue
} }
if !checkPacketChecksum(buf[:numRead][22:38], buf[:numRead][38:54], buf[:numRead][54:numRead]) {
if GlobalDebug {
fmt.Println("Dropping packet because of invalid checksum")
}
continue
}
responder <- &ndpRequest{ responder <- &ndpRequest{
requestType: requestType, requestType: requestType,
srcIP: buf[:numRead][22:38], srcIP: buf[22:38],
dstIP: buf[:numRead][38:54], dstIP: buf[38:54],
answeringForIP: buf[:numRead][62:78], answeringForIP: buf[62:78],
mac: buf[:numRead][80:86], mac: buf[80:86],
receivedIfaceMac: niface.HardwareAddr, receivedIfaceMac: niface.HardwareAddr,
sourceIface: iface, sourceIface: iface,
rawPacket: buf[:numRead],
} }
} }
} }
func checkPacketChecksum(scrip, dstip, payload []byte) bool {
v6, err := newIpv6Header(scrip, dstip)
if err != nil {
return false
}
packetsum := make([]byte, 2)
copy(packetsum, payload[2:4])
bPayloadLen := make([]byte, 2)
binary.BigEndian.PutUint16(bPayloadLen, uint16(len(payload)))
v6.payloadLen = bPayloadLen
payload[2] = 0x0
payload[3] = 0x0
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 {
fmt.Println("Received packet checksum validation failed")
}
return false
}
}

View File

@ -9,62 +9,107 @@ 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, 100) var ndpQuestionsList = make([]*ndpQuestion, 0, 40)
stopWG.Add(1) stopWG.Add(1)
defer stopWG.Done() defer stopWG.Done()
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)
} }
defer syscall.Close(fd) defer func(fd int) {
_ = syscall.Close(fd)
}(fd)
err = syscall.BindToDevice(fd, iface) err = syscall.BindToDevice(fd, iface)
if err != nil { if err != nil {
panic(err) panic(err)
} }
niface, err := net.InterfaceByName(iface) nIface, 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 := nIface.Addrs()
for _, n := range ifaceaddrs { for _, n := range ifaceaddrs {
tip, _, err := net.ParseCIDR(n.String()) tip, _, err := net.ParseCIDR(n.String())
if err != nil { if err != nil {
break break
} }
var haveUla = false
if isIpv6(tip.String()) { if isIpv6(tip.String()) {
if tip.IsGlobalUnicast() { if tip.IsGlobalUnicast() {
haveUla = true
result = tip result = tip
_, tnet, _ := net.ParseCIDR("fc00::/7") _, tnet, _ := net.ParseCIDR("fc00::/7")
if !tnet.Contains(tip) { if !tnet.Contains(tip) {
break break
} }
} else if tip.IsLinkLocalUnicast() && !haveUla {
result = tip
} }
} }
} }
for { for {
var n *ndpRequest var n *ndpRequest
if ndpQuestionChan == nil && respondType == ndp_ADV { if (ndpQuestionChan == nil && respondType == ndp_ADV) || (ndpQuestionChan != nil && respondType == ndp_SOL) {
select { select {
case <-stopChan: case <-stopChan:
return return
case n = <-requests: case n = <-requests:
} }
} else { } else {
// THis is if ndpQuestionChan != nil && respondType == ndp_ADV
select { select {
case <-stopChan: case <-stopChan:
return return
case q := <-ndpQuestionChan: case q := <-ndpQuestionChan:
ndpQuestionsList = append(ndpQuestionsList, q) ndpQuestionsList = append(ndpQuestionsList, q)
ndpQuestionsList = cleanupQuestionList(ndpQuestionsList)
continue continue
case n = <-requests: case n = <-requests:
} }
} }
var _, LinkLocalSpace, _ = net.ParseCIDR("fe80::/10")
if LinkLocalSpace.Contains(n.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 GlobalDebug {
fmt.Println("Dropping Advertisement packet without target Source address set")
}
continue
}
if n.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 GlobalDebug {
fmt.Println("Dropping Solicitation packet without Source address set")
}
continue
}
}
if !checkPacketChecksum(n.srcIP, n.dstIP, n.rawPacket[54:]) {
if GlobalDebug {
fmt.Println("Dropping packet because of invalid checksum")
}
continue
}
if autoSense != "" { if autoSense != "" {
autoiface, err := net.InterfaceByName(autoSense) autoiface, err := net.InterfaceByName(autoSense)
if err != nil { if err != nil {
@ -72,8 +117,8 @@ func respond(iface string, requests chan *ndpRequest, respondType ndpType, ndpQu
} }
autoifaceaddrs, err := autoiface.Addrs() autoifaceaddrs, err := autoiface.Addrs()
for _, n := range autoifaceaddrs { for _, l := range autoifaceaddrs {
_, anet, err := net.ParseCIDR(n.String()) _, anet, err := net.ParseCIDR(l.String())
if err != nil { if err != nil {
break break
} }
@ -104,7 +149,7 @@ func respond(iface string, requests chan *ndpRequest, respondType ndpType, ndpQu
} }
if n.sourceIface == iface { if n.sourceIface == iface {
pkt(fd, result, n.srcIP, n.answeringForIP, niface.HardwareAddr, respondType) pkt(fd, result, n.srcIP, n.answeringForIP, nIface.HardwareAddr, respondType)
} else { } else {
if respondType == ndp_ADV { if respondType == ndp_ADV {
success := false success := false
@ -121,7 +166,7 @@ func respond(iface string, requests chan *ndpRequest, respondType ndpType, ndpQu
askedBy: n.srcIP, askedBy: n.srcIP,
} }
} }
pkt(fd, result, n.dstIP, n.answeringForIP, niface.HardwareAddr, respondType) pkt(fd, result, n.dstIP, n.answeringForIP, nIface.HardwareAddr, respondType)
} }
} }
} }
@ -177,7 +222,7 @@ forloop:
} }
func getAddressFromQuestionList(targetIP []byte, ndpQuestionsList []*ndpQuestion) ([]byte, bool) { func getAddressFromQuestionList(targetIP []byte, ndpQuestionsList []*ndpQuestion) ([]byte, bool) {
for i, _ := range ndpQuestionsList { for i := range ndpQuestionsList {
if bytes.Equal((*ndpQuestionsList[i]).targetIP, targetIP) { if bytes.Equal((*ndpQuestionsList[i]).targetIP, targetIP) {
result := (*ndpQuestionsList[i]).askedBy result := (*ndpQuestionsList[i]).askedBy
ndpQuestionsList = removeFromQuestionList(ndpQuestionsList, i) ndpQuestionsList = removeFromQuestionList(ndpQuestionsList, i)
@ -190,3 +235,10 @@ func removeFromQuestionList(s []*ndpQuestion, i int) []*ndpQuestion {
s[i] = s[len(s)-1] s[i] = s[len(s)-1]
return s[:len(s)-1] return s[:len(s)-1]
} }
func cleanupQuestionList(s []*ndpQuestion) []*ndpQuestion {
for len(s) >= 40 {
s = removeFromQuestionList(s, 0)
}
return s
}

18
pndpd.service Normal file
View File

@ -0,0 +1,18 @@
[Unit]
Description=Proxy NDP Daemon
Wants=network-online.target
After=network.target network-online.target
[Service]
Type=simple
Restart=on-failure
RestartSec=5s
ExecStart=/usr/bin/pndpd config /etc/pndpd/pndpd.conf
DynamicUser=yes
AmbientCapabilities=CAP_NET_RAW
CapabilityBoundingSet=
ProtectHome=yes
[Install]
WantedBy=multi-user.target