Merge branch 'go'

This commit is contained in:
Cyberes 2023-06-20 12:08:03 -06:00
parent e895bf322a
commit 8051775b2c
15 changed files with 1023 additions and 0 deletions

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
.idea
config/*.sh
!config/*.sh.example
go/wlan2eth/wlan2eth
# ---> Python
# Byte-compiled / optimized / DLL files

12
go/README.md Normal file
View File

@ -0,0 +1,12 @@
`sudo apt install libpcap-dev gcc`
```bash
wlan2eth build wlan2eth.wlan2eth
sudo ./wlan2eth
```
If build fails, try `CGO_ENABLED=1`
# Issues
- To prevent forwarding duplicates, incoming hash of in packets are cached and checked against subsequent packets. This may cause issues where duplicate packets are blocked.

View File

@ -0,0 +1,63 @@
package arp
import (
"bytes"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/sirupsen/logrus"
"net"
"syscall"
"wlan2eth/logger"
)
func HandleARPRequest(packet gopacket.Packet, srcSocket, dstSocket int, srcName, dstName string, srcMAC, dstMAC net.HardwareAddr, wlan0MAC net.HardwareAddr, eth0 string) {
log := logger.GetLogger()
arpLayer := packet.Layer(layers.LayerTypeARP).(*layers.ARP)
ethLayer := packet.Layer(layers.LayerTypeEthernet).(*layers.Ethernet)
// Add the client to the bridgedClients map
srcIP := net.IP(arpLayer.SourceProtAddress)
srcHwAddr := arpLayer.SourceHwAddress
AddBridgedClient(srcIP, srcHwAddr, wlan0MAC, eth0)
// Check if the ARP request is an ARP announcement
isARPAnnouncement := bytes.Equal(arpLayer.SourceProtAddress, arpLayer.DstProtAddress)
// If the ARP request is an ARP announcement, use the MAC address of the wlan0 interface
if isARPAnnouncement {
arpLayer.SourceHwAddress = dstMAC
} else {
// Modify the Ethernet source MAC address to appear as if it originated from the wlan0 interface
arpLayer.SourceHwAddress = wlan0MAC // Use the MAC address of the wlan0 interface
}
// Create an Ethernet frame for the proxied ARP request
ethLayer.SrcMAC = wlan0MAC // Use the MAC address of the eth0 interface
// Serialize the proxied ARP request
buf := gopacket.NewSerializeBuffer()
opts := gopacket.SerializeOptions{
FixLengths: true,
ComputeChecksums: true,
}
err := gopacket.SerializeLayers(buf, opts, ethLayer, arpLayer)
if err != nil {
log.Errorf("Error serializing proxied ARP request: %v", err)
return
}
// Send the proxied ARP request on the destination socket
_, err = syscall.Write(dstSocket, buf.Bytes())
if err != nil {
log.Errorf("Error sending proxied ARP request on %s: %v", dstName, err)
return
}
log.WithFields(logrus.Fields{
"IP": srcIP.String(),
"MAC": net.HardwareAddr(srcHwAddr).String(),
}).Debugf("Forwarded ARP request from %s to %s\n", srcName, dstName)
//log.Debugf("Forwarded ARP request from %s to %s\n", srcName, dstName)
}

View File

@ -0,0 +1,79 @@
package arp
import (
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/sirupsen/logrus"
"net"
"syscall"
"wlan2eth/logger"
)
//var bridgedClients = make(map[string]net.HardwareAddr)
//var bridgedClientsLock sync.Mutex
func HandleARPResponse(packet gopacket.Packet, srcSocket, dstSocket int, srcName, dstName string, srcMAC, dstMAC net.HardwareAddr, eth0MAC net.HardwareAddr) {
log := logger.GetLogger()
arpLayer := packet.Layer(layers.LayerTypeARP).(*layers.ARP)
targetIP := net.IP(arpLayer.DstProtAddress)
clientMAC, ok := bridgedClients[targetIP.String()]
if !ok {
return // Not a bridged client, ignore the ARP response
}
// Forward the ARP response to the client
ethLayer := packet.Layer(layers.LayerTypeEthernet).(*layers.Ethernet)
ethLayer.DstMAC = clientMAC.MAC
ethLayer.SrcMAC = eth0MAC
arpLayer.DstHwAddress = clientMAC.MAC
arpLayer.SourceHwAddress = eth0MAC
// Serialize the modified ARP response
buf := gopacket.NewSerializeBuffer()
opts := gopacket.SerializeOptions{
FixLengths: true,
ComputeChecksums: true,
}
err := gopacket.SerializeLayers(buf, opts, ethLayer, arpLayer)
if err != nil {
log.Errorf("Error serializing ARP response: %v", err)
return
}
// Send the modified ARP response on the destination socket
_, err = syscall.Write(dstSocket, buf.Bytes())
if err != nil {
log.Errorf("Error sending ARP response on %s: %v", dstName, err)
return
}
log.WithFields(logrus.Fields{
"IP": targetIP.String(),
"MAC": clientMAC.StringMAC(),
}).Debugf("Forwarded ARP request from %s to %s\n", srcName, dstName)
//log.Debugf("Forwarded ARP response from %s to %s\n", srcName, dstName)
}
//func AddBridgedClient(ip net.IP, mac net.HardwareAddr, wlan0MAC net.HardwareAddr) {
// log := logger.GetLogger()
//
// // Ignore the wlan0 MAC address
// if bytes.Equal(mac, wlan0MAC) {
// return
// }
//
// bridgedClientsLock.Lock()
// defer bridgedClientsLock.Unlock()
//
// ipStr := ip.String()
// if _, exists := bridgedClients[ipStr]; !exists {
// bridgedClients[ipStr] = mac
// log.WithFields(logrus.Fields{
// "IP": ipStr,
// "MAC": mac,
// }).Info("Added bridged client")
// }
//}

114
go/wlan2eth/arp/clients.go Normal file
View File

@ -0,0 +1,114 @@
package arp
import (
"bytes"
"github.com/sirupsen/logrus"
"net"
"os/exec"
"sync"
"time"
"wlan2eth/logger"
)
type ClientInfo struct {
MAC net.HardwareAddr
Timestamp time.Time
}
func (c ClientInfo) StringMAC() string {
return c.MAC.String()
}
var bridgedClients = make(map[string]ClientInfo)
var bridgedClientsLock sync.Mutex
func AddBridgedClient(ip net.IP, mac net.HardwareAddr, wlan0MAC net.HardwareAddr, eth0 string) {
log := logger.GetLogger()
// Ignore the wlan0 MAC address
if bytes.Equal(mac, wlan0MAC) {
return
}
bridgedClientsLock.Lock()
defer bridgedClientsLock.Unlock()
ipStr := ip.String()
if clientInfo, exists := bridgedClients[ipStr]; !exists {
bridgedClients[ipStr] = ClientInfo{MAC: mac, Timestamp: time.Now()}
//AddClientRoutes(eth0)
log.WithFields(logrus.Fields{
"IP": ipStr,
"MAC": mac,
"Interface": eth0,
}).Info("Added bridged client")
} else {
// Update the timestamp if the client already exists
clientInfo.Timestamp = time.Now()
bridgedClients[ipStr] = clientInfo
}
}
func removeTimedOutClients(timeout time.Duration) {
log := logger.GetLogger()
bridgedClientsLock.Lock()
defer bridgedClientsLock.Unlock()
now := time.Now()
for ip, clientInfo := range bridgedClients {
if now.Sub(clientInfo.Timestamp) > timeout {
delete(bridgedClients, ip)
log.WithFields(logrus.Fields{
"IP": ip,
"MAC": clientInfo.StringMAC(),
}).Info("Removed timed-out bridged client")
}
}
}
func StartClientTimeoutChecker(timeout time.Duration, checkInterval time.Duration) {
ticker := time.NewTicker(checkInterval)
for range ticker.C {
removeTimedOutClients(timeout)
}
}
func GetClientMACByIP(ip net.IP) (net.HardwareAddr, bool) {
bridgedClientsLock.Lock()
defer bridgedClientsLock.Unlock()
ipStr := ip.String()
if clientInfo, exists := bridgedClients[ipStr]; exists {
return clientInfo.MAC, true
}
return nil, false
}
func AddClientToARPTable(ip net.IP, mac net.HardwareAddr, iface string) {
log := logger.GetLogger()
arpCmd := exec.Command("arp", "-i", iface, "-s", ip.String(), mac.String())
err := arpCmd.Run()
if err != nil {
log.Errorf("Error adding client to ARP table on %s: %v", iface, err)
} else {
log.Infof("Added client %s (%s) to ARP table on %s", ip.String(), mac.String(), iface)
}
}
func AddClientRoutes(eth0 string) {
log := logger.GetLogger()
bridgedClientsLock.Lock()
defer bridgedClientsLock.Unlock()
for ipStr := range bridgedClients {
cmd := exec.Command("ip", "route", "add", ipStr, "dev", eth0)
err := cmd.Run()
if err != nil {
log.Errorf("Error adding route for IP %s: %v", ipStr, err)
} else {
log.Infof("Added route for IP %s", ipStr)
}
}
}

View File

@ -0,0 +1,80 @@
package bridge
import (
"fmt"
"github.com/vishvananda/netlink"
"net"
"wlan2eth/logger"
)
func GetInterfacesHW(wlan0 string, eth0 string) (net.HardwareAddr, net.HardwareAddr) {
log := logger.GetLogger()
// Get the hardware address of the host's interfaces
var wlan0MAC net.HardwareAddr
var eth0MAC net.HardwareAddr
var err error
wlan0MAC, err = GetInterfaceMAC(wlan0)
if err != nil {
log.Fatalf("Error getting MAC address of %s: %v", wlan0, err)
}
eth0MAC, err = GetInterfaceMAC(eth0)
if err != nil {
log.Fatalf("Error getting MAC address of %s: %v", eth0, err)
}
return wlan0MAC, eth0MAC
}
func FlushIPAddresses(ifaceName string) error {
// Get the network interface by name
iface, err := netlink.LinkByName(ifaceName)
if err != nil {
return fmt.Errorf("failed to get interface %s: %v", ifaceName, err)
}
// Get all IP addresses associated with the interface
addrs, err := netlink.AddrList(iface, netlink.FAMILY_ALL)
if err != nil {
return fmt.Errorf("failed to get IP addresses for interface %s: %v", ifaceName, err)
}
// Iterate through the IP addresses and remove them
for _, addr := range addrs {
err := netlink.AddrDel(iface, &addr)
if err != nil {
return fmt.Errorf("failed to remove IP address %s from interface %s: %v", addr.IP.String(), ifaceName, err)
}
}
return nil
}
func GetIPByInterface(ifaceName string) net.IP {
log := logger.GetLogger()
iface, err := net.InterfaceByName(ifaceName)
if err != nil {
log.Errorf("Error getting interface by name %s: %v", ifaceName, err)
return nil
}
addrs, err := iface.Addrs()
if err != nil {
log.Errorf("Error getting addresses for interface %s: %v", ifaceName, err)
return nil
}
for _, addr := range addrs {
ip, _, err := net.ParseCIDR(addr.String())
if err != nil {
log.Errorf("Error parsing CIDR for address %s: %v", addr.String(), err)
continue
}
if ip.To4() != nil {
return ip
}
}
return nil
}

97
go/wlan2eth/bridge/net.go Normal file
View File

@ -0,0 +1,97 @@
package bridge
import (
"bufio"
"fmt"
"github.com/mdlayher/arp"
"net"
"net/netip"
"os"
"strconv"
"strings"
"syscall"
)
func GetInterfaceMAC(interfaceName string) (net.HardwareAddr, error) {
iface, err := net.InterfaceByName(interfaceName)
if err != nil {
return nil, err
}
return iface.HardwareAddr, nil
}
func getInterfaceMTU(ifaceName string) (int, error) {
iface, err := net.InterfaceByName(ifaceName)
if err != nil {
return 0, err
}
return iface.MTU, nil
}
func getDefaultGatewayIP() (net.IP, error) {
file, err := os.Open("/proc/net/route")
if err != nil {
return nil, err
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
fields := strings.Fields(scanner.Text())
if len(fields) < 3 || fields[0] == "Iface" {
continue
}
dest, err := strconv.ParseUint(fields[1], 16, 32)
if err != nil {
return nil, err
}
if dest == 0 {
gateway, err := strconv.ParseUint(fields[2], 16, 32)
if err != nil {
return nil, err
}
return net.IPv4(byte(gateway), byte(gateway>>8), byte(gateway>>16), byte(gateway>>24)), nil
}
}
return nil, fmt.Errorf("default gateway not found")
}
func GetGatewayMAC(wlan0 string) (net.HardwareAddr, error) {
// Get the default gateway IP
gatewayIP, err := getDefaultGatewayIP()
if err != nil {
return nil, err
}
// Convert gatewayIP (net.IP) to netaddr.IP
gatewayIPAddr, err := netip.ParseAddr(gatewayIP.String())
// Get the network interface by name
iface, err := net.InterfaceByName(wlan0)
if err != nil {
return nil, fmt.Errorf("error getting network interface: %v", err)
}
// Create an ARP client using the network interface
client, err := arp.Dial(iface)
if err != nil {
return nil, fmt.Errorf("error creating ARP client: %v", err)
}
defer client.Close()
// Resolve the gateway's MAC address using ARP
gatewayMAC, err := client.Resolve(gatewayIPAddr)
if err != nil {
return nil, fmt.Errorf("error resolving gateway MAC address: %v", err)
}
return gatewayMAC, nil
}
func EnablePathMTUDiscovery(srcSocket int, srcName string) error {
return syscall.SetsockoptInt(srcSocket, syscall.IPPROTO_IP, syscall.IP_MTU_DISCOVER, syscall.IP_PMTUDISC_DO)
}

View File

@ -0,0 +1,38 @@
package bridge
import (
"net"
"syscall"
)
func CreateRawSocket(ifaceName string) (int, error) {
// Create a raw socket
sock, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, int(htons(syscall.ETH_P_ALL)))
if err != nil {
return 0, err
}
// Get the interface index
iface, err := net.InterfaceByName(ifaceName)
if err != nil {
syscall.Close(sock)
return 0, err
}
// Bind the socket to the interface
addr := syscall.SockaddrLinklayer{
Protocol: htons(syscall.ETH_P_ALL),
Ifindex: iface.Index,
}
err = syscall.Bind(sock, &addr)
if err != nil {
syscall.Close(sock)
return 0, err
}
return sock, nil
}
func htons(i uint16) uint16 {
return (i<<8)&0xff00 | i>>8
}

View File

@ -0,0 +1,66 @@
package dhcp
import (
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"net"
"syscall"
"wlan2eth/logger"
)
func HandleDHCPRequest(packet gopacket.Packet, srcSocket, dstSocket int, srcName, dstName string, srcMAC, dstMAC net.HardwareAddr, modifyClientSrc bool) {
log := logger.GetLogger()
// Get the DHCP layer
dhcpLayer := packet.Layer(layers.LayerTypeDHCPv4)
if dhcpLayer == nil {
return
}
dhcp := dhcpLayer.(*layers.DHCPv4)
// Store the client's MAC address and the transaction ID in the map
transactionIDToClientMAC[dhcp.Xid] = dhcp.ClientHWAddr
// Modify the DHCP client hardware address to appear as if it originated from the wlan0 interface
if modifyClientSrc {
dhcp.ClientHWAddr = dstMAC
}
// Get the Ethernet layer and cast it to its type
ethLayer := packet.Layer(layers.LayerTypeEthernet).(*layers.Ethernet)
// Modify the Ethernet source MAC address to appear as if it originated from the wlan0 interface
ethLayer.SrcMAC = dstMAC
// Get the IPv4 and UDP layers and cast them to their respective types
ipLayer := packet.Layer(layers.LayerTypeIPv4).(*layers.IPv4)
udpLayer := packet.Layer(layers.LayerTypeUDP).(*layers.UDP)
// Set the network layer for the UDP checksum computation
err := udpLayer.SetNetworkLayerForChecksum(ipLayer)
if err != nil {
log.Fatal("Failed to set the network layer for the UDP checksum computation in handleDHCPRequest(). This is a bug???")
return
}
// Serialize the modified packet
buf := gopacket.NewSerializeBuffer()
opts := gopacket.SerializeOptions{
FixLengths: true,
ComputeChecksums: true,
}
err = gopacket.SerializeLayers(buf, opts, ethLayer, ipLayer, udpLayer, dhcp)
if err != nil {
log.Errorf("Error serializing DHCP request: %v", err)
return
}
// Send the modified DHCP request on the destination socket
_, err = syscall.Write(dstSocket, buf.Bytes())
if err != nil {
log.Errorf("Error sending DHCP request on %s: %v", dstName, err)
return
}
log.Debugf("Forwarded DHCP request from %s to %s\n", srcName, dstName)
}

View File

@ -0,0 +1,73 @@
package dhcp
import (
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"net"
"syscall"
"wlan2eth/arp"
"wlan2eth/logger"
)
func HandleDHCPResponse(packet gopacket.Packet, srcSocket, dstSocket int, srcName, dstName string, srcMAC, dstMAC net.HardwareAddr, wlan0MAC net.HardwareAddr, modifyClientSrc bool, eth0 string) {
log := logger.GetLogger()
// Get the DHCP layer
dhcpLayer := packet.Layer(layers.LayerTypeDHCPv4)
if dhcpLayer == nil {
log.Error("DHCP layer not found in the packet")
return
}
// Update the client's IP address in the bridgedClients map
dhcp := dhcpLayer.(*layers.DHCPv4)
clientIP := dhcp.YourClientIP
if !clientIP.IsUnspecified() {
arp.AddBridgedClient(clientIP, dhcp.ClientHWAddr, wlan0MAC, eth0)
}
clientMAC, ok := transactionIDToClientMAC[dhcp.Xid]
if !ok {
log.Errorf("Unknown transaction ID: %v", dhcp.Xid)
return
}
// Set the address the DHCP packet to clientMAC (a bridged client behind eth0)
if modifyClientSrc {
dhcp.ClientHWAddr = clientMAC
}
// Get the Ethernet layer and cast it to its type
ethLayer := packet.Layer(layers.LayerTypeEthernet).(*layers.Ethernet)
// Modify the Ethernet source MAC address of the DHCP packet to clientMAC
ethLayer.SrcMAC = clientMAC
// Get the IPv4 and UDP layers and cast them to their respective types
ipLayer := packet.Layer(layers.LayerTypeIPv4).(*layers.IPv4)
udpLayer := packet.Layer(layers.LayerTypeUDP).(*layers.UDP)
// Set the network layer for the UDP checksum computation
udpLayer.SetNetworkLayerForChecksum(ipLayer)
// Serialize the modified packet
buf := gopacket.NewSerializeBuffer()
opts := gopacket.SerializeOptions{
FixLengths: true,
ComputeChecksums: true,
}
err := gopacket.SerializeLayers(buf, opts, ethLayer, ipLayer, udpLayer, dhcp)
if err != nil {
log.Errorf("Error serializing DHCP response: %v", err)
return
}
// Send the modified DHCP response on the destination socket
_, err = syscall.Write(dstSocket, buf.Bytes())
if err != nil {
log.Errorf("Error sending DHCP response on %s: %v", dstName, err)
return
}
log.Debugf("Forwarded DHCP response from %s to %s\n", srcName, dstName)
}

View File

@ -0,0 +1,5 @@
package dhcp
import "net"
var transactionIDToClientMAC = make(map[uint32]net.HardwareAddr)

21
go/wlan2eth/go.mod Normal file
View File

@ -0,0 +1,21 @@
module wlan2eth
go 1.20
require (
github.com/google/gopacket v1.1.19
github.com/mdlayher/arp v0.0.0-20220512170110-6706a2966875
github.com/sirupsen/logrus v1.9.3
github.com/vishvananda/netlink v1.1.0
)
require (
github.com/josharian/native v1.1.0 // indirect
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118 // indirect
github.com/mdlayher/packet v1.1.2 // indirect
github.com/mdlayher/socket v0.4.1 // indirect
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect
golang.org/x/net v0.11.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.9.0 // indirect
)

61
go/wlan2eth/go.sum Normal file
View File

@ -0,0 +1,61 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/mdlayher/arp v0.0.0-20220512170110-6706a2966875 h1:ql8x//rJsHMjS+qqEag8n3i4azw1QneKh5PieH9UEbY=
github.com/mdlayher/arp v0.0.0-20220512170110-6706a2966875/go.mod h1:kfOoFJuHWp76v1RgZCb9/gVUc7XdY877S2uVYbNliGc=
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118 h1:2oDp6OOhLxQ9JBoUuysVz9UZ9uI6oLUbvAZu0x8o+vE=
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118/go.mod h1:ZFUnHIVchZ9lJoWoEGUg8Q3M4U8aNNWA3CVSUTkW4og=
github.com/mdlayher/packet v1.0.0/go.mod h1:eE7/ctqDhoiRhQ44ko5JZU2zxB88g+JH/6jmnjzPjOU=
github.com/mdlayher/packet v1.1.2 h1:3Up1NG6LZrsgDVn6X4L9Ge/iyRyxFEFD9o6Pr3Q1nQY=
github.com/mdlayher/packet v1.1.2/go.mod h1:GEu1+n9sG5VtiRE4SydOmX5GTwyyYlteZiFU+x0kew4=
github.com/mdlayher/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E=
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -0,0 +1,32 @@
package logger
import (
"github.com/sirupsen/logrus"
)
type ColorFormatter struct {
}
var log *logrus.Logger
func init() {
log = logrus.New()
// Set log output format
log.SetFormatter(&logrus.TextFormatter{})
customFormatter := new(logrus.TextFormatter)
customFormatter.TimestampFormat = "2006-01-02 15:04:05"
customFormatter.FullTimestamp = true
log.SetFormatter(customFormatter)
}
// InitLogger initializes the global logger with the specified log level
func InitLogger(logLevel logrus.Level) {
log.SetLevel(logLevel)
}
// GetLogger returns the global logger instance
func GetLogger() *logrus.Logger {
return log
}

281
go/wlan2eth/wlan2eth.go Normal file
View File

@ -0,0 +1,281 @@
package main
import (
"bytes"
"flag"
"fmt"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/sirupsen/logrus"
"net"
"os"
"syscall"
"time"
"wlan2eth/arp"
"wlan2eth/bridge"
"wlan2eth/dhcp"
"wlan2eth/logger"
)
type config struct {
mode string
debug bool
packets bool
dhcpModifySrc bool
eth string
wlan string
help bool
}
var onlyDHCP bool
var dhcpModifyClientSrc bool
var printPackets bool
var wlan0 string
var wlan0MAC net.HardwareAddr
var eth0 string
var eth0MAC net.HardwareAddr
var gatewayMAC net.HardwareAddr
var log *logrus.Logger
// TODO: don't let the bridged client see the host's traffic
// TODO: faster detecting of bridged clients
// TODO: manually fragment packets to fix the error `Error writing packet to wlan0: message too long`
func main() {
var err error
cfg := parseArgs()
if cfg.help {
flag.Usage()
os.Exit(0)
}
wlan0 = cfg.wlan
eth0 = cfg.eth
if cfg.mode == "dhcp" {
onlyDHCP = true
} else {
onlyDHCP = false
}
dhcpModifyClientSrc = cfg.dhcpModifySrc
printPackets = cfg.packets
var dhcpMode string
if cfg.dhcpModifySrc {
dhcpMode = "rewrite"
} else {
dhcpMode = "translate"
}
if cfg.debug {
logger.InitLogger(logrus.DebugLevel)
} else {
logger.InitLogger(logrus.InfoLevel)
}
log = logger.GetLogger()
err = bridge.FlushIPAddresses(eth0)
if err != nil {
log.Fatalf("Error flushing IP addresses from interface %s: %v\n", eth0, err)
} else {
log.Debugf("Successfully flushed IP addresses from interface %s\n", eth0)
}
wlan0MAC, err = bridge.GetInterfaceMAC(wlan0)
if err != nil {
log.Fatalf("Error creating raw socket for wlan0: %v", err)
}
eth0MAC, err = bridge.GetInterfaceMAC(eth0)
if err != nil {
log.Fatalf("Error creating raw socket for eth0: %v", err)
}
ethSocket, err := bridge.CreateRawSocket(eth0)
if err != nil {
log.Fatalf("Error creating raw socket for eth0: %v", err)
}
defer syscall.Close(ethSocket)
wlanSocket, err := bridge.CreateRawSocket(wlan0)
if err != nil {
log.Fatalf("Error creating raw socket for wlan0: %v", err)
}
defer syscall.Close(wlanSocket)
if cfg.mode == "dhcp" {
gatewayMAC, _ = net.ParseMAC("00:00:00:00:00:00")
go forwardPackets(ethSocket, wlanSocket, eth0, wlan0)
go forwardPackets(wlanSocket, ethSocket, wlan0, eth0)
log.Infof("Started DHCP relay in %s mode\n", dhcpMode)
} else if cfg.mode == "proxyarp" {
gatewayMAC, err = bridge.GetGatewayMAC(wlan0)
if err != nil {
log.Fatalf("Error getting gateway MAC address: %v", err)
}
go forwardPackets(ethSocket, wlanSocket, eth0, wlan0)
go forwardPackets(wlanSocket, ethSocket, wlan0, eth0)
log.Infof("Started ARP proxy between %s and %s\n", wlan0, eth0)
log.Infof("Started DHCP relay in %s mode\n", dhcpMode)
}
go arp.StartClientTimeoutChecker(5*time.Minute, 1*time.Minute)
select {}
}
func forwardPackets(srcSocket, dstSocket int, srcName string, dstName string) {
srcMAC, err := bridge.GetInterfaceMAC(srcName)
if err != nil {
log.Errorf("Error getting MAC address of %s: %v", srcName, err)
}
dstMAC, err := bridge.GetInterfaceMAC(dstName)
if err != nil {
log.Errorf("Error getting MAC address of %s: %v", dstName, err)
}
readBuf := make([]byte, 65536)
for {
n, _, err := syscall.Recvfrom(srcSocket, readBuf, 0)
if err != nil {
log.Errorf("Error reading packet from %s: %v", srcName, err)
continue
}
packet := gopacket.NewPacket(readBuf[:n], layers.LayerTypeEthernet, gopacket.Default)
if packet.ErrorLayer() != nil {
log.Errorf("Packet is not in Ethernet format: %v", packet.ErrorLayer().Error())
continue
}
ethLayer := packet.Layer(layers.LayerTypeEthernet).(*layers.Ethernet)
if ethLayer == nil {
log.Error("Packet does not have Ethernet layer.")
continue
}
arpLayer := packet.Layer(layers.LayerTypeARP)
if arpLayer != nil {
arpPacket := arpLayer.(*layers.ARP)
if arpPacket.Operation == layers.ARPRequest {
arp.HandleARPRequest(packet, srcSocket, dstSocket, srcName, dstName, srcMAC, dstMAC, wlan0MAC, eth0)
continue
} else if arpPacket.Operation == layers.ARPReply {
arp.HandleARPResponse(packet, srcSocket, dstSocket, srcName, dstName, srcMAC, dstMAC, eth0MAC)
continue
}
}
udpLayer := packet.Layer(layers.LayerTypeUDP)
if udpLayer != nil {
udp := udpLayer.(*layers.UDP)
if udp.SrcPort == layers.UDPPort(67) && udp.DstPort == layers.UDPPort(68) {
dhcp.HandleDHCPResponse(packet, srcSocket, dstSocket, srcName, dstName, srcMAC, dstMAC, wlan0MAC, dhcpModifyClientSrc, eth0)
continue
} else if udp.SrcPort == layers.UDPPort(68) && udp.DstPort == layers.UDPPort(67) {
dhcp.HandleDHCPRequest(packet, srcSocket, dstSocket, srcName, dstName, srcMAC, dstMAC, dhcpModifyClientSrc)
continue
}
}
if onlyDHCP {
continue
}
rawPacket := readBuf[:n]
if len(rawPacket) < 14 {
fmt.Println("too short")
continue
}
ipv4Layer := packet.Layer(layers.LayerTypeIPv4)
// Check if the packet is addressed for eth0
if bytes.Equal(ethLayer.DstMAC, eth0MAC) {
copy(rawPacket[0:6], gatewayMAC) // Modify the destination to gatewayMAC
copy(rawPacket[6:12], wlan0MAC) // Modify the source to the address of wlan0
} else if ipv4Layer != nil {
ipv4Packet := ipv4Layer.(*layers.IPv4)
// Get the destination IP address
dstIP := ipv4Packet.DstIP
// TODO: add a route to the bridged client so the bridge host can ping them.
// Check if the packet is addressed for eth0
if bytes.Equal(ethLayer.DstMAC, wlan0MAC) {
// Get the client's MAC address based on the IP address
// Get the client's MAC address based on the IP address
clientMAC, found := arp.GetClientMACByIP(dstIP)
if found {
// Modify the destination to the client's MAC address
copy(rawPacket[0:6], clientMAC)
} else {
// Modify the destination MAC address directly in the raw packet data
copy(rawPacket[0:6], dstMAC)
}
} else {
// Modify the destination MAC address directly in the raw packet data
copy(rawPacket[0:6], dstMAC)
}
// Modify the source MAC address directly in the raw packet data
copy(rawPacket[6:12], srcMAC)
} else {
log.Debugf("Packet was not IPv4!\n%s", packet)
continue
}
//else {
// copy(rawPacket[0:6], dstMAC) // Modify the destination MAC address directly in the raw packet data
// copy(rawPacket[6:12], srcMAC) // Modify the source MAC address directly in the raw packet data
//}
_, err = syscall.Write(dstSocket, rawPacket)
if err != nil {
log.Errorf("Error writing packet to %s: %v", dstName, err)
continue
}
if printPackets {
log.Debugf("Forwarded packet from %s to %s:\n%s", srcName, dstName, packet)
} else {
log.Debugf("Forwarded packet from %s to %s", srcName, dstName)
}
time.Sleep(1 * time.Millisecond)
}
}
func parseArgs() config {
var cfg config
flag.StringVar(&cfg.mode, "mode", "proxyarp", "Mode of operation")
flag.BoolVar(&cfg.debug, "d", false, "Enable debug mode")
flag.BoolVar(&cfg.debug, "debug", false, "Enable debug mode")
flag.BoolVar(&cfg.packets, "p", false, "Print packet info in debug mode")
flag.BoolVar(&cfg.packets, "packets", false, "Print packet info in debug mode")
flag.BoolVar(&cfg.dhcpModifySrc, "dhcp-modify-src", false, "Modify the source of a DHCP message. The frame source is always modified, but this will modify the \"Client MAC Address\" field as well.")
flag.StringVar(&cfg.eth, "eth", "", "Ethernet interface name")
flag.StringVar(&cfg.wlan, "wlan", "", "Wireless LAN interface name")
flag.BoolVar(&cfg.help, "h", false, "Display help information")
flag.BoolVar(&cfg.help, "help", false, "Display help information")
flag.Parse()
if cfg.eth == "" || cfg.wlan == "" {
if !cfg.help {
fmt.Println("Error: --eth and --wlan are required")
}
flag.Usage()
os.Exit(1)
}
if cfg.mode != "dhcp" && cfg.mode != "proxyarp" {
fmt.Println("Error: Invalid mode. Allowed values are 'dhcp' or 'proxyarp'")
flag.Usage()
os.Exit(1)
}
return cfg
}