tuntap: Return TunTapLink instead of GenericLink

For tuntap interfaces, return a TunTap Interface instead of
a Generic link when retrieving the interface.
Use netlink extended attributes to populate the Link attributes
for the tuntap link.
In case of older tun driver which does not provide these
attributes, use sysfs to retrieve these attributes.

This commit also adds Owner and Group attributes for the TunTap
Link.

Signed-off-by: Archana Shinde <archana.m.shinde@intel.com>
This commit is contained in:
Archana Shinde 2019-05-13 17:50:15 -07:00 committed by Alessandro Boch
parent e99361632b
commit db99c040b9
4 changed files with 138 additions and 1 deletions

View File

@ -311,6 +311,8 @@ type Tuntap struct {
NonPersist bool
Queues int
Fds []*os.File
Owner uint32
Group uint32
}
func (tuntap *Tuntap) Attrs() *LinkAttrs {

View File

@ -4,8 +4,10 @@ import (
"bytes"
"encoding/binary"
"fmt"
"io/ioutil"
"net"
"os"
"strconv"
"strings"
"syscall"
"unsafe"
@ -1459,6 +1461,8 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
link = &GTP{}
case "xfrm":
link = &Xfrmi{}
case "tun":
link = &Tuntap{}
default:
link = &GenericLink{LinkType: linkType}
}
@ -1502,6 +1506,8 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
parseGTPData(link, data)
case "xfrm":
parseXfrmiData(link, data)
case "tun":
parseTuntapData(link, data)
}
}
}
@ -1580,9 +1586,57 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
}
*link.Attrs() = base
// If the tuntap attributes are not updated by netlink due to
// an older driver, use sysfs
if link != nil && linkType == "tun" {
tuntap := link.(*Tuntap)
if tuntap.Mode == 0 {
ifname := tuntap.Attrs().Name
if flags, err := readSysPropAsInt64(ifname, "tun_flags"); err == nil {
if flags&unix.IFF_TUN != 0 {
tuntap.Mode = unix.IFF_TUN
} else if flags&unix.IFF_TAP != 0 {
tuntap.Mode = unix.IFF_TAP
}
tuntap.NonPersist = false
if flags&unix.IFF_PERSIST == 0 {
tuntap.NonPersist = true
}
}
// The sysfs interface for owner/group returns -1 for root user, instead of returning 0.
// So explicitly check for negative value, before assigning the owner uid/gid.
if owner, err := readSysPropAsInt64(ifname, "owner"); err == nil && owner > 0 {
tuntap.Owner = uint32(owner)
}
if group, err := readSysPropAsInt64(ifname, "group"); err == nil && group > 0 {
tuntap.Group = uint32(group)
}
}
}
return link, nil
}
func readSysPropAsInt64(ifname, prop string) (int64, error) {
fname := fmt.Sprintf("/sys/class/net/%s/%s", ifname, prop)
contents, err := ioutil.ReadFile(fname)
if err != nil {
return 0, err
}
num, err := strconv.ParseInt(strings.TrimSpace(string(contents)), 0, 64)
if err == nil {
return num, nil
}
return 0, err
}
// LinkList gets a list of link devices.
// Equivalent to: `ip link show`
func LinkList() ([]Link, error) {
@ -2586,3 +2640,22 @@ func VethPeerIndex(link *Veth) (int, error) {
}
return int(stats.data[0]), nil
}
func parseTuntapData(link Link, data []syscall.NetlinkRouteAttr) {
tuntap := link.(*Tuntap)
for _, datum := range data {
switch datum.Attr.Type {
case nl.IFLA_TUN_OWNER:
tuntap.Owner = native.Uint32(datum.Value)
case nl.IFLA_TUN_GROUP:
tuntap.Group = native.Uint32(datum.Value)
case nl.IFLA_TUN_TYPE:
tuntap.Mode = TuntapMode(uint8(datum.Value[0]))
case nl.IFLA_TUN_PERSIST:
tuntap.NonPersist = false
if uint8(datum.Value[0]) == 0 {
tuntap.NonPersist = true
}
}
}
}

View File

@ -6,6 +6,7 @@ import (
"bytes"
"net"
"os"
"syscall"
"testing"
"time"
@ -209,6 +210,14 @@ func testLinkAddDel(t *testing.T, link Link) {
compareXfrmi(t, xfrmi, other)
}
if tuntap, ok := link.(*Tuntap); ok {
other, ok := result.(*Tuntap)
if !ok {
t.Fatal("Result of create is not a tuntap")
}
compareTuntap(t, tuntap, other)
}
if err = LinkDel(link); err != nil {
t.Fatal(err)
}
@ -423,6 +432,24 @@ func compareXfrmi(t *testing.T, expected, actual *Xfrmi) {
}
}
func compareTuntap(t *testing.T, expected, actual *Tuntap) {
if expected.Mode != actual.Mode {
t.Fatalf("Tuntap.Mode doesn't match: expected : %+v, got %+v", expected.Mode, actual.Mode)
}
if expected.Owner != actual.Owner {
t.Fatal("Tuntap.Owner doesn't match")
}
if expected.Group != actual.Group {
t.Fatal("Tuntap.Group doesn't match")
}
if expected.NonPersist != actual.NonPersist {
t.Fatal("Tuntap.Group doesn't match")
}
}
func TestLinkAddDelWithIndex(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
@ -1839,16 +1866,37 @@ func TestLinkAddDelTuntap(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
// Mount sysfs so that sysfs gets the namespace tag of the current network namespace
// This is necessary so that /sys shows the network interfaces of the current namespace.
if err := syscall.Mount("sysfs", "/sys", "sysfs", syscall.MS_RDONLY, ""); err != nil {
t.Fatal("Cannot mount sysfs")
}
defer func() {
if err := syscall.Unmount("/sys", 0); err != nil {
t.Fatal("Cannot umount /sys")
}
}()
testLinkAddDel(t, &Tuntap{
LinkAttrs: LinkAttrs{Name: "foo"},
Mode: TUNTAP_MODE_TAP})
}
func TestLinkAddDelTuntapMq(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
if err := syscall.Mount("sysfs", "/sys", "sysfs", syscall.MS_RDONLY, ""); err != nil {
t.Fatal("Cannot mount sysfs")
}
defer func() {
if err := syscall.Unmount("/sys", 0); err != nil {
t.Fatal("Cannot umount /sys")
}
}()
testLinkAddDel(t, &Tuntap{
LinkAttrs: LinkAttrs{Name: "foo"},
Mode: TUNTAP_MODE_TAP,

View File

@ -581,3 +581,17 @@ const (
IFLA_XFRM_MAX = iota - 1
)
const (
IFLA_TUN_UNSPEC = iota
IFLA_TUN_OWNER
IFLA_TUN_GROUP
IFLA_TUN_TYPE
IFLA_TUN_PI
IFLA_TUN_VNET_HDR
IFLA_TUN_PERSIST
IFLA_TUN_MULTI_QUEUE
IFLA_TUN_NUM_QUEUES
IFLA_TUN_NUM_DISABLED_QUEUES
IFLA_TUN_MAX = IFLA_TUN_NUM_DISABLED_QUEUES
)