mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #1 from icecrime/bridge_refactoring
Bridge refactoring - Step 1
This commit is contained in:
commit
16e08a142a
13 changed files with 441 additions and 20 deletions
|
@ -6,28 +6,76 @@ import (
|
||||||
"github.com/docker/libnetwork"
|
"github.com/docker/libnetwork"
|
||||||
)
|
)
|
||||||
|
|
||||||
const networkType = "bridgednetwork"
|
const (
|
||||||
|
NetworkType = "simplebridge"
|
||||||
|
VethPrefix = "veth"
|
||||||
|
)
|
||||||
|
|
||||||
type bridgeConfiguration struct {
|
type Configuration struct {
|
||||||
Subnet net.IPNet
|
BridgeName string
|
||||||
|
AddressIPv4 *net.IPNet
|
||||||
|
FixedCIDR *net.IPNet
|
||||||
|
FixedCIDRv6 *net.IPNet
|
||||||
|
EnableIPv6 bool
|
||||||
|
EnableIPTables bool
|
||||||
|
EnableIPForwarding bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
libnetwork.RegisterNetworkType(networkType, Create, &bridgeConfiguration{})
|
libnetwork.RegisterNetworkType(NetworkType, Create, &Configuration{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func Create(config *bridgeConfiguration) (libnetwork.Network, error) {
|
func Create(config *Configuration) (libnetwork.Network, error) {
|
||||||
return &bridgeNetwork{Config: *config}, nil
|
bridgeIntfc := NewInterface(config)
|
||||||
}
|
bridgeSetup := NewBridgeSetup(bridgeIntfc)
|
||||||
|
|
||||||
type bridgeNetwork struct {
|
// If the bridge interface doesn't exist, we need to start the setup steps
|
||||||
Config bridgeConfiguration
|
// by creating a new device and assigning it an IPv4 address.
|
||||||
}
|
bridgeAlreadyExists := bridgeIntfc.Exists()
|
||||||
|
if !bridgeAlreadyExists {
|
||||||
|
bridgeSetup.QueueStep(SetupDevice)
|
||||||
|
bridgeSetup.QueueStep(SetupBridgeIPv4)
|
||||||
|
}
|
||||||
|
|
||||||
func (b *bridgeNetwork) Type() string {
|
// Conditionnally queue setup steps depending on configuration values.
|
||||||
return networkType
|
for _, step := range []struct {
|
||||||
}
|
Condition bool
|
||||||
|
Fn SetupStep
|
||||||
|
}{
|
||||||
|
// Enable IPv6 on the bridge if required. We do this even for a
|
||||||
|
// previously existing bridge, as it may be here from a previous
|
||||||
|
// installation where IPv6 wasn't supported yet and needs to be
|
||||||
|
// assigned an IPv6 link-local address.
|
||||||
|
{config.EnableIPv6, SetupBridgeIPv6},
|
||||||
|
|
||||||
func (b *bridgeNetwork) Link(name string) ([]*libnetwork.Interface, error) {
|
// We ensure that the bridge has the expectedIPv4 and IPv6 addresses in
|
||||||
return nil, nil
|
// the case of a previously existing device.
|
||||||
|
{bridgeAlreadyExists, SetupVerifyConfiguredAddresses},
|
||||||
|
|
||||||
|
// Setup the bridge to allocate containers IPv4 addresses in the
|
||||||
|
// specified subnet.
|
||||||
|
{config.FixedCIDR != nil, SetupFixedCIDRv4},
|
||||||
|
|
||||||
|
// Setup the bridge to allocate containers global IPv6 addresses in the
|
||||||
|
// specified subnet.
|
||||||
|
{config.FixedCIDRv6 != nil, SetupFixedCIDRv6},
|
||||||
|
|
||||||
|
// Setup IPTables.
|
||||||
|
{config.EnableIPTables, SetupIPTables},
|
||||||
|
|
||||||
|
// Setup IP forwarding.
|
||||||
|
{config.EnableIPForwarding, SetupIPForwarding},
|
||||||
|
} {
|
||||||
|
if step.Condition {
|
||||||
|
bridgeSetup.QueueStep(step.Fn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the prepared list of steps, and abort at the first error.
|
||||||
|
bridgeSetup.QueueStep(SetupDeviceUp)
|
||||||
|
if err := bridgeSetup.Apply(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &bridgeNetwork{*config}, nil
|
||||||
}
|
}
|
||||||
|
|
48
libnetwork/bridge/interface.go
Normal file
48
libnetwork/bridge/interface.go
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package bridge
|
||||||
|
|
||||||
|
import "github.com/vishvananda/netlink"
|
||||||
|
|
||||||
|
const (
|
||||||
|
DefaultBridgeName = "docker0"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Interface struct {
|
||||||
|
Config *Configuration
|
||||||
|
Link netlink.Link
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInterface(config *Configuration) *Interface {
|
||||||
|
i := &Interface{
|
||||||
|
Config: config,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the bridge name to the default if unspecified.
|
||||||
|
if i.Config.BridgeName == "" {
|
||||||
|
i.Config.BridgeName = DefaultBridgeName
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to find an existing bridge named with the specified name.
|
||||||
|
i.Link, _ = netlink.LinkByName(i.Config.BridgeName)
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exists indicates if the existing bridge interface exists on the system.
|
||||||
|
func (i *Interface) Exists() bool {
|
||||||
|
return i.Link != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addresses returns a single IPv4 address and all IPv6 addresses for the
|
||||||
|
// bridge interface.
|
||||||
|
func (i *Interface) Addresses() (netlink.Addr, []netlink.Addr, error) {
|
||||||
|
v4addr, err := netlink.AddrList(i.Link, netlink.FAMILY_V4)
|
||||||
|
if err != nil {
|
||||||
|
return netlink.Addr{}, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
v6addr, err := netlink.AddrList(i.Link, netlink.FAMILY_V6)
|
||||||
|
if err != nil {
|
||||||
|
return netlink.Addr{}, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return v4addr[0], v6addr, nil
|
||||||
|
}
|
15
libnetwork/bridge/network.go
Normal file
15
libnetwork/bridge/network.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package bridge
|
||||||
|
|
||||||
|
import "github.com/docker/libnetwork"
|
||||||
|
|
||||||
|
type bridgeNetwork struct {
|
||||||
|
Config Configuration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bridgeNetwork) Type() string {
|
||||||
|
return NetworkType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bridgeNetwork) Link(name string) ([]*libnetwork.Interface, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
35
libnetwork/bridge/setup.go
Normal file
35
libnetwork/bridge/setup.go
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
package bridge
|
||||||
|
|
||||||
|
type SetupStep func(*Interface) error
|
||||||
|
|
||||||
|
type BridgeSetup struct {
|
||||||
|
bridge *Interface
|
||||||
|
steps []SetupStep
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBridgeSetup(i *Interface) *BridgeSetup {
|
||||||
|
return &BridgeSetup{bridge: i}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BridgeSetup) Apply() error {
|
||||||
|
for _, fn := range b.steps {
|
||||||
|
if err := fn(b.bridge); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BridgeSetup) QueueStep(step SetupStep) {
|
||||||
|
b.steps = append(b.steps, step)
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------//
|
||||||
|
|
||||||
|
func SetupIPTables(i *Interface) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetupIPForwarding(i *Interface) error {
|
||||||
|
return nil
|
||||||
|
}
|
53
libnetwork/bridge/setup_device.go
Normal file
53
libnetwork/bridge/setup_device.go
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package bridge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/pkg/parsers/kernel"
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetupDevice create a new bridge interface/
|
||||||
|
func SetupDevice(i *Interface) error {
|
||||||
|
// We only attempt to create the bridge when the requested device name is
|
||||||
|
// the default one.
|
||||||
|
if i.Config.BridgeName != DefaultBridgeName {
|
||||||
|
return fmt.Errorf("bridge device with non default name %q must be created manually", i.Config.BridgeName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the Interface netlink.Bridge.
|
||||||
|
i.Link = &netlink.Bridge{
|
||||||
|
LinkAttrs: netlink.LinkAttrs{
|
||||||
|
Name: i.Config.BridgeName,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only set the bridge's MAC address if the kernel version is > 3.3, as it
|
||||||
|
// was not supported before that.
|
||||||
|
kv, err := kernel.GetKernelVersion()
|
||||||
|
if err == nil && (kv.Kernel >= 3 && kv.Major >= 3) {
|
||||||
|
i.Link.Attrs().HardwareAddr = generateRandomMAC()
|
||||||
|
log.Debugf("Setting bridge mac address to %s", i.Link.Attrs().HardwareAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call out to netlink to create the device.
|
||||||
|
return netlink.LinkAdd(i.Link)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupDeviceUp ups the given bridge interface.
|
||||||
|
func SetupDeviceUp(i *Interface) error {
|
||||||
|
return netlink.LinkSetUp(i.Link)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateRandomMAC() net.HardwareAddr {
|
||||||
|
hw := make(net.HardwareAddr, 6)
|
||||||
|
for i := 0; i < 6; i++ {
|
||||||
|
hw[i] = byte(rand.Intn(255))
|
||||||
|
}
|
||||||
|
hw[0] &^= 0x1 // clear multicast bit
|
||||||
|
hw[0] |= 0x2 // set local assignment bit (IEEE802)
|
||||||
|
return hw
|
||||||
|
}
|
16
libnetwork/bridge/setup_fixedcidrv4.go
Normal file
16
libnetwork/bridge/setup_fixedcidrv4.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package bridge
|
||||||
|
|
||||||
|
import (
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/daemon/networkdriver/ipallocator"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetupFixedCIDRv4(i *Interface) error {
|
||||||
|
addrv4, _, err := i.Addresses()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("Using IPv4 subnet: %v", i.Config.FixedCIDR)
|
||||||
|
return ipallocator.RegisterSubnet(addrv4.IPNet, i.Config.FixedCIDR)
|
||||||
|
}
|
11
libnetwork/bridge/setup_fixedcidrv6.go
Normal file
11
libnetwork/bridge/setup_fixedcidrv6.go
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package bridge
|
||||||
|
|
||||||
|
import (
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/daemon/networkdriver/ipallocator"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetupFixedCIDRv6(i *Interface) error {
|
||||||
|
log.Debugf("Using IPv6 subnet: %v", i.Config.FixedCIDRv6)
|
||||||
|
return ipallocator.RegisterSubnet(i.Config.FixedCIDRv6, i.Config.FixedCIDRv6)
|
||||||
|
}
|
135
libnetwork/bridge/setup_ipv4.go
Normal file
135
libnetwork/bridge/setup_ipv4.go
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
package bridge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/pkg/networkfs/resolvconf"
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
)
|
||||||
|
|
||||||
|
var bridgeNetworks []*net.IPNet
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Here we don't follow the convention of using the 1st IP of the range for the gateway.
|
||||||
|
// This is to use the same gateway IPs as the /24 ranges, which predate the /16 ranges.
|
||||||
|
// In theory this shouldn't matter - in practice there's bound to be a few scripts relying
|
||||||
|
// on the internal addressing or other stupid things like that.
|
||||||
|
// They shouldn't, but hey, let's not break them unless we really have to.
|
||||||
|
for _, addr := range []string{
|
||||||
|
"172.17.42.1/16", // Don't use 172.16.0.0/16, it conflicts with EC2 DNS 172.16.0.23
|
||||||
|
"10.0.42.1/16", // Don't even try using the entire /8, that's too intrusive
|
||||||
|
"10.1.42.1/16",
|
||||||
|
"10.42.42.1/16",
|
||||||
|
"172.16.42.1/24",
|
||||||
|
"172.16.43.1/24",
|
||||||
|
"172.16.44.1/24",
|
||||||
|
"10.0.42.1/24",
|
||||||
|
"10.0.43.1/24",
|
||||||
|
"192.168.42.1/24",
|
||||||
|
"192.168.43.1/24",
|
||||||
|
"192.168.44.1/24",
|
||||||
|
} {
|
||||||
|
ip, net, err := net.ParseCIDR(addr)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Failed to parse address %s", addr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
net.IP = ip
|
||||||
|
bridgeNetworks = append(bridgeNetworks, net)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetupBridgeIPv4(i *Interface) error {
|
||||||
|
bridgeIPv4, err := electBridgeIPv4(i.Config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("Creating bridge interface %q with network %s", i.Config.BridgeName, bridgeIPv4)
|
||||||
|
return netlink.AddrAdd(i.Link, &netlink.Addr{bridgeIPv4, ""})
|
||||||
|
}
|
||||||
|
|
||||||
|
func electBridgeIPv4(config *Configuration) (*net.IPNet, error) {
|
||||||
|
// Use the requested IPv4 CIDR when available.
|
||||||
|
if config.AddressIPv4 != nil {
|
||||||
|
return config.AddressIPv4, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't check for an error here, because we don't really care if we
|
||||||
|
// 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)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to automatically elect appropriate brige IPv4 settings.
|
||||||
|
for _, n := range bridgeNetworks {
|
||||||
|
if err := checkNameserverOverlaps(nameservers, n); err == nil {
|
||||||
|
if err := checkRouteOverlaps(n); err == nil {
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("Couldn't find an address range for interface %q", config.BridgeName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkNameserverOverlaps(nameservers []string, toCheck *net.IPNet) error {
|
||||||
|
for _, ns := range nameservers {
|
||||||
|
_, nsNetwork, err := net.ParseCIDR(ns)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if networkOverlaps(toCheck, nsNetwork) {
|
||||||
|
return fmt.Errorf("Requested network %s overlaps with name server")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkRouteOverlaps(toCheck *net.IPNet) error {
|
||||||
|
networks, err := netlink.RouteList(nil, netlink.FAMILY_V4)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, network := range networks {
|
||||||
|
// TODO Is that right?
|
||||||
|
if network.Dst != nil && networkOverlaps(toCheck, network.Dst) {
|
||||||
|
return fmt.Errorf("Requested network %s overlaps with an existing network")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func networkOverlaps(netX *net.IPNet, netY *net.IPNet) bool {
|
||||||
|
if len(netX.IP) == len(netY.IP) {
|
||||||
|
if firstIP, _ := networkRange(netX); netY.Contains(firstIP) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if firstIP, _ := networkRange(netY); netX.Contains(firstIP) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func networkRange(network *net.IPNet) (net.IP, net.IP) {
|
||||||
|
var netIP net.IP
|
||||||
|
if network.IP.To4() != nil {
|
||||||
|
netIP = network.IP.To4()
|
||||||
|
} else if network.IP.To16() != nil {
|
||||||
|
netIP = network.IP.To16()
|
||||||
|
} else {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
lastIP := make([]byte, len(netIP), len(netIP))
|
||||||
|
for i := 0; i < len(netIP); i++ {
|
||||||
|
lastIP[i] = netIP[i] | ^network.Mask[i]
|
||||||
|
}
|
||||||
|
return netIP.Mask(network.Mask), net.IP(lastIP)
|
||||||
|
}
|
32
libnetwork/bridge/setup_ipv6.go
Normal file
32
libnetwork/bridge/setup_ipv6.go
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package bridge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
)
|
||||||
|
|
||||||
|
var BridgeIPv6 *net.IPNet
|
||||||
|
|
||||||
|
const BridgeIPv6Str = "fe80::1/64"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// We allow ourselves to panic in this special case because we indicate a
|
||||||
|
// failure to parse a compile-time define constant.
|
||||||
|
if ip, netw, err := net.ParseCIDR(BridgeIPv6Str); err == nil {
|
||||||
|
BridgeIPv6 = &net.IPNet{IP: ip, Mask: netw.Mask}
|
||||||
|
} else {
|
||||||
|
panic(fmt.Sprintf("Cannot parse default bridge IPv6 address %q: %v", BridgeIPv6Str, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetupBridgeIPv6(i *Interface) error {
|
||||||
|
// Enable IPv6 on the bridge
|
||||||
|
procFile := "/proc/sys/net/ipv6/conf/" + i.Config.BridgeName + "/disable_ipv6"
|
||||||
|
if err := ioutil.WriteFile(procFile, []byte{'0', '\n'}, 0644); err != nil {
|
||||||
|
return fmt.Errorf("Unable to enable IPv6 addresses on bridge: %v", err)
|
||||||
|
}
|
||||||
|
return netlink.AddrAdd(i.Link, &netlink.Addr{BridgeIPv6, ""})
|
||||||
|
}
|
26
libnetwork/bridge/setup_verify.go
Normal file
26
libnetwork/bridge/setup_verify.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package bridge
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func SetupVerifyConfiguredAddresses(i *Interface) error {
|
||||||
|
// Fetch a single IPv4 and a slice of IPv6 addresses from the bridge.
|
||||||
|
addrv4, addrsv6, err := i.Addresses()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that the bridge IPv4 address matches the requested configuration.
|
||||||
|
if i.Config.AddressIPv4 != nil && !addrv4.IP.Equal(i.Config.AddressIPv4.IP) {
|
||||||
|
return fmt.Errorf("Bridge IPv4 (%s) does not match requested configuration %s", addrv4.IP, i.Config.AddressIPv4.IP)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that one of the bridge IPv6 addresses matches the requested
|
||||||
|
// configuration.
|
||||||
|
for _, addrv6 := range addrsv6 {
|
||||||
|
if addrv6.String() == BridgeIPv6.String() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("Bridge IPv6 addresses do not match the expected bridge configuration %s", BridgeIPv6)
|
||||||
|
}
|
|
@ -10,10 +10,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
_, net, _ := net.ParseCIDR("192.168.100.1/24")
|
ip, net, _ := net.ParseCIDR("192.168.100.1/24")
|
||||||
|
net.IP = ip
|
||||||
|
|
||||||
options := libnetwork.DriverParams{"Subnet": *net}
|
options := libnetwork.DriverParams{"AddressIPv4": net}
|
||||||
netw, err := libnetwork.NewNetwork("bridgednetwork", options)
|
netw, err := libnetwork.NewNetwork("simplebridge", options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ func NewNamespace(path string) (Namespace, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *networkNamespace) AddInterface(i *Interface) error {
|
func (n *networkNamespace) AddInterface(i *Interface) error {
|
||||||
|
// TODO Open pipe, pass fd to child and write serialized Interface on it.
|
||||||
if err := reexec(reexecMoveInterface, i.SrcName, i.DstName); err != nil {
|
if err := reexec(reexecMoveInterface, i.SrcName, i.DstName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,10 +45,10 @@ func GenerateFromModel(options Generic, model interface{}) (interface{}, error)
|
||||||
for name, value := range options {
|
for name, value := range options {
|
||||||
field := res.Elem().FieldByName(name)
|
field := res.Elem().FieldByName(name)
|
||||||
if !field.IsValid() {
|
if !field.IsValid() {
|
||||||
return nil, NoSuchFieldError{name, reflect.TypeOf(model).Name()}
|
return nil, NoSuchFieldError{name, resType.String()}
|
||||||
}
|
}
|
||||||
if !field.CanSet() {
|
if !field.CanSet() {
|
||||||
return nil, CannotSetFieldError{name, reflect.TypeOf(model).Name()}
|
return nil, CannotSetFieldError{name, resType.String()}
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(value))
|
field.Set(reflect.ValueOf(value))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue