Improvements and Fixes to proxying

This commit is contained in:
Kioubit 2021-12-24 16:43:49 -05:00
parent f69d8c190e
commit 94b1683c8d
6 changed files with 141 additions and 13 deletions

View File

@ -3,7 +3,7 @@
- Efficiently process incoming packets using bpf (which runs in the kernel) - Efficiently process incoming packets using bpf (which runs in the kernel)
- Respond to all NDP solicitations on an interface - Respond to all NDP solicitations on an interface
- Respond to NDP solicitations for whitelisted addresses on an interface - Respond to NDP solicitations for whitelisted addresses on an interface
- Proxy NDP between interfaces with an optional whitelist for neighbor solicitations - Proxy NDP between interfaces with an optional whitelist
- Optionally determine whitelist automatically based on the IPs assigned to the interfaces - Optionally determine whitelist automatically based on the IPs assigned to the interfaces
- Permissions required: root or CAP_NET_RAW - Permissions required: root or CAP_NET_RAW

View File

@ -16,3 +16,8 @@ type ndpRequest struct {
receivedIfaceMac []byte receivedIfaceMac []byte
sourceIface string sourceIface string
} }
type ndpQuestion struct {
targetIP []byte
askedBy []byte
}

View File

@ -8,6 +8,7 @@ import (
) )
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)

View File

@ -54,7 +54,7 @@ func (obj *ResponderObj) start() {
close(requests) close(requests)
obj.stopWG.Done() obj.stopWG.Done()
}() }()
go respond(obj.iface, requests, ndp_ADV, obj.filter, obj.autosense, obj.stopWG, obj.stopChan) go respond(obj.iface, requests, ndp_ADV, nil, obj.filter, obj.autosense, obj.stopWG, obj.stopChan)
go listen(obj.iface, requests, ndp_SOL, obj.stopWG, obj.stopChan) go listen(obj.iface, requests, ndp_SOL, obj.stopWG, obj.stopChan)
fmt.Println("Started responder instance on interface ", obj.iface) fmt.Println("Started responder instance on interface ", obj.iface)
<-obj.stopChan <-obj.stopChan
@ -102,25 +102,30 @@ func (obj *ProxyObj) start() {
obj.stopWG.Done() obj.stopWG.Done()
}() }()
out_iface1_sol_questions_iface2_adv := make(chan *ndpQuestion, 100)
defer close(out_iface1_sol_questions_iface2_adv)
out_iface2_sol_questions_iface1_adv := make(chan *ndpQuestion, 100)
defer close(out_iface2_sol_questions_iface1_adv)
req_iface1_sol_iface2 := make(chan *ndpRequest, 100) req_iface1_sol_iface2 := make(chan *ndpRequest, 100)
defer close(req_iface1_sol_iface2) defer close(req_iface1_sol_iface2)
go listen(obj.iface1, req_iface1_sol_iface2, ndp_SOL, obj.stopWG, obj.stopChan) go listen(obj.iface1, req_iface1_sol_iface2, ndp_SOL, obj.stopWG, obj.stopChan)
go respond(obj.iface2, req_iface1_sol_iface2, ndp_SOL, obj.filter, obj.autosense, obj.stopWG, obj.stopChan) go respond(obj.iface2, req_iface1_sol_iface2, ndp_SOL, out_iface2_sol_questions_iface1_adv, obj.filter, obj.autosense, obj.stopWG, obj.stopChan)
req_iface2_sol_iface1 := make(chan *ndpRequest, 100) req_iface2_sol_iface1 := make(chan *ndpRequest, 100)
defer close(req_iface2_sol_iface1) defer close(req_iface2_sol_iface1)
go listen(obj.iface2, req_iface2_sol_iface1, ndp_SOL, obj.stopWG, obj.stopChan) go listen(obj.iface2, req_iface2_sol_iface1, ndp_SOL, obj.stopWG, obj.stopChan)
go respond(obj.iface1, req_iface2_sol_iface1, ndp_SOL, nil, "", obj.stopWG, obj.stopChan) go respond(obj.iface1, req_iface2_sol_iface1, ndp_SOL, out_iface1_sol_questions_iface2_adv, nil, "", obj.stopWG, obj.stopChan)
req_iface1_adv_iface2 := make(chan *ndpRequest, 100) req_iface1_adv_iface2 := make(chan *ndpRequest, 100)
defer close(req_iface1_adv_iface2) defer close(req_iface1_adv_iface2)
go listen(obj.iface1, req_iface1_adv_iface2, ndp_ADV, obj.stopWG, obj.stopChan) go listen(obj.iface1, req_iface1_adv_iface2, ndp_ADV, obj.stopWG, obj.stopChan)
go respond(obj.iface2, req_iface1_adv_iface2, ndp_ADV, nil, "", obj.stopWG, obj.stopChan) go respond(obj.iface2, req_iface1_adv_iface2, ndp_ADV, out_iface1_sol_questions_iface2_adv, nil, "", obj.stopWG, obj.stopChan)
req_iface2_adv_iface1 := make(chan *ndpRequest, 100) req_iface2_adv_iface1 := make(chan *ndpRequest, 100)
defer close(req_iface2_adv_iface1) defer close(req_iface2_adv_iface1)
go listen(obj.iface2, req_iface2_adv_iface1, ndp_ADV, obj.stopWG, obj.stopChan) go listen(obj.iface2, req_iface2_adv_iface1, ndp_ADV, obj.stopWG, obj.stopChan)
go respond(obj.iface1, req_iface2_adv_iface1, ndp_ADV, nil, "", obj.stopWG, obj.stopChan) go respond(obj.iface1, req_iface2_adv_iface1, ndp_ADV, out_iface2_sol_questions_iface1_adv, nil, "", obj.stopWG, obj.stopChan)
fmt.Println("Started Proxy instance for interfaces: ", obj.iface1, " and ", obj.iface2) fmt.Println("Started Proxy instance for interfaces: ", obj.iface1, " and ", obj.iface2)
<-obj.stopChan <-obj.stopChan

View File

@ -1,6 +1,8 @@
package pndp package pndp
import ( import (
"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"
@ -114,8 +116,20 @@ func listen(iface string, responder chan *ndpRequest, requestType ndpType, stopW
if err != nil { if err != nil {
panic(err) panic(err)
} }
if numRead < 86 {
if GlobalDebug {
fmt.Println("Dropping packet since it does not meet the minimum length requirement")
fmt.Printf("% X\n", buf[:numRead])
}
continue
}
if GlobalDebug { if GlobalDebug {
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.Println("Source MAC ETHER")
fmt.Printf("% X\n", buf[:numRead][6:12])
fmt.Println("Source IP:") fmt.Println("Source IP:")
fmt.Printf("% X\n", buf[:numRead][22:38]) fmt.Printf("% X\n", buf[:numRead][22:38])
fmt.Println("Destination IP:") fmt.Println("Destination IP:")
@ -126,6 +140,21 @@ func listen(iface string, responder chan *ndpRequest, requestType ndpType, stopW
fmt.Printf("% X\n", buf[:numRead][80:86]) fmt.Printf("% X\n", buf[:numRead][80:86])
fmt.Println() fmt.Println()
} }
if bytes.Equal(buf[:numRead][6:12], niface.HardwareAddr) {
if GlobalDebug {
fmt.Println("Dropping packet from ourselves")
}
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[:numRead][22:38],
@ -137,3 +166,34 @@ func listen(iface string, responder chan *ndpRequest, requestType ndpType, stopW
} }
} }
} }
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

@ -8,7 +8,8 @@ import (
"syscall" "syscall"
) )
func respond(iface string, requests chan *ndpRequest, respondType ndpType, 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)
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)
@ -47,11 +48,22 @@ func respond(iface string, requests chan *ndpRequest, respondType ndpType, filte
for { for {
var n *ndpRequest var n *ndpRequest
if ndpQuestionChan == nil && respondType == ndp_ADV {
select { select {
case <-stopChan: case <-stopChan:
return return
case n = <-requests: case n = <-requests:
} }
} else {
select {
case <-stopChan:
return
case q := <-ndpQuestionChan:
ndpQuestionsList = append(ndpQuestionsList, q)
continue
case n = <-requests:
}
}
if autoSense != "" { if autoSense != "" {
autoiface, err := net.InterfaceByName(autoSense) autoiface, err := net.InterfaceByName(autoSense)
@ -94,9 +106,22 @@ func respond(iface string, requests chan *ndpRequest, respondType ndpType, filte
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 !bytes.Equal(n.mac, n.receivedIfaceMac) { if respondType == ndp_ADV {
pkt(fd, n.srcIP, n.dstIP, n.answeringForIP, niface.HardwareAddr, respondType) success := false
n.dstIP, success = getAddressFromQuestionListRetry(n.answeringForIP, ndpQuestionChan, ndpQuestionsList)
if !success {
if GlobalDebug {
fmt.Println("Nobody has asked for this IP")
} }
continue
}
} else {
ndpQuestionChan <- &ndpQuestion{
targetIP: n.answeringForIP,
askedBy: n.srcIP,
}
}
pkt(fd, result, n.dstIP, n.answeringForIP, niface.HardwareAddr, respondType)
} }
} }
} }
@ -129,3 +154,35 @@ func pkt(fd int, ownIP []byte, dstIP []byte, tgtip []byte, mac []byte, respondTy
fmt.Println(err.Error()) fmt.Println(err.Error())
} }
} }
func getAddressFromQuestionListRetry(targetIP []byte, ndpQuestionChan chan *ndpQuestion, ndpQuestionsList []*ndpQuestion) ([]byte, bool) {
success := false
var result []byte
result, success = getAddressFromQuestionList(targetIP, ndpQuestionsList)
if success {
return result, true
}
select {
case q := <-ndpQuestionChan:
ndpQuestionsList = append(ndpQuestionsList, q)
default:
return nil, false
}
result, success = getAddressFromQuestionList(targetIP, ndpQuestionsList)
return result, success
}
func getAddressFromQuestionList(targetIP []byte, ndpQuestionsList []*ndpQuestion) ([]byte, bool) {
for i, _ := range ndpQuestionsList {
if bytes.Equal((*ndpQuestionsList[i]).targetIP, targetIP) {
result := (*ndpQuestionsList[i]).askedBy
ndpQuestionsList = removeFromQuestionList(ndpQuestionsList, i)
return result, true
}
}
return nil, false
}
func removeFromQuestionList(s []*ndpQuestion, i int) []*ndpQuestion {
s[i] = s[len(s)-1]
return s[:len(s)-1]
}