refactor-for-testability (#3)
Co-authored-by: jdl <jdl@desktop> Co-authored-by: jdl <jdl@crumpington.com> Reviewed-on: #3
This commit is contained in:
137
peer/interface.go
Normal file
137
peer/interface.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package peer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user