mirror of
https://github.com/clearlinux/libnetwork.git
synced 2026-08-19 04:17:48 +00:00
@@ -0,0 +1,67 @@
|
||||
package bridge
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
const (
|
||||
ipv4NumBlock = `(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)`
|
||||
ipv4Address = `(` + ipv4NumBlock + `\.){3}` + ipv4NumBlock
|
||||
|
||||
// This is not an IPv6 address verifier as it will accept a super-set of IPv6, and also
|
||||
// will *not match* IPv4-Embedded IPv6 Addresses (RFC6052), but that and other variants
|
||||
// -- e.g. other link-local types -- either won't work in containers or are unnecessary.
|
||||
// For readability and sufficiency for Docker purposes this seemed more reasonable than a
|
||||
// 1000+ character regexp with exact and complete IPv6 validation
|
||||
ipv6Address = `([0-9A-Fa-f]{0,4}:){2,7}([0-9A-Fa-f]{0,4})`
|
||||
)
|
||||
|
||||
var nsRegexp = regexp.MustCompile(`^\s*nameserver\s*((` + ipv4Address + `)|(` + ipv6Address + `))\s*$`)
|
||||
|
||||
func readResolvConf() ([]byte, error) {
|
||||
resolv, err := ioutil.ReadFile("/etc/resolv.conf")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resolv, nil
|
||||
}
|
||||
|
||||
// getLines parses input into lines and strips away comments.
|
||||
func getLines(input []byte, commentMarker []byte) [][]byte {
|
||||
lines := bytes.Split(input, []byte("\n"))
|
||||
var output [][]byte
|
||||
for _, currentLine := range lines {
|
||||
var commentIndex = bytes.Index(currentLine, commentMarker)
|
||||
if commentIndex == -1 {
|
||||
output = append(output, currentLine)
|
||||
} else {
|
||||
output = append(output, currentLine[:commentIndex])
|
||||
}
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
// GetNameserversAsCIDR returns nameservers (if any) listed in
|
||||
// /etc/resolv.conf as CIDR blocks (e.g., "1.2.3.4/32")
|
||||
// This function's output is intended for net.ParseCIDR
|
||||
func getNameserversAsCIDR(resolvConf []byte) []string {
|
||||
nameservers := []string{}
|
||||
for _, nameserver := range getNameservers(resolvConf) {
|
||||
nameservers = append(nameservers, nameserver+"/32")
|
||||
}
|
||||
return nameservers
|
||||
}
|
||||
|
||||
// GetNameservers returns nameservers (if any) listed in /etc/resolv.conf
|
||||
func getNameservers(resolvConf []byte) []string {
|
||||
nameservers := []string{}
|
||||
for _, line := range getLines(resolvConf, []byte("#")) {
|
||||
var ns = nsRegexp.FindSubmatch(line)
|
||||
if len(ns) > 0 {
|
||||
nameservers = append(nameservers, string(ns[1]))
|
||||
}
|
||||
}
|
||||
return nameservers
|
||||
}
|
||||
+11
-1
@@ -39,7 +39,17 @@ func SetupDevice(i *Interface) error {
|
||||
|
||||
// SetupDeviceUp ups the given bridge interface.
|
||||
func SetupDeviceUp(i *Interface) error {
|
||||
return netlink.LinkSetUp(i.Link)
|
||||
err := netlink.LinkSetUp(i.Link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Attempt to update the bridge interface to refresh the flags status,
|
||||
// ignoring any failure to do so.
|
||||
if lnk, err := netlink.LinkByName(i.Config.BridgeName); err == nil {
|
||||
i.Link = lnk
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateRandomMAC() net.HardwareAddr {
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package bridge
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/libnetwork"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
func TestSetupNewBridge(t *testing.T) {
|
||||
defer libnetwork.SetupTestNetNS(t)()
|
||||
|
||||
br := &Interface{
|
||||
Config: &Configuration{
|
||||
BridgeName: DefaultBridgeName,
|
||||
},
|
||||
}
|
||||
if err := SetupDevice(br); err != nil {
|
||||
t.Fatalf("Bridge creation failed: %v", err)
|
||||
}
|
||||
if br.Link == nil {
|
||||
t.Fatal("Interface link is nil (expected valid link)")
|
||||
}
|
||||
if _, err := netlink.LinkByName(DefaultBridgeName); err != nil {
|
||||
t.Fatalf("Failed to retrieve bridge device: %v", err)
|
||||
}
|
||||
if br.Link.Attrs().Flags&net.FlagUp == net.FlagUp {
|
||||
t.Fatalf("Interface should be created down")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupNewNonDefaultBridge(t *testing.T) {
|
||||
defer libnetwork.SetupTestNetNS(t)()
|
||||
|
||||
br := &Interface{
|
||||
Config: &Configuration{
|
||||
BridgeName: "test0",
|
||||
},
|
||||
}
|
||||
if err := SetupDevice(br); err == nil || !strings.Contains(err.Error(), "non default name") {
|
||||
t.Fatalf("Expected bridge creation failure with \"non default name\", got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupDeviceUp(t *testing.T) {
|
||||
defer libnetwork.SetupTestNetNS(t)()
|
||||
|
||||
br := &Interface{
|
||||
Config: &Configuration{
|
||||
BridgeName: DefaultBridgeName,
|
||||
},
|
||||
}
|
||||
if err := SetupDevice(br); err != nil {
|
||||
t.Fatalf("Bridge creation failed: %v", err)
|
||||
}
|
||||
if err := SetupDeviceUp(br); err != nil {
|
||||
t.Fatalf("Failed to up bridge device: %v", err)
|
||||
}
|
||||
|
||||
lnk, _ := netlink.LinkByName(DefaultBridgeName)
|
||||
if lnk.Attrs().Flags&net.FlagUp != net.FlagUp {
|
||||
t.Fatalf("Interface should be up")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateRandomMAC(t *testing.T) {
|
||||
defer libnetwork.SetupTestNetNS(t)()
|
||||
|
||||
mac1 := generateRandomMAC()
|
||||
mac2 := generateRandomMAC()
|
||||
if bytes.Compare(mac1, mac2) == 0 {
|
||||
t.Fatalf("Generated twice the same MAC address %v", mac1)
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"net"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/networkfs/resolvconf"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
@@ -61,8 +60,8 @@ func electBridgeIPv4(config *Configuration) (*net.IPNet, error) {
|
||||
// can't read /etc/resolv.conf. So instead we skip the append if resolvConf
|
||||
// is nil. It either doesn't exist, or we can't read it for some reason.
|
||||
nameservers := []string{}
|
||||
if resolvConf, _ := resolvconf.Get(); resolvConf != nil {
|
||||
nameservers = append(nameservers, resolvconf.GetNameserversAsCIDR(resolvConf)...)
|
||||
if resolvConf, _ := readResolvConf(); resolvConf != nil {
|
||||
nameservers = append(nameservers, getNameserversAsCIDR(resolvConf)...)
|
||||
}
|
||||
|
||||
// Try to automatically elect appropriate brige IPv4 settings.
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package bridge
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/libnetwork"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
func setupTestInterface(t *testing.T) *Interface {
|
||||
br := &Interface{
|
||||
Config: &Configuration{
|
||||
BridgeName: DefaultBridgeName,
|
||||
},
|
||||
}
|
||||
if err := SetupDevice(br); err != nil {
|
||||
t.Fatalf("Bridge creation failed: %v", err)
|
||||
}
|
||||
return br
|
||||
}
|
||||
|
||||
func TestSetupBridgeIPv4Fixed(t *testing.T) {
|
||||
defer libnetwork.SetupTestNetNS(t)()
|
||||
|
||||
ip, netw, err := net.ParseCIDR("192.168.1.1/24")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse bridge IPv4: %v", err)
|
||||
}
|
||||
|
||||
br := setupTestInterface(t)
|
||||
br.Config.AddressIPv4 = &net.IPNet{IP: ip, Mask: netw.Mask}
|
||||
if err := SetupBridgeIPv4(br); err != nil {
|
||||
t.Fatalf("Failed to setup bridge IPv4: %v", err)
|
||||
}
|
||||
|
||||
addrsv4, err := netlink.AddrList(br.Link, netlink.FAMILY_V4)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to list device IPv4 addresses: %v", err)
|
||||
}
|
||||
|
||||
var found bool
|
||||
for _, addr := range addrsv4 {
|
||||
if br.Config.AddressIPv4.String() == addr.IPNet.String() {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatalf("Bridge device does not have requested IPv4 address %v", br.Config.AddressIPv4)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupBridgeIPv4Auto(t *testing.T) {
|
||||
defer libnetwork.SetupTestNetNS(t)()
|
||||
|
||||
br := setupTestInterface(t)
|
||||
if err := SetupBridgeIPv4(br); err != nil {
|
||||
t.Fatalf("Failed to setup bridge IPv4: %v", err)
|
||||
}
|
||||
|
||||
addrsv4, err := netlink.AddrList(br.Link, netlink.FAMILY_V4)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to list device IPv4 addresses: %v", err)
|
||||
}
|
||||
|
||||
var found bool
|
||||
for _, addr := range addrsv4 {
|
||||
if bridgeNetworks[0].String() == addr.IPNet.String() {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatalf("Bridge device does not have the automatic IPv4 address %v", bridgeNetworks[0].String())
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package libnetwork
|
||||
|
||||
import "syscall"
|
||||
|
||||
type networkNamespace struct {
|
||||
path string
|
||||
interfaces []*Interface
|
||||
@@ -29,3 +31,9 @@ func (n *networkNamespace) Interfaces() []*Interface {
|
||||
func (n *networkNamespace) Path() string {
|
||||
return n.path
|
||||
}
|
||||
|
||||
func (n *networkNamespace) Destroy() error {
|
||||
// Assuming no running process is executing in this network namespace,
|
||||
// unmounting is sufficient to destroy it.
|
||||
return syscall.Unmount(n.path, syscall.MNT_DETACH)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package options
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -23,6 +24,39 @@ func TestGenerate(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cast, ok := result.(Model)
|
||||
if !ok {
|
||||
t.Fatalf("result has unexpected type %s", reflect.TypeOf(result))
|
||||
}
|
||||
if expected := 1; cast.Int != expected {
|
||||
t.Fatalf("wrong value for field Int: expected %v, got %v", expected, cast.Int)
|
||||
}
|
||||
if expected := 'b'; cast.Rune != expected {
|
||||
t.Fatalf("wrong value for field Rune: expected %v, got %v", expected, cast.Rune)
|
||||
}
|
||||
if expected := 2.0; cast.Float64 != expected {
|
||||
t.Fatalf("wrong value for field Int: expected %v, got %v", expected, cast.Float64)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGeneratePtr(t *testing.T) {
|
||||
gen := NewGeneric()
|
||||
gen["Int"] = 1
|
||||
gen["Rune"] = 'b'
|
||||
gen["Float64"] = 2.0
|
||||
|
||||
type Model struct {
|
||||
Int int
|
||||
Rune rune
|
||||
Float64 float64
|
||||
}
|
||||
|
||||
result, err := GenerateFromModel(gen, &Model{})
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cast, ok := result.(*Model)
|
||||
if !ok {
|
||||
t.Fatalf("result has unexpected type %s", reflect.TypeOf(result))
|
||||
@@ -44,6 +78,8 @@ func TestGenerateMissingField(t *testing.T) {
|
||||
|
||||
if _, ok := err.(NoSuchFieldError); !ok {
|
||||
t.Fatalf("expected NoSuchFieldError, got %#v", err)
|
||||
} else if expected := "no field"; !strings.Contains(err.Error(), expected) {
|
||||
t.Fatalf("expected %q in error message, got %s", expected, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,5 +89,7 @@ func TestFieldCannotBeSet(t *testing.T) {
|
||||
|
||||
if _, ok := err.(CannotSetFieldError); !ok {
|
||||
t.Fatalf("expected CannotSetFieldError, got %#v", err)
|
||||
} else if expected := "cannot set field"; !strings.Contains(err.Error(), expected) {
|
||||
t.Fatalf("expected %q in error message, got %s", expected, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package libnetwork
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package libnetwork
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// SetupTestNetNS joins a new network namespace, and returns its associated
|
||||
// teardown function.
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// defer SetupTestNetNS(t)()
|
||||
//
|
||||
func SetupTestNetNS(t *testing.T) func() {
|
||||
runtime.LockOSThread()
|
||||
if err := syscall.Unshare(syscall.CLONE_NEWNET); err != nil {
|
||||
t.Fatalf("Failed to enter netns: %v", err)
|
||||
}
|
||||
|
||||
fd, err := syscall.Open("/proc/self/ns/net", syscall.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
t.Fatal("Failed to open netns file")
|
||||
}
|
||||
|
||||
return func() {
|
||||
if err := syscall.Close(fd); err != nil {
|
||||
t.Logf("Warning: netns closing failed (%v)", err)
|
||||
}
|
||||
runtime.UnlockOSThread()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user