Created
September 4, 2018 14:02
-
-
Save warriorpaw/85c26a928c01cf4233ea3198dc71a09c to your computer and use it in GitHub Desktop.
Fake vxlan "server": use tap interface to simulate vxlan for kernel < 3.7 AND ipv4 only; on "client" side set this "server" as remote; hardcode VxlanId/ip/MTU etc...;; Do not use this in production environment ; 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 ( | |
"github.com/songgao/water" | |
"net" | |
"fmt" | |
"os" | |
"os/signal" | |
"syscall" | |
"unsafe" | |
"bytes" | |
"encoding/binary" | |
"os/exec" | |
"sync" | |
) | |
type vxlanHeader struct { | |
Flags uint16 | |
Group uint16 | |
VxlanId uint32 | |
} | |
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) | |
} | |
} | |
func getUdpConn(port int) (ret *net.UDPConn, err error) { | |
ret, err = net.ListenUDP("udp", &net.UDPAddr{IP: net.ParseIP("0.0.0.0"), Port: port}) | |
return | |
} | |
type ifReqMtu struct { | |
Name [0x10]byte | |
Mtu int32 | |
pad [0x28 - 0x10 - 4]byte | |
} | |
var innerIpMap = make(map[uint32]net.IP) | |
var mapLock sync.RWMutex | |
func getPackageOffset(p []byte) int { | |
if p[12] == 0x81 && p[13] == 0x00 { | |
return 4 | |
} else if p[12] == 0x88 && p[13] == 0xa8 { | |
return 8 | |
} | |
return 0 | |
} | |
func parseTargetIP(p []byte) (ret net.IP, err error) { | |
offset := getPackageOffset(p) | |
err = nil | |
if p[12+offset] == 0x08 && p[13+offset] == 0x00 { | |
// ipv4 | |
ipPos := 14 + offset + 16 | |
ret = net.IP(p[ipPos : ipPos+4 : ipPos+4]) | |
return | |
} else if p[12+offset] == 0x08 && p[13+offset] == 0x06 { | |
// arp | |
if p[14+offset+2] == 0x08 && p[14+offset+3] == 0x00 { | |
// arp for ipv4 | |
ipPos := 14 + offset + 24 | |
ret = net.IP(p[ipPos : ipPos+4 : ipPos+4]) | |
return | |
} | |
} | |
ret = nil | |
err = fmt.Errorf("unsupported package") | |
return | |
} | |
func parseSenderIP(p []byte) (ret net.IP, err error) { | |
offset := getPackageOffset(p) | |
err = nil | |
if p[12+offset] == 0x08 && p[13+offset] == 0x00 { | |
// ipv4 | |
ipPos := 14 + offset + 12 | |
ret = net.IP(p[ipPos : ipPos+4 : ipPos+4]) | |
return | |
} else if p[12+offset] == 0x08 && p[13+offset] == 0x06 { | |
// arp | |
if p[14+offset+2] == 0x08 && p[14+offset+3] == 0x00 { | |
// arp for ipv4 | |
ipPos := 14 + offset + 14 | |
ret = net.IP(p[ipPos : ipPos+4 : ipPos+4]) | |
return | |
} | |
} | |
ret = nil | |
err = fmt.Errorf("unsupported package") | |
return | |
} | |
func setMTU() { | |
var req ifReqMtu | |
req.Mtu = 1350 | |
// Physical interface MTU - 50 | |
copy(req.Name[:], []byte("vxlan0")) | |
tmpUdp, err := getUdpConn(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)) | |
} | |
} | |
func handleRecv(s *net.UDPConn, tapIF *water.Interface) { | |
data := make([]byte, 1500) | |
for { | |
data = data[0:1500] | |
n, addr, err := s.ReadFromUDP(data) | |
if err != nil { | |
errorPrint("error in handleRecv s.ReadFromUDP %+v \n", err) | |
return | |
} | |
if n < 42 { // min vxlan 8 + inner eth 14 + ip 20 | |
errorPrint("size error, drop package \n") | |
continue | |
} | |
var h vxlanHeader | |
buf := bytes.NewReader(data[:8]) | |
err = binary.Read(buf, binary.BigEndian, &h) | |
if err != nil { | |
errorPrint("error in binary.Read %+v \n", err) | |
continue | |
} | |
if h.VxlanId != 512 { | |
// VxlanId hardcode in 2;; ignore flags and Group | |
errorPrint("vxlanId not match %d \n", h.VxlanId) | |
continue | |
} | |
sendData := data[8:n] | |
ip, err := parseSenderIP(sendData) | |
if err == nil { | |
// fmt.Printf("recv from %s \n", ip.String()) | |
intIP := binary.BigEndian.Uint32([]byte(ip)) | |
v, exist := innerIpMap[intIP] | |
if exist == false || addr.IP.Equal(v) == false { | |
mapLock.Lock() | |
innerIpMap[intIP] = addr.IP | |
mapLock.Unlock() | |
} | |
} | |
sendBytes, err := tapIF.Write(sendData) | |
if sendBytes != (n-8) || err != nil { | |
errorPrint("tapIF.Write err %d -> , %+v \n", n, sendBytes, err) | |
} | |
} | |
} | |
func handleSend(s *net.UDPConn, tapIF *water.Interface) { | |
data := make([]byte, 1500) | |
h := &vxlanHeader{Flags: 0x0800, Group: 0, VxlanId: 2 * 256} | |
// VxlanId hardcode in 2 | |
buf := new(bytes.Buffer) | |
err := binary.Write(buf, binary.BigEndian, h) | |
errorPanic(err, "binary.Write error \n") | |
copy(data[:8], buf.Bytes()) | |
for { | |
n, err := tapIF.Read(data[8:]) | |
if err != nil { | |
errorPrint("error in handleSend tapIF.Read %+v \n", err) | |
return | |
} | |
ip, err := parseTargetIP(data[8:]) | |
if err != nil { | |
// errorPrint("only ipv4 supported\n") | |
continue | |
} | |
// fmt.Printf("sendto %s \n", ip.String()) | |
intIP := binary.BigEndian.Uint32([]byte(ip)) | |
mapLock.RLock() | |
remoteIP, exist := innerIpMap[intIP] | |
mapLock.RUnlock() | |
if exist == false { | |
// errorPrint("remote ip not found \n") | |
continue | |
} | |
remote := &net.UDPAddr{IP: remoteIP, Port: 4789} | |
sendData := data[:n+8] | |
n, err = s.WriteToUDP(sendData, remote) | |
if err != nil { | |
errorPrint("error in handleSend s.WriteToUDP %+v \n", err) | |
continue | |
} | |
} | |
} | |
func main() { | |
config := water.Config{ | |
DeviceType: water.TAP, | |
} | |
config.Name = "vxlan0" | |
tapIF, err := water.New(config) | |
errorPanic(err, "water.New if error") | |
setMTU() | |
cmd := exec.Command("ip", "addr", "add", "172.30.60.1/24", "dev", "vxlan0") | |
err = cmd.Run() | |
errorPanic(err, "set ip error %+v \n", err) | |
cmd = exec.Command("ip", "link", "set", "vxlan0", "up") | |
err = cmd.Run() | |
errorPanic(err, "set link up error %+v \n", err) | |
recvSocket, err := getUdpConn(4789) | |
errorPanic(err, "get socket error") | |
go handleRecv(recvSocket, tapIF) | |
go handleSend(recvSocket, tapIF) | |
signalChan := make(chan os.Signal, 2) | |
signal.Notify(signalChan, os.Interrupt) | |
<-signalChan | |
recvSocket.Close() | |
tapIF.Close() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment