14 Commits

Author SHA1 Message Date
Madhu Venugopal 3daf672705 Merge pull request #354 from aboch/docker1.7.0_integ
Interface Statistics() API
2015-06-30 17:13:21 -07:00
Alessandro Boch 96afee8b4c Interface Statistics() API
- Reworked implementation for docker_1.7.0_integ branch

Signed-off-by: Alessandro Boch <aboch@docker.com>
2015-06-30 16:27:06 -07:00
Jana Radhakrishnan fc286182cd Merge pull request #353 from mavenugo/170integ_Godeps
Updating to latest netns to fix amd64 / RPI issues
2015-06-30 15:38:00 -07:00
Madhu Venugopal d82b7a9573 Updating to latest netns to fix amd64 / RPI issues
Signed-off-by: Madhu Venugopal <madhu@docker.com>
2015-06-30 15:27:12 -07:00
aboch 75716d8756 Merge pull request #351 from mrjana/docker1.7.0_integ
Honor driver side resolv.conf file
2015-06-30 15:26:47 -07:00
Jana Radhakrishnan 0c4a707c98 Honor driver side resolv.conf file
For the moment in 1.7.1 since we provide a resolv.conf set api
to the driver honor that so that for host driver we can use the
the host's /etc/resolv.conf file as is rather than putting the
contents through a filtering logic.

It should be noted that the driver side capability to set the
resolv.conf file is most likely going to go away in the future
but this should be fine for 1.7.1

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
2015-06-30 13:50:32 -07:00
aboch 4d49958029 Merge pull request #350 from mrjana/docker1.7.0_integ
Update netlink and manually bring up host side veth interface
2015-06-30 13:30:35 -07:00
Jana Radhakrishnan eb4ecdf537 Update vishvananda/netlink package
PR to update to vishvananda/netlink package

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
2015-06-30 11:10:08 -07:00
Jana Radhakrishnan 30d1b7861f Manually bring up the host side veth interface
In preparation for the new update of vishvananda/netlink package
we need to bringup the host veth interface manually.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
2015-06-30 11:08:16 -07:00
Madhu Venugopal 7465ee11d3 Merge pull request #349 from mrjana/docker1.7.0_integ
Fix networking issues in RHEL/Centos 6.6
2015-06-30 09:02:01 -07:00
Jana Radhakrishnan 6cacb70196 Fix networking issues in RHEL/Centos 6.6
Some parts of the bridge driver code needs to use a different kernel
api or use the already existing apis in slightly different ways to
make the bridge driver work in RHEL/Centos 6.6. This PR provides
those fixes.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
2015-06-29 23:10:30 -07:00
Madhu Venugopal cb9c2bfaa2 Merge pull request #315 from mrjana/docker1.7.0_integ
Use ioctl to create bridge
2015-06-18 16:25:50 -07:00
Jana Radhakrishnan 36bcacd1c6 Use ioctls to create bridge
The netlink way of creating bridge has problems in older
kernels like the one used on RHEL 6 (which is a supported
one). So trying to use ioctl method to create bridge
so that it works on any version.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
2015-06-18 16:04:06 -07:00
Jana Radhakrishnan 7b9631cd46 Updated Godeps
Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
2015-06-18 16:04:06 -07:00
33 changed files with 2455 additions and 88 deletions
+8 -3
View File
@@ -1,6 +1,6 @@
{
"ImportPath": "github.com/docker/libnetwork",
"GoVersion": "go1.4.1",
"GoVersion": "go1.4.2",
"Packages": [
"./..."
],
@@ -55,6 +55,11 @@
"Comment": "v1.4.1-3479-ga9172f5",
"Rev": "a9172f572e13086859c652e2d581950e910d63d4"
},
{
"ImportPath": "github.com/docker/libcontainer/netlink",
"Comment": "v1.4.0-495-g3e66118",
"Rev": "3e661186ba24f259d3860f067df052c7f6904bee"
},
{
"ImportPath": "github.com/docker/libcontainer/user",
"Comment": "v1.4.0-495-g3e66118",
@@ -75,11 +80,11 @@
},
{
"ImportPath": "github.com/vishvananda/netlink",
"Rev": "8eb64238879fed52fd51c5b30ad20b928fb4c36c"
"Rev": "20397a138846e4d6590e01783ed023ed7e1c38a6"
},
{
"ImportPath": "github.com/vishvananda/netns",
"Rev": "5478c060110032f972e86a1f844fdb9a2f008f2c"
"Rev": "493029407eeb434d0c2d44e02ea072ff2488d322"
}
]
}
@@ -0,0 +1,2 @@
Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
Guillaume J. Charmes <guillaume@docker.com> (@creack)
@@ -0,0 +1,31 @@
// Packet netlink provide access to low level Netlink sockets and messages.
//
// Actual implementations are in:
// netlink_linux.go
// netlink_darwin.go
package netlink
import (
"errors"
"net"
)
var (
ErrWrongSockType = errors.New("Wrong socket type")
ErrShortResponse = errors.New("Got short response from netlink")
ErrInterfaceExists = errors.New("Network interface already exists")
)
// A Route is a subnet associated with the interface to reach it.
type Route struct {
*net.IPNet
Iface *net.Interface
Default bool
}
// An IfAddr defines IP network settings for a given network interface
type IfAddr struct {
Iface *net.Interface
IP net.IP
IPNet *net.IPNet
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,5 @@
package netlink
func ifrDataByte(b byte) uint8 {
return uint8(b)
}
@@ -0,0 +1,7 @@
// +build !arm
package netlink
func ifrDataByte(b byte) int8 {
return int8(b)
}
@@ -0,0 +1,408 @@
package netlink
import (
"net"
"strings"
"syscall"
"testing"
)
type testLink struct {
name string
linkType string
}
func addLink(t *testing.T, name string, linkType string) {
if err := NetworkLinkAdd(name, linkType); err != nil {
t.Fatalf("Unable to create %s link: %s", name, err)
}
}
func readLink(t *testing.T, name string) *net.Interface {
iface, err := net.InterfaceByName(name)
if err != nil {
t.Fatalf("Could not find %s interface: %s", name, err)
}
return iface
}
func deleteLink(t *testing.T, name string) {
if err := NetworkLinkDel(name); err != nil {
t.Fatalf("Unable to delete %s link: %s", name, err)
}
}
func upLink(t *testing.T, name string) {
iface := readLink(t, name)
if err := NetworkLinkUp(iface); err != nil {
t.Fatalf("Could not bring UP %#v interface: %s", iface, err)
}
}
func downLink(t *testing.T, name string) {
iface := readLink(t, name)
if err := NetworkLinkDown(iface); err != nil {
t.Fatalf("Could not bring DOWN %#v interface: %s", iface, err)
}
}
func ipAssigned(iface *net.Interface, ip net.IP) bool {
addrs, _ := iface.Addrs()
for _, addr := range addrs {
args := strings.SplitN(addr.String(), "/", 2)
if args[0] == ip.String() {
return true
}
}
return false
}
func TestNetworkLinkAddDel(t *testing.T) {
if testing.Short() {
return
}
testLinks := []testLink{
{"tstEth", "dummy"},
{"tstBr", "bridge"},
}
for _, tl := range testLinks {
addLink(t, tl.name, tl.linkType)
defer deleteLink(t, tl.name)
readLink(t, tl.name)
}
}
func TestNetworkLinkUpDown(t *testing.T) {
if testing.Short() {
return
}
tl := testLink{name: "tstEth", linkType: "dummy"}
addLink(t, tl.name, tl.linkType)
defer deleteLink(t, tl.name)
upLink(t, tl.name)
ifcAfterUp := readLink(t, tl.name)
if (ifcAfterUp.Flags & syscall.IFF_UP) != syscall.IFF_UP {
t.Fatalf("Could not bring UP %#v initerface", tl)
}
downLink(t, tl.name)
ifcAfterDown := readLink(t, tl.name)
if (ifcAfterDown.Flags & syscall.IFF_UP) == syscall.IFF_UP {
t.Fatalf("Could not bring DOWN %#v initerface", tl)
}
}
func TestNetworkSetMacAddress(t *testing.T) {
if testing.Short() {
return
}
tl := testLink{name: "tstEth", linkType: "dummy"}
macaddr := "22:ce:e0:99:63:6f"
addLink(t, tl.name, tl.linkType)
defer deleteLink(t, tl.name)
ifcBeforeSet := readLink(t, tl.name)
if err := NetworkSetMacAddress(ifcBeforeSet, macaddr); err != nil {
t.Fatalf("Could not set %s MAC address on %#v interface: %s", macaddr, tl, err)
}
ifcAfterSet := readLink(t, tl.name)
if ifcAfterSet.HardwareAddr.String() != macaddr {
t.Fatalf("Could not set %s MAC address on %#v interface", macaddr, tl)
}
}
func TestNetworkSetMTU(t *testing.T) {
if testing.Short() {
return
}
tl := testLink{name: "tstEth", linkType: "dummy"}
mtu := 1400
addLink(t, tl.name, tl.linkType)
defer deleteLink(t, tl.name)
ifcBeforeSet := readLink(t, tl.name)
if err := NetworkSetMTU(ifcBeforeSet, mtu); err != nil {
t.Fatalf("Could not set %d MTU on %#v interface: %s", mtu, tl, err)
}
ifcAfterSet := readLink(t, tl.name)
if ifcAfterSet.MTU != mtu {
t.Fatalf("Could not set %d MTU on %#v interface", mtu, tl)
}
}
func TestNetworkSetMasterNoMaster(t *testing.T) {
if testing.Short() {
return
}
master := testLink{"tstBr", "bridge"}
slave := testLink{"tstEth", "dummy"}
testLinks := []testLink{master, slave}
for _, tl := range testLinks {
addLink(t, tl.name, tl.linkType)
defer deleteLink(t, tl.name)
upLink(t, tl.name)
}
masterIfc := readLink(t, master.name)
slaveIfc := readLink(t, slave.name)
if err := NetworkSetMaster(slaveIfc, masterIfc); err != nil {
t.Fatalf("Could not set %#v to be the master of %#v: %s", master, slave, err)
}
// Trying to figure out a way to test which will not break on RHEL6.
// We could check for existence of /sys/class/net/tstEth/upper_tstBr
// which should point to the ../tstBr which is the UPPER device i.e. network bridge
if err := NetworkSetNoMaster(slaveIfc); err != nil {
t.Fatalf("Could not UNset %#v master of %#v: %s", master, slave, err)
}
}
func TestNetworkChangeName(t *testing.T) {
if testing.Short() {
return
}
tl := testLink{"tstEth", "dummy"}
newName := "newTst"
addLink(t, tl.name, tl.linkType)
linkIfc := readLink(t, tl.name)
if err := NetworkChangeName(linkIfc, newName); err != nil {
deleteLink(t, tl.name)
t.Fatalf("Could not change %#v interface name to %s: %s", tl, newName, err)
}
readLink(t, newName)
deleteLink(t, newName)
}
func TestNetworkLinkAddVlan(t *testing.T) {
if testing.Short() {
return
}
tl := struct {
name string
id uint16
}{
name: "tstVlan",
id: 32,
}
masterLink := testLink{"tstEth", "dummy"}
addLink(t, masterLink.name, masterLink.linkType)
defer deleteLink(t, masterLink.name)
if err := NetworkLinkAddVlan(masterLink.name, tl.name, tl.id); err != nil {
t.Fatalf("Unable to create %#v VLAN interface: %s", tl, err)
}
readLink(t, tl.name)
}
func TestNetworkLinkAddMacVlan(t *testing.T) {
if testing.Short() {
return
}
tl := struct {
name string
mode string
}{
name: "tstVlan",
mode: "private",
}
masterLink := testLink{"tstEth", "dummy"}
addLink(t, masterLink.name, masterLink.linkType)
defer deleteLink(t, masterLink.name)
if err := NetworkLinkAddMacVlan(masterLink.name, tl.name, tl.mode); err != nil {
t.Fatalf("Unable to create %#v MAC VLAN interface: %s", tl, err)
}
readLink(t, tl.name)
}
func TestNetworkLinkAddMacVtap(t *testing.T) {
if testing.Short() {
return
}
tl := struct {
name string
mode string
}{
name: "tstVtap",
mode: "private",
}
masterLink := testLink{"tstEth", "dummy"}
addLink(t, masterLink.name, masterLink.linkType)
defer deleteLink(t, masterLink.name)
if err := NetworkLinkAddMacVtap(masterLink.name, tl.name, tl.mode); err != nil {
t.Fatalf("Unable to create %#v MAC VTAP interface: %s", tl, err)
}
readLink(t, tl.name)
}
func TestAddDelNetworkIp(t *testing.T) {
if testing.Short() {
return
}
ifaceName := "lo"
ip := net.ParseIP("127.0.1.1")
mask := net.IPv4Mask(255, 255, 255, 255)
ipNet := &net.IPNet{IP: ip, Mask: mask}
iface, err := net.InterfaceByName(ifaceName)
if err != nil {
t.Skip("No 'lo' interface; skipping tests")
}
if err := NetworkLinkAddIp(iface, ip, ipNet); err != nil {
t.Fatalf("Could not add IP address %s to interface %#v: %s", ip.String(), iface, err)
}
if !ipAssigned(iface, ip) {
t.Fatalf("Could not locate address '%s' in lo address list.", ip.String())
}
if err := NetworkLinkDelIp(iface, ip, ipNet); err != nil {
t.Fatalf("Could not delete IP address %s from interface %#v: %s", ip.String(), iface, err)
}
if ipAssigned(iface, ip) {
t.Fatalf("Located address '%s' in lo address list after removal.", ip.String())
}
}
func TestAddRouteSourceSelection(t *testing.T) {
tstIp := "127.1.1.1"
tl := testLink{name: "tstEth", linkType: "dummy"}
addLink(t, tl.name, tl.linkType)
defer deleteLink(t, tl.name)
ip := net.ParseIP(tstIp)
mask := net.IPv4Mask(255, 255, 255, 255)
ipNet := &net.IPNet{IP: ip, Mask: mask}
iface, err := net.InterfaceByName(tl.name)
if err != nil {
t.Fatalf("Lost created link %#v", tl)
}
if err := NetworkLinkAddIp(iface, ip, ipNet); err != nil {
t.Fatalf("Could not add IP address %s to interface %#v: %s", ip.String(), iface, err)
}
upLink(t, tl.name)
defer downLink(t, tl.name)
if err := AddRoute("127.0.0.0/8", tstIp, "", tl.name); err != nil {
t.Fatalf("Failed to add route with source address")
}
}
func TestCreateVethPair(t *testing.T) {
if testing.Short() {
return
}
var (
name1 = "veth1"
name2 = "veth2"
)
if err := NetworkCreateVethPair(name1, name2, 0); err != nil {
t.Fatalf("Could not create veth pair %s %s: %s", name1, name2, err)
}
defer NetworkLinkDel(name1)
readLink(t, name1)
readLink(t, name2)
}
//
// netlink package tests which do not use RTNETLINK
//
func TestCreateBridgeWithMac(t *testing.T) {
if testing.Short() {
return
}
name := "testbridge"
if err := CreateBridge(name, true); err != nil {
t.Fatal(err)
}
if _, err := net.InterfaceByName(name); err != nil {
t.Fatal(err)
}
// cleanup and tests
if err := DeleteBridge(name); err != nil {
t.Fatal(err)
}
if _, err := net.InterfaceByName(name); err == nil {
t.Fatalf("expected error getting interface because %s bridge was deleted", name)
}
}
func TestSetMacAddress(t *testing.T) {
if testing.Short() {
return
}
name := "testmac"
mac := randMacAddr()
if err := NetworkLinkAdd(name, "bridge"); err != nil {
t.Fatal(err)
}
defer NetworkLinkDel(name)
if err := SetMacAddress(name, mac); err != nil {
t.Fatal(err)
}
iface, err := net.InterfaceByName(name)
if err != nil {
t.Fatal(err)
}
if iface.HardwareAddr.String() != mac {
t.Fatalf("mac address %q does not match %q", iface.HardwareAddr, mac)
}
}
@@ -0,0 +1,88 @@
// +build !linux
package netlink
import (
"errors"
"net"
)
var (
ErrNotImplemented = errors.New("not implemented")
)
func NetworkGetRoutes() ([]Route, error) {
return nil, ErrNotImplemented
}
func NetworkLinkAdd(name string, linkType string) error {
return ErrNotImplemented
}
func NetworkLinkDel(name string) error {
return ErrNotImplemented
}
func NetworkLinkUp(iface *net.Interface) error {
return ErrNotImplemented
}
func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
return ErrNotImplemented
}
func NetworkLinkDelIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
return ErrNotImplemented
}
func AddRoute(destination, source, gateway, device string) error {
return ErrNotImplemented
}
func AddDefaultGw(ip, device string) error {
return ErrNotImplemented
}
func NetworkSetMTU(iface *net.Interface, mtu int) error {
return ErrNotImplemented
}
func NetworkSetTxQueueLen(iface *net.Interface, txQueueLen int) error {
return ErrNotImplemented
}
func NetworkCreateVethPair(name1, name2 string, txQueueLen int) error {
return ErrNotImplemented
}
func NetworkChangeName(iface *net.Interface, newName string) error {
return ErrNotImplemented
}
func NetworkSetNsFd(iface *net.Interface, fd int) error {
return ErrNotImplemented
}
func NetworkSetNsPid(iface *net.Interface, nspid int) error {
return ErrNotImplemented
}
func NetworkSetMaster(iface, master *net.Interface) error {
return ErrNotImplemented
}
func NetworkLinkDown(iface *net.Interface) error {
return ErrNotImplemented
}
func CreateBridge(name string, setMacAddr bool) error {
return ErrNotImplemented
}
func DeleteBridge(name string) error {
return ErrNotImplemented
}
func AddToBridge(iface, master *net.Interface) error {
return ErrNotImplemented
}
+7 -1
View File
@@ -43,13 +43,19 @@ import (
)
func main() {
mybridge := &netlink.Bridge{netlink.LinkAttrs{Name: "foo"}}
la := netlink.NewLinkAttrs()
la.Name = "foo"
mybridge := &netlink.Bridge{la}}
_ := netlink.LinkAdd(mybridge)
eth1, _ := netlink.LinkByName("eth1")
netlink.LinkSetMaster(eth1, mybridge)
}
```
Note `NewLinkAttrs` constructor, it sets default values in structure. For now
it sets only `TxQLen` to `-1`, so kernel will set default by itself. If you're
using simple initialization(`LinkAttrs{Name: "foo"}`) `TxQLen` will be set to
`0` unless you specify it like `LinkAttrs{Name: "foo", TxQLen: 1000}`.
Add a new ip address to loopback:
+2 -2
View File
@@ -14,8 +14,8 @@ type Addr struct {
}
// String returns $ip/$netmask $label
func (addr Addr) String() string {
return fmt.Sprintf("%s %s", addr.IPNet, addr.Label)
func (a Addr) String() string {
return fmt.Sprintf("%s %s", a.IPNet, a.Label)
}
// ParseAddr parses the string representation of an address in the
+16 -2
View File
@@ -81,7 +81,7 @@ func AddrList(link Link, family int) ([]Addr, error) {
index = base.Index
}
res := make([]Addr, 0)
var res []Addr
for _, m := range msgs {
msg := nl.DeserializeIfAddrmsg(m)
@@ -95,11 +95,17 @@ func AddrList(link Link, family int) ([]Addr, error) {
return nil, err
}
var local, dst *net.IPNet
var addr Addr
for _, attr := range attrs {
switch attr.Attr.Type {
case syscall.IFA_ADDRESS:
addr.IPNet = &net.IPNet{
dst = &net.IPNet{
IP: attr.Value,
Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)),
}
case syscall.IFA_LOCAL:
local = &net.IPNet{
IP: attr.Value,
Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)),
}
@@ -107,6 +113,14 @@ func AddrList(link Link, family int) ([]Addr, error) {
addr.Label = string(attr.Value[:len(attr.Value)-1])
}
}
// IFA_LOCAL should be there but if not, fall back to IFA_ADDRESS
if local != nil {
addr.IPNet = local
} else {
addr.IPNet = dst
}
res = append(res, addr)
}
+28 -3
View File
@@ -10,16 +10,29 @@ type Link interface {
Type() string
}
type (
NsPid int
NsFd int
)
// LinkAttrs represents data shared by most link types
type LinkAttrs struct {
Index int
MTU int
TxQLen uint32 // Transmit Queue Length
TxQLen int // Transmit Queue Length
Name string
HardwareAddr net.HardwareAddr
Flags net.Flags
ParentIndex int // index of the parent link device
MasterIndex int // must be the index of a bridge
ParentIndex int // index of the parent link device
MasterIndex int // must be the index of a bridge
Namespace interface{} // nil | NsPid | NsFd
}
// NewLinkAttrs returns LinkAttrs structure filled with default values
func NewLinkAttrs() LinkAttrs {
return LinkAttrs{
TxQLen: -1,
}
}
// Device links cannot be created via netlink. These links
@@ -76,9 +89,21 @@ func (vlan *Vlan) Type() string {
return "vlan"
}
type MacvlanMode uint16
const (
MACVLAN_MODE_DEFAULT MacvlanMode = iota
MACVLAN_MODE_PRIVATE
MACVLAN_MODE_VEPA
MACVLAN_MODE_BRIDGE
MACVLAN_MODE_PASSTHRU
MACVLAN_MODE_SOURCE
)
// Macvlan links have ParentIndex set in their Attrs()
type Macvlan struct {
LinkAttrs
Mode MacvlanMode
}
func (macvlan *Macvlan) Attrs() *LinkAttrs {
+73 -15
View File
@@ -13,6 +13,15 @@ import (
var native = nl.NativeEndian()
var lookupByDump = false
var macvlanModes = [...]uint32{
0,
nl.MACVLAN_MODE_PRIVATE,
nl.MACVLAN_MODE_VEPA,
nl.MACVLAN_MODE_BRIDGE,
nl.MACVLAN_MODE_PASSTHRU,
nl.MACVLAN_MODE_SOURCE,
}
func ensureIndex(link *LinkAttrs) {
if link != nil && link.Index == 0 {
newlink, _ := LinkByName(link.Name)
@@ -39,7 +48,7 @@ func LinkSetUp(link Link) error {
return err
}
// LinkSetUp disables link device.
// LinkSetDown disables link device.
// Equivalent to: `ip link set $link down`
func LinkSetDown(link Link) error {
base := link.Attrs()
@@ -67,7 +76,7 @@ func LinkSetMTU(link Link, mtu int) error {
msg.Type = syscall.RTM_SETLINK
msg.Flags = syscall.NLM_F_REQUEST
msg.Index = int32(base.Index)
msg.Change = nl.DEFAULT_CHANGE
msg.Change = syscall.IFLA_MTU
req.AddData(msg)
b := make([]byte, 4)
@@ -91,7 +100,7 @@ func LinkSetName(link Link, name string) error {
msg.Type = syscall.RTM_SETLINK
msg.Flags = syscall.NLM_F_REQUEST
msg.Index = int32(base.Index)
msg.Change = nl.DEFAULT_CHANGE
msg.Change = syscall.IFLA_IFNAME
req.AddData(msg)
data := nl.NewRtAttr(syscall.IFLA_IFNAME, []byte(name))
@@ -112,7 +121,7 @@ func LinkSetHardwareAddr(link Link, hwaddr net.HardwareAddr) error {
msg.Type = syscall.RTM_SETLINK
msg.Flags = syscall.NLM_F_REQUEST
msg.Index = int32(base.Index)
msg.Change = nl.DEFAULT_CHANGE
msg.Change = syscall.IFLA_ADDRESS
req.AddData(msg)
data := nl.NewRtAttr(syscall.IFLA_ADDRESS, []byte(hwaddr))
@@ -145,7 +154,7 @@ func LinkSetMasterByIndex(link Link, masterIndex int) error {
msg.Type = syscall.RTM_SETLINK
msg.Flags = syscall.NLM_F_REQUEST
msg.Index = int32(base.Index)
msg.Change = nl.DEFAULT_CHANGE
msg.Change = syscall.IFLA_MASTER
req.AddData(msg)
b := make([]byte, 4)
@@ -170,7 +179,7 @@ func LinkSetNsPid(link Link, nspid int) error {
msg.Type = syscall.RTM_SETLINK
msg.Flags = syscall.NLM_F_REQUEST
msg.Index = int32(base.Index)
msg.Change = nl.DEFAULT_CHANGE
msg.Change = syscall.IFLA_NET_NS_PID
req.AddData(msg)
b := make([]byte, 4)
@@ -183,7 +192,7 @@ func LinkSetNsPid(link Link, nspid int) error {
return err
}
// LinkSetNsPid puts the device into a new network namespace. The
// LinkSetNsFd puts the device into a new network namespace. The
// fd must be an open file descriptor to a network namespace.
// Similar to: `ip link set $link netns $ns`
func LinkSetNsFd(link Link, fd int) error {
@@ -195,7 +204,7 @@ func LinkSetNsFd(link Link, fd int) error {
msg.Type = syscall.RTM_SETLINK
msg.Flags = syscall.NLM_F_REQUEST
msg.Index = int32(base.Index)
msg.Change = nl.DEFAULT_CHANGE
msg.Change = nl.IFLA_NET_NS_FD
req.AddData(msg)
b := make([]byte, 4)
@@ -312,11 +321,28 @@ func LinkAdd(link Link) error {
req.AddData(mtu)
}
if base.TxQLen >= 0 {
qlen := nl.NewRtAttr(syscall.IFLA_TXQLEN, nl.Uint32Attr(uint32(base.TxQLen)))
req.AddData(qlen)
}
if base.Namespace != nil {
var attr *nl.RtAttr
switch base.Namespace.(type) {
case NsPid:
val := nl.Uint32Attr(uint32(base.Namespace.(NsPid)))
attr = nl.NewRtAttr(syscall.IFLA_NET_NS_PID, val)
case NsFd:
val := nl.Uint32Attr(uint32(base.Namespace.(NsFd)))
attr = nl.NewRtAttr(nl.IFLA_NET_NS_FD, val)
}
req.AddData(attr)
}
linkInfo := nl.NewRtAttr(syscall.IFLA_LINKINFO, nil)
nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_KIND, nl.NonZeroTerminated(link.Type()))
nl.NewRtAttrChild(linkInfo, syscall.IFLA_TXQLEN, nl.Uint32Attr(base.TxQLen))
if vlan, ok := link.(*Vlan); ok {
b := make([]byte, 2)
native.PutUint16(b, uint16(vlan.VlanId))
@@ -327,15 +353,23 @@ func LinkAdd(link Link) error {
peer := nl.NewRtAttrChild(data, nl.VETH_INFO_PEER, nil)
nl.NewIfInfomsgChild(peer, syscall.AF_UNSPEC)
nl.NewRtAttrChild(peer, syscall.IFLA_IFNAME, nl.ZeroTerminated(veth.PeerName))
nl.NewRtAttrChild(peer, syscall.IFLA_TXQLEN, nl.Uint32Attr(base.TxQLen))
if base.TxQLen >= 0 {
nl.NewRtAttrChild(peer, syscall.IFLA_TXQLEN, nl.Uint32Attr(uint32(base.TxQLen)))
}
if base.MTU > 0 {
nl.NewRtAttrChild(peer, syscall.IFLA_MTU, nl.Uint32Attr(uint32(base.MTU)))
}
} else if vxlan, ok := link.(*Vxlan); ok {
addVxlanAttrs(vxlan, linkInfo)
} else if ipv, ok := link.(*IPVlan); ok {
data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
nl.NewRtAttrChild(data, nl.IFLA_IPVLAN_MODE, nl.Uint16Attr(uint16(ipv.Mode)))
} else if macv, ok := link.(*Macvlan); ok {
if macv.Mode != MACVLAN_MODE_DEFAULT {
data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
nl.NewRtAttrChild(data, nl.IFLA_MACVLAN_MODE, nl.Uint32Attr(macvlanModes[macv.Mode]))
}
}
req.AddData(linkInfo)
@@ -483,6 +517,8 @@ func linkDeserialize(m []byte) (Link, error) {
link = &Vxlan{}
case "ipvlan":
link = &IPVlan{}
case "macvlan":
link = &Macvlan{}
default:
link = &Generic{LinkType: linkType}
}
@@ -498,6 +534,8 @@ func linkDeserialize(m []byte) (Link, error) {
parseVxlanData(link, data)
case "ipvlan":
parseIPVlanData(link, data)
case "macvlan":
parseMacvlanData(link, data)
}
}
}
@@ -520,7 +558,7 @@ func linkDeserialize(m []byte) (Link, error) {
case syscall.IFLA_MASTER:
base.MasterIndex = int(native.Uint32(attr.Value[0:4]))
case syscall.IFLA_TXQLEN:
base.TxQLen = native.Uint32(attr.Value[0:4])
base.TxQLen = int(native.Uint32(attr.Value[0:4]))
}
}
// Links that don't have IFLA_INFO_KIND are hardware devices
@@ -547,8 +585,7 @@ func LinkList() ([]Link, error) {
return nil, err
}
res := make([]Link, 0)
var res []Link
for _, m := range msgs {
link, err := linkDeserialize(m)
if err != nil {
@@ -593,7 +630,7 @@ func setProtinfoAttr(link Link, mode bool, attr int) error {
msg.Type = syscall.RTM_SETLINK
msg.Flags = syscall.NLM_F_REQUEST
msg.Index = int32(base.Index)
msg.Change = nl.DEFAULT_CHANGE
msg.Change = syscall.IFLA_PROTINFO | syscall.NLA_F_NESTED
req.AddData(msg)
br := nl.NewRtAttr(syscall.IFLA_PROTINFO|syscall.NLA_F_NESTED, nil)
@@ -674,6 +711,27 @@ func parseIPVlanData(link Link, data []syscall.NetlinkRouteAttr) {
}
}
func parseMacvlanData(link Link, data []syscall.NetlinkRouteAttr) {
macv := link.(*Macvlan)
for _, datum := range data {
if datum.Attr.Type == nl.IFLA_MACVLAN_MODE {
switch native.Uint32(datum.Value[0:4]) {
case nl.MACVLAN_MODE_PRIVATE:
macv.Mode = MACVLAN_MODE_PRIVATE
case nl.MACVLAN_MODE_VEPA:
macv.Mode = MACVLAN_MODE_VEPA
case nl.MACVLAN_MODE_BRIDGE:
macv.Mode = MACVLAN_MODE_BRIDGE
case nl.MACVLAN_MODE_PASSTHRU:
macv.Mode = MACVLAN_MODE_PASSTHRU
case nl.MACVLAN_MODE_SOURCE:
macv.Mode = MACVLAN_MODE_SOURCE
}
return
}
}
}
// copied from pkg/net_linux.go
func linkFlags(rawFlags uint32) net.Flags {
var f net.Flags
+114 -5
View File
@@ -8,7 +8,10 @@ import (
"github.com/vishvananda/netns"
)
const testTxQLen uint32 = 100
const (
testTxQLen int = 100
defaultTxQLen int = 1000
)
func testLinkAddDel(t *testing.T, link Link) {
links, err := LinkList()
@@ -50,9 +53,9 @@ func testLinkAddDel(t *testing.T, link Link) {
}
}
if veth, ok := link.(*Veth); ok {
if veth.TxQLen != testTxQLen {
t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, testTxQLen)
if veth, ok := result.(*Veth); ok {
if rBase.TxQLen != base.TxQLen {
t.Fatalf("qlen is %d, should be %d", rBase.TxQLen, base.TxQLen)
}
if rBase.MTU != base.MTU {
t.Fatalf("MTU is %d, should be %d", rBase.MTU, base.MTU)
@@ -91,6 +94,16 @@ func testLinkAddDel(t *testing.T, link Link) {
}
}
if macv, ok := link.(*Macvlan); ok {
other, ok := result.(*Macvlan)
if !ok {
t.Fatal("Result of create is not a macvlan")
}
if macv.Mode != other.Mode {
t.Fatalf("Got unexpected mode: %d, expected: %d", other.Mode, macv.Mode)
}
}
if err = LinkDel(link); err != nil {
t.Fatal(err)
}
@@ -199,7 +212,10 @@ func TestLinkAddDelMacvlan(t *testing.T) {
t.Fatal(err)
}
testLinkAddDel(t, &Macvlan{LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index}})
testLinkAddDel(t, &Macvlan{
LinkAttrs: LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index},
Mode: MACVLAN_MODE_PRIVATE,
})
if err := LinkDel(parent); err != nil {
t.Fatal(err)
@@ -213,6 +229,99 @@ func TestLinkAddDelVeth(t *testing.T) {
testLinkAddDel(t, &Veth{LinkAttrs{Name: "foo", TxQLen: testTxQLen, MTU: 1400}, "bar"})
}
func TestLinkAddVethWithDefaultTxQLen(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
la := NewLinkAttrs()
la.Name = "foo"
veth := &Veth{LinkAttrs: la, PeerName: "bar"}
if err := LinkAdd(veth); err != nil {
t.Fatal(err)
}
link, err := LinkByName("foo")
if err != nil {
t.Fatal(err)
}
if veth, ok := link.(*Veth); !ok {
t.Fatalf("unexpected link type: %T", link)
} else {
if veth.TxQLen != defaultTxQLen {
t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, defaultTxQLen)
}
}
peer, err := LinkByName("bar")
if err != nil {
t.Fatal(err)
}
if veth, ok := peer.(*Veth); !ok {
t.Fatalf("unexpected link type: %T", link)
} else {
if veth.TxQLen != defaultTxQLen {
t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, defaultTxQLen)
}
}
}
func TestLinkAddVethWithZeroTxQLen(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
la := NewLinkAttrs()
la.Name = "foo"
la.TxQLen = 0
veth := &Veth{LinkAttrs: la, PeerName: "bar"}
if err := LinkAdd(veth); err != nil {
t.Fatal(err)
}
link, err := LinkByName("foo")
if err != nil {
t.Fatal(err)
}
if veth, ok := link.(*Veth); !ok {
t.Fatalf("unexpected link type: %T", link)
} else {
if veth.TxQLen != 0 {
t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, 0)
}
}
peer, err := LinkByName("bar")
if err != nil {
t.Fatal(err)
}
if veth, ok := peer.(*Veth); !ok {
t.Fatalf("unexpected link type: %T", link)
} else {
if veth.TxQLen != 0 {
t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, 0)
}
}
}
func TestLinkAddDummyWithTxQLen(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
la := NewLinkAttrs()
la.Name = "foo"
la.TxQLen = 1500
dummy := &Dummy{LinkAttrs: la}
if err := LinkAdd(dummy); err != nil {
t.Fatal(err)
}
link, err := LinkByName("foo")
if err != nil {
t.Fatal(err)
}
if dummy, ok := link.(*Dummy); !ok {
t.Fatalf("unexpected link type: %T", link)
} else {
if dummy.TxQLen != 1500 {
t.Fatalf("TxQLen is %d, should be %d", dummy.TxQLen, 1500)
}
}
}
func TestLinkAddDelBridgeMaster(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
+1 -1
View File
@@ -141,7 +141,7 @@ func NeighList(linkIndex, family int) ([]Neigh, error) {
return nil, err
}
res := make([]Neigh, 0)
var res []Neigh
for _, m := range msgs {
ndm := deserializeNdmsg(m)
if linkIndex != 0 && int(ndm.Index) != linkIndex {
+15
View File
@@ -79,3 +79,18 @@ const (
// not defined in syscall
IFLA_NET_NS_FD = 28
)
const (
IFLA_MACVLAN_UNSPEC = iota
IFLA_MACVLAN_MODE
IFLA_MACVLAN_FLAGS
IFLA_MACVLAN_MAX = IFLA_MACVLAN_FLAGS
)
const (
MACVLAN_MODE_PRIVATE = 1
MACVLAN_MODE_VEPA = 2
MACVLAN_MODE_BRIDGE = 4
MACVLAN_MODE_PASSTHRU = 8
MACVLAN_MODE_SOURCE = 16
)
+11 -11
View File
@@ -172,16 +172,16 @@ type NetlinkRequest struct {
}
// Serialize the Netlink Request into a byte array
func (msg *NetlinkRequest) Serialize() []byte {
func (req *NetlinkRequest) Serialize() []byte {
length := syscall.SizeofNlMsghdr
dataBytes := make([][]byte, len(msg.Data))
for i, data := range msg.Data {
dataBytes := make([][]byte, len(req.Data))
for i, data := range req.Data {
dataBytes[i] = data.Serialize()
length = length + len(dataBytes[i])
}
msg.Len = uint32(length)
req.Len = uint32(length)
b := make([]byte, length)
hdr := (*(*[syscall.SizeofNlMsghdr]byte)(unsafe.Pointer(msg)))[:]
hdr := (*(*[syscall.SizeofNlMsghdr]byte)(unsafe.Pointer(req)))[:]
next := syscall.SizeofNlMsghdr
copy(b[0:next], hdr)
for _, data := range dataBytes {
@@ -193,9 +193,9 @@ func (msg *NetlinkRequest) Serialize() []byte {
return b
}
func (msg *NetlinkRequest) AddData(data NetlinkRequestData) {
func (req *NetlinkRequest) AddData(data NetlinkRequestData) {
if data != nil {
msg.Data = append(msg.Data, data)
req.Data = append(req.Data, data)
}
}
@@ -218,11 +218,11 @@ func (req *NetlinkRequest) Execute(sockType int, resType uint16) ([][]byte, erro
return nil, err
}
res := make([][]byte, 0)
var res [][]byte
done:
for {
msgs, err := s.Recieve()
msgs, err := s.Receive()
if err != nil {
return nil, err
}
@@ -294,7 +294,7 @@ func getNetlinkSocket(protocol int) (*NetlinkSocket, error) {
// Create a netlink socket with a given protocol (e.g. NETLINK_ROUTE)
// and subscribe it to multicast groups passed in variable argument list.
// Returns the netlink socket on whic hReceive() method can be called
// Returns the netlink socket on which Receive() method can be called
// to retrieve the messages from the kernel.
func Subscribe(protocol int, groups ...uint) (*NetlinkSocket, error) {
fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, protocol)
@@ -329,7 +329,7 @@ func (s *NetlinkSocket) Send(request *NetlinkRequest) error {
return nil
}
func (s *NetlinkSocket) Recieve() ([]syscall.NetlinkMessage, error) {
func (s *NetlinkSocket) Receive() ([]syscall.NetlinkMessage, error) {
rb := make([]byte, syscall.Getpagesize())
nr, _, err := syscall.Recvfrom(s.fd, rb, 0)
if err != nil {
+3 -4
View File
@@ -104,9 +104,8 @@ func (x *XfrmAddress) ToIPNet(prefixlen uint8) *net.IPNet {
ip := x.ToIP()
if GetIPFamily(ip) == FAMILY_V4 {
return &net.IPNet{IP: ip, Mask: net.CIDRMask(int(prefixlen), 32)}
} else {
return &net.IPNet{IP: ip, Mask: net.CIDRMask(int(prefixlen), 128)}
}
return &net.IPNet{IP: ip, Mask: net.CIDRMask(int(prefixlen), 128)}
}
func (x *XfrmAddress) FromIP(ip net.IP) {
@@ -125,8 +124,8 @@ func DeserializeXfrmAddress(b []byte) *XfrmAddress {
return (*XfrmAddress)(unsafe.Pointer(&b[0:SizeofXfrmAddress][0]))
}
func (msg *XfrmAddress) Serialize() []byte {
return (*(*[SizeofXfrmAddress]byte)(unsafe.Pointer(msg)))[:]
func (x *XfrmAddress) Serialize() []byte {
return (*(*[SizeofXfrmAddress]byte)(unsafe.Pointer(x)))[:]
}
// struct xfrm_selector {
+1 -1
View File
@@ -16,7 +16,7 @@ type Protinfo struct {
// String returns a list of enabled flags
func (prot *Protinfo) String() string {
boolStrings := make([]string, 0)
var boolStrings []string
if prot.Hairpin {
boolStrings = append(boolStrings, "Hairpin")
}
+2 -2
View File
@@ -119,7 +119,7 @@ func RouteList(link Link, family int) ([]Route, error) {
}
native := nl.NativeEndian()
res := make([]Route, 0)
var res []Route
for _, m := range msgs {
msg := nl.DeserializeRtMsg(m)
@@ -193,7 +193,7 @@ func RouteGet(destination net.IP) ([]Route, error) {
}
native := nl.NativeEndian()
res := make([]Route, 0)
var res []Route
for _, m := range msgs {
msg := nl.DeserializeRtMsg(m)
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
+1 -1
View File
@@ -84,7 +84,7 @@ func XfrmPolicyList(family int) ([]XfrmPolicy, error) {
return nil, err
}
res := make([]XfrmPolicy, 0)
var res []XfrmPolicy
for _, m := range msgs {
msg := nl.DeserializeXfrmUserpolicyInfo(m)
+1 -1
View File
@@ -118,7 +118,7 @@ func XfrmStateList(family int) ([]XfrmState, error) {
return nil, err
}
res := make([]XfrmState, 0)
var res []XfrmState
for _, m := range msgs {
msg := nl.DeserializeXfrmUsersaInfo(m)
+12 -15
View File
@@ -52,33 +52,30 @@ func Get() (NsHandle, error) {
return GetFromThread(os.Getpid(), syscall.Gettid())
}
// GetFromName gets a handle to a named network namespace such as one
// created by `ip netns add`.
func GetFromName(name string) (NsHandle, error) {
fd, err := syscall.Open(fmt.Sprintf("/var/run/netns/%s", name), syscall.O_RDONLY, 0)
// GetFromPath gets a handle to a network namespace
// identified by the path
func GetFromPath(path string) (NsHandle, error) {
fd, err := syscall.Open(path, syscall.O_RDONLY, 0)
if err != nil {
return -1, err
}
return NsHandle(fd), nil
}
// GetFromName gets a handle to a named network namespace such as one
// created by `ip netns add`.
func GetFromName(name string) (NsHandle, error) {
return GetFromPath(fmt.Sprintf("/var/run/netns/%s", name))
}
// GetFromPid gets a handle to the network namespace of a given pid.
func GetFromPid(pid int) (NsHandle, error) {
fd, err := syscall.Open(fmt.Sprintf("/proc/%d/ns/net", pid), syscall.O_RDONLY, 0)
if err != nil {
return -1, err
}
return NsHandle(fd), nil
return GetFromPath(fmt.Sprintf("/proc/%d/ns/net", pid))
}
// GetFromThread gets a handle to the network namespace of a given pid and tid.
func GetFromThread(pid, tid int) (NsHandle, error) {
name := fmt.Sprintf("/proc/%d/task/%d/ns/net", pid, tid)
fd, err := syscall.Open(name, syscall.O_RDONLY, 0)
if err != nil {
return -1, err
}
return NsHandle(fd), nil
return GetFromPath(fmt.Sprintf("/proc/%d/task/%d/ns/net", pid, tid))
}
// GetFromDocker gets a handle to the network namespace of a docker container.
+1 -1
View File
@@ -3,5 +3,5 @@
package netns
const (
SYS_SETNS = 374
SYS_SETNS = 375
)
@@ -0,0 +1,7 @@
// +build linux,arm64
package netns
const (
SYS_SETNS = 268
)
+36 -11
View File
@@ -2,12 +2,14 @@ package bridge
import (
"errors"
"fmt"
"net"
"os/exec"
"strings"
"sync"
"github.com/Sirupsen/logrus"
bri "github.com/docker/libcontainer/netlink"
"github.com/docker/libnetwork/driverapi"
"github.com/docker/libnetwork/ipallocator"
"github.com/docker/libnetwork/iptables"
@@ -397,6 +399,20 @@ func (d *driver) DeleteNetwork(nid types.UUID) error {
return err
}
func addToBridge(ifaceName, bridgeName string) error {
iface, err := net.InterfaceByName(ifaceName)
if err != nil {
return fmt.Errorf("could not find interface %s: %v", ifaceName, err)
}
master, err := net.InterfaceByName(bridgeName)
if err != nil {
return fmt.Errorf("could not find bridge %s: %v", bridgeName, err)
}
return bri.AddToBridge(iface, master)
}
func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error {
var (
ipv6Addr *net.IPNet
@@ -461,27 +477,27 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
}()
// Generate a name for what will be the host side pipe interface
name1, err := generateIfaceName()
hostIfName, err := generateIfaceName()
if err != nil {
return err
}
// Generate a name for what will be the sandbox side pipe interface
name2, err := generateIfaceName()
containerIfName, err := generateIfaceName()
if err != nil {
return err
}
// Generate and add the interface pipe host <-> sandbox
veth := &netlink.Veth{
LinkAttrs: netlink.LinkAttrs{Name: name1, TxQLen: 0},
PeerName: name2}
LinkAttrs: netlink.LinkAttrs{Name: hostIfName, TxQLen: 0},
PeerName: containerIfName}
if err = netlink.LinkAdd(veth); err != nil {
return err
}
// Get the host side pipe interface handler
host, err := netlink.LinkByName(name1)
host, err := netlink.LinkByName(hostIfName)
if err != nil {
return err
}
@@ -492,7 +508,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
}()
// Get the sandbox side pipe interface handler
sbox, err := netlink.LinkByName(name2)
sbox, err := netlink.LinkByName(containerIfName)
if err != nil {
return err
}
@@ -515,9 +531,8 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
}
// Attach host side pipe interface into the bridge
if err = netlink.LinkSetMaster(host,
&netlink.Bridge{LinkAttrs: netlink.LinkAttrs{Name: config.BridgeName}}); err != nil {
return err
if err = addToBridge(hostIfName, config.BridgeName); err != nil {
return fmt.Errorf("adding interface %s to bridge %s failed: %v", hostIfName, config.BridgeName, err)
}
if !config.EnableUserlandProxy {
@@ -534,14 +549,24 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
}
ipv4Addr := &net.IPNet{IP: ip4, Mask: n.bridge.bridgeIPv4.Mask}
// Down the interface before configuring mac address.
if err := netlink.LinkSetDown(sbox); err != nil {
return fmt.Errorf("could not set link down for container interface %s: %v", containerIfName, err)
}
// Set the sbox's MAC. If specified, use the one configured by user, otherwise generate one based on IP.
mac := electMacAddress(epConfig, ip4)
err = netlink.LinkSetHardwareAddr(sbox, mac)
if err != nil {
return err
return fmt.Errorf("could not set mac address for container interface %s: %v", containerIfName, err)
}
endpoint.macAddress = mac
// Up the host interface after finishing all netlink configuration
if err := netlink.LinkSetUp(host); err != nil {
return fmt.Errorf("could not set link up for host interface %s: %v", hostIfName, err)
}
// v6 address for the sandbox side pipe interface
ipv6Addr = &net.IPNet{}
if config.EnableIPv6 {
@@ -571,7 +596,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
// Create the sandbox side pipe interface
intf := &sandbox.Interface{}
intf.SrcName = name2
intf.SrcName = containerIfName
intf.DstName = containerVethPrefix
intf.Address = ipv4Addr
+5 -6
View File
@@ -1,14 +1,15 @@
package bridge
import (
log "github.com/Sirupsen/logrus"
"github.com/docker/docker/pkg/parsers/kernel"
"github.com/docker/libnetwork/netutils"
bri "github.com/docker/libcontainer/netlink"
"github.com/vishvananda/netlink"
)
// SetupDevice create a new bridge interface/
func setupDevice(config *NetworkConfiguration, i *bridgeInterface) error {
var setMac bool
// We only attempt to create the bridge when the requested device name is
// the default one.
if config.BridgeName != DefaultBridgeName && !config.AllowNonDefaultBridge {
@@ -26,12 +27,10 @@ func setupDevice(config *NetworkConfiguration, i *bridgeInterface) error {
// was not supported before that.
kv, err := kernel.GetKernelVersion()
if err == nil && (kv.Kernel >= 3 && kv.Major >= 3) {
i.Link.Attrs().HardwareAddr = netutils.GenerateRandomMAC()
log.Debugf("Setting bridge mac address to %s", i.Link.Attrs().HardwareAddr)
setMac = true
}
// Call out to netlink to create the device.
return netlink.LinkAdd(i.Link)
return bri.CreateBridge(config.BridgeName, setMac)
}
// SetupDeviceUp ups the given bridge interface.
+5 -1
View File
@@ -40,7 +40,11 @@ func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{},
// Join method is invoked when a Sandbox is attached to an endpoint.
func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
return (jinfo.SetHostsPath("/etc/hosts"))
if err := jinfo.SetHostsPath("/etc/hosts"); err != nil {
return err
}
return jinfo.SetResolvConfPath("/etc/resolv.conf")
}
// Leave method is invoked when a Sandbox detaches from an endpoint.
+49
View File
@@ -2,6 +2,7 @@ package libnetwork
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path"
@@ -45,6 +46,9 @@ type Endpoint interface {
// Delete and detaches this endpoint from the network.
Delete() error
// Retrieve the interfaces' statistics from the sandbox
Statistics() (map[string]*sandbox.InterfaceStatistics, error)
}
// EndpointOption is a option setter function type used to pass varios options to Network
@@ -402,6 +406,33 @@ func (ep *endpoint) Delete() error {
return err
}
func (ep *endpoint) Statistics() (map[string]*sandbox.InterfaceStatistics, error) {
m := make(map[string]*sandbox.InterfaceStatistics)
ep.Lock()
n := ep.network
skey := ep.container.data.SandboxKey
ep.Unlock()
n.Lock()
c := n.ctrlr
n.Unlock()
sbox := c.sandboxGet(skey)
if sbox == nil {
return m, nil
}
var err error
for _, i := range sbox.Interfaces() {
if m[i.DstName], err = i.Statistics(); err != nil {
return m, err
}
}
return m, nil
}
func (ep *endpoint) buildHostsFiles() error {
var extraContent []etchosts.Record
@@ -567,9 +598,19 @@ func (ep *endpoint) updateDNS(resolvConf []byte) error {
return os.Rename(tmpResolvFile.Name(), container.config.resolvConfPath)
}
func copyFile(src, dst string) error {
sBytes, err := ioutil.ReadFile(src)
if err != nil {
return err
}
return ioutil.WriteFile(dst, sBytes, 0644)
}
func (ep *endpoint) setupDNS() error {
ep.Lock()
container := ep.container
joinInfo := ep.joinInfo
ep.Unlock()
if container == nil {
@@ -586,6 +627,14 @@ func (ep *endpoint) setupDNS() error {
return err
}
if joinInfo.resolvConfPath != "" {
if err := copyFile(joinInfo.resolvConfPath, container.config.resolvConfPath); err != nil {
return fmt.Errorf("could not copy source resolv.conf file %s to %s: %v", joinInfo.resolvConfPath, container.config.resolvConfPath, err)
}
return nil
}
resolvConf, err := resolvconf.Get()
if err != nil {
return err
+77
View File
@@ -817,6 +817,15 @@ func TestEndpointJoin(t *testing.T) {
t.Fatalf("Expected an non-empty sandbox key for a joined endpoint. Instead found a empty sandbox key")
}
// Attempt retrieval of endpoint interfaces statistics
stats, err := ep.Statistics()
if err != nil {
t.Fatal(err)
}
if _, ok := stats["eth0"]; !ok {
t.Fatalf("Did not find eth0 statistics")
}
checkSandbox(t, info)
}
@@ -1119,6 +1128,74 @@ func TestEnableIPv6(t *testing.T) {
}
}
func TestResolvConfHost(t *testing.T) {
if !netutils.IsRunningInContainer() {
defer netutils.SetupTestNetNS(t)()
}
tmpResolvConf := []byte("search localhost.net\nnameserver 127.0.0.1\nnameserver 2001:4860:4860::8888")
//take a copy of resolv.conf for restoring after test completes
resolvConfSystem, err := ioutil.ReadFile("/etc/resolv.conf")
if err != nil {
t.Fatal(err)
}
//cleanup
defer func() {
if err := ioutil.WriteFile("/etc/resolv.conf", resolvConfSystem, 0644); err != nil {
t.Fatal(err)
}
}()
n, err := createTestNetwork("host", "testnetwork", options.Generic{}, options.Generic{})
if err != nil {
t.Fatal(err)
}
ep1, err := n.CreateEndpoint("ep1", nil)
if err != nil {
t.Fatal(err)
}
if err := ioutil.WriteFile("/etc/resolv.conf", tmpResolvConf, 0644); err != nil {
t.Fatal(err)
}
resolvConfPath := "/tmp/libnetwork_test/resolv.conf"
defer os.Remove(resolvConfPath)
_, err = ep1.Join(containerID,
libnetwork.JoinOptionResolvConfPath(resolvConfPath))
if err != nil {
t.Fatal(err)
}
defer func() {
err = ep1.Leave(containerID)
if err != nil {
t.Fatal(err)
}
}()
finfo, err := os.Stat(resolvConfPath)
if err != nil {
t.Fatal(err)
}
fmode := (os.FileMode)(0644)
if finfo.Mode() != fmode {
t.Fatalf("Expected file mode %s, got %s", fmode.String(), finfo.Mode().String())
}
content, err := ioutil.ReadFile(resolvConfPath)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(content, tmpResolvConf) {
t.Fatalf("Expected %s, Got %s", string(tmpResolvConf), string(content))
}
}
func TestResolvConf(t *testing.T) {
if !netutils.IsRunningInContainer() {
defer netutils.SetupTestNetNS(t)()
+5 -2
View File
@@ -276,6 +276,7 @@ func (n *networkNamespace) AddInterface(i *Interface) error {
n.Lock()
i.DstName = fmt.Sprintf("%s%d", i.DstName, n.nextIfIndex)
n.nextIfIndex++
path := n.path
n.Unlock()
runtime.LockOSThread()
@@ -287,9 +288,9 @@ func (n *networkNamespace) AddInterface(i *Interface) error {
}
defer origns.Close()
f, err := os.OpenFile(n.path, os.O_RDONLY, 0)
f, err := os.OpenFile(path, os.O_RDONLY, 0)
if err != nil {
return fmt.Errorf("failed get network namespace %q: %v", n.path, err)
return fmt.Errorf("failed get network namespace %q: %v", path, err)
}
defer f.Close()
@@ -325,6 +326,8 @@ func (n *networkNamespace) AddInterface(i *Interface) error {
return err
}
i.sandboxKey = path
n.Lock()
n.sinfo.Interfaces = append(n.sinfo.Interfaces, i)
n.Unlock()
+101
View File
@@ -1,9 +1,15 @@
package sandbox
import (
"fmt"
"net"
"os"
"os/exec"
"regexp"
"runtime"
"github.com/docker/libnetwork/types"
"github.com/vishvananda/netns"
)
// Sandbox represents a network sandbox, identified by a specific key. It
@@ -74,6 +80,9 @@ type Interface struct {
// IPv6 address for the interface.
AddressIPv6 *net.IPNet
// Parent sandbox's key
sandboxKey string
}
// GetCopy returns a copy of this Interface structure
@@ -157,3 +166,95 @@ func (s *Info) Equal(o *Info) bool {
return true
}
func nsInvoke(path string, inNsfunc func(callerFD int) error) error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
origns, err := netns.Get()
if err != nil {
return err
}
defer origns.Close()
f, err := os.OpenFile(path, os.O_RDONLY, 0)
if err != nil {
return fmt.Errorf("failed get network namespace %q: %v", path, err)
}
defer f.Close()
nsFD := f.Fd()
if err = netns.Set(netns.NsHandle(nsFD)); err != nil {
return err
}
defer netns.Set(origns)
// Invoked after the namespace switch.
return inNsfunc(int(origns))
}
// Statistics returns the statistics for this interface
func (i *Interface) Statistics() (*InterfaceStatistics, error) {
s := &InterfaceStatistics{}
err := nsInvoke(i.sandboxKey, func(callerFD int) error {
// For some reason ioutil.ReadFile(netStatsFile) reads the file in
// the default netns when this code is invoked from docker.
// Executing "cat <netStatsFile>" works as expected.
data, err := exec.Command("cat", netStatsFile).Output()
if err != nil {
return fmt.Errorf("failed to open %s: %v", netStatsFile, err)
}
return scanInterfaceStats(string(data), i.DstName, s)
})
if err != nil {
err = fmt.Errorf("failed to retrieve the statistics for %s in netns %s: %v", i.DstName, i.sandboxKey, err)
}
return s, err
}
// InterfaceStatistics represents the interface's statistics
type InterfaceStatistics struct {
RxBytes uint64
RxPackets uint64
RxErrors uint64
RxDropped uint64
TxBytes uint64
TxPackets uint64
TxErrors uint64
TxDropped uint64
}
func (is *InterfaceStatistics) String() string {
return fmt.Sprintf("\nRxBytes: %d, RxPackets: %d, RxErrors: %d, RxDropped: %d, TxBytes: %d, TxPackets: %d, TxErrors: %d, TxDropped: %d",
is.RxBytes, is.RxPackets, is.RxErrors, is.RxDropped, is.TxBytes, is.TxPackets, is.TxErrors, is.TxDropped)
}
// In older kernels (like the one in Centos 6.6 distro) sysctl does not have netns support. Therefore
// we cannot gather the statistics from /sys/class/net/<dev>/statistics/<counter> files. Per-netns stats
// are naturally found in /proc/net/dev in kernels which support netns (ifconfig relyes on that).
const (
netStatsFile = "/proc/net/dev"
base = "[ ]*%s:([ ]+[0-9]+){16}"
)
func scanInterfaceStats(data, ifName string, i *InterfaceStatistics) error {
var (
bktStr string
bkt uint64
)
regex := fmt.Sprintf(base, ifName)
re := regexp.MustCompile(regex)
line := re.FindString(data)
_, err := fmt.Sscanf(line, "%s %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
&bktStr, &i.RxBytes, &i.RxPackets, &i.RxErrors, &i.RxDropped, &bkt, &bkt, &bkt,
&bkt, &i.TxBytes, &i.TxPackets, &i.TxErrors, &i.TxDropped, &bkt, &bkt, &bkt, &bkt)
return err
}
+26
View File
@@ -157,3 +157,29 @@ func verifyCleanup(t *testing.T, s Sandbox, wait bool) {
}
}
}
func TestScanStatistics(t *testing.T) {
data :=
"Inter-| Receive | Transmit\n" +
" face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed\n" +
" eth0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" +
" wlan0: 7787685 11141 0 0 0 0 0 0 1681390 7220 0 0 0 0 0 0\n" +
" lo: 783782 1853 0 0 0 0 0 0 783782 1853 0 0 0 0 0 0\n" +
"lxcbr0: 0 0 0 0 0 0 0 0 9006 61 0 0 0 0 0 0\n"
i := &InterfaceStatistics{}
if err := scanInterfaceStats(data, "wlan0", i); err != nil {
t.Fatal(err)
}
if i.TxBytes != 1681390 || i.TxPackets != 7220 || i.RxBytes != 7787685 || i.RxPackets != 11141 {
t.Fatalf("Error scanning the statistics")
}
if err := scanInterfaceStats(data, "lxcbr0", i); err != nil {
t.Fatal(err)
}
if i.TxBytes != 9006 || i.TxPackets != 61 || i.RxBytes != 0 || i.RxPackets != 0 {
t.Fatalf("Error scanning the statistics")
}
}