Improvements and Fixes to proxying
This commit is contained in:
parent
f69d8c190e
commit
94b1683c8d
@ -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
|
||||||
|
|
||||||
|
@ -16,3 +16,8 @@ type ndpRequest struct {
|
|||||||
receivedIfaceMac []byte
|
receivedIfaceMac []byte
|
||||||
sourceIface string
|
sourceIface string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ndpQuestion struct {
|
||||||
|
targetIP []byte
|
||||||
|
askedBy []byte
|
||||||
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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,10 +48,21 @@ func respond(iface string, requests chan *ndpRequest, respondType ndpType, filte
|
|||||||
|
|
||||||
for {
|
for {
|
||||||
var n *ndpRequest
|
var n *ndpRequest
|
||||||
select {
|
if ndpQuestionChan == nil && respondType == ndp_ADV {
|
||||||
case <-stopChan:
|
select {
|
||||||
return
|
case <-stopChan:
|
||||||
case n = <-requests:
|
return
|
||||||
|
case n = <-requests:
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
select {
|
||||||
|
case <-stopChan:
|
||||||
|
return
|
||||||
|
case q := <-ndpQuestionChan:
|
||||||
|
ndpQuestionsList = append(ndpQuestionsList, q)
|
||||||
|
continue
|
||||||
|
case n = <-requests:
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if autoSense != "" {
|
if 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]
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user