// // Copyright (c) 2017 anygone contributors (see AUTHORS file) // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // * Neither the name of anygone nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // package tuntap import ( "context" "fmt" "net" "os/exec" "strings" "time" extlib "github.com/lab11/go-tuntap/tuntap" "github.com/vishvananda/netlink" ) const ( EtherTypeIPv4 = 0x0800 EtherTypeIPv6 = 0x86DD EtherTypeTransparentEthernetBridge = 0x6558 PostUpScriptTimeout = 10 * time.Second ) type Device struct { iface *extlib.Interface iftype extlib.DevKind nl netlink.Link } func (dev *Device) Name() string { return dev.iface.Name() } func (dev *Device) AddAddress(addr net.IPNet) error { // TODO: this only works on linux... if err := netlink.AddrAdd(dev.nl, &netlink.Addr{IPNet: &addr}); err != nil { return fmt.Errorf("netlink(addr add) failed: %v", err) } return nil } func (dev *Device) SetMTU(mtu int) error { // TODO: this only works on linux... if err := netlink.LinkSetMTU(dev.nl, mtu); err != nil { return fmt.Errorf("netlink(link set mtu) failed: %v", err) } return nil } func (dev *Device) Up() error { // TODO: this only works on linux... if err := netlink.LinkSetUp(dev.nl); err != nil { return fmt.Errorf("netlink(link set up) failed: %v", err) } return nil } func (dev *Device) Down() error { // TODO: this only works on linux... if err := netlink.LinkSetDown(dev.nl); err != nil { return fmt.Errorf("netlink(link set down) failed: %v", err) } return nil } func (dev *Device) RunPostUp(script string) error { ctx, cancel := context.WithTimeout(context.Background(), PostUpScriptTimeout) defer cancel() if err := exec.CommandContext(ctx, script, dev.iface.Name()).Run(); err != nil { return err } return nil } func (dev *Device) ReadPacket() (pkt *extlib.Packet, err error) { pkt, err = dev.iface.ReadPacket() if dev.iftype == extlib.DevTap { pkt.Protocol = EtherTypeTransparentEthernetBridge } return } func (dev *Device) WritePacket(pkt *extlib.Packet) error { pkt.Protocol = 0 return dev.iface.WritePacket(pkt) } func NewTunDevice(name string) (dev *Device, err error) { dev = &Device{iftype: extlib.DevTun} if dev.iface, err = extlib.Open(name, extlib.DevTun, true); err != nil { return } return } func NewTapDevice(name string) (dev *Device, err error) { dev = &Device{iftype: extlib.DevTap} if dev.iface, err = extlib.Open(name, extlib.DevTap, false); err != nil { return } return } func NewDevice(iftype, name string) (dev *Device, err error) { if iftype == "" { if strings.HasPrefix(name, "tun") { iftype = "tun" } else if strings.HasPrefix(name, "tap") { iftype = "tap" } else { return nil, fmt.Errorf("no device type is set and the given interface name(%s) doesn't start with 'tun' or 'tap'", name) } } switch strings.ToLower(iftype) { case "tun": dev, err = NewTunDevice(name) case "tap": dev, err = NewTapDevice(name) default: return nil, fmt.Errorf("invalid interface type: %s", iftype) } if err != nil { return nil, err } // TODO: this only works on linux... if dev.nl, err = netlink.LinkByName(dev.iface.Name()); err != nil { return nil, fmt.Errorf("netlink(open netlink by name) failed: %v", err) } return dev, nil }