Initial import

Signed-off-by: Arnaud Porterie <arnaud.porterie@docker.com>
This commit is contained in:
Arnaud Porterie 2015-02-19 17:21:42 -08:00
parent d7c688f0f6
commit 0d29ca540f
9 changed files with 456 additions and 0 deletions

View 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
View 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
View 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
View 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
View 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()
}

View 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)
}

View 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
View 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
View 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
}