Last active
October 30, 2022 10:12
-
-
Save warriorpaw/6858526fad5d2fe25a016fda141806a1 to your computer and use it in GitHub Desktop.
vxlan package mix up to fool some distributed deep packet inspection ;; Just for fun
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bytes" | |
"crypto/aes" | |
"crypto/cipher" | |
"crypto/hmac" | |
"crypto/rand" | |
"crypto/sha256" | |
"encoding/binary" | |
"encoding/json" | |
"flag" | |
"fmt" | |
"io" | |
"io/ioutil" | |
pseudoRand "math/rand" | |
"net" | |
"os" | |
"os/exec" | |
"os/signal" | |
"runtime" | |
"strconv" | |
"sync" | |
"syscall" | |
"time" | |
"unsafe" | |
"github.com/google/gopacket" | |
"github.com/google/gopacket/layers" | |
"github.com/songgao/water" | |
"golang.org/x/net/bpf" | |
"golang.org/x/net/ipv4" | |
) | |
/* | |
config example | |
{ | |
"tunnels":[ | |
{ | |
"type":"udp", | |
"config":"{\"port\":111,\"peer\":{\"ip\":\"1.1.1.1\",\"port\":23}}", | |
"vxlanId":1, | |
"remoteId":0 | |
}, | |
{ | |
"type":"tap", | |
"config":"{\"name\":\"vxlan111\",\"ip\":\"1.1.1.1/24\"}", | |
"vxlanId":2, | |
"remoteId":0 | |
}, | |
{ | |
"type":"tun", | |
"config":"{\"name\":\"vxlan111\",\"ip\":\"1.1.1.1/24\",\"p2p\":\"2.2.2.2/24\"}", | |
"vxlanId":3, | |
"remoteId":0 | |
} | |
], | |
"transporter":{ | |
"remotes":[ | |
{ | |
"RemoteId":0, | |
"ipPorts":[ | |
{ | |
"ip":"", | |
"port":0 | |
}, | |
{ | |
"ip":"", | |
"port":0 | |
} | |
] | |
}, | |
{ | |
"RemoteId":0, | |
"ipPorts":[ | |
{ | |
"ip":"", | |
"port":0 | |
}, | |
{ | |
"ip":"", | |
"port":0 | |
} | |
] | |
} | |
], | |
"tba-interval": 120, | |
"port":0, | |
"listen-ip":"0.0.0.0", | |
"send-ip":"0,0,0,0", | |
"send-port-start": 10000, | |
"send-port-end": 60000, | |
"key":"", | |
"outMtu":1450 | |
} | |
} | |
*/ | |
func errorPanic(err error, format string, a ...interface{}) { | |
if err != nil { | |
fmt.Printf("Panic : "+format, a...) | |
panic(err) | |
} | |
} | |
func errorPrint(format string, a ...interface{}) { | |
if a != nil && len(a) > 0 { | |
fmt.Printf("Error : "+format, a...) | |
} else { | |
fmt.Printf("Error : " + format) | |
} | |
} | |
type vxlanHeader struct { | |
flags uint16 | |
group uint16 | |
vxlanId uint32 | |
} | |
type IpPort struct { | |
Ip string `json:"ip"` | |
Port int `json:"port"` | |
} | |
type TunnelConfig struct { | |
TunnelPeerType string `json:"type"` | |
TunnelPeerConfig string `json:"config"` | |
VxlanId uint8 `json:"vxlanId"` | |
RemoteId int `json:"remoteId"` | |
} | |
type RemoteIpPorts struct { | |
RemoteId int `json:id` | |
IpPorts []IpPort `json:"ipPorts"` | |
} | |
type TransporterConfig struct { | |
Remotes []RemoteIpPorts `json:"remotes"` | |
ReceivingPort int `json:"port"` | |
ListenIp string `json:"listen-ip"` | |
SendIp string `json:"send-ip"` | |
SendPortStart uint16 `json:"send-port-start"` | |
SendPortEnd uint16 `json:"send-port-end"` | |
Key string `json:"key"` | |
OutMtu int `json:"outMtu"` | |
TbaInterval int64 `json:"tba-interval"` | |
} | |
type Config struct { | |
Tunnels []TunnelConfig `json:"tunnels"` | |
Transporter TransporterConfig `json:"transporter"` | |
} | |
func (c *Config) load(f string) { | |
jsonBytes, err := ioutil.ReadFile(f) | |
errorPanic(err, "open file %s error %+v \n", f, err) | |
err = json.Unmarshal(jsonBytes, c) | |
errorPanic(err, "Unmarshal file %s error %+v \n", f, err) | |
// no input legality check, just panic in New if config is miss some value. | |
} | |
const nonceSize int = 12 | |
const overHeadSize int = 16 | |
const udpIpHeader int = 28 | |
const saltOne string = "YourFirstSalt" | |
const saltTwo string = "YourSecondSalt" | |
type TimeBasedAeadCipher struct { | |
memPool *sync.Pool | |
hashedKey []byte | |
interval int64 | |
stopChan chan interface{} | |
sync.RWMutex | |
current cipher.AEAD | |
toleration cipher.AEAD | |
} | |
func CountToKey(TOTCount int64, k []byte) []byte { | |
sig := hmac.New(sha256.New, k) | |
sig.Write([]byte(strconv.FormatInt(TOTCount, 16))) | |
salter := sha256.New() | |
salter.Write(sig.Sum(nil)) | |
salter.Write([]byte(saltTwo)) | |
return salter.Sum(nil) | |
} | |
func (c *TimeBasedAeadCipher) getTOTEncryptKey(TOTCount int64) (current []byte, toleration []byte) { | |
if TOTCount%2 == 0 { | |
// tolerate delay package | |
current = CountToKey(TOTCount, c.hashedKey) | |
toleration = CountToKey(TOTCount-2, c.hashedKey) | |
} else { | |
// tolerate future package | |
current = CountToKey(TOTCount-1, c.hashedKey) | |
toleration = CountToKey(TOTCount+1, c.hashedKey) | |
} | |
return | |
} | |
func newAesGcm(key []byte) (ret cipher.AEAD, err error) { | |
block, err := aes.NewCipher(key) | |
if err != nil { | |
return | |
} | |
ret, err = cipher.NewGCM(block) | |
return | |
} | |
func (c *TimeBasedAeadCipher) updateCipher(now int64) { | |
cKey, tKey := c.getTOTEncryptKey(now / c.interval) | |
cAead, err := newAesGcm(cKey) | |
errorPanic(err, "newAesGcm error") | |
tAead, err := newAesGcm(tKey) | |
errorPanic(err, "newAesGcm error") | |
c.Lock() | |
defer c.Unlock() | |
c.current = cAead | |
c.toleration = tAead | |
} | |
func (c *TimeBasedAeadCipher) nextDuration() (ret time.Duration) { | |
now := time.Now() | |
nowSec := now.Unix() | |
nextTimer := time.Unix(nowSec+c.interval-nowSec%c.interval, 0) | |
ret = nextTimer.Sub(now) | |
return | |
} | |
func (c *TimeBasedAeadCipher) updateCipherTimer() { | |
stopChan := c.stopChan | |
t := time.NewTimer(c.nextDuration()) | |
for { | |
select { | |
case <-t.C: | |
c.updateCipher(time.Now().Unix()) | |
t.Reset(c.nextDuration()) | |
case <-stopChan: | |
return | |
} | |
} | |
} | |
func newTimeBasedAead(k string, interval int64, stopChan chan interface{}, p *sync.Pool) *TimeBasedAeadCipher { | |
ret := TimeBasedAeadCipher{} | |
ret.memPool = p | |
ret.stopChan = stopChan | |
ret.interval = interval / 2 | |
salter := sha256.New() | |
salter.Write([]byte(k)) | |
salter.Write([]byte(saltOne)) | |
ret.hashedKey = salter.Sum(nil) | |
ret.updateCipher(time.Now().Unix()) | |
go ret.updateCipherTimer() | |
return &ret | |
} | |
func getNonce(nonce []byte) (err error) { | |
// TODO: reduce nonce size. since we use TBKey | |
_, err = io.ReadFull(rand.Reader, nonce) | |
return | |
} | |
func (c *TimeBasedAeadCipher) Encrypt(udpPackage []byte) ([]byte, error) { | |
// TODO: make nonceSize transparent to the upper layer | |
plainData := udpPackage[nonceSize:] | |
if cap(plainData) < len(plainData)+overHeadSize { | |
errorPrint("udpPackage has no enough cap") | |
return udpPackage, fmt.Errorf("udpPackage has no enough cap") | |
} | |
cipherPackage := c.memPool.Get().([]byte) | |
cipherData := cipherPackage[nonceSize:] | |
nonce := cipherPackage[:nonceSize] | |
getNonce(nonce) | |
c.RLock() | |
currentAead := c.current | |
c.RUnlock() | |
cipherData = currentAead.Seal(cipherData[:0], nonce, plainData, nil) | |
c.memPool.Put(udpPackage) | |
return cipherPackage[:len(cipherData)+nonceSize], nil | |
} | |
func (c *TimeBasedAeadCipher) Decrypt(udpPackage []byte) ([]byte, error) { | |
cipherData := udpPackage[nonceSize:] | |
nonce := udpPackage[:nonceSize] | |
decryptedPackage := c.memPool.Get().([]byte) | |
plainData := decryptedPackage[nonceSize:] | |
c.RLock() | |
currentAead := c.current | |
tolerationAead := c.toleration | |
c.RUnlock() | |
var data []byte | |
var err error | |
if data, err = currentAead.Open(plainData[:0], nonce, cipherData, nil); err != nil { | |
if data, err = tolerationAead.Open(plainData[:0], nonce, cipherData, nil); err != nil { | |
errorPrint("[%s] Decrypt msg error: %v, drop package", time.Now().String(), err) | |
return udpPackage, err | |
} | |
} | |
c.memPool.Put(udpPackage) | |
// TODO: make nonceSize transparent to the upper layer | |
return decryptedPackage[:len(data)+nonceSize], nil | |
} | |
type RawUdpConn struct { | |
c net.PacketConn | |
srcIp net.IP | |
} | |
func NewRawUdpConn(srcIp string, bufferSize int, sendOnly bool) (ret *RawUdpConn, err error) { | |
conn, err := net.ListenPacket("ip4:udp", srcIp) | |
if err != nil { | |
return | |
} | |
ipConn := conn.(*net.IPConn) | |
if sendOnly { | |
ipConn.SetReadBuffer(0) | |
} else { | |
ipConn.SetReadBuffer(bufferSize) | |
} | |
ipConn.SetWriteBuffer(bufferSize) | |
if sendOnly { | |
filter := []bpf.Instruction{ | |
bpf.RetConstant{Val: 0x0}, | |
} | |
var assembled []bpf.RawInstruction | |
if assembled, err = bpf.Assemble(filter); err != nil { | |
return | |
} | |
ipv4.NewPacketConn(conn).SetBPF(assembled) | |
} | |
ret = &RawUdpConn{} | |
ret.c = conn | |
ret.srcIp = net.ParseIP(srcIp) | |
return | |
} | |
func (r *RawUdpConn) Close() { | |
if r.c != nil { | |
r.c.Close() | |
} | |
} | |
func (r *RawUdpConn) Read(buffer []byte) (n int, err error) { | |
n, _, err = r.c.ReadFrom(buffer) | |
return | |
} | |
func (r *RawUdpConn) Write(srcPort uint16, dstIp net.IP, dstPort uint16, payload []byte) (n int, err error) { | |
ip := &layers.IPv4{ | |
Version: 4, | |
TTL: 64, | |
SrcIP: r.srcIp, | |
DstIP: dstIp, | |
Protocol: layers.IPProtocolUDP, | |
} | |
udp := &layers.UDP{ | |
SrcPort: layers.UDPPort(srcPort), | |
DstPort: layers.UDPPort(dstPort), | |
} | |
udp.SetNetworkLayerForChecksum(ip) | |
buf := gopacket.NewSerializeBuffer() | |
opts := gopacket.SerializeOptions{ | |
ComputeChecksums: true, | |
FixLengths: true, | |
} | |
err = gopacket.SerializeLayers(buf, opts, udp, gopacket.Payload(payload)) | |
if err != nil { | |
return | |
} | |
n, err = r.c.WriteTo(buf.Bytes(), &net.IPAddr{IP: dstIp, Zone: ""}) | |
return | |
} | |
type SendData struct { | |
data []byte | |
remoteId int | |
} | |
type TunnelChan struct { | |
recvChan chan []byte | |
sendChan chan []byte | |
remoteId int | |
} | |
type Transporter struct { | |
memPool *sync.Pool | |
sendConn *RawUdpConn | |
recvConn *net.UDPConn | |
upRemotes map[int][]*net.UDPAddr | |
tbAead *TimeBasedAeadCipher | |
conf *TransporterConfig | |
TunnelChans []TunnelChan | |
sendCipherChan chan SendData | |
sendPlainChan chan SendData | |
recvCipherChan chan []byte | |
stopChan chan interface{} | |
outMtu int | |
} | |
func getUdpConn(port int, ip net.IP) (ret *net.UDPConn, err error) { | |
ret, err = net.ListenUDP("udp", &net.UDPAddr{IP: ip, Port: port}) | |
return | |
} | |
func newTransporter(c *Config) *Transporter { | |
var err error = nil | |
ret := &Transporter{} | |
ret.conf = &c.Transporter | |
ret.memPool = &sync.Pool{New: func() interface{} { return make([]byte, c.Transporter.OutMtu-udpIpHeader) }} | |
ret.upRemotes = make(map[int][]*net.UDPAddr) | |
for _, r := range ret.conf.Remotes { | |
ret.upRemotes[r.RemoteId] = make([]*net.UDPAddr, len(r.IpPorts)) | |
for pos, ipPort := range r.IpPorts { | |
ret.upRemotes[r.RemoteId][pos] = &net.UDPAddr{IP: net.ParseIP(ipPort.Ip), Port: ipPort.Port} | |
} | |
} | |
ret.recvConn, err = getUdpConn(ret.conf.ReceivingPort, net.ParseIP(ret.conf.ListenIp)) | |
errorPanic(err, "creat Transporter recvConn error \n") | |
ret.sendConn, err = NewRawUdpConn(ret.conf.SendIp, 20*1024*1024, true) | |
errorPanic(err, "NewRawUdpConn error \n") | |
ret.stopChan = make(chan interface{}, 2) | |
ret.tbAead = newTimeBasedAead(ret.conf.Key, ret.conf.TbaInterval, ret.stopChan, ret.getMemPool()) | |
ret.sendCipherChan = make(chan SendData, 5000) | |
ret.sendPlainChan = make(chan SendData, 5000) | |
ret.recvCipherChan = make(chan []byte, 5000) | |
ret.TunnelChans = make([]TunnelChan, 256) | |
ret.outMtu = ret.conf.OutMtu | |
for pos := range c.Tunnels { | |
vxlanId := c.Tunnels[pos].VxlanId | |
ret.TunnelChans[vxlanId].recvChan = make(chan []byte, 5000) | |
ret.TunnelChans[vxlanId].sendChan = make(chan []byte, 5000) | |
ret.TunnelChans[vxlanId].remoteId = c.Tunnels[pos].RemoteId | |
if _, ok := ret.upRemotes[c.Tunnels[pos].RemoteId]; !ok { | |
errorPanic(fmt.Errorf("remote id error"), "remote id error") | |
} | |
go ret.handleTunnelChan(vxlanId) | |
} | |
go ret.handleSend() | |
for i := 0; i < runtime.NumCPU(); i++ { | |
go ret.handleEncrypt() | |
} | |
for i := 0; i < runtime.NumCPU(); i++ { | |
go ret.handleDecrypt() | |
} | |
go ret.handleRecv() | |
return ret | |
} | |
func (s *Transporter) handleTunnelChan(vxlanId uint8) { | |
sendChan := s.TunnelChans[vxlanId].sendChan | |
stopChan := s.stopChan | |
sendData := SendData{} | |
sendData.remoteId = s.TunnelChans[vxlanId].remoteId | |
sendPlainChan := s.sendPlainChan | |
outMtu := s.outMtu | |
for { | |
select { | |
case udpPackage, ok := <-sendChan: | |
if ok == false { | |
errorPrint("Transporter TunnelChans broken, exit\n") | |
return | |
} | |
n := len(udpPackage) | |
if (n + overHeadSize + 1) > (outMtu - udpIpHeader) { | |
errorPrint("package is too large, pls reduce your vxlan mtu\n") | |
continue | |
} | |
if n < (20 + nonceSize) { | |
errorPrint("package is too small\n") | |
continue | |
} | |
udpPackage = udpPackage[:n+1] | |
udpPackage[n] = vxlanId | |
sendData.data = udpPackage | |
sendPlainChan <- sendData | |
case <-stopChan: | |
return | |
} | |
} | |
} | |
func (s *Transporter) handleSend() { | |
cipherChan := s.sendCipherChan | |
portStart := s.conf.SendPortStart | |
portLen := int(s.conf.SendPortEnd - s.conf.SendPortStart) | |
remotes := s.upRemotes | |
stopChan := s.stopChan | |
sendConn := s.sendConn | |
for { | |
select { | |
case udpPackage, ok := <-cipherChan: | |
if ok == false { | |
errorPrint("upLink cipherChan broken, exit\n") | |
return | |
} | |
remote := remotes[udpPackage.remoteId] | |
srcPort := uint16(pseudoRand.Intn(portLen)) + portStart | |
remoteAddr := remote[pseudoRand.Intn(len(remote))] | |
if _, err := sendConn.Write(srcPort, remoteAddr.IP, uint16(remoteAddr.Port), udpPackage.data); err != nil { | |
errorPrint("up link sendConn.Write error %+v \n", err) | |
} | |
s.memPool.Put(udpPackage.data) | |
case <-stopChan: | |
return | |
} | |
} | |
} | |
func (s *Transporter) handleEncrypt() { | |
plainChan := s.sendPlainChan | |
cipherChan := s.sendCipherChan | |
aead := s.tbAead | |
stopChan := s.stopChan | |
for { | |
select { | |
case udpPackage, ok := <-plainChan: | |
if ok == false { | |
errorPrint("upLink plainChan broken, exit\n") | |
return | |
} | |
if cipherData, err := aead.Encrypt(udpPackage.data); err != nil { | |
errorPrint("Encrypt error, drop package \n") | |
} else { | |
udpPackage.data = cipherData | |
cipherChan <- udpPackage | |
} | |
case <-stopChan: | |
return | |
} | |
} | |
} | |
func (s *Transporter) handleRecv() { | |
listenConn := s.recvConn | |
cipherChan := s.recvCipherChan | |
outMtu := s.outMtu | |
for { | |
data := s.memPool.Get().([]byte) | |
data = data[:outMtu-udpIpHeader] | |
n, _, err := listenConn.ReadFromUDP(data) | |
if err != nil { | |
errorPrint("error in downLink listenConn.ReadFromUDP %+v \n", err) | |
return | |
} | |
if n < nonceSize+overHeadSize+20+1 { | |
errorPrint("size error, drop package \n") | |
continue | |
} | |
cipherChan <- data[:n] | |
} | |
} | |
func (s *Transporter) handleDecrypt() { | |
cipherChan := s.recvCipherChan | |
aead := s.tbAead | |
stopChan := s.stopChan | |
for { | |
select { | |
case udpPackage, ok := <-cipherChan: | |
if ok == false { | |
errorPrint("upLink plainChan broken\n") | |
return | |
} | |
if plainPackage, err := aead.Decrypt(udpPackage); err != nil { | |
errorPrint("Encrypt error, drop package \n") | |
} else { | |
packageLen := len(plainPackage) | |
vxlanId := plainPackage[packageLen-1] | |
if s.TunnelChans[vxlanId].recvChan == nil { | |
errorPrint("vxlanId is error \n") | |
} else { | |
s.TunnelChans[vxlanId].recvChan <- plainPackage[:packageLen-1] | |
} | |
} | |
case <-stopChan: | |
return | |
} | |
} | |
} | |
func (s *Transporter) getTunnelChan(vxlanId uint8) *TunnelChan { | |
return &s.TunnelChans[vxlanId] | |
} | |
func (s *Transporter) getMemPool() *sync.Pool { | |
return s.memPool | |
} | |
func (s *Transporter) stop() { | |
close(s.stopChan) | |
close(s.sendCipherChan) | |
close(s.sendPlainChan) | |
close(s.recvCipherChan) | |
s.recvConn.Close() | |
s.sendConn.Close() | |
for _, c := range s.TunnelChans { | |
if c.sendChan != nil { | |
close(c.recvChan) | |
close(c.sendChan) | |
} | |
} | |
} | |
type TunnelPeer interface { | |
recvFrom([]byte) (int, error) | |
sendTo([]byte) (int, error) | |
close() | |
} | |
type UdpPeerConfig struct { | |
ListenPort int `json:"port"` | |
Peer IpPort `json:"peer"` | |
} | |
type UdpPeer struct { | |
listenConn *net.UDPConn | |
peerAddr *net.UDPAddr | |
vxlanHeader []byte | |
config UdpPeerConfig | |
} | |
func newUdpPeer(confStr string, vxlanId uint8) TunnelPeer { | |
ret := &UdpPeer{} | |
err := json.Unmarshal([]byte(confStr), &ret.config) | |
errorPanic(err, "Unmarshal newUdpPeer confStr %s error %+v \n", confStr, err) | |
// make vxlan header | |
h := &vxlanHeader{flags: 0x0800, group: 0, vxlanId: uint32(vxlanId) * 256} | |
buf := new(bytes.Buffer) | |
err = binary.Write(buf, binary.BigEndian, h) | |
errorPanic(err, "binary.Write error \n") | |
ret.vxlanHeader = buf.Bytes() | |
// get port | |
ret.listenConn, err = getUdpConn(ret.config.ListenPort, net.ParseIP("0.0.0.0")) | |
errorPanic(err, "creat Tunnel listenConn error \n") | |
ret.peerAddr = &net.UDPAddr{IP: net.ParseIP(ret.config.Peer.Ip), Port: ret.config.Peer.Port} | |
return ret | |
} | |
func (udp *UdpPeer) recvFrom(data []byte) (int, error) { | |
n, _, err := udp.listenConn.ReadFromUDP(data) | |
return n, err | |
} | |
func (udp *UdpPeer) sendTo(data []byte) (int, error) { | |
copy(data[nonceSize-8:nonceSize], udp.vxlanHeader) | |
return udp.listenConn.WriteToUDP(data[nonceSize-8:], udp.peerAddr) | |
} | |
func (udp *UdpPeer) close() { | |
udp.listenConn.Close() | |
} | |
type TapPeerConfig struct { | |
TapName string `json:"name"` | |
TapIp string `json:"ip"` | |
P2PIp string `json:"p2p"` | |
} | |
type TapPeer struct { | |
tapIF *water.Interface | |
config TapPeerConfig | |
} | |
type ifReqMtu struct { | |
Name [0x10]byte | |
Mtu int32 | |
pad [0x28 - 0x10 - 4]byte | |
} | |
func newTapPeer(confStr string, outMtu int, isTap bool) TunnelPeer { | |
ret := &TapPeer{} | |
err := json.Unmarshal([]byte(confStr), &ret.config) | |
errorPanic(err, "Unmarshal newTapPeer confStr %s error %+v \n", confStr, err) | |
// new tap | |
tapConfig := water.Config{} | |
mtuReduce := 0 | |
if !isTap { | |
tapConfig.DeviceType = water.TUN | |
mtuReduce = udpIpHeader + nonceSize + overHeadSize + 1 | |
} else { | |
tapConfig.DeviceType = water.TAP | |
mtuReduce = udpIpHeader + 14 + nonceSize + overHeadSize + 1 | |
} | |
tapConfig.Name = ret.config.TapName | |
ret.tapIF, err = water.New(tapConfig) | |
errorPanic(err, "water.New if error") | |
// set mtu | |
var req ifReqMtu | |
req.Mtu = int32(outMtu - mtuReduce) | |
copy(req.Name[:], []byte(ret.config.TapName)) | |
tmpUdp, err := getUdpConn(0, net.ParseIP("0.0.0.0")) | |
errorPanic(err, "error in get udp") | |
defer tmpUdp.Close() | |
fileUdp, _ := tmpUdp.File() | |
fd := fileUdp.Fd() | |
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.SIOCSIFMTU), uintptr(unsafe.Pointer(&req))) | |
if errno != 0 { | |
fmt.Printf("SIOCSIFMTU syscall return %d\n", errno) | |
panic(fmt.Errorf("SIOCSIFMTU syscall return %d\n", errno)) | |
} | |
// set ip on tap | |
cmd := exec.Command("ip", "addr", "add", ret.config.TapIp, "dev", ret.config.TapName) | |
err = cmd.Run() | |
errorPanic(err, "set ip error %+v \n", err) | |
if !isTap { | |
// set p2p ip | |
if len(ret.config.P2PIp) == 0 { | |
errorPanic(fmt.Errorf("tun dev need p2p ip"), "tun dev need p2p ip") | |
} | |
cmd := exec.Command("ifconfig", ret.config.TapName, "pointopoint", ret.config.P2PIp) | |
err = cmd.Run() | |
errorPanic(err, "set p2p ip error %+v \n", err) | |
} | |
// set tap up | |
cmd = exec.Command("ip", "link", "set", ret.config.TapName, "up") | |
err = cmd.Run() | |
errorPanic(err, "set link up error %+v \n", err) | |
return ret | |
} | |
func (tap *TapPeer) recvFrom(data []byte) (int, error) { | |
n, err := tap.tapIF.Read(data[nonceSize:]) | |
if err != nil { | |
errorPrint("error in handleSend tapIF.Read %+v \n", err) | |
return n, err | |
} | |
return n + nonceSize, err | |
} | |
func (tap *TapPeer) sendTo(data []byte) (int, error) { | |
n := len(data) | |
sendBytes, err := tap.tapIF.Write(data[nonceSize:]) | |
if sendBytes != (n-nonceSize) || err != nil { | |
errorPrint("tapIF.Write err %d -> %d, %+v \n", n, sendBytes, err) | |
return sendBytes, err | |
} | |
return n, err | |
} | |
func (tap *TapPeer) close() { | |
tap.tapIF.Close() | |
} | |
type Tunnel struct { | |
memPool *sync.Pool | |
peer TunnelPeer | |
tunnelChan *TunnelChan | |
conf *TunnelConfig | |
outMtu int | |
stopChan chan interface{} | |
} | |
func newTunnel(c *TunnelConfig, tunnelChan *TunnelChan, p *sync.Pool, outMtu int) *Tunnel { | |
ret := &Tunnel{} | |
ret.tunnelChan = tunnelChan | |
ret.conf = c | |
ret.memPool = p | |
ret.outMtu = outMtu | |
switch c.TunnelPeerType { | |
case "udp": | |
ret.peer = newUdpPeer(c.TunnelPeerConfig, c.VxlanId) | |
case "tap": | |
ret.peer = newTapPeer(c.TunnelPeerConfig, outMtu, true) | |
case "tun": | |
ret.peer = newTapPeer(c.TunnelPeerConfig, outMtu, false) | |
default: | |
panic(fmt.Errorf("newTunnel unknow type %s\n", c.TunnelPeerType)) | |
} | |
ret.stopChan = make(chan interface{}, 2) | |
go ret.handleSendToPeer() | |
go ret.handleListen() | |
return ret | |
} | |
func (s *Tunnel) handleSendToPeer() { | |
plainChan := s.tunnelChan.recvChan | |
stopChan := s.stopChan | |
peer := s.peer | |
for { | |
select { | |
case udpPackage, ok := <-plainChan: | |
if ok == false { | |
errorPrint("Tunnel plainChan broken \n") | |
return | |
} | |
if _, err := peer.sendTo(udpPackage); err != nil { | |
errorPrint("Tunnel peer.sendTo error %+v \n", err) | |
} | |
s.memPool.Put(udpPackage) | |
case <-stopChan: | |
return | |
} | |
} | |
} | |
func (s *Tunnel) handleListen() { | |
peer := s.peer | |
sendChan := s.tunnelChan.sendChan | |
outMtu := s.outMtu | |
for { | |
data := s.memPool.Get().([]byte) | |
data = data[:outMtu-udpIpHeader] | |
n, err := peer.recvFrom(data) | |
if err != nil { | |
errorPrint("error in Tunnel peer.recvFrom %+v \n", err) | |
return | |
} | |
sendChan <- data[:n] | |
} | |
} | |
func (s *Tunnel) stop() { | |
close(s.stopChan) | |
s.peer.close() | |
} | |
func main() { | |
configPath := "" | |
flag.StringVar(&configPath, "c", "./conf.json", "config file path") | |
flag.Parse() | |
pseudoRand.Seed(time.Now().Unix()) | |
c := &Config{} | |
c.load(configPath) | |
transporter := newTransporter(c) | |
tunnels := make([]*Tunnel, len(c.Tunnels)) | |
for pos := range c.Tunnels { | |
tunnels[pos] = newTunnel(&c.Tunnels[pos], transporter.getTunnelChan(c.Tunnels[pos].VxlanId), transporter.getMemPool(), c.Transporter.OutMtu) | |
} | |
signalChan := make(chan os.Signal, 2) | |
signal.Notify(signalChan, os.Interrupt) | |
<-signalChan | |
transporter.stop() | |
for _, tunnel := range tunnels { | |
tunnel.stop() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment