github.com/Mirantis/virtlet@v1.5.2-0.20191204181327-1659b8a48e9b/pkg/nettools/tap_linux.go (about)

     1  // +build linux
     2  
     3  /*
     4  Copyright 2018 Mirantis
     5  
     6  Licensed under the Apache License, Version 2.0 (the "License");
     7  you may not use this file except in compliance with the License.
     8  You may obtain a copy of the License at
     9  
    10      http://www.apache.org/licenses/LICENSE-2.0
    11  
    12  Unless required by applicable law or agreed to in writing, software
    13  distributed under the License is distributed on an "AS IS" BASIS,
    14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15  See the License for the specific language governing permissions and
    16  limitations under the License.
    17  */
    18  
    19  package nettools
    20  
    21  import (
    22  	"fmt"
    23  	"net"
    24  	"os"
    25  	"syscall"
    26  	"unsafe"
    27  
    28  	"github.com/vishvananda/netlink"
    29  )
    30  
    31  const (
    32  	sizeOfIfReq = 40
    33  	ifnamsiz    = 16
    34  )
    35  
    36  // Had to duplicate ifReq here as it's not exported
    37  type ifReq struct {
    38  	Name  [ifnamsiz]byte
    39  	Flags uint16
    40  	pad   [sizeOfIfReq - ifnamsiz - 2]byte
    41  }
    42  
    43  // OpenTAP opens a tap device and returns an os.File for it
    44  func OpenTAP(devName string) (*os.File, error) {
    45  	tapFile, err := os.OpenFile("/dev/net/tun", os.O_RDWR, 0)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  
    50  	var req ifReq
    51  
    52  	// set IFF_NO_PI to not provide packet information
    53  	// If flag IFF_NO_PI is not set each frame format is:
    54  	// Flags [2 bytes]
    55  	// Proto [2 bytes]
    56  	// Raw protocol ethernet frame.
    57  	// This extra 4-byte header breaks connectivity as in this case kernel truncates initial package
    58  	req.Flags = uint16(syscall.IFF_TAP | syscall.IFF_NO_PI | syscall.IFF_ONE_QUEUE)
    59  	copy(req.Name[:15], devName)
    60  	_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, tapFile.Fd(), uintptr(syscall.TUNSETIFF), uintptr(unsafe.Pointer(&req)))
    61  	if errno != 0 {
    62  		return nil, fmt.Errorf("tuntap IOCTL TUNSETIFF failed, errno %v", errno)
    63  	}
    64  	return tapFile, nil
    65  }
    66  
    67  // CreateTAP sets up a tap link and brings it up
    68  func CreateTAP(devName string, mtu int) (netlink.Link, error) {
    69  	tap := &netlink.Tuntap{
    70  		LinkAttrs: netlink.LinkAttrs{
    71  			Name:  devName,
    72  			Flags: net.FlagUp,
    73  			MTU:   mtu,
    74  		},
    75  		Mode: netlink.TUNTAP_MODE_TAP,
    76  	}
    77  
    78  	if err := netlink.LinkAdd(tap); err != nil {
    79  		return nil, fmt.Errorf("failed to create tap interface: %v", err)
    80  	}
    81  
    82  	if err := netlink.LinkSetUp(tap); err != nil {
    83  		return nil, fmt.Errorf("failed to set %q up: %v", devName, err)
    84  	}
    85  
    86  	// NOTE: link mtu in LinkAttrs above is actually ignored
    87  	if err := netlink.LinkSetMTU(tap, mtu); err != nil {
    88  		return nil, fmt.Errorf("LinkSetMTU(): %v", err)
    89  	}
    90  
    91  	return tap, nil
    92  }