3.18.4 virtual private network
This commit is contained in:
+292
@@ -0,0 +1,292 @@
|
||||
package openp2p
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type PacketHeader struct {
|
||||
version int
|
||||
// src uint32
|
||||
// prot uint8
|
||||
protocol byte
|
||||
dst uint32
|
||||
port uint16
|
||||
}
|
||||
|
||||
func parseHeader(b []byte, h *PacketHeader) error {
|
||||
if len(b) < 20 {
|
||||
return fmt.Errorf("small packet")
|
||||
}
|
||||
h.version = int(b[0] >> 4)
|
||||
h.protocol = byte(b[9])
|
||||
if h.version == 4 {
|
||||
h.dst = binary.BigEndian.Uint32(b[16:20])
|
||||
} else if h.version != 6 {
|
||||
return fmt.Errorf("unknown version in ip header:%d", h.version)
|
||||
}
|
||||
if h.protocol == 6 || h.protocol == 17 { // TCP or UDP
|
||||
h.port = binary.BigEndian.Uint16(b[22:24])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type sdwanNode struct {
|
||||
name string
|
||||
id uint64
|
||||
}
|
||||
|
||||
type p2pSDWAN struct {
|
||||
nodeName string
|
||||
tun *optun
|
||||
sysRoute sync.Map // ip:sdwanNode
|
||||
subnet *net.IPNet
|
||||
gateway net.IP
|
||||
virtualIP *net.IPNet
|
||||
internalRoute *IPTree
|
||||
}
|
||||
|
||||
func (s *p2pSDWAN) init(name string) error {
|
||||
if gConf.getSDWAN().Gateway == "" {
|
||||
gLog.Println(LvDEBUG, "not in sdwan clear all ")
|
||||
}
|
||||
if s.internalRoute == nil {
|
||||
s.internalRoute = NewIPTree("")
|
||||
}
|
||||
|
||||
s.nodeName = name
|
||||
s.gateway, s.subnet, _ = net.ParseCIDR(gConf.getSDWAN().Gateway)
|
||||
for _, node := range gConf.getDelNodes() {
|
||||
gLog.Println(LvDEBUG, "deal deleted node: ", node.Name)
|
||||
delRoute(node.IP, s.gateway.String())
|
||||
s.internalRoute.Del(node.IP, node.IP)
|
||||
ipNum, _ := inetAtoN(node.IP)
|
||||
s.sysRoute.Delete(ipNum)
|
||||
gConf.delete(AppConfig{SrcPort: 0, PeerNode: node.Name})
|
||||
GNetwork.DeleteApp(AppConfig{SrcPort: 0, PeerNode: node.Name})
|
||||
arr := strings.Split(node.Resource, ",")
|
||||
for _, r := range arr {
|
||||
_, ipnet, err := net.ParseCIDR(r)
|
||||
if err != nil {
|
||||
// fmt.Println("Error parsing CIDR:", err)
|
||||
continue
|
||||
}
|
||||
if ipnet.Contains(net.ParseIP(gConf.Network.localIP)) { // local ip and resource in the same lan
|
||||
continue
|
||||
}
|
||||
minIP := ipnet.IP
|
||||
maxIP := make(net.IP, len(minIP))
|
||||
copy(maxIP, minIP)
|
||||
for i := range minIP {
|
||||
maxIP[i] = minIP[i] | ^ipnet.Mask[i]
|
||||
}
|
||||
s.internalRoute.Del(minIP.String(), maxIP.String())
|
||||
delRoute(ipnet.String(), s.gateway.String())
|
||||
}
|
||||
}
|
||||
for _, node := range gConf.getAddNodes() {
|
||||
gLog.Println(LvDEBUG, "deal add node: ", node.Name)
|
||||
ipNet := &net.IPNet{
|
||||
IP: net.ParseIP(node.IP),
|
||||
Mask: s.subnet.Mask,
|
||||
}
|
||||
if node.Name == s.nodeName {
|
||||
s.virtualIP = ipNet
|
||||
gLog.Println(LvINFO, "start tun ", ipNet.String())
|
||||
err := s.StartTun()
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "start tun error:", err)
|
||||
return err
|
||||
}
|
||||
gLog.Println(LvINFO, "start tun ok")
|
||||
allowTunForward()
|
||||
addRoute(s.subnet.String(), s.gateway.String(), s.tun.tunName)
|
||||
// addRoute("255.255.255.255/32", s.gateway.String(), s.tun.tunName) // for broadcast
|
||||
// addRoute("224.0.0.0/4", s.gateway.String(), s.tun.tunName) // for multicast
|
||||
initSNATRule(s.subnet.String()) // for network resource
|
||||
continue
|
||||
}
|
||||
ip, err := inetAtoN(ipNet.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.sysRoute.Store(ip, &sdwanNode{name: node.Name, id: NodeNameToID(node.Name)})
|
||||
s.internalRoute.AddIntIP(ip, ip, &sdwanNode{name: node.Name, id: NodeNameToID(node.Name)})
|
||||
}
|
||||
for _, node := range gConf.getAddNodes() {
|
||||
if node.Name == s.nodeName { // not deal resource itself
|
||||
continue
|
||||
}
|
||||
if len(node.Resource) > 0 {
|
||||
gLog.Printf(LvINFO, "deal add node: %s resource: %s", node.Name, node.Resource)
|
||||
arr := strings.Split(node.Resource, ",")
|
||||
for _, r := range arr {
|
||||
// add internal route
|
||||
_, ipnet, err := net.ParseCIDR(r)
|
||||
if err != nil {
|
||||
fmt.Println("Error parsing CIDR:", err)
|
||||
continue
|
||||
}
|
||||
if ipnet.Contains(net.ParseIP(gConf.Network.localIP)) { // local ip and resource in the same lan
|
||||
continue
|
||||
}
|
||||
minIP := ipnet.IP
|
||||
maxIP := make(net.IP, len(minIP))
|
||||
copy(maxIP, minIP)
|
||||
for i := range minIP {
|
||||
maxIP[i] = minIP[i] | ^ipnet.Mask[i]
|
||||
}
|
||||
s.internalRoute.Add(minIP.String(), maxIP.String(), &sdwanNode{name: node.Name, id: NodeNameToID(node.Name)})
|
||||
// add sys route
|
||||
addRoute(ipnet.String(), s.gateway.String(), s.tun.tunName)
|
||||
}
|
||||
}
|
||||
}
|
||||
gConf.retryAllMemApp()
|
||||
gLog.Printf(LvINFO, "sdwan init ok")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *p2pSDWAN) run() {
|
||||
s.sysRoute.Range(func(key, value interface{}) bool {
|
||||
node := value.(*sdwanNode)
|
||||
GNetwork.ConnectNode(node.name)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func (s *p2pSDWAN) readNodeLoop() {
|
||||
gLog.Printf(LvDEBUG, "sdwan readNodeLoop start")
|
||||
defer gLog.Printf(LvDEBUG, "sdwan readNodeLoop end")
|
||||
writeBuff := make([][]byte, 1)
|
||||
for {
|
||||
nd := GNetwork.ReadNode(time.Second * 10) // TODO: read multi packet
|
||||
if nd == nil {
|
||||
gLog.Printf(LvDev, "waiting for node data")
|
||||
continue
|
||||
}
|
||||
head := PacketHeader{}
|
||||
parseHeader(nd.Data, &head)
|
||||
gLog.Printf(LvDev, "write tun dst ip=%s,len=%d", net.IP{byte(head.dst >> 24), byte(head.dst >> 16), byte(head.dst >> 8), byte(head.dst)}.String(), len(nd.Data))
|
||||
if PIHeaderSize == 0 {
|
||||
writeBuff[0] = nd.Data
|
||||
} else {
|
||||
writeBuff[0] = make([]byte, PIHeaderSize+len(nd.Data))
|
||||
copy(writeBuff[0][PIHeaderSize:], nd.Data)
|
||||
}
|
||||
|
||||
len, err := s.tun.Write(writeBuff, PIHeaderSize)
|
||||
if err != nil {
|
||||
gLog.Printf(LvDEBUG, "write tun dst ip=%s,len=%d,error:%s", net.IP{byte(head.dst >> 24), byte(head.dst >> 16), byte(head.dst >> 8), byte(head.dst)}.String(), len, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func isBroadcastOrMulticast(ipUint32 uint32, subnet *net.IPNet) bool {
|
||||
// return ipUint32 == 0xffffffff || (byte(ipUint32) == 0xff) || (ipUint32>>28 == 0xe)
|
||||
return ipUint32 == 0xffffffff || (ipUint32>>28 == 0xe) // 225.255.255.255/32, 224.0.0.0/4
|
||||
}
|
||||
|
||||
func (s *p2pSDWAN) routeTunPacket(p []byte, head *PacketHeader) {
|
||||
var node *sdwanNode
|
||||
// v, ok := s.routes.Load(ih.dst)
|
||||
v, ok := s.internalRoute.Load(head.dst)
|
||||
if !ok || v == nil {
|
||||
if isBroadcastOrMulticast(head.dst, s.subnet) {
|
||||
gLog.Printf(LvDev, "multicast ip=%s", net.IP{byte(head.dst >> 24), byte(head.dst >> 16), byte(head.dst >> 8), byte(head.dst)}.String())
|
||||
GNetwork.WriteBroadcast(p)
|
||||
}
|
||||
return
|
||||
} else {
|
||||
node = v.(*sdwanNode)
|
||||
}
|
||||
|
||||
err := GNetwork.WriteNode(node.id, p)
|
||||
if err != nil {
|
||||
gLog.Printf(LvDev, "write packet to %s fail: %s", node.name, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *p2pSDWAN) readTunLoop() {
|
||||
gLog.Printf(LvDEBUG, "sdwan readTunLoop start")
|
||||
defer gLog.Printf(LvDEBUG, "sdwan readTunLoop end")
|
||||
readBuff := make([][]byte, ReadTunBuffNum)
|
||||
for i := 0; i < ReadTunBuffNum; i++ {
|
||||
readBuff[i] = make([]byte, ReadTunBuffSize+PIHeaderSize)
|
||||
}
|
||||
readBuffSize := make([]int, ReadTunBuffNum)
|
||||
ih := PacketHeader{}
|
||||
for {
|
||||
n, err := s.tun.Read(readBuff, readBuffSize, PIHeaderSize)
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "read tun fail: ", err)
|
||||
return
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
if readBuffSize[i] > ReadTunBuffSize {
|
||||
gLog.Printf(LvERROR, "read tun overflow: len=", readBuffSize[i])
|
||||
continue
|
||||
}
|
||||
parseHeader(readBuff[i][PIHeaderSize:readBuffSize[i]+PIHeaderSize], &ih)
|
||||
gLog.Printf(LvDev, "read tun dst ip=%s,len=%d", net.IP{byte(ih.dst >> 24), byte(ih.dst >> 16), byte(ih.dst >> 8), byte(ih.dst)}.String(), readBuffSize[0])
|
||||
s.routeTunPacket(readBuff[i][PIHeaderSize:readBuffSize[i]+PIHeaderSize], &ih)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *p2pSDWAN) StartTun() error {
|
||||
sdwan := gConf.getSDWAN()
|
||||
if s.tun == nil {
|
||||
tun := &optun{}
|
||||
err := tun.Start(s.virtualIP.String(), &sdwan)
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "open tun fail:", err)
|
||||
return err
|
||||
}
|
||||
s.tun = tun
|
||||
go s.readTunLoop()
|
||||
go s.readNodeLoop() // multi-thread read will cause packets out of order, resulting in slower speeds
|
||||
}
|
||||
err := setTunAddr(s.tun.tunName, s.virtualIP.String(), sdwan.Gateway, s.tun.dev)
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "setTunAddr error:%s,%s,%s,%s", err, s.tun.tunName, s.virtualIP.String(), sdwan.Gateway)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleSDWAN(subType uint16, msg []byte) error {
|
||||
gLog.Printf(LvDEBUG, "handle sdwan msg type:%d", subType)
|
||||
var err error
|
||||
switch subType {
|
||||
case MsgSDWANInfoRsp:
|
||||
rsp := SDWANInfo{}
|
||||
if err = json.Unmarshal(msg[openP2PHeaderSize:], &rsp); err != nil {
|
||||
return ErrMsgFormat
|
||||
}
|
||||
gLog.Println(LvINFO, "sdwan init:", prettyJson(rsp))
|
||||
if runtime.GOOS == "android" {
|
||||
AndroidSDWANConfig <- msg[openP2PHeaderSize:]
|
||||
}
|
||||
// GNetwork.sdwan.detail = &rsp
|
||||
gConf.setSDWAN(rsp)
|
||||
err = GNetwork.sdwan.init(gConf.Network.Node)
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "sdwan init fail: ", err)
|
||||
if GNetwork.sdwan.tun != nil {
|
||||
GNetwork.sdwan.tun.Stop()
|
||||
GNetwork.sdwan.tun = nil
|
||||
return err
|
||||
}
|
||||
}
|
||||
go GNetwork.sdwan.run()
|
||||
default:
|
||||
}
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user