mirror of
https://github.com/clearlinux/libnetwork.git
synced 2026-06-16 10:35:54 +00:00
Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3daf672705 | |||
| 96afee8b4c | |||
| fc286182cd | |||
| d82b7a9573 | |||
| 75716d8756 | |||
| 0c4a707c98 | |||
| 4d49958029 | |||
| eb4ecdf537 | |||
| 30d1b7861f | |||
| 7465ee11d3 | |||
| 6cacb70196 | |||
| cb9c2bfaa2 | |||
| 36bcacd1c6 | |||
| 7b9631cd46 | |||
| b116b5c0d2 | |||
| 78fef19fc6 | |||
| 6c07f504c3 | |||
| c32ef60c4b | |||
| e578e95aa1 | |||
| 9548cbe674 | |||
| 90638ec9cf | |||
| 22f8e6bbab | |||
| 8b3374153e | |||
| 9c6e929b63 | |||
| ace6b57623 | |||
| 68d42b860c | |||
| c624d72ce9 | |||
| 6532b64a07 | |||
| f72ad20491 | |||
| 2133cdc219 | |||
| 005bc475ee | |||
| d1d67dca84 | |||
| 4ded6fe364 | |||
| effb423db7 | |||
| a5ac79f562 | |||
| 694754890c | |||
| 7c2a2c8a87 | |||
| 1ab46c7c4b | |||
| 2da2dc055d | |||
| 83799f7458 | |||
| 60da50e183 | |||
| b028371e0a | |||
| fc2dfe5201 | |||
| 051f4ccdad | |||
| a4d595b6e7 | |||
| 05462c27b2 |
Generated
+8
-3
@@ -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": "008d17ae001344769b031375bdb38a86219154c6"
|
||||
"Rev": "493029407eeb434d0c2d44e02ea072ff2488d322"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
|
||||
Guillaume J. Charmes <guillaume@docker.com> (@creack)
|
||||
+31
@@ -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
|
||||
}
|
||||
+1307
File diff suppressed because it is too large
Load Diff
Generated
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
package netlink
|
||||
|
||||
func ifrDataByte(b byte) uint8 {
|
||||
return uint8(b)
|
||||
}
|
||||
Generated
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
// +build !arm
|
||||
|
||||
package netlink
|
||||
|
||||
func ifrDataByte(b byte) int8 {
|
||||
return int8(b)
|
||||
}
|
||||
Generated
Vendored
+408
@@ -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)
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+88
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
|
||||
+1
@@ -12,6 +12,7 @@ import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// NsHandle is a handle to a network namespace. It can be cast directly
|
||||
// to an int and used as a file descriptor.
|
||||
type NsHandle int
|
||||
|
||||
+14
-15
@@ -1,3 +1,5 @@
|
||||
// +build linux
|
||||
|
||||
package netns
|
||||
|
||||
import (
|
||||
@@ -50,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.
|
||||
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
// +build linux,386
|
||||
|
||||
package netns
|
||||
|
||||
const (
|
||||
|
||||
Generated
Vendored
+2
@@ -1,3 +1,5 @@
|
||||
// +build linux,amd64
|
||||
|
||||
package netns
|
||||
|
||||
const (
|
||||
+3
-1
@@ -1,5 +1,7 @@
|
||||
// +build linux,arm
|
||||
|
||||
package netns
|
||||
|
||||
const (
|
||||
SYS_SETNS = 374
|
||||
SYS_SETNS = 375
|
||||
)
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// +build linux,arm64
|
||||
|
||||
package netns
|
||||
|
||||
const (
|
||||
SYS_SETNS = 268
|
||||
)
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// +build linux,ppc64le
|
||||
|
||||
package netns
|
||||
|
||||
const (
|
||||
SYS_SETNS = 350
|
||||
)
|
||||
+6
-6
@@ -10,26 +10,26 @@ var (
|
||||
ErrNotImplemented = errors.New("not implemented")
|
||||
)
|
||||
|
||||
func Set(ns Namespace) (err error) {
|
||||
func Set(ns NsHandle) (err error) {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func New() (ns Namespace, err error) {
|
||||
func New() (ns NsHandle, err error) {
|
||||
return -1, ErrNotImplemented
|
||||
}
|
||||
|
||||
func Get() (Namespace, error) {
|
||||
func Get() (NsHandle, error) {
|
||||
return -1, ErrNotImplemented
|
||||
}
|
||||
|
||||
func GetFromName(name string) (Namespace, error) {
|
||||
func GetFromName(name string) (NsHandle, error) {
|
||||
return -1, ErrNotImplemented
|
||||
}
|
||||
|
||||
func GetFromPid(pid int) (Namespace, error) {
|
||||
func GetFromPid(pid int) (NsHandle, error) {
|
||||
return -1, ErrNotImplemented
|
||||
}
|
||||
|
||||
func GetFromDocker(id string) (Namespace, error) {
|
||||
func GetFromDocker(id string) (NsHandle, error) {
|
||||
return -1, ErrNotImplemented
|
||||
}
|
||||
|
||||
@@ -76,6 +76,9 @@ type NetworkController interface {
|
||||
|
||||
// NetworkByID returns the Network which has the passed id. If not found, the error ErrNoSuchNetwork is returned.
|
||||
NetworkByID(id string) (Network, error)
|
||||
|
||||
// GC triggers immediate garbage collection of resources which are garbage collected.
|
||||
GC()
|
||||
}
|
||||
|
||||
// NetworkWalker is a client provided function which will be used to walk the Networks.
|
||||
@@ -299,3 +302,7 @@ func (c *controller) loadDriver(networkType string) (driverapi.Driver, error) {
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func (c *controller) GC() {
|
||||
sandbox.GC()
|
||||
}
|
||||
|
||||
+106
-20
@@ -2,12 +2,17 @@ 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"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/docker/libnetwork/options"
|
||||
@@ -102,6 +107,15 @@ func newDriver() driverapi.Driver {
|
||||
|
||||
// Init registers a new instance of bridge driver
|
||||
func Init(dc driverapi.DriverCallback) error {
|
||||
// try to modprobe bridge first
|
||||
// see gh#12177
|
||||
if out, err := exec.Command("modprobe", "-va", "bridge", "nf_nat", "br_netfilter").Output(); err != nil {
|
||||
logrus.Warnf("Running modprobe bridge nf_nat failed with message: %s, error: %v", out, err)
|
||||
}
|
||||
if err := iptables.RemoveExistingChain(DockerChain, iptables.Nat); err != nil {
|
||||
logrus.Warnf("Failed to remove existing iptables entries in %s : %v", DockerChain, err)
|
||||
}
|
||||
|
||||
return dc.RegisterDriver(networkType, newDriver())
|
||||
}
|
||||
|
||||
@@ -287,6 +301,11 @@ func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) err
|
||||
// Even if a bridge exists try to setup IPv4.
|
||||
bridgeSetup.queueStep(setupBridgeIPv4)
|
||||
|
||||
enableIPv6Forwarding := false
|
||||
if d.config != nil && d.config.EnableIPForwarding && config.FixedCIDRv6 != nil {
|
||||
enableIPv6Forwarding = true
|
||||
}
|
||||
|
||||
// Conditionally queue setup steps depending on configuration values.
|
||||
for _, step := range []struct {
|
||||
Condition bool
|
||||
@@ -310,6 +329,9 @@ func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) err
|
||||
// specified subnet.
|
||||
{config.FixedCIDRv6 != nil, setupFixedCIDRv6},
|
||||
|
||||
// Enable IPv6 Forwarding
|
||||
{enableIPv6Forwarding, setupIPv6Forwarding},
|
||||
|
||||
// Setup Loopback Adresses Routing
|
||||
{!config.EnableUserlandProxy, setupLoopbackAdressesRouting},
|
||||
|
||||
@@ -377,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
|
||||
@@ -441,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
|
||||
}
|
||||
@@ -472,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
|
||||
}
|
||||
@@ -482,14 +518,6 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
|
||||
}
|
||||
}()
|
||||
|
||||
// Set the sbox's MAC. If specified, use the one configured by user, otherwise use a random one
|
||||
mac := electMacAddress(epConfig)
|
||||
err = netlink.LinkSetHardwareAddr(sbox, mac)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
endpoint.macAddress = mac
|
||||
|
||||
// Add bridge inherited attributes to pipe interfaces
|
||||
if config.Mtu != 0 {
|
||||
err = netlink.LinkSetMTU(host, config.Mtu)
|
||||
@@ -503,9 +531,15 @@ 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 {
|
||||
err = netlink.LinkSetHairpin(host, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// v4 address for the sandbox side pipe interface
|
||||
@@ -515,6 +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 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 {
|
||||
@@ -544,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
|
||||
|
||||
@@ -670,6 +722,15 @@ func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{},
|
||||
|
||||
m := make(map[string]interface{})
|
||||
|
||||
if ep.config.ExposedPorts != nil {
|
||||
// Return a copy of the config data
|
||||
epc := make([]types.TransportPort, 0, len(ep.config.ExposedPorts))
|
||||
for _, tp := range ep.config.ExposedPorts {
|
||||
epc = append(epc, tp.GetCopy())
|
||||
}
|
||||
m[netlabel.ExposedPorts] = epc
|
||||
}
|
||||
|
||||
if ep.portMapping != nil {
|
||||
// Return a copy of the operational data
|
||||
pmc := make([]types.PortBinding, 0, len(ep.portMapping))
|
||||
@@ -901,11 +962,36 @@ func parseContainerOptions(cOptions map[string]interface{}) (*ContainerConfigura
|
||||
}
|
||||
}
|
||||
|
||||
func electMacAddress(epConfig *EndpointConfiguration) net.HardwareAddr {
|
||||
// Generate a IEEE802 compliant MAC address from the given IP address.
|
||||
//
|
||||
// The generator is guaranteed to be consistent: the same IP will always yield the same
|
||||
// MAC address. This is to avoid ARP cache issues.
|
||||
func generateMacAddr(ip net.IP) net.HardwareAddr {
|
||||
hw := make(net.HardwareAddr, 6)
|
||||
|
||||
// The first byte of the MAC address has to comply with these rules:
|
||||
// 1. Unicast: Set the least-significant bit to 0.
|
||||
// 2. Address is locally administered: Set the second-least-significant bit (U/L) to 1.
|
||||
// 3. As "small" as possible: The veth address has to be "smaller" than the bridge address.
|
||||
hw[0] = 0x02
|
||||
|
||||
// The first 24 bits of the MAC represent the Organizationally Unique Identifier (OUI).
|
||||
// Since this address is locally administered, we can do whatever we want as long as
|
||||
// it doesn't conflict with other addresses.
|
||||
hw[1] = 0x42
|
||||
|
||||
// Insert the IP address into the last 32 bits of the MAC address.
|
||||
// This is a simple way to guarantee the address will be consistent and unique.
|
||||
copy(hw[2:], ip.To4())
|
||||
|
||||
return hw
|
||||
}
|
||||
|
||||
func electMacAddress(epConfig *EndpointConfiguration, ip net.IP) net.HardwareAddr {
|
||||
if epConfig != nil && epConfig.MacAddress != nil {
|
||||
return epConfig.MacAddress
|
||||
}
|
||||
return netutils.GenerateRandomMAC()
|
||||
return generateMacAddr(ip)
|
||||
}
|
||||
|
||||
// Generates a name to be used for a virtual ethernet
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package bridge
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
func setupFixedCIDRv6(config *NetworkConfiguration, i *bridgeInterface) error {
|
||||
@@ -10,5 +13,15 @@ func setupFixedCIDRv6(config *NetworkConfiguration, i *bridgeInterface) error {
|
||||
return &FixedCIDRv6Error{Net: config.FixedCIDRv6, Err: err}
|
||||
}
|
||||
|
||||
// Setting route to global IPv6 subnet
|
||||
log.Debugf("Adding route to IPv6 network %s via device %s", config.FixedCIDRv6.String(), config.BridgeName)
|
||||
err := netlink.RouteAdd(&netlink.Route{
|
||||
Scope: netlink.SCOPE_UNIVERSE,
|
||||
LinkIndex: i.Link.Attrs().Index,
|
||||
Dst: config.FixedCIDRv6,
|
||||
})
|
||||
if err != nil && !os.IsExist(err) {
|
||||
log.Errorf("Could not add route to IPv6 network %s via device %s", config.FixedCIDRv6.String(), config.BridgeName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -5,12 +5,16 @@ import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
var bridgeIPv6 *net.IPNet
|
||||
|
||||
const bridgeIPv6Str = "fe80::1/64"
|
||||
const (
|
||||
bridgeIPv6Str = "fe80::1/64"
|
||||
ipv6ForwardConfPerm = 0644
|
||||
)
|
||||
|
||||
func init() {
|
||||
// We allow ourselves to panic in this special case because we indicate a
|
||||
@@ -25,7 +29,7 @@ func init() {
|
||||
func setupBridgeIPv6(config *NetworkConfiguration, i *bridgeInterface) error {
|
||||
// Enable IPv6 on the bridge
|
||||
procFile := "/proc/sys/net/ipv6/conf/" + config.BridgeName + "/disable_ipv6"
|
||||
if err := ioutil.WriteFile(procFile, []byte{'0', '\n'}, 0644); err != nil {
|
||||
if err := ioutil.WriteFile(procFile, []byte{'0', '\n'}, ipv6ForwardConfPerm); err != nil {
|
||||
return fmt.Errorf("Unable to enable IPv6 addresses on bridge: %v", err)
|
||||
}
|
||||
|
||||
@@ -64,3 +68,14 @@ func setupGatewayIPv6(config *NetworkConfiguration, i *bridgeInterface) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupIPv6Forwarding(config *NetworkConfiguration, i *bridgeInterface) error {
|
||||
// Enable IPv6 forwarding
|
||||
if err := ioutil.WriteFile("/proc/sys/net/ipv6/conf/default/forwarding", []byte{'1', '\n'}, ipv6ForwardConfPerm); err != nil {
|
||||
logrus.Warnf("Unable to enable IPv6 default forwarding: %v", err)
|
||||
}
|
||||
if err := ioutil.WriteFile("/proc/sys/net/ipv6/conf/all/forwarding", []byte{'1', '\n'}, ipv6ForwardConfPerm); err != nil {
|
||||
logrus.Warnf("Unable to enable IPv6 all forwarding: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
+23
-15
@@ -44,9 +44,10 @@ var (
|
||||
|
||||
// Chain defines the iptables chain.
|
||||
type Chain struct {
|
||||
Name string
|
||||
Bridge string
|
||||
Table Table
|
||||
Name string
|
||||
Bridge string
|
||||
Table Table
|
||||
HairpinMode bool
|
||||
}
|
||||
|
||||
// ChainError is returned to represent errors during ip table operation.
|
||||
@@ -75,9 +76,10 @@ func initCheck() error {
|
||||
// NewChain adds a new chain to ip table.
|
||||
func NewChain(name, bridge string, table Table, hairpinMode bool) (*Chain, error) {
|
||||
c := &Chain{
|
||||
Name: name,
|
||||
Bridge: bridge,
|
||||
Table: table,
|
||||
Name: name,
|
||||
Bridge: bridge,
|
||||
Table: table,
|
||||
HairpinMode: hairpinMode,
|
||||
}
|
||||
|
||||
if string(c.Table) == "" {
|
||||
@@ -97,7 +99,8 @@ func NewChain(name, bridge string, table Table, hairpinMode bool) (*Chain, error
|
||||
case Nat:
|
||||
preroute := []string{
|
||||
"-m", "addrtype",
|
||||
"--dst-type", "LOCAL"}
|
||||
"--dst-type", "LOCAL",
|
||||
"-j", c.Name}
|
||||
if !Exists(Nat, "PREROUTING", preroute...) {
|
||||
if err := c.Prerouting(Append, preroute...); err != nil {
|
||||
return nil, fmt.Errorf("Failed to inject docker in PREROUTING chain: %s", err)
|
||||
@@ -105,7 +108,8 @@ func NewChain(name, bridge string, table Table, hairpinMode bool) (*Chain, error
|
||||
}
|
||||
output := []string{
|
||||
"-m", "addrtype",
|
||||
"--dst-type", "LOCAL"}
|
||||
"--dst-type", "LOCAL",
|
||||
"-j", c.Name}
|
||||
if !hairpinMode {
|
||||
output = append(output, "!", "--dst", "127.0.0.0/8")
|
||||
}
|
||||
@@ -151,12 +155,16 @@ func (c *Chain) Forward(action Action, ip net.IP, port int, proto, destAddr stri
|
||||
// value" by both iptables and ip6tables.
|
||||
daddr = "0/0"
|
||||
}
|
||||
if output, err := Raw("-t", string(Nat), string(action), c.Name,
|
||||
args := []string{"-t", string(Nat), string(action), c.Name,
|
||||
"-p", proto,
|
||||
"-d", daddr,
|
||||
"--dport", strconv.Itoa(port),
|
||||
"-j", "DNAT",
|
||||
"--to-destination", net.JoinHostPort(destAddr, strconv.Itoa(destPort))); err != nil {
|
||||
"--to-destination", net.JoinHostPort(destAddr, strconv.Itoa(destPort))}
|
||||
if !c.HairpinMode {
|
||||
args = append(args, "!", "-i", c.Bridge)
|
||||
}
|
||||
if output, err := Raw(args...); err != nil {
|
||||
return err
|
||||
} else if len(output) != 0 {
|
||||
return ChainError{Chain: "FORWARD", Output: output}
|
||||
@@ -222,7 +230,7 @@ func (c *Chain) Prerouting(action Action, args ...string) error {
|
||||
if len(args) > 0 {
|
||||
a = append(a, args...)
|
||||
}
|
||||
if output, err := Raw(append(a, "-j", c.Name)...); err != nil {
|
||||
if output, err := Raw(a...); err != nil {
|
||||
return err
|
||||
} else if len(output) != 0 {
|
||||
return ChainError{Chain: "PREROUTING", Output: output}
|
||||
@@ -236,7 +244,7 @@ func (c *Chain) Output(action Action, args ...string) error {
|
||||
if len(args) > 0 {
|
||||
a = append(a, args...)
|
||||
}
|
||||
if output, err := Raw(append(a, "-j", c.Name)...); err != nil {
|
||||
if output, err := Raw(a...); err != nil {
|
||||
return err
|
||||
} else if len(output) != 0 {
|
||||
return ChainError{Chain: "OUTPUT", Output: output}
|
||||
@@ -248,9 +256,9 @@ func (c *Chain) Output(action Action, args ...string) error {
|
||||
func (c *Chain) Remove() error {
|
||||
// Ignore errors - This could mean the chains were never set up
|
||||
if c.Table == Nat {
|
||||
c.Prerouting(Delete, "-m", "addrtype", "--dst-type", "LOCAL")
|
||||
c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8")
|
||||
c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL") // Created in versions <= 0.1.6
|
||||
c.Prerouting(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "-j", c.Name)
|
||||
c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8", "-j", c.Name)
|
||||
c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "-j", c.Name) // Created in versions <= 0.1.6
|
||||
|
||||
c.Prerouting(Delete)
|
||||
c.Output(Delete)
|
||||
|
||||
@@ -48,6 +48,7 @@ func TestForward(t *testing.T) {
|
||||
"--dport", strconv.Itoa(port),
|
||||
"-j", "DNAT",
|
||||
"--to-destination", dstAddr + ":" + strconv.Itoa(dstPort),
|
||||
"!", "-i", natChain.Bridge,
|
||||
}
|
||||
|
||||
if !Exists(natChain.Table, natChain.Name, dnatRule...) {
|
||||
@@ -130,16 +131,11 @@ func TestPrerouting(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rule := []string{
|
||||
"-j", natChain.Name}
|
||||
|
||||
rule = append(rule, args...)
|
||||
|
||||
if !Exists(natChain.Table, "PREROUTING", rule...) {
|
||||
if !Exists(natChain.Table, "PREROUTING", args...) {
|
||||
t.Fatalf("rule does not exist")
|
||||
}
|
||||
|
||||
delRule := append([]string{"-D", "PREROUTING", "-t", string(Nat)}, rule...)
|
||||
delRule := append([]string{"-D", "PREROUTING", "-t", string(Nat)}, args...)
|
||||
if _, err = Raw(delRule...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -155,17 +151,12 @@ func TestOutput(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rule := []string{
|
||||
"-j", natChain.Name}
|
||||
|
||||
rule = append(rule, args...)
|
||||
|
||||
if !Exists(natChain.Table, "OUTPUT", rule...) {
|
||||
if !Exists(natChain.Table, "OUTPUT", args...) {
|
||||
t.Fatalf("rule does not exist")
|
||||
}
|
||||
|
||||
delRule := append([]string{"-D", "OUTPUT", "-t",
|
||||
string(natChain.Table)}, rule...)
|
||||
string(natChain.Table)}, args...)
|
||||
if _, err = Raw(delRule...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -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)()
|
||||
|
||||
@@ -60,14 +60,23 @@ type network struct {
|
||||
}
|
||||
|
||||
func (n *network) Name() string {
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
|
||||
return n.name
|
||||
}
|
||||
|
||||
func (n *network) ID() string {
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
|
||||
return string(n.id)
|
||||
}
|
||||
|
||||
func (n *network) Type() string {
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
|
||||
if n.driver == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -94,7 +92,6 @@ func Get() *PortAllocator {
|
||||
func newInstance() *PortAllocator {
|
||||
start, end, err := getDynamicPortRange()
|
||||
if err != nil {
|
||||
logrus.Warn(err)
|
||||
start, end = DefaultPortRangeStart, DefaultPortRangeEnd
|
||||
}
|
||||
return &PortAllocator{
|
||||
|
||||
+134
-25
@@ -4,17 +4,28 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/reexec"
|
||||
"github.com/vishvananda/netlink"
|
||||
"github.com/vishvananda/netns"
|
||||
)
|
||||
|
||||
const prefix = "/var/run/docker/netns"
|
||||
|
||||
var once sync.Once
|
||||
var (
|
||||
once sync.Once
|
||||
garbagePathMap = make(map[string]bool)
|
||||
gpmLock sync.Mutex
|
||||
gpmWg sync.WaitGroup
|
||||
gpmCleanupPeriod = 60 * time.Second
|
||||
gpmChan = make(chan chan struct{})
|
||||
)
|
||||
|
||||
// The networkNamespace type is the linux implementation of the Sandbox
|
||||
// interface. It represents a linux network namespace, and moves an interface
|
||||
@@ -26,11 +37,87 @@ type networkNamespace struct {
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func init() {
|
||||
reexec.Register("netns-create", reexecCreateNamespace)
|
||||
}
|
||||
|
||||
func createBasePath() {
|
||||
err := os.MkdirAll(prefix, 0644)
|
||||
if err != nil && !os.IsExist(err) {
|
||||
panic("Could not create net namespace path directory")
|
||||
}
|
||||
|
||||
// Start the garbage collection go routine
|
||||
go removeUnusedPaths()
|
||||
}
|
||||
|
||||
func removeUnusedPaths() {
|
||||
gpmLock.Lock()
|
||||
period := gpmCleanupPeriod
|
||||
gpmLock.Unlock()
|
||||
|
||||
ticker := time.NewTicker(period)
|
||||
for {
|
||||
var (
|
||||
gc chan struct{}
|
||||
gcOk bool
|
||||
)
|
||||
|
||||
select {
|
||||
case <-ticker.C:
|
||||
case gc, gcOk = <-gpmChan:
|
||||
}
|
||||
|
||||
gpmLock.Lock()
|
||||
pathList := make([]string, 0, len(garbagePathMap))
|
||||
for path := range garbagePathMap {
|
||||
pathList = append(pathList, path)
|
||||
}
|
||||
garbagePathMap = make(map[string]bool)
|
||||
gpmWg.Add(1)
|
||||
gpmLock.Unlock()
|
||||
|
||||
for _, path := range pathList {
|
||||
os.Remove(path)
|
||||
}
|
||||
|
||||
gpmWg.Done()
|
||||
if gcOk {
|
||||
close(gc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addToGarbagePaths(path string) {
|
||||
gpmLock.Lock()
|
||||
garbagePathMap[path] = true
|
||||
gpmLock.Unlock()
|
||||
}
|
||||
|
||||
func removeFromGarbagePaths(path string) {
|
||||
gpmLock.Lock()
|
||||
delete(garbagePathMap, path)
|
||||
gpmLock.Unlock()
|
||||
}
|
||||
|
||||
// GC triggers garbage collection of namespace path right away
|
||||
// and waits for it.
|
||||
func GC() {
|
||||
gpmLock.Lock()
|
||||
if len(garbagePathMap) == 0 {
|
||||
// No need for GC if map is empty
|
||||
gpmLock.Unlock()
|
||||
return
|
||||
}
|
||||
gpmLock.Unlock()
|
||||
|
||||
// if content exists in the garbage paths
|
||||
// we can trigger GC to run, providing a
|
||||
// channel to be notified on completion
|
||||
waitGC := make(chan struct{})
|
||||
gpmChan <- waitGC
|
||||
// wait for GC completion
|
||||
<-waitGC
|
||||
}
|
||||
|
||||
// GenerateKey generates a sandbox key based on the passed
|
||||
@@ -55,6 +142,20 @@ func NewSandbox(key string, osCreate bool) (Sandbox, error) {
|
||||
return &networkNamespace{path: key, sinfo: info}, nil
|
||||
}
|
||||
|
||||
func reexecCreateNamespace() {
|
||||
if len(os.Args) < 2 {
|
||||
log.Fatal("no namespace path provided")
|
||||
}
|
||||
|
||||
if err := syscall.Mount("/proc/self/ns/net", os.Args[1], "bind", syscall.MS_BIND, ""); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := loopbackUp(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func createNetworkNamespace(path string, osCreate bool) (*Info, error) {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
@@ -69,23 +170,18 @@ func createNetworkNamespace(path string, osCreate bool) (*Info, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if osCreate {
|
||||
defer netns.Set(origns)
|
||||
newns, err := netns.New()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer newns.Close()
|
||||
|
||||
if err := loopbackUp(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd := &exec.Cmd{
|
||||
Path: reexec.Self(),
|
||||
Args: append([]string{"netns-create"}, path),
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
}
|
||||
|
||||
procNet := fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), syscall.Gettid())
|
||||
|
||||
if err := syscall.Mount(procNet, path, "bind", syscall.MS_BIND, ""); err != nil {
|
||||
return nil, err
|
||||
if osCreate {
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||
cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWNET
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
return nil, fmt.Errorf("namespace creation reexec command failed: %v", err)
|
||||
}
|
||||
|
||||
interfaces := []*Interface{}
|
||||
@@ -93,10 +189,9 @@ func createNetworkNamespace(path string, osCreate bool) (*Info, error) {
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func cleanupNamespaceFile(path string) {
|
||||
func unmountNamespaceFile(path string) {
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
n := &networkNamespace{path: path}
|
||||
n.Destroy()
|
||||
syscall.Unmount(path, syscall.MNT_DETACH)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,11 +199,20 @@ func createNamespaceFile(path string) (err error) {
|
||||
var f *os.File
|
||||
|
||||
once.Do(createBasePath)
|
||||
// cleanup namespace file if it already exists because of a previous ungraceful exit.
|
||||
cleanupNamespaceFile(path)
|
||||
// Remove it from garbage collection list if present
|
||||
removeFromGarbagePaths(path)
|
||||
|
||||
// If the path is there unmount it first
|
||||
unmountNamespaceFile(path)
|
||||
|
||||
// wait for garbage collection to complete if it is in progress
|
||||
// before trying to create the file.
|
||||
gpmWg.Wait()
|
||||
|
||||
if f, err = os.Create(path); err == nil {
|
||||
f.Close()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -172,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()
|
||||
@@ -183,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()
|
||||
|
||||
@@ -221,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()
|
||||
@@ -269,5 +376,7 @@ func (n *networkNamespace) Destroy() error {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.Remove(n.path)
|
||||
// Stash it into the garbage collection list
|
||||
addToGarbagePaths(n.path)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/vishvananda/netlink"
|
||||
@@ -31,6 +32,11 @@ func newKey(t *testing.T) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Set the rpmCleanupPeriod to be low to make the test run quicker
|
||||
gpmLock.Lock()
|
||||
gpmCleanupPeriod = 2 * time.Second
|
||||
gpmLock.Unlock()
|
||||
|
||||
return name, nil
|
||||
}
|
||||
|
||||
@@ -137,3 +143,43 @@ func verifySandbox(t *testing.T, s Sandbox) {
|
||||
err)
|
||||
}
|
||||
}
|
||||
|
||||
func verifyCleanup(t *testing.T, s Sandbox, wait bool) {
|
||||
if wait {
|
||||
time.Sleep(time.Duration(gpmCleanupPeriod * 2))
|
||||
}
|
||||
|
||||
if _, err := os.Stat(s.Key()); err == nil {
|
||||
if wait {
|
||||
t.Fatalf("The sandbox path %s is not getting cleaned up even after twice the cleanup period", s.Key())
|
||||
} else {
|
||||
t.Fatalf("The sandbox path %s is not cleaned up after running gc", s.Key())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,19 @@ package sandbox
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/pkg/reexec"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
if reexec.Init() {
|
||||
return
|
||||
}
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func TestSandboxCreate(t *testing.T) {
|
||||
key, err := newKey(t)
|
||||
if err != nil {
|
||||
@@ -44,6 +54,7 @@ func TestSandboxCreate(t *testing.T) {
|
||||
|
||||
verifySandbox(t, s)
|
||||
s.Destroy()
|
||||
verifyCleanup(t, s, true)
|
||||
}
|
||||
|
||||
func TestSandboxCreateTwice(t *testing.T) {
|
||||
@@ -66,6 +77,23 @@ func TestSandboxCreateTwice(t *testing.T) {
|
||||
s.Destroy()
|
||||
}
|
||||
|
||||
func TestSandboxGC(t *testing.T) {
|
||||
key, err := newKey(t)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to obtain a key: %v", err)
|
||||
}
|
||||
|
||||
s, err := NewSandbox(key, true)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create a new sandbox: %v", err)
|
||||
}
|
||||
|
||||
s.Destroy()
|
||||
|
||||
GC()
|
||||
verifyCleanup(t, s, false)
|
||||
}
|
||||
|
||||
func TestInterfaceEqual(t *testing.T) {
|
||||
list := getInterfaceList()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user