183 lines
5.4 KiB
Go
183 lines
5.4 KiB
Go
package pndp
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
var GlobalDebug = false
|
|
|
|
type ResponderObj struct {
|
|
stopChan chan struct{}
|
|
stopWG *sync.WaitGroup
|
|
iface string
|
|
filter []*net.IPNet
|
|
autosense string
|
|
}
|
|
type ProxyObj struct {
|
|
stopChan chan struct{}
|
|
stopWG *sync.WaitGroup
|
|
iface1 string
|
|
iface2 string
|
|
filter []*net.IPNet
|
|
autosense string
|
|
}
|
|
|
|
// NewResponder
|
|
//
|
|
// iface - The interface to listen to and respond from
|
|
//
|
|
// filter - Optional (can be nil) list of CIDRs to whitelist. Must be IPV6! ParseFilter verifies ipv6
|
|
//
|
|
// 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 {
|
|
if filter == nil && autosenseInterface == "" {
|
|
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{}),
|
|
stopWG: &s,
|
|
iface: iface,
|
|
filter: filter,
|
|
autosense: autosenseInterface,
|
|
}
|
|
}
|
|
func (obj *ResponderObj) Start() {
|
|
go obj.start()
|
|
}
|
|
func (obj *ResponderObj) start() {
|
|
obj.stopWG.Add(1)
|
|
requests := make(chan *ndpRequest, 100)
|
|
defer func() {
|
|
close(requests)
|
|
obj.stopWG.Done()
|
|
}()
|
|
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)
|
|
fmt.Printf("Started responder instance on interface %s", obj.iface)
|
|
fmt.Println()
|
|
<-obj.stopChan
|
|
}
|
|
|
|
//Stop a running Responder instance
|
|
// Returns false on error
|
|
func (obj *ResponderObj) Stop() bool {
|
|
close(obj.stopChan)
|
|
fmt.Println("Shutting down responder instance..")
|
|
if wgWaitTimout(obj.stopWG, 10*time.Second) {
|
|
fmt.Println("Done")
|
|
return true
|
|
} else {
|
|
fmt.Println("Error shutting down instance")
|
|
return false
|
|
}
|
|
}
|
|
|
|
// NewProxy Proxy NDP between interfaces iface1 and iface2 with an optional filter (whitelist)
|
|
//
|
|
// filter - Optional (can be nil) list of CIDRs to whitelist. Must be IPV6! ParseFilter verifies ipv6
|
|
//
|
|
// 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 proxying
|
|
func NewProxy(iface1 string, iface2 string, filter []*net.IPNet, autosenseInterface string) *ProxyObj {
|
|
var s sync.WaitGroup
|
|
return &ProxyObj{
|
|
stopChan: make(chan struct{}),
|
|
stopWG: &s,
|
|
iface1: iface1,
|
|
iface2: iface2,
|
|
filter: filter,
|
|
autosense: autosenseInterface,
|
|
}
|
|
}
|
|
|
|
func (obj *ProxyObj) Start() {
|
|
go obj.start()
|
|
}
|
|
func (obj *ProxyObj) start() {
|
|
obj.stopWG.Add(1)
|
|
defer func() {
|
|
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)
|
|
defer close(req_iface1_sol_iface2)
|
|
go listen(obj.iface1, req_iface1_sol_iface2, ndp_SOL, 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)
|
|
defer close(req_iface2_sol_iface1)
|
|
go listen(obj.iface2, req_iface2_sol_iface1, ndp_SOL, 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)
|
|
defer close(req_iface1_adv_iface2)
|
|
go listen(obj.iface1, req_iface1_adv_iface2, ndp_ADV, 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)
|
|
defer close(req_iface2_adv_iface1)
|
|
go listen(obj.iface2, req_iface2_adv_iface1, ndp_ADV, 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.Printf("Started Proxy instance on interfaces %s and %s (if enabled, the whitelist is applied on %s)", obj.iface1, obj.iface2, obj.iface2)
|
|
fmt.Println()
|
|
<-obj.stopChan
|
|
}
|
|
|
|
//Stop a running Proxy instance
|
|
// Returns false on error
|
|
func (obj *ProxyObj) Stop() bool {
|
|
close(obj.stopChan)
|
|
fmt.Println("Shutting down proxy instance..")
|
|
if wgWaitTimout(obj.stopWG, 10*time.Second) {
|
|
fmt.Println("Done")
|
|
return true
|
|
} else {
|
|
fmt.Println("Error shutting down instance")
|
|
return false
|
|
}
|
|
}
|
|
|
|
// 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
|
|
}
|
|
s := strings.Split(f, ";")
|
|
result := make([]*net.IPNet, len(s))
|
|
for i, n := range s {
|
|
_, cidr, err := net.ParseCIDR(n)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
result[i] = cidr
|
|
}
|
|
return result
|
|
}
|
|
|
|
func wgWaitTimout(wg *sync.WaitGroup, timeout time.Duration) bool {
|
|
t := make(chan struct{})
|
|
go func() {
|
|
defer close(t)
|
|
wg.Wait()
|
|
}()
|
|
select {
|
|
case <-t:
|
|
return true
|
|
case <-time.After(timeout):
|
|
return false
|
|
}
|
|
}
|