mirror of
https://github.com/lucheng0127/virtuallan.git
synced 2025-12-24 13:17:50 +08:00
Windows supported
Support build windows client. Use in windows, make sure you have been install tap-windows properly. After launch, the windows client will now listen udp multicast so if you need to config routes by yourself
This commit is contained in:
8
Makefile
8
Makefile
@@ -12,8 +12,12 @@ build: gen
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf virtuallan
|
||||
rm -rf virtuallan virtuallan.exe
|
||||
|
||||
.PHONY: build-docker
|
||||
build-docker: gen
|
||||
$(CONTAINER_TOOL) build -t ${IMG} .
|
||||
$(CONTAINER_TOOL) build -t ${IMG} .
|
||||
|
||||
.PHONY: build-windows
|
||||
build-windows:
|
||||
GOOS=windows GOARCH=amd64 go build -o virtuallan.exe main.go
|
||||
@@ -41,14 +41,13 @@ make
|
||||
```
|
||||
.\devcon.exe OemVista.inf tap0901
|
||||
```
|
||||
3. Checkout to win branch
|
||||
4. Build a windows exe
|
||||
3. Build a windows exe
|
||||
```
|
||||
GOOS=windows GOARCH=amd64 go build -o virtuallan.exe main.go
|
||||
make build-windows
|
||||
```
|
||||
5. Launch virtuallan.exe
|
||||
4. Launch virtuallan.exe
|
||||

|
||||
6. Enjoy it
|
||||
5. Enjoy it
|
||||

|
||||

|
||||
|
||||
|
||||
6
main.go
6
main.go
@@ -11,12 +11,16 @@ import (
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Commands: []*cli.Command{
|
||||
vcli.NewServerCmd(),
|
||||
vcli.NewClientCmd(),
|
||||
vcli.NewUserCmd(),
|
||||
},
|
||||
}
|
||||
|
||||
serverCmd := vcli.NewServerCmd()
|
||||
if serverCmd != nil {
|
||||
app.Commands = append(app.Commands, serverCmd)
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Panic(err)
|
||||
os.Exit(1)
|
||||
|
||||
9
pkg/cli/server_windows.go
Normal file
9
pkg/cli/server_windows.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func NewServerCmd() *cli.Command {
|
||||
return nil
|
||||
}
|
||||
206
pkg/client/client_windows.go
Normal file
206
pkg/client/client_windows.go
Normal file
@@ -0,0 +1,206 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/lucheng0127/virtuallan/pkg/cipher"
|
||||
"github.com/lucheng0127/virtuallan/pkg/packet"
|
||||
"github.com/lucheng0127/virtuallan/pkg/utils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
func GetLoginInfo() (string, string, error) {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
fmt.Println("Username:")
|
||||
user, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
fmt.Println("Password:")
|
||||
bytePasswd, err := term.ReadPassword(int(syscall.Stdin))
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
passwd := string(bytePasswd)
|
||||
|
||||
return strings.TrimSpace(user), strings.TrimSpace(passwd), nil
|
||||
}
|
||||
|
||||
func checkLoginTimeout(c chan string) {
|
||||
select {
|
||||
case <-c:
|
||||
return
|
||||
case <-time.After(10 * time.Second):
|
||||
log.Error("login timeout")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func handleSignal(conn *net.UDPConn, sigChan chan os.Signal) {
|
||||
sig := <-sigChan
|
||||
log.Infof("received signal: %v, send fin pkt to close conn\n", sig)
|
||||
finPkt := packet.NewFinPkt()
|
||||
|
||||
stream, err := finPkt.Encode()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
_, err = conn.Write(stream)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func Run(cCtx *cli.Context) error {
|
||||
logLevel := cCtx.String("log-level")
|
||||
|
||||
switch strings.ToUpper(logLevel) {
|
||||
case "DEBUG":
|
||||
log.SetLevel(log.DebugLevel)
|
||||
case "INFO":
|
||||
log.SetLevel(log.InfoLevel)
|
||||
case "WARN":
|
||||
log.SetLevel(log.WarnLevel)
|
||||
default:
|
||||
log.SetLevel(log.InfoLevel)
|
||||
}
|
||||
|
||||
var user, passwd string
|
||||
|
||||
if err := cipher.SetAESKey(cCtx.String("key")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cCtx.String("passwd") == "" || cCtx.String("user") == "" {
|
||||
u, p, err := GetLoginInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user = u
|
||||
passwd = p
|
||||
} else {
|
||||
user = cCtx.String("user")
|
||||
passwd = cCtx.String("passwd")
|
||||
}
|
||||
|
||||
udpAddr, err := net.ResolveUDPAddr("udp4", cCtx.String("target"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conn, err := net.DialUDP("udp4", nil, udpAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Handle signal
|
||||
sigChan := make(chan os.Signal, 8)
|
||||
signal.Notify(sigChan, os.Interrupt)
|
||||
go handleSignal(conn, sigChan)
|
||||
|
||||
// Do auth
|
||||
ipChan := make(chan string)
|
||||
netToIface := make(chan *packet.VLPkt, 1024)
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(3)
|
||||
|
||||
// Handle udp packet
|
||||
go func() {
|
||||
for {
|
||||
var buf [65535]byte
|
||||
n, _, err := conn.ReadFromUDP(buf[:])
|
||||
|
||||
if err != nil {
|
||||
log.Error("read from conn ", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if n < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
pkt, err := packet.Decode(buf[:n])
|
||||
if err != nil {
|
||||
log.Error("parse packet ", err)
|
||||
continue
|
||||
}
|
||||
|
||||
switch pkt.Type {
|
||||
case packet.P_RESPONSE:
|
||||
switch pkt.VLBody.(*packet.RspBody).Code {
|
||||
case packet.RSP_AUTH_REQUIRED:
|
||||
log.Error("auth failed")
|
||||
os.Exit(1)
|
||||
case packet.RSP_IP_NOT_MATCH:
|
||||
log.Error("ip not match")
|
||||
os.Exit(1)
|
||||
case packet.RSP_USER_LOGGED:
|
||||
log.Error("user already logged by other endpoint")
|
||||
os.Exit(1)
|
||||
default:
|
||||
continue
|
||||
}
|
||||
case packet.P_RAW:
|
||||
netToIface <- pkt
|
||||
case packet.P_DHCP:
|
||||
ipAddr := pkt.VLBody.(*packet.KeepaliveBody).Parse()
|
||||
ipChan <- ipAddr
|
||||
default:
|
||||
log.Debug("unknow stream, do nothing")
|
||||
continue
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Auth
|
||||
authPkt := packet.NewAuthPkt(user, passwd)
|
||||
authStream, err := authPkt.Encode()
|
||||
if err != nil {
|
||||
log.Error("encode auth packet ", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
_, err = conn.Write(authStream)
|
||||
if err != nil {
|
||||
log.Error("send auth packet ", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
authChan := make(chan string, 1)
|
||||
go checkLoginTimeout(authChan)
|
||||
|
||||
// Waiting for dhcp ip
|
||||
ipAddr := <-ipChan
|
||||
authChan <- "ok"
|
||||
log.Infof("auth with %s succeed, endpoint ip %s\n", user, ipAddr)
|
||||
|
||||
iface, err := utils.NewTap(ipAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Send keepalive
|
||||
go DoKeepalive(conn, ipAddr, 10)
|
||||
|
||||
// Switch io between udp net and tap interface
|
||||
go HandleConn(iface, netToIface, conn)
|
||||
|
||||
wg.Wait()
|
||||
return nil
|
||||
}
|
||||
92
pkg/utils/interface_windows.go
Normal file
92
pkg/utils/interface_windows.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"os/exec"
|
||||
|
||||
"github.com/songgao/water"
|
||||
)
|
||||
|
||||
const (
|
||||
UNKNOW_IP = "UNKNOW_IP"
|
||||
)
|
||||
|
||||
type LinkMessages struct {
|
||||
InterfaceName string
|
||||
RX_SIZE string
|
||||
TX_SIZE string
|
||||
RX_PKT uint64
|
||||
TX_PKT uint64
|
||||
}
|
||||
|
||||
func RandStr(n int) string {
|
||||
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
|
||||
b := make([]rune, n)
|
||||
for i := range b {
|
||||
b[i] = letters[rand.Intn(len(letters))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func GetMacFromIP(ip net.IP) net.HardwareAddr {
|
||||
ip = ip.To4()
|
||||
return net.HardwareAddr{0x60, 0xe2, ip[0], ip[1], ip[2], ip[3]}
|
||||
}
|
||||
|
||||
//func SetMACAddress(interfaceName, macAddress string) error {
|
||||
// // Disable the network interface
|
||||
// disableCmd := exec.Command("netsh", "interface", "set", "interface", interfaceName, "admin=disable")
|
||||
// if err := disableCmd.Run(); err != nil {
|
||||
// return fmt.Errorf("failed to disable interface: %v", err)
|
||||
// }
|
||||
//
|
||||
// // Set the new MAC address
|
||||
// setMacCmd := exec.Command("netsh", "interface", "set", "interface", interfaceName, "newmac", macAddress)
|
||||
// if err := setMacCmd.Run(); err != nil {
|
||||
// return fmt.Errorf("failed to set MAC address: %v", err)
|
||||
// }
|
||||
//
|
||||
// // Enable the network interface
|
||||
// enableCmd := exec.Command("netsh", "interface", "set", "interface", interfaceName, "admin=enable")
|
||||
// if err := enableCmd.Run(); err != nil {
|
||||
// return fmt.Errorf("failed to enable interface: %v", err)
|
||||
// }
|
||||
//
|
||||
// return nil
|
||||
//}
|
||||
|
||||
func NewTap(ipAddr string) (*water.Interface, error) {
|
||||
// Create tap
|
||||
config := new(water.Config)
|
||||
config.DeviceType = water.TAP
|
||||
config.PlatformSpecificParams = water.PlatformSpecificParams{
|
||||
ComponentID: "tap0901",
|
||||
}
|
||||
|
||||
iface, err := water.New(*config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create tap %s", err.Error())
|
||||
}
|
||||
|
||||
ip, ipNet, err := net.ParseCIDR(ipAddr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse cidr %s %s", ipAddr, err.Error())
|
||||
}
|
||||
|
||||
// Add ip to tap
|
||||
netMask := fmt.Sprintf("%d.%d.%d.%d", ipNet.Mask[0], ipNet.Mask[1], ipNet.Mask[2], ipNet.Mask[3])
|
||||
cmd := exec.Command("netsh", "interface", "ip", "set", "address", fmt.Sprintf("name=%s", iface.Name()), "static", ip.String(), netMask)
|
||||
if err := cmd.Run(); err != nil {
|
||||
return nil, fmt.Errorf("assign ip %s to %s %s", ipAddr, iface.Name(), err.Error())
|
||||
}
|
||||
|
||||
// Set tap mac
|
||||
//mac := GetMacFromIP(ip)
|
||||
//if err := SetMACAddress(iface.Name(), mac.String()); err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
return iface, nil
|
||||
}
|
||||
Reference in New Issue
Block a user