Pndpd/pndp/flow.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
}
}