mirror of
https://github.com/yggdrasil-network/yggdrasil-go.git
synced 2025-04-29 14:45:07 +03:00
Refactoring: move tuntap and icmpv6 into separate package
This commit is contained in:
parent
67c670ab4c
commit
0b494a8255
20 changed files with 307 additions and 240 deletions
331
src/tuntap/icmpv6.go
Normal file
331
src/tuntap/icmpv6.go
Normal file
|
@ -0,0 +1,331 @@
|
|||
package tuntap
|
||||
|
||||
// The ICMPv6 module implements functions to easily create ICMPv6
|
||||
// packets. These functions, when mixed with the built-in Go IPv6
|
||||
// and ICMP libraries, can be used to send control messages back
|
||||
// to the host. Examples include:
|
||||
// - NDP messages, when running in TAP mode
|
||||
// - Packet Too Big messages, when packets exceed the session MTU
|
||||
// - Destination Unreachable messages, when a session prohibits
|
||||
// incoming traffic
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/icmp"
|
||||
"golang.org/x/net/ipv6"
|
||||
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||
)
|
||||
|
||||
type macAddress [6]byte
|
||||
|
||||
const len_ETHER = 14
|
||||
|
||||
type ICMPv6 struct {
|
||||
tun *TunAdapter
|
||||
mylladdr net.IP
|
||||
mymac macAddress
|
||||
peermacs map[address.Address]neighbor
|
||||
}
|
||||
|
||||
type neighbor struct {
|
||||
mac macAddress
|
||||
learned bool
|
||||
lastadvertisement time.Time
|
||||
lastsolicitation time.Time
|
||||
}
|
||||
|
||||
// Marshal returns the binary encoding of h.
|
||||
func ipv6Header_Marshal(h *ipv6.Header) ([]byte, error) {
|
||||
b := make([]byte, 40)
|
||||
b[0] |= byte(h.Version) << 4
|
||||
b[0] |= byte(h.TrafficClass) >> 4
|
||||
b[1] |= byte(h.TrafficClass) << 4
|
||||
b[1] |= byte(h.FlowLabel >> 16)
|
||||
b[2] = byte(h.FlowLabel >> 8)
|
||||
b[3] = byte(h.FlowLabel)
|
||||
binary.BigEndian.PutUint16(b[4:6], uint16(h.PayloadLen))
|
||||
b[6] = byte(h.NextHeader)
|
||||
b[7] = byte(h.HopLimit)
|
||||
copy(b[8:24], h.Src)
|
||||
copy(b[24:40], h.Dst)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Initialises the ICMPv6 module by assigning our link-local IPv6 address and
|
||||
// our MAC address. ICMPv6 messages will always appear to originate from these
|
||||
// addresses.
|
||||
func (i *ICMPv6) Init(t *TunAdapter) {
|
||||
i.tun = t
|
||||
i.peermacs = make(map[address.Address]neighbor)
|
||||
|
||||
// Our MAC address and link-local address
|
||||
i.mymac = macAddress{
|
||||
0x02, 0x00, 0x00, 0x00, 0x00, 0x02}
|
||||
i.mylladdr = net.IP{
|
||||
0xFE, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFE}
|
||||
copy(i.mymac[:], i.tun.addr[:])
|
||||
copy(i.mylladdr[9:], i.tun.addr[1:])
|
||||
}
|
||||
|
||||
// Parses an incoming ICMPv6 packet. The packet provided may be either an
|
||||
// ethernet frame containing an IP packet, or the IP packet alone. This is
|
||||
// determined by whether the TUN/TAP adapter is running in TUN (layer 3) or
|
||||
// TAP (layer 2) mode.
|
||||
func (i *ICMPv6) ParsePacket(datain []byte) {
|
||||
var response []byte
|
||||
var err error
|
||||
|
||||
// Parse the frame/packet
|
||||
if i.tun.IsTAP() {
|
||||
response, err = i.UnmarshalPacketL2(datain)
|
||||
} else {
|
||||
response, err = i.UnmarshalPacket(datain, nil)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Write the packet to TUN/TAP
|
||||
i.tun.Send <- response
|
||||
}
|
||||
|
||||
// Unwraps the ethernet headers of an incoming ICMPv6 packet and hands off
|
||||
// the IP packet to the ParsePacket function for further processing.
|
||||
// A response buffer is also created for the response message, also complete
|
||||
// with ethernet headers.
|
||||
func (i *ICMPv6) UnmarshalPacketL2(datain []byte) ([]byte, error) {
|
||||
// Ignore non-IPv6 frames
|
||||
if binary.BigEndian.Uint16(datain[12:14]) != uint16(0x86DD) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Hand over to ParsePacket to interpret the IPv6 packet
|
||||
mac := datain[6:12]
|
||||
ipv6packet, err := i.UnmarshalPacket(datain[len_ETHER:], &mac)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create the response buffer
|
||||
dataout := make([]byte, len_ETHER+ipv6.HeaderLen+32)
|
||||
|
||||
// Populate the response ethernet headers
|
||||
copy(dataout[:6], datain[6:12])
|
||||
copy(dataout[6:12], i.mymac[:])
|
||||
binary.BigEndian.PutUint16(dataout[12:14], uint16(0x86DD))
|
||||
|
||||
// Copy the returned packet to our response ethernet frame
|
||||
copy(dataout[len_ETHER:], ipv6packet)
|
||||
return dataout, nil
|
||||
}
|
||||
|
||||
// Unwraps the IP headers of an incoming IPv6 packet and performs various
|
||||
// sanity checks on the packet - i.e. is the packet an ICMPv6 packet, does the
|
||||
// ICMPv6 message match a known expected type. The relevant handler function
|
||||
// is then called and a response packet may be returned.
|
||||
func (i *ICMPv6) UnmarshalPacket(datain []byte, datamac *[]byte) ([]byte, error) {
|
||||
// Parse the IPv6 packet headers
|
||||
ipv6Header, err := ipv6.ParseHeader(datain[:ipv6.HeaderLen])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check if the packet is IPv6
|
||||
if ipv6Header.Version != ipv6.Version {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check if the packet is ICMPv6
|
||||
if ipv6Header.NextHeader != 58 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Parse the ICMPv6 message contents
|
||||
icmpv6Header, err := icmp.ParseMessage(58, datain[ipv6.HeaderLen:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check for a supported message type
|
||||
switch icmpv6Header.Type {
|
||||
case ipv6.ICMPTypeNeighborSolicitation:
|
||||
if !i.tun.IsTAP() {
|
||||
return nil, errors.New("Ignoring Neighbor Solicitation in TUN mode")
|
||||
}
|
||||
response, err := i.HandleNDP(datain[ipv6.HeaderLen:])
|
||||
if err == nil {
|
||||
// Create our ICMPv6 response
|
||||
responsePacket, err := CreateICMPv6(
|
||||
ipv6Header.Src, i.mylladdr,
|
||||
ipv6.ICMPTypeNeighborAdvertisement, 0,
|
||||
&icmp.DefaultMessageBody{Data: response})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Send it back
|
||||
return responsePacket, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
case ipv6.ICMPTypeNeighborAdvertisement:
|
||||
if !i.tun.IsTAP() {
|
||||
return nil, errors.New("Ignoring Neighbor Advertisement in TUN mode")
|
||||
}
|
||||
if datamac != nil {
|
||||
var addr address.Address
|
||||
var target address.Address
|
||||
var mac macAddress
|
||||
copy(addr[:], ipv6Header.Src[:])
|
||||
copy(target[:], datain[48:64])
|
||||
copy(mac[:], (*datamac)[:])
|
||||
// i.tun.core.log.Printf("Learning peer MAC %x for %x\n", mac, target)
|
||||
neighbor := i.peermacs[target]
|
||||
neighbor.mac = mac
|
||||
neighbor.learned = true
|
||||
neighbor.lastadvertisement = time.Now()
|
||||
i.peermacs[target] = neighbor
|
||||
}
|
||||
return nil, errors.New("No response needed")
|
||||
}
|
||||
|
||||
return nil, errors.New("ICMPv6 type not matched")
|
||||
}
|
||||
|
||||
// Creates an ICMPv6 packet based on the given icmp.MessageBody and other
|
||||
// parameters, complete with ethernet and IP headers, which can be written
|
||||
// directly to a TAP adapter.
|
||||
func (i *ICMPv6) CreateICMPv6L2(dstmac macAddress, dst net.IP, src net.IP, mtype ipv6.ICMPType, mcode int, mbody icmp.MessageBody) ([]byte, error) {
|
||||
// Pass through to CreateICMPv6
|
||||
ipv6packet, err := CreateICMPv6(dst, src, mtype, mcode, mbody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create the response buffer
|
||||
dataout := make([]byte, len_ETHER+len(ipv6packet))
|
||||
|
||||
// Populate the response ethernet headers
|
||||
copy(dataout[:6], dstmac[:6])
|
||||
copy(dataout[6:12], i.mymac[:])
|
||||
binary.BigEndian.PutUint16(dataout[12:14], uint16(0x86DD))
|
||||
|
||||
// Copy the returned packet to our response ethernet frame
|
||||
copy(dataout[len_ETHER:], ipv6packet)
|
||||
return dataout, nil
|
||||
}
|
||||
|
||||
// Creates an ICMPv6 packet based on the given icmp.MessageBody and other
|
||||
// parameters, complete with IP headers only, which can be written directly to
|
||||
// a TUN adapter, or called directly by the CreateICMPv6L2 function when
|
||||
// generating a message for TAP adapters.
|
||||
func CreateICMPv6(dst net.IP, src net.IP, mtype ipv6.ICMPType, mcode int, mbody icmp.MessageBody) ([]byte, error) {
|
||||
// Create the ICMPv6 message
|
||||
icmpMessage := icmp.Message{
|
||||
Type: mtype,
|
||||
Code: mcode,
|
||||
Body: mbody,
|
||||
}
|
||||
|
||||
// Convert the ICMPv6 message into []byte
|
||||
icmpMessageBuf, err := icmpMessage.Marshal(icmp.IPv6PseudoHeader(src, dst))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create the IPv6 header
|
||||
ipv6Header := ipv6.Header{
|
||||
Version: ipv6.Version,
|
||||
NextHeader: 58,
|
||||
PayloadLen: len(icmpMessageBuf),
|
||||
HopLimit: 255,
|
||||
Src: src,
|
||||
Dst: dst,
|
||||
}
|
||||
|
||||
// Convert the IPv6 header into []byte
|
||||
ipv6HeaderBuf, err := ipv6Header_Marshal(&ipv6Header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Construct the packet
|
||||
responsePacket := make([]byte, ipv6.HeaderLen+ipv6Header.PayloadLen)
|
||||
copy(responsePacket[:ipv6.HeaderLen], ipv6HeaderBuf)
|
||||
copy(responsePacket[ipv6.HeaderLen:], icmpMessageBuf)
|
||||
|
||||
// Send it back
|
||||
return responsePacket, nil
|
||||
}
|
||||
|
||||
func (i *ICMPv6) CreateNDPL2(dst address.Address) ([]byte, error) {
|
||||
// Create the ND payload
|
||||
var payload [28]byte
|
||||
copy(payload[:4], []byte{0x00, 0x00, 0x00, 0x00})
|
||||
copy(payload[4:20], dst[:])
|
||||
copy(payload[20:22], []byte{0x01, 0x01})
|
||||
copy(payload[22:28], i.mymac[:6])
|
||||
|
||||
// Create the ICMPv6 solicited-node address
|
||||
var dstaddr address.Address
|
||||
copy(dstaddr[:13], []byte{
|
||||
0xFF, 0x02, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, 0xFF})
|
||||
copy(dstaddr[13:], dst[13:16])
|
||||
|
||||
// Create the multicast MAC
|
||||
var dstmac macAddress
|
||||
copy(dstmac[:2], []byte{0x33, 0x33})
|
||||
copy(dstmac[2:6], dstaddr[12:16])
|
||||
|
||||
// Create the ND request
|
||||
requestPacket, err := i.CreateICMPv6L2(
|
||||
dstmac, dstaddr[:], i.mylladdr,
|
||||
ipv6.ICMPTypeNeighborSolicitation, 0,
|
||||
&icmp.DefaultMessageBody{Data: payload[:]})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
neighbor := i.peermacs[dstaddr]
|
||||
neighbor.lastsolicitation = time.Now()
|
||||
i.peermacs[dstaddr] = neighbor
|
||||
|
||||
return requestPacket, nil
|
||||
}
|
||||
|
||||
// Generates a response to an NDP discovery packet. This is effectively called
|
||||
// when the host operating system generates an NDP request for any address in
|
||||
// the fd00::/8 range, so that the operating system knows to route that traffic
|
||||
// to the Yggdrasil TAP adapter.
|
||||
func (i *ICMPv6) HandleNDP(in []byte) ([]byte, error) {
|
||||
// Ignore NDP requests for anything outside of fd00::/8
|
||||
var source address.Address
|
||||
copy(source[:], in[8:])
|
||||
var snet address.Subnet
|
||||
copy(snet[:], in[8:])
|
||||
switch {
|
||||
case source.IsValid():
|
||||
case snet.IsValid():
|
||||
default:
|
||||
return nil, errors.New("Not an NDP for 0200::/7")
|
||||
}
|
||||
|
||||
// Create our NDP message body response
|
||||
body := make([]byte, 28)
|
||||
binary.BigEndian.PutUint32(body[:4], uint32(0x20000000))
|
||||
copy(body[4:20], in[8:24]) // Target address
|
||||
body[20] = uint8(2)
|
||||
body[21] = uint8(1)
|
||||
copy(body[22:28], i.mymac[:6])
|
||||
|
||||
// Send it back
|
||||
return body, nil
|
||||
}
|
296
src/tuntap/tun.go
Normal file
296
src/tuntap/tun.go
Normal file
|
@ -0,0 +1,296 @@
|
|||
package tuntap
|
||||
|
||||
// This manages the tun driver to send/recv packets to/from applications
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gologme/log"
|
||||
|
||||
"github.com/songgao/packets/ethernet"
|
||||
"github.com/yggdrasil-network/water"
|
||||
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/util"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil"
|
||||
)
|
||||
|
||||
const tun_IPv6_HEADER_LENGTH = 40
|
||||
const tun_ETHER_HEADER_LENGTH = 14
|
||||
|
||||
// Represents a running TUN/TAP interface.
|
||||
type TunAdapter struct {
|
||||
yggdrasil.Adapter
|
||||
addr address.Address
|
||||
subnet address.Subnet
|
||||
log *log.Logger
|
||||
config *config.NodeState
|
||||
icmpv6 ICMPv6
|
||||
mtu int
|
||||
iface *water.Interface
|
||||
mutex sync.RWMutex // Protects the below
|
||||
isOpen bool
|
||||
}
|
||||
|
||||
// Gets the maximum supported MTU for the platform based on the defaults in
|
||||
// defaults.GetDefaults().
|
||||
func getSupportedMTU(mtu int) int {
|
||||
if mtu > defaults.GetDefaults().MaximumIfMTU {
|
||||
return defaults.GetDefaults().MaximumIfMTU
|
||||
}
|
||||
return mtu
|
||||
}
|
||||
|
||||
// Get the adapter name
|
||||
func (tun *TunAdapter) Name() string {
|
||||
return tun.iface.Name()
|
||||
}
|
||||
|
||||
// Get the adapter MTU
|
||||
func (tun *TunAdapter) MTU() int {
|
||||
return getSupportedMTU(tun.mtu)
|
||||
}
|
||||
|
||||
// Get the adapter mode
|
||||
func (tun *TunAdapter) IsTAP() bool {
|
||||
return tun.iface.IsTAP()
|
||||
}
|
||||
|
||||
// Initialises the TUN/TAP adapter.
|
||||
func (tun *TunAdapter) Init(config *config.NodeState, log *log.Logger, send chan<- []byte, recv <-chan []byte) {
|
||||
tun.config = config
|
||||
tun.log = log
|
||||
tun.Adapter.Init(config, log, send, recv)
|
||||
tun.icmpv6.Init(tun)
|
||||
go func() {
|
||||
for {
|
||||
e := <-tun.Reconfigure
|
||||
tun.config.Mutex.RLock()
|
||||
updated := tun.config.Current.IfName != tun.config.Previous.IfName ||
|
||||
tun.config.Current.IfTAPMode != tun.config.Previous.IfTAPMode ||
|
||||
tun.config.Current.IfMTU != tun.config.Previous.IfMTU
|
||||
tun.config.Mutex.RUnlock()
|
||||
if updated {
|
||||
tun.log.Warnln("Reconfiguring TUN/TAP is not supported yet")
|
||||
e <- nil
|
||||
} else {
|
||||
e <- nil
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Starts the setup process for the TUN/TAP adapter, and if successful, starts
|
||||
// the read/write goroutines to handle packets on that interface.
|
||||
func (tun *TunAdapter) Start(a address.Address, s address.Subnet) error {
|
||||
tun.addr = a
|
||||
tun.subnet = s
|
||||
if tun.config == nil {
|
||||
return errors.New("No configuration available to TUN/TAP")
|
||||
}
|
||||
tun.config.Mutex.RLock()
|
||||
ifname := tun.config.Current.IfName
|
||||
iftapmode := tun.config.Current.IfTAPMode
|
||||
addr := fmt.Sprintf("%s/%d", net.IP(tun.addr[:]).String(), 8*len(address.GetPrefix())-1)
|
||||
mtu := tun.config.Current.IfMTU
|
||||
tun.config.Mutex.RUnlock()
|
||||
if ifname != "none" {
|
||||
if err := tun.setup(ifname, iftapmode, addr, mtu); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if ifname == "none" || ifname == "dummy" {
|
||||
return nil
|
||||
}
|
||||
tun.mutex.Lock()
|
||||
tun.isOpen = true
|
||||
tun.mutex.Unlock()
|
||||
go func() { tun.log.Errorln("WARNING: tun.read() exited with error:", tun.Read()) }()
|
||||
go func() { tun.log.Errorln("WARNING: tun.write() exited with error:", tun.Write()) }()
|
||||
if iftapmode {
|
||||
go func() {
|
||||
for {
|
||||
if _, ok := tun.icmpv6.peermacs[tun.addr]; ok {
|
||||
break
|
||||
}
|
||||
request, err := tun.icmpv6.CreateNDPL2(tun.addr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if _, err := tun.iface.Write(request); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Writes a packet to the TUN/TAP adapter. If the adapter is running in TAP
|
||||
// mode then additional ethernet encapsulation is added for the benefit of the
|
||||
// host operating system.
|
||||
func (tun *TunAdapter) Write() error {
|
||||
for {
|
||||
data := <-tun.Recv
|
||||
if tun.iface == nil {
|
||||
continue
|
||||
}
|
||||
if tun.iface.IsTAP() {
|
||||
var destAddr address.Address
|
||||
if data[0]&0xf0 == 0x60 {
|
||||
if len(data) < 40 {
|
||||
panic("Tried to send a packet shorter than an IPv6 header...")
|
||||
}
|
||||
copy(destAddr[:16], data[24:])
|
||||
} else if data[0]&0xf0 == 0x40 {
|
||||
if len(data) < 20 {
|
||||
panic("Tried to send a packet shorter than an IPv4 header...")
|
||||
}
|
||||
copy(destAddr[:4], data[16:])
|
||||
} else {
|
||||
return errors.New("Invalid address family")
|
||||
}
|
||||
sendndp := func(destAddr address.Address) {
|
||||
neigh, known := tun.icmpv6.peermacs[destAddr]
|
||||
known = known && (time.Since(neigh.lastsolicitation).Seconds() < 30)
|
||||
if !known {
|
||||
request, err := tun.icmpv6.CreateNDPL2(destAddr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if _, err := tun.iface.Write(request); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
tun.icmpv6.peermacs[destAddr] = neighbor{
|
||||
lastsolicitation: time.Now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
var peermac macAddress
|
||||
var peerknown bool
|
||||
if data[0]&0xf0 == 0x40 {
|
||||
destAddr = tun.addr
|
||||
} else if data[0]&0xf0 == 0x60 {
|
||||
if !bytes.Equal(tun.addr[:16], destAddr[:16]) && !bytes.Equal(tun.subnet[:8], destAddr[:8]) {
|
||||
destAddr = tun.addr
|
||||
}
|
||||
}
|
||||
if neighbor, ok := tun.icmpv6.peermacs[destAddr]; ok && neighbor.learned {
|
||||
peermac = neighbor.mac
|
||||
peerknown = true
|
||||
} else if neighbor, ok := tun.icmpv6.peermacs[tun.addr]; ok && neighbor.learned {
|
||||
peermac = neighbor.mac
|
||||
peerknown = true
|
||||
sendndp(destAddr)
|
||||
} else {
|
||||
sendndp(tun.addr)
|
||||
}
|
||||
if peerknown {
|
||||
var proto ethernet.Ethertype
|
||||
switch {
|
||||
case data[0]&0xf0 == 0x60:
|
||||
proto = ethernet.IPv6
|
||||
case data[0]&0xf0 == 0x40:
|
||||
proto = ethernet.IPv4
|
||||
}
|
||||
var frame ethernet.Frame
|
||||
frame.Prepare(
|
||||
peermac[:6], // Destination MAC address
|
||||
tun.icmpv6.mymac[:6], // Source MAC address
|
||||
ethernet.NotTagged, // VLAN tagging
|
||||
proto, // Ethertype
|
||||
len(data)) // Payload length
|
||||
copy(frame[tun_ETHER_HEADER_LENGTH:], data[:])
|
||||
if _, err := tun.iface.Write(frame); err != nil {
|
||||
tun.mutex.RLock()
|
||||
open := tun.isOpen
|
||||
tun.mutex.RUnlock()
|
||||
if !open {
|
||||
return nil
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if _, err := tun.iface.Write(data); err != nil {
|
||||
tun.mutex.RLock()
|
||||
open := tun.isOpen
|
||||
tun.mutex.RUnlock()
|
||||
if !open {
|
||||
return nil
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
util.PutBytes(data)
|
||||
}
|
||||
}
|
||||
|
||||
// Reads any packets that are waiting on the TUN/TAP adapter. If the adapter
|
||||
// is running in TAP mode then the ethernet headers will automatically be
|
||||
// processed and stripped if necessary. If an ICMPv6 packet is found, then
|
||||
// the relevant helper functions in icmpv6.go are called.
|
||||
func (tun *TunAdapter) Read() error {
|
||||
mtu := tun.mtu
|
||||
if tun.iface.IsTAP() {
|
||||
mtu += tun_ETHER_HEADER_LENGTH
|
||||
}
|
||||
buf := make([]byte, mtu)
|
||||
for {
|
||||
n, err := tun.iface.Read(buf)
|
||||
if err != nil {
|
||||
tun.mutex.RLock()
|
||||
open := tun.isOpen
|
||||
tun.mutex.RUnlock()
|
||||
if !open {
|
||||
return nil
|
||||
} else {
|
||||
// panic(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
o := 0
|
||||
if tun.iface.IsTAP() {
|
||||
o = tun_ETHER_HEADER_LENGTH
|
||||
}
|
||||
switch {
|
||||
case buf[o]&0xf0 == 0x60 && n == 256*int(buf[o+4])+int(buf[o+5])+tun_IPv6_HEADER_LENGTH+o:
|
||||
case buf[o]&0xf0 == 0x40 && n == 256*int(buf[o+2])+int(buf[o+3])+o:
|
||||
default:
|
||||
continue
|
||||
}
|
||||
if buf[o+6] == 58 {
|
||||
if tun.iface.IsTAP() {
|
||||
// Found an ICMPv6 packet
|
||||
b := make([]byte, n)
|
||||
copy(b, buf)
|
||||
go tun.icmpv6.ParsePacket(b)
|
||||
}
|
||||
}
|
||||
packet := append(util.GetBytes(), buf[o:n]...)
|
||||
tun.Send <- packet
|
||||
}
|
||||
}
|
||||
|
||||
// Closes the TUN/TAP adapter. This is only usually called when the Yggdrasil
|
||||
// process stops. Typically this operation will happen quickly, but on macOS
|
||||
// it can block until a read operation is completed.
|
||||
func (tun *TunAdapter) Close() error {
|
||||
tun.mutex.Lock()
|
||||
tun.isOpen = false
|
||||
tun.mutex.Unlock()
|
||||
if tun.iface == nil {
|
||||
return nil
|
||||
}
|
||||
return tun.iface.Close()
|
||||
}
|
171
src/tuntap/tun_bsd.go
Normal file
171
src/tuntap/tun_bsd.go
Normal file
|
@ -0,0 +1,171 @@
|
|||
// +build openbsd freebsd netbsd
|
||||
|
||||
package tuntap
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/yggdrasil-network/water"
|
||||
)
|
||||
|
||||
const SIOCSIFADDR_IN6 = (0x80000000) | ((288 & 0x1fff) << 16) | uint32(byte('i'))<<8 | 12
|
||||
|
||||
type in6_addrlifetime struct {
|
||||
ia6t_expire float64
|
||||
ia6t_preferred float64
|
||||
ia6t_vltime uint32
|
||||
ia6t_pltime uint32
|
||||
}
|
||||
|
||||
type sockaddr_in6 struct {
|
||||
sin6_len uint8
|
||||
sin6_family uint8
|
||||
sin6_port uint8
|
||||
sin6_flowinfo uint32
|
||||
sin6_addr [8]uint16
|
||||
sin6_scope_id uint32
|
||||
}
|
||||
|
||||
/*
|
||||
from <netinet6/in6_var.h>
|
||||
struct in6_ifreq {
|
||||
277 char ifr_name[IFNAMSIZ];
|
||||
278 union {
|
||||
279 struct sockaddr_in6 ifru_addr;
|
||||
280 struct sockaddr_in6 ifru_dstaddr;
|
||||
281 int ifru_flags;
|
||||
282 int ifru_flags6;
|
||||
283 int ifru_metric;
|
||||
284 caddr_t ifru_data;
|
||||
285 struct in6_addrlifetime ifru_lifetime;
|
||||
286 struct in6_ifstat ifru_stat;
|
||||
287 struct icmp6_ifstat ifru_icmp6stat;
|
||||
288 u_int32_t ifru_scope_id[16];
|
||||
289 } ifr_ifru;
|
||||
290 };
|
||||
*/
|
||||
|
||||
type in6_ifreq_mtu struct {
|
||||
ifr_name [syscall.IFNAMSIZ]byte
|
||||
ifru_mtu int
|
||||
}
|
||||
|
||||
type in6_ifreq_addr struct {
|
||||
ifr_name [syscall.IFNAMSIZ]byte
|
||||
ifru_addr sockaddr_in6
|
||||
}
|
||||
|
||||
type in6_ifreq_flags struct {
|
||||
ifr_name [syscall.IFNAMSIZ]byte
|
||||
flags int
|
||||
}
|
||||
|
||||
type in6_ifreq_lifetime struct {
|
||||
ifr_name [syscall.IFNAMSIZ]byte
|
||||
ifru_addrlifetime in6_addrlifetime
|
||||
}
|
||||
|
||||
// Sets the IPv6 address of the utun adapter. On all BSD platforms (FreeBSD,
|
||||
// OpenBSD, NetBSD) an attempt is made to set the adapter properties by using
|
||||
// a system socket and making syscalls to the kernel. This is not refined though
|
||||
// and often doesn't work (if at all), therefore if a call fails, it resorts
|
||||
// to calling "ifconfig" instead.
|
||||
func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error {
|
||||
var config water.Config
|
||||
if ifname[:4] == "auto" {
|
||||
ifname = "/dev/tap0"
|
||||
}
|
||||
if len(ifname) < 9 {
|
||||
panic("TUN/TAP name must be in format /dev/tunX or /dev/tapX")
|
||||
}
|
||||
switch {
|
||||
case iftapmode || ifname[:8] == "/dev/tap":
|
||||
config = water.Config{DeviceType: water.TAP}
|
||||
case !iftapmode || ifname[:8] == "/dev/tun":
|
||||
panic("TUN mode is not currently supported on this platform, please use TAP instead")
|
||||
default:
|
||||
panic("TUN/TAP name must be in format /dev/tunX or /dev/tapX")
|
||||
}
|
||||
config.Name = ifname
|
||||
iface, err := water.New(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
tun.iface = iface
|
||||
tun.mtu = getSupportedMTU(mtu)
|
||||
return tun.setupAddress(addr)
|
||||
}
|
||||
|
||||
func (tun *TunAdapter) setupAddress(addr string) error {
|
||||
var sfd int
|
||||
var err error
|
||||
|
||||
// Create system socket
|
||||
if sfd, err = unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0); err != nil {
|
||||
tun.core.log.Printf("Create AF_INET socket failed: %v.", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Friendly output
|
||||
tun.core.log.Infof("Interface name: %s", tun.iface.Name())
|
||||
tun.core.log.Infof("Interface IPv6: %s", addr)
|
||||
tun.core.log.Infof("Interface MTU: %d", tun.mtu)
|
||||
|
||||
// Create the MTU request
|
||||
var ir in6_ifreq_mtu
|
||||
copy(ir.ifr_name[:], tun.iface.Name())
|
||||
ir.ifru_mtu = int(tun.mtu)
|
||||
|
||||
// Set the MTU
|
||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(sfd), uintptr(syscall.SIOCSIFMTU), uintptr(unsafe.Pointer(&ir))); errno != 0 {
|
||||
err = errno
|
||||
tun.core.log.Errorf("Error in SIOCSIFMTU: %v", errno)
|
||||
|
||||
// Fall back to ifconfig to set the MTU
|
||||
cmd := exec.Command("ifconfig", tun.iface.Name(), "mtu", string(tun.mtu))
|
||||
tun.core.log.Warnf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " "))
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
tun.core.log.Errorf("SIOCSIFMTU fallback failed: %v.", err)
|
||||
tun.core.log.Traceln(string(output))
|
||||
}
|
||||
}
|
||||
|
||||
// Create the address request
|
||||
// FIXME: I don't work!
|
||||
var ar in6_ifreq_addr
|
||||
copy(ar.ifr_name[:], tun.iface.Name())
|
||||
ar.ifru_addr.sin6_len = uint8(unsafe.Sizeof(ar.ifru_addr))
|
||||
ar.ifru_addr.sin6_family = unix.AF_INET6
|
||||
parts := strings.Split(strings.Split(addr, "/")[0], ":")
|
||||
for i := 0; i < 8; i++ {
|
||||
addr, _ := strconv.ParseUint(parts[i], 16, 16)
|
||||
b := make([]byte, 16)
|
||||
binary.LittleEndian.PutUint16(b, uint16(addr))
|
||||
ar.ifru_addr.sin6_addr[i] = uint16(binary.BigEndian.Uint16(b))
|
||||
}
|
||||
|
||||
// Set the interface address
|
||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(sfd), uintptr(SIOCSIFADDR_IN6), uintptr(unsafe.Pointer(&ar))); errno != 0 {
|
||||
err = errno
|
||||
tun.core.log.Errorf("Error in SIOCSIFADDR_IN6: %v", errno)
|
||||
|
||||
// Fall back to ifconfig to set the address
|
||||
cmd := exec.Command("ifconfig", tun.iface.Name(), "inet6", addr)
|
||||
tun.core.log.Warnf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " "))
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
tun.core.log.Errorf("SIOCSIFADDR_IN6 fallback failed: %v.", err)
|
||||
tun.core.log.Traceln(string(output))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
118
src/tuntap/tun_darwin.go
Normal file
118
src/tuntap/tun_darwin.go
Normal file
|
@ -0,0 +1,118 @@
|
|||
// +build !mobile
|
||||
|
||||
package tuntap
|
||||
|
||||
// The darwin platform specific tun parts
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
water "github.com/yggdrasil-network/water"
|
||||
)
|
||||
|
||||
// Configures the "utun" adapter with the correct IPv6 address and MTU.
|
||||
func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error {
|
||||
if iftapmode {
|
||||
tun.log.Warnln("TAP mode is not supported on this platform, defaulting to TUN")
|
||||
}
|
||||
config := water.Config{DeviceType: water.TUN}
|
||||
iface, err := water.New(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
tun.iface = iface
|
||||
tun.mtu = getSupportedMTU(mtu)
|
||||
return tun.setupAddress(addr)
|
||||
}
|
||||
|
||||
const darwin_SIOCAIFADDR_IN6 = 2155899162
|
||||
|
||||
type in6_addrlifetime struct {
|
||||
ia6t_expire float64
|
||||
ia6t_preferred float64
|
||||
ia6t_vltime uint32
|
||||
ia6t_pltime uint32
|
||||
}
|
||||
|
||||
type sockaddr_in6 struct {
|
||||
sin6_len uint8
|
||||
sin6_family uint8
|
||||
sin6_port uint8
|
||||
sin6_flowinfo uint32
|
||||
sin6_addr [8]uint16
|
||||
sin6_scope_id uint32
|
||||
}
|
||||
|
||||
type in6_aliasreq struct {
|
||||
ifra_name [16]byte
|
||||
ifra_addr sockaddr_in6
|
||||
ifra_dstaddr sockaddr_in6
|
||||
ifra_prefixmask sockaddr_in6
|
||||
ifra_flags uint32
|
||||
ifra_lifetime in6_addrlifetime
|
||||
}
|
||||
|
||||
type ifreq struct {
|
||||
ifr_name [16]byte
|
||||
ifru_mtu uint32
|
||||
}
|
||||
|
||||
// Sets the IPv6 address of the utun adapter. On Darwin/macOS this is done using
|
||||
// a system socket and making direct syscalls to the kernel.
|
||||
func (tun *TunAdapter) setupAddress(addr string) error {
|
||||
var fd int
|
||||
var err error
|
||||
|
||||
if fd, err = unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, 0); err != nil {
|
||||
tun.log.Printf("Create AF_SYSTEM socket failed: %v.", err)
|
||||
return err
|
||||
}
|
||||
|
||||
var ar in6_aliasreq
|
||||
copy(ar.ifra_name[:], tun.iface.Name())
|
||||
|
||||
ar.ifra_prefixmask.sin6_len = uint8(unsafe.Sizeof(ar.ifra_prefixmask))
|
||||
b := make([]byte, 16)
|
||||
binary.LittleEndian.PutUint16(b, uint16(0xFE00))
|
||||
ar.ifra_prefixmask.sin6_addr[0] = uint16(binary.BigEndian.Uint16(b))
|
||||
|
||||
ar.ifra_addr.sin6_len = uint8(unsafe.Sizeof(ar.ifra_addr))
|
||||
ar.ifra_addr.sin6_family = unix.AF_INET6
|
||||
parts := strings.Split(strings.Split(addr, "/")[0], ":")
|
||||
for i := 0; i < 8; i++ {
|
||||
addr, _ := strconv.ParseUint(parts[i], 16, 16)
|
||||
b := make([]byte, 16)
|
||||
binary.LittleEndian.PutUint16(b, uint16(addr))
|
||||
ar.ifra_addr.sin6_addr[i] = uint16(binary.BigEndian.Uint16(b))
|
||||
}
|
||||
|
||||
ar.ifra_lifetime.ia6t_vltime = 0xFFFFFFFF
|
||||
ar.ifra_lifetime.ia6t_pltime = 0xFFFFFFFF
|
||||
|
||||
var ir ifreq
|
||||
copy(ir.ifr_name[:], tun.iface.Name())
|
||||
ir.ifru_mtu = uint32(tun.mtu)
|
||||
|
||||
tun.log.Infof("Interface name: %s", ar.ifra_name)
|
||||
tun.log.Infof("Interface IPv6: %s", addr)
|
||||
tun.log.Infof("Interface MTU: %d", ir.ifru_mtu)
|
||||
|
||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(darwin_SIOCAIFADDR_IN6), uintptr(unsafe.Pointer(&ar))); errno != 0 {
|
||||
err = errno
|
||||
tun.log.Errorf("Error in darwin_SIOCAIFADDR_IN6: %v", errno)
|
||||
return err
|
||||
}
|
||||
|
||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCSIFMTU), uintptr(unsafe.Pointer(&ir))); errno != 0 {
|
||||
err = errno
|
||||
tun.log.Errorf("Error in SIOCSIFMTU: %v", errno)
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
19
src/tuntap/tun_dummy.go
Normal file
19
src/tuntap/tun_dummy.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
// +build mobile
|
||||
|
||||
package tuntap
|
||||
|
||||
// This is to catch unsupported platforms
|
||||
// If your platform supports tun devices, you could try configuring it manually
|
||||
|
||||
// Creates the TUN/TAP adapter, if supported by the Water library. Note that
|
||||
// no guarantees are made at this point on an unsupported platform.
|
||||
func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error {
|
||||
tun.mtu = getSupportedMTU(mtu)
|
||||
return tun.setupAddress(addr)
|
||||
}
|
||||
|
||||
// We don't know how to set the IPv6 address on an unknown platform, therefore
|
||||
// write about it to stdout and don't try to do anything further.
|
||||
func (tun *TunAdapter) setupAddress(addr string) error {
|
||||
return nil
|
||||
}
|
86
src/tuntap/tun_linux.go
Normal file
86
src/tuntap/tun_linux.go
Normal file
|
@ -0,0 +1,86 @@
|
|||
// +build !mobile
|
||||
|
||||
package tuntap
|
||||
|
||||
// The linux platform specific tun parts
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/docker/libcontainer/netlink"
|
||||
|
||||
water "github.com/yggdrasil-network/water"
|
||||
)
|
||||
|
||||
// Configures the TAP adapter with the correct IPv6 address and MTU.
|
||||
func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error {
|
||||
var config water.Config
|
||||
if iftapmode {
|
||||
config = water.Config{DeviceType: water.TAP}
|
||||
} else {
|
||||
config = water.Config{DeviceType: water.TUN}
|
||||
}
|
||||
if ifname != "" && ifname != "auto" {
|
||||
config.Name = ifname
|
||||
}
|
||||
iface, err := water.New(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
tun.iface = iface
|
||||
tun.mtu = getSupportedMTU(mtu)
|
||||
// The following check is specific to Linux, as the TAP driver only supports
|
||||
// an MTU of 65535-14 to make room for the ethernet headers. This makes sure
|
||||
// that the MTU gets rounded down to 65521 instead of causing a panic.
|
||||
if iftapmode {
|
||||
if tun.mtu > 65535-tun_ETHER_HEADER_LENGTH {
|
||||
tun.mtu = 65535 - tun_ETHER_HEADER_LENGTH
|
||||
}
|
||||
}
|
||||
// Friendly output
|
||||
tun.core.log.Infof("Interface name: %s", tun.iface.Name())
|
||||
tun.core.log.Infof("Interface IPv6: %s", addr)
|
||||
tun.core.log.Infof("Interface MTU: %d", tun.mtu)
|
||||
return tun.setupAddress(addr)
|
||||
}
|
||||
|
||||
// Configures the TAP adapter with the correct IPv6 address and MTU. Netlink
|
||||
// is used to do this, so there is not a hard requirement on "ip" or "ifconfig"
|
||||
// to exist on the system, but this will fail if Netlink is not present in the
|
||||
// kernel (it nearly always is).
|
||||
func (tun *TunAdapter) setupAddress(addr string) error {
|
||||
// Set address
|
||||
var netIF *net.Interface
|
||||
ifces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, ifce := range ifces {
|
||||
if ifce.Name == tun.iface.Name() {
|
||||
var newIF = ifce
|
||||
netIF = &newIF // Don't point inside ifces, it's apparently unsafe?...
|
||||
}
|
||||
}
|
||||
if netIF == nil {
|
||||
return errors.New(fmt.Sprintf("Failed to find interface: %s", tun.iface.Name()))
|
||||
}
|
||||
ip, ipNet, err := net.ParseCIDR(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = netlink.NetworkLinkAddIp(netIF, ip, ipNet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = netlink.NetworkSetMTU(netIF, tun.mtu)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
netlink.NetworkLinkUp(netIF)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
33
src/tuntap/tun_other.go
Normal file
33
src/tuntap/tun_other.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
// +build !linux,!darwin,!windows,!openbsd,!freebsd,!netbsd,!mobile
|
||||
|
||||
package tuntap
|
||||
|
||||
import water "github.com/yggdrasil-network/water"
|
||||
|
||||
// This is to catch unsupported platforms
|
||||
// If your platform supports tun devices, you could try configuring it manually
|
||||
|
||||
// Creates the TUN/TAP adapter, if supported by the Water library. Note that
|
||||
// no guarantees are made at this point on an unsupported platform.
|
||||
func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error {
|
||||
var config water.Config
|
||||
if iftapmode {
|
||||
config = water.Config{DeviceType: water.TAP}
|
||||
} else {
|
||||
config = water.Config{DeviceType: water.TUN}
|
||||
}
|
||||
iface, err := water.New(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
tun.iface = iface
|
||||
tun.mtu = getSupportedMTU(mtu)
|
||||
return tun.setupAddress(addr)
|
||||
}
|
||||
|
||||
// We don't know how to set the IPv6 address on an unknown platform, therefore
|
||||
// write about it to stdout and don't try to do anything further.
|
||||
func (tun *TunAdapter) setupAddress(addr string) error {
|
||||
tun.core.log.Warnln("Platform not supported, you must set the address of", tun.iface.Name(), "to", addr)
|
||||
return nil
|
||||
}
|
99
src/tuntap/tun_windows.go
Normal file
99
src/tuntap/tun_windows.go
Normal file
|
@ -0,0 +1,99 @@
|
|||
package tuntap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
water "github.com/yggdrasil-network/water"
|
||||
)
|
||||
|
||||
// This is to catch Windows platforms
|
||||
|
||||
// Configures the TAP adapter with the correct IPv6 address and MTU. On Windows
|
||||
// we don't make use of a direct operating system API to do this - we instead
|
||||
// delegate the hard work to "netsh".
|
||||
func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error {
|
||||
if !iftapmode {
|
||||
tun.core.log.Warnln("TUN mode is not supported on this platform, defaulting to TAP")
|
||||
}
|
||||
config := water.Config{DeviceType: water.TAP}
|
||||
config.PlatformSpecificParams.ComponentID = "tap0901"
|
||||
config.PlatformSpecificParams.Network = "169.254.0.1/32"
|
||||
if ifname == "auto" {
|
||||
config.PlatformSpecificParams.InterfaceName = ""
|
||||
} else {
|
||||
config.PlatformSpecificParams.InterfaceName = ifname
|
||||
}
|
||||
iface, err := water.New(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Disable/enable the interface to resets its configuration (invalidating iface)
|
||||
cmd := exec.Command("netsh", "interface", "set", "interface", iface.Name(), "admin=DISABLED")
|
||||
tun.core.log.Printf("netsh command: %v", strings.Join(cmd.Args, " "))
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
tun.core.log.Errorf("Windows netsh failed: %v.", err)
|
||||
tun.core.log.Traceln(string(output))
|
||||
return err
|
||||
}
|
||||
cmd = exec.Command("netsh", "interface", "set", "interface", iface.Name(), "admin=ENABLED")
|
||||
tun.core.log.Printf("netsh command: %v", strings.Join(cmd.Args, " "))
|
||||
output, err = cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
tun.core.log.Errorf("Windows netsh failed: %v.", err)
|
||||
tun.core.log.Traceln(string(output))
|
||||
return err
|
||||
}
|
||||
// Get a new iface
|
||||
iface, err = water.New(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
tun.iface = iface
|
||||
tun.mtu = getSupportedMTU(mtu)
|
||||
err = tun.setupMTU(tun.mtu)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Friendly output
|
||||
tun.core.log.Infof("Interface name: %s", tun.iface.Name())
|
||||
tun.core.log.Infof("Interface IPv6: %s", addr)
|
||||
tun.core.log.Infof("Interface MTU: %d", tun.mtu)
|
||||
return tun.setupAddress(addr)
|
||||
}
|
||||
|
||||
// Sets the MTU of the TAP adapter.
|
||||
func (tun *TunAdapter) setupMTU(mtu int) error {
|
||||
// Set MTU
|
||||
cmd := exec.Command("netsh", "interface", "ipv6", "set", "subinterface",
|
||||
fmt.Sprintf("interface=%s", tun.iface.Name()),
|
||||
fmt.Sprintf("mtu=%d", mtu),
|
||||
"store=active")
|
||||
tun.core.log.Debugln("netsh command: %v", strings.Join(cmd.Args, " "))
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
tun.core.log.Errorf("Windows netsh failed: %v.", err)
|
||||
tun.core.log.Traceln(string(output))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sets the IPv6 address of the TAP adapter.
|
||||
func (tun *TunAdapter) setupAddress(addr string) error {
|
||||
// Set address
|
||||
cmd := exec.Command("netsh", "interface", "ipv6", "add", "address",
|
||||
fmt.Sprintf("interface=%s", tun.iface.Name()),
|
||||
fmt.Sprintf("addr=%s", addr),
|
||||
"store=active")
|
||||
tun.core.log.Debugln("netsh command: %v", strings.Join(cmd.Args, " "))
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
tun.core.log.Errorf("Windows netsh failed: %v.", err)
|
||||
tun.core.log.Traceln(string(output))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue