178 lines
4.3 KiB
Go
178 lines
4.3 KiB
Go
package peer
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net"
|
|
"os"
|
|
"syscall"
|
|
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
// Get next packet, returning packet, ip, and possible error.
|
|
func readNextPacket(iface io.ReadWriteCloser, buf []byte) ([]byte, byte, error) {
|
|
var (
|
|
version byte
|
|
ip byte
|
|
)
|
|
for {
|
|
n, err := iface.Read(buf[:cap(buf)])
|
|
if err != nil {
|
|
return nil, ip, err
|
|
}
|
|
|
|
buf = buf[:n]
|
|
version = buf[0] >> 4
|
|
|
|
switch version {
|
|
case 4:
|
|
if n < 20 {
|
|
log.Printf("Short IPv4 packet: %d", len(buf))
|
|
continue
|
|
}
|
|
ip = buf[19]
|
|
|
|
case 6:
|
|
if len(buf) < 40 {
|
|
log.Printf("Short IPv6 packet: %d", len(buf))
|
|
continue
|
|
}
|
|
ip = buf[39]
|
|
|
|
default:
|
|
log.Printf("Invalid IP packet version: %v", version)
|
|
continue
|
|
}
|
|
|
|
return buf, ip, nil
|
|
}
|
|
}
|
|
|
|
func openInterface(network []byte, localIP byte, name string) (io.ReadWriteCloser, error) {
|
|
if len(network) != 4 {
|
|
return nil, fmt.Errorf("expected network to be 4 bytes, got %d", len(network))
|
|
}
|
|
ip := net.IPv4(network[0], network[1], network[2], localIP)
|
|
|
|
//////////////////////////
|
|
// Create TUN Interface //
|
|
//////////////////////////
|
|
|
|
tunFD, err := syscall.Open("/dev/net/tun", syscall.O_RDWR|unix.O_CLOEXEC, 0600)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to open TUN device: %w", err)
|
|
}
|
|
|
|
// New interface request.
|
|
req, err := unix.NewIfreq(name)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create new TUN interface request: %w", err)
|
|
}
|
|
|
|
// Flags:
|
|
//
|
|
// IFF_NO_PI => don't add packet info data to packets sent to the interface.
|
|
// IFF_TUN => create a TUN device handling IP packets.
|
|
req.SetUint16(unix.IFF_NO_PI | unix.IFF_TUN)
|
|
|
|
err = unix.IoctlIfreq(tunFD, unix.TUNSETIFF, req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to set TUN device settings: %w", err)
|
|
}
|
|
|
|
// Name may not be exactly the same?
|
|
name = req.Name()
|
|
|
|
/////////////
|
|
// Set MTU //
|
|
/////////////
|
|
|
|
// We need a socket file descriptor to set other options for some reason.
|
|
sockFD, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, unix.IPPROTO_IP)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to open socket: %w", err)
|
|
}
|
|
defer unix.Close(sockFD)
|
|
|
|
req, err = unix.NewIfreq(name)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create MTU interface request: %w", err)
|
|
}
|
|
|
|
req.SetUint32(if_mtu)
|
|
if err = unix.IoctlIfreq(sockFD, unix.SIOCSIFMTU, req); err != nil {
|
|
return nil, fmt.Errorf("failed to set interface MTU: %w", err)
|
|
}
|
|
|
|
//////////////////////
|
|
// Set Queue Length //
|
|
//////////////////////
|
|
|
|
req, err = unix.NewIfreq(name)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create IP interface request: %w", err)
|
|
}
|
|
|
|
req.SetUint16(if_queue_len)
|
|
if err = unix.IoctlIfreq(sockFD, unix.SIOCSIFTXQLEN, req); err != nil {
|
|
return nil, fmt.Errorf("failed to set interface queue length: %w", err)
|
|
}
|
|
|
|
/////////////////////
|
|
// Set IP and Mask //
|
|
/////////////////////
|
|
|
|
req, err = unix.NewIfreq(name)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create IP interface request: %w", err)
|
|
}
|
|
|
|
if err := req.SetInet4Addr(ip.To4()); err != nil {
|
|
return nil, fmt.Errorf("failed to set interface request IP: %w", err)
|
|
}
|
|
|
|
if err = unix.IoctlIfreq(sockFD, unix.SIOCSIFADDR, req); err != nil {
|
|
return nil, fmt.Errorf("failed to set interface IP: %w", err)
|
|
}
|
|
|
|
// SET MASK - must happen after setting address.
|
|
req, err = unix.NewIfreq(name)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create mask interface request: %w", err)
|
|
}
|
|
|
|
if err := req.SetInet4Addr(net.IPv4(255, 255, 255, 0).To4()); err != nil {
|
|
return nil, fmt.Errorf("failed to set interface request mask: %w", err)
|
|
}
|
|
|
|
if err := unix.IoctlIfreq(sockFD, unix.SIOCSIFNETMASK, req); err != nil {
|
|
return nil, fmt.Errorf("failed to set interface mask: %w", err)
|
|
}
|
|
|
|
////////////////////////
|
|
// Bring Interface Up //
|
|
////////////////////////
|
|
|
|
req, err = unix.NewIfreq(name)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create up interface request: %w", err)
|
|
}
|
|
|
|
// Get current flags.
|
|
if err = unix.IoctlIfreq(sockFD, unix.SIOCGIFFLAGS, req); err != nil {
|
|
return nil, fmt.Errorf("failed to get interface flags: %w", err)
|
|
}
|
|
|
|
flags := req.Uint16() | unix.IFF_UP | unix.IFF_RUNNING
|
|
|
|
// Set UP flag / broadcast flags.
|
|
req.SetUint16(flags)
|
|
if err = unix.IoctlIfreq(sockFD, unix.SIOCSIFFLAGS, req); err != nil {
|
|
return nil, fmt.Errorf("failed to set interface up: %w", err)
|
|
}
|
|
|
|
return os.NewFile(uintptr(tunFD), "tun"), nil
|
|
}
|