Initial import
Signed-off-by: Arnaud Porterie <arnaud.porterie@docker.com>
This commit is contained in:
parent
d7c688f0f6
commit
0d29ca540f
9 changed files with 456 additions and 0 deletions
37
libnetwork/bridge/bridge.go
Normal file
37
libnetwork/bridge/bridge.go
Normal file
|
@ -0,0 +1,37 @@
|
|||
package bridge
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/docker/libnetwork"
|
||||
)
|
||||
|
||||
const networkType = "bridgednetwork"
|
||||
|
||||
func init() {
|
||||
libnetwork.RegisterNetworkType(networkType, Create)
|
||||
}
|
||||
|
||||
func Create(options libnetwork.strategyParams) libnetwork.Network {
|
||||
return &bridgeNetwork{}
|
||||
}
|
||||
|
||||
type Configuration struct {
|
||||
Subnet net.IPNet
|
||||
}
|
||||
|
||||
type bridgeNetwork struct {
|
||||
Config Configuration
|
||||
}
|
||||
|
||||
func (b *bridgeNetwork) Name() string {
|
||||
return b.Id
|
||||
}
|
||||
|
||||
func (b *bridgeNetwork) Type() string {
|
||||
return networkType
|
||||
}
|
||||
|
||||
func (b *bridgeNetwork) Link(name string) ([]*libnetwork.Interface, error) {
|
||||
return nil, nil
|
||||
}
|
95
libnetwork/configure.go
Normal file
95
libnetwork/configure.go
Normal file
|
@ -0,0 +1,95 @@
|
|||
package libnetwork
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
func configureInterface(iface netlink.Link, settings *Interface) error {
|
||||
ifaceName := iface.Attrs().Name
|
||||
ifaceConfigurators := []struct {
|
||||
Fn func(netlink.Link, *Interface) error
|
||||
ErrMessage string
|
||||
}{
|
||||
{setInterfaceName, fmt.Sprintf("error renaming interface %q to %q", ifaceName, settings.DstName)},
|
||||
{setInterfaceMAC, fmt.Sprintf("error setting interface %q MAC address to %q", ifaceName, settings.MacAddress)},
|
||||
{setInterfaceIP, fmt.Sprintf("error setting interface %q IP to %q", ifaceName, settings.Address)},
|
||||
{setInterfaceIPv6, fmt.Sprintf("error setting interface %q IPv6 to %q", ifaceName, settings.AddressIPv6)},
|
||||
{setInterfaceMTU, fmt.Sprintf("error setting interface %q MTU to %q", ifaceName, settings.MTU)},
|
||||
{setInterfaceGateway, fmt.Sprintf("error setting interface %q gateway to %q", ifaceName, settings.Gateway)},
|
||||
{setInterfaceGatewayIPv6, fmt.Sprintf("error setting interface %q IPv6 gateway to %q", ifaceName, settings.GatewayIPv6)},
|
||||
}
|
||||
|
||||
for _, config := range ifaceConfigurators {
|
||||
if err := config.Fn(iface, settings); err != nil {
|
||||
return fmt.Errorf("%s: %v", config.ErrMessage, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setGatewayIP(iface netlink.Link, ip net.IP) error {
|
||||
return netlink.RouteAdd(&netlink.Route{
|
||||
LinkIndex: iface.Attrs().Index,
|
||||
Scope: netlink.SCOPE_UNIVERSE,
|
||||
Gw: ip,
|
||||
})
|
||||
}
|
||||
|
||||
func setInterfaceGateway(iface netlink.Link, settings *Interface) error {
|
||||
ip := net.ParseIP(settings.Gateway)
|
||||
if ip == nil {
|
||||
return fmt.Errorf("bad address format %q", settings.Gateway)
|
||||
}
|
||||
return setGatewayIP(iface, ip)
|
||||
}
|
||||
|
||||
func setInterfaceGatewayIPv6(iface netlink.Link, settings *Interface) error {
|
||||
if settings.GatewayIPv6 != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
ip := net.ParseIP(settings.GatewayIPv6)
|
||||
if ip == nil {
|
||||
return fmt.Errorf("bad address format %q", settings.GatewayIPv6)
|
||||
}
|
||||
return setGatewayIP(iface, ip)
|
||||
}
|
||||
|
||||
func setInterfaceIP(iface netlink.Link, settings *Interface) (err error) {
|
||||
var ipAddr *netlink.Addr
|
||||
if ipAddr, err = netlink.ParseAddr(settings.Address); err == nil {
|
||||
err = netlink.AddrAdd(iface, ipAddr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func setInterfaceIPv6(iface netlink.Link, settings *Interface) (err error) {
|
||||
if settings.AddressIPv6 != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var ipAddr *netlink.Addr
|
||||
if ipAddr, err = netlink.ParseAddr(settings.AddressIPv6); err == nil {
|
||||
err = netlink.AddrAdd(iface, ipAddr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func setInterfaceMAC(iface netlink.Link, settings *Interface) (err error) {
|
||||
var hwAddr net.HardwareAddr
|
||||
if hwAddr, err = net.ParseMAC(settings.MacAddress); err == nil {
|
||||
err = netlink.LinkSetHardwareAddr(iface, hwAddr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func setInterfaceMTU(iface netlink.Link, settings *Interface) error {
|
||||
return netlink.LinkSetMTU(iface, settings.MTU)
|
||||
}
|
||||
|
||||
func setInterfaceName(iface netlink.Link, settings *Interface) error {
|
||||
return netlink.LinkSetName(iface, settings.DstName)
|
||||
}
|
30
libnetwork/namespace.go
Normal file
30
libnetwork/namespace.go
Normal file
|
@ -0,0 +1,30 @@
|
|||
package libnetwork
|
||||
|
||||
type networkNamespace struct {
|
||||
path string
|
||||
interfaces []*Interface
|
||||
}
|
||||
|
||||
// Create a new network namespace mounted on the provided path.
|
||||
func NewNamespace(path string) (Namespace, error) {
|
||||
if err := Reexec(ReexecCreateNamespace, path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &networkNamespace{path: path}, nil
|
||||
}
|
||||
|
||||
func (n *networkNamespace) AddInterface(i *Interface) error {
|
||||
if err := Reexec(ReexecMoveInterface, i.SrcName, i.DstName); err != nil {
|
||||
return err
|
||||
}
|
||||
n.interfaces = append(n.interfaces, i)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *networkNamespace) Interfaces() []*Interface {
|
||||
return n.interfaces
|
||||
}
|
||||
|
||||
func (n *networkNamespace) Path() string {
|
||||
return n.path
|
||||
}
|
69
libnetwork/network.go
Normal file
69
libnetwork/network.go
Normal file
|
@ -0,0 +1,69 @@
|
|||
// Package libnetwork provides basic fonctionalities and extension points to
|
||||
// create network namespaces and allocate interfaces for containers to use.
|
||||
//
|
||||
// // Create a network for containers to join.
|
||||
// network, err := libnetwork.NewNetwork("simplebridge", &Options{})
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// // For a new container: create network namespace (providing the path).
|
||||
// networkPath := "/var/lib/docker/.../4d23e"
|
||||
// networkNamespace, err := libnetwork.NewNamespace(networkPath)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// // For each new container: allocate IP and interfaces. The returned network
|
||||
// // settings will be used for container infos (inspect and such), as well as
|
||||
// // iptables rules for port publishing.
|
||||
// interfaces, err := network.CreateInterfaces(containerID)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// // Add interfaces to the namespace.
|
||||
// for _, interface := range interfaces {
|
||||
// if err := networkNamespace.AddInterface(interface); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
package libnetwork
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Network interface {
|
||||
Name() string
|
||||
Type() string
|
||||
Link(name string) ([]*Interface, error)
|
||||
}
|
||||
|
||||
type Interface struct {
|
||||
// The name of the interface in the origin network namespace.
|
||||
SrcName string
|
||||
|
||||
// The name that will be assigned to the interface once moves inside a
|
||||
// network namespace.
|
||||
DstName string
|
||||
|
||||
MacAddress string
|
||||
Address string
|
||||
AddressIPv6 string
|
||||
Gateway string
|
||||
GatewayIPv6 string
|
||||
MTU int
|
||||
}
|
||||
|
||||
type Namespace interface {
|
||||
Path() string
|
||||
Interfaces() []*Interface
|
||||
AddInterface(*Interface) error
|
||||
}
|
||||
|
||||
// TODO Figure out the proper options type
|
||||
func NewNetwork(networkType string, options strategyParams) (Network, error) {
|
||||
if ctor, ok := strategies[networkType]; ok {
|
||||
return ctor(options)
|
||||
}
|
||||
return nil, fmt.Errorf("Unknown network type %q", networkType)
|
||||
}
|
45
libnetwork/reexec.go
Normal file
45
libnetwork/reexec.go
Normal file
|
@ -0,0 +1,45 @@
|
|||
package libnetwork
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/docker/docker/pkg/reexec"
|
||||
)
|
||||
|
||||
type ReexecCommand int
|
||||
|
||||
const (
|
||||
ReexecCreateNamespace ReexecCommand = iota
|
||||
ReexecMoveInterface
|
||||
)
|
||||
|
||||
var ReexecCommands = map[ReexecCommand]struct {
|
||||
Key string
|
||||
Entrypoint func()
|
||||
}{
|
||||
ReexecCreateNamespace: {"netns-create", createNetworkNamespace},
|
||||
ReexecMoveInterface: {"netns-moveif", namespaceMoveInterface},
|
||||
}
|
||||
|
||||
func init() {
|
||||
for _, reexecCmd := range ReexecCommands {
|
||||
reexec.Register(reexecCmd.Key, reexecCmd.Entrypoint)
|
||||
}
|
||||
}
|
||||
|
||||
func Reexec(command ReexecCommand, params ...string) error {
|
||||
reexecCommand, ok := ReexecCommands[command]
|
||||
if !ok {
|
||||
return fmt.Errorf("unknown reexec command %q", command)
|
||||
}
|
||||
|
||||
cmd := &exec.Cmd{
|
||||
Path: reexec.Self(),
|
||||
Args: append([]string{reexecCommand.Key}, params...),
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
}
|
||||
return cmd.Run()
|
||||
}
|
78
libnetwork/reexec_move_interface.go
Normal file
78
libnetwork/reexec_move_interface.go
Normal file
|
@ -0,0 +1,78 @@
|
|||
package libnetwork
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
type setupError struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
func (s setupError) Error() string {
|
||||
return s.Message
|
||||
}
|
||||
|
||||
func namespaceMoveInterface() {
|
||||
runtime.LockOSThread()
|
||||
|
||||
var (
|
||||
err error
|
||||
pipe = os.NewFile(3, "child")
|
||||
)
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
ioutil.ReadAll(pipe)
|
||||
if err := json.NewEncoder(pipe).Encode(setupError{Message: err.Error()}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
pipe.Close()
|
||||
}()
|
||||
|
||||
n := &Interface{}
|
||||
if err = json.NewDecoder(pipe).Decode(n); err == nil {
|
||||
err = setupInNS(os.Args[1], n)
|
||||
}
|
||||
}
|
||||
|
||||
func setupInNS(nsPath string, settings *Interface) error {
|
||||
f, err := os.OpenFile(nsPath, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed get network namespace %q: %v", nsPath, err)
|
||||
}
|
||||
|
||||
// Find the network inteerface identified by the SrcName attribute.
|
||||
iface, err := netlink.LinkByName(settings.SrcName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Move the network interface to the destination namespace.
|
||||
nsFD := f.Fd()
|
||||
if err := netlink.LinkSetNsFd(iface, int(nsFD)); err != nil {
|
||||
return err
|
||||
}
|
||||
f.Close()
|
||||
|
||||
// Move the executing code to the destination namespace so we can start
|
||||
// configure the interface.
|
||||
if err := Setns(nsFD, syscall.CLONE_NEWNET); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Configure the interface now this is moved in the proper namespace.
|
||||
if err := configureInterface(iface, settings); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Up the interface.
|
||||
return netlink.LinkSetUp(iface)
|
||||
}
|
52
libnetwork/reexec_netns_create.go
Normal file
52
libnetwork/reexec_netns_create.go
Normal file
|
@ -0,0 +1,52 @@
|
|||
package libnetwork
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
func createNetworkNamespace() {
|
||||
runtime.LockOSThread()
|
||||
|
||||
if len(os.Args) < 2 {
|
||||
log.Fatalf("no namespace path provided")
|
||||
}
|
||||
|
||||
if err := createNamespaceFile(os.Args[1]); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := syscall.Unshare(syscall.CLONE_NEWNET); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := loopbackUp(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := syscall.Mount("/proc/self/ns/net", os.Args[1], "bind", syscall.MS_BIND, ""); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func createNamespaceFile(path string) (err error) {
|
||||
var f *os.File
|
||||
if f, err = os.Create(path); err == nil {
|
||||
f.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func loopbackUp() error {
|
||||
iface, err := netlink.LinkByName("lo")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return netlink.LinkSetUp(iface)
|
||||
}
|
16
libnetwork/strategies.go
Normal file
16
libnetwork/strategies.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
package libnetwork
|
||||
|
||||
import "fmt"
|
||||
|
||||
type strategyParams map[string]interface{}
|
||||
type strategyConstructor func(strategyParams) (Network, error)
|
||||
|
||||
var strategies = map[string]strategyConstructor{}
|
||||
|
||||
func RegisterNetworkType(name string, ctor strategyConstructor) error {
|
||||
if _, ok := strategies[name]; ok {
|
||||
return fmt.Errorf("network type %q is already registed", name)
|
||||
}
|
||||
strategies[name] = ctor
|
||||
return nil
|
||||
}
|
34
libnetwork/system.go
Normal file
34
libnetwork/system.go
Normal file
|
@ -0,0 +1,34 @@
|
|||
package libnetwork
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Via http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=7b21fddd087678a70ad64afc0f632e0f1071b092
|
||||
//
|
||||
// We need different setns values for the different platforms and arch
|
||||
// We are declaring the macro here because the SETNS syscall does not exist in th stdlib
|
||||
var setNsMap = map[string]uintptr{
|
||||
"linux/386": 346,
|
||||
"linux/amd64": 308,
|
||||
"linux/arm": 374,
|
||||
"linux/ppc64": 350,
|
||||
"linux/ppc64le": 350,
|
||||
"linux/s390x": 339,
|
||||
}
|
||||
|
||||
func Setns(fd uintptr, flags uintptr) error {
|
||||
ns, exists := setNsMap[fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)]
|
||||
if !exists {
|
||||
return fmt.Errorf("unsupported platform %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
|
||||
_, _, err := syscall.RawSyscall(ns, fd, flags, 0)
|
||||
if err != 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Loading…
Add table
Reference in a new issue