2013-02-21 05:47:09 +04:00
package docker
import (
2013-02-22 06:33:23 +04:00
"encoding/binary"
2013-02-25 22:45:23 +04:00
"errors"
2013-02-21 06:20:18 +04:00
"fmt"
2013-04-20 06:35:44 +04:00
"io"
2013-02-28 23:50:02 +04:00
"log"
2013-02-21 05:47:09 +04:00
"net"
2013-02-28 23:50:02 +04:00
"os/exec"
"strconv"
"strings"
2013-04-05 09:56:12 +04:00
"sync"
2013-02-21 05:47:09 +04:00
)
2013-04-04 01:53:09 +04:00
var NetworkBridgeIface string
2013-02-21 05:47:09 +04:00
const (
2013-04-06 01:16:19 +04:00
DefaultNetworkBridge = "docker0"
2013-04-04 16:33:28 +04:00
portRangeStart = 49153
portRangeEnd = 65535
2013-02-21 05:47:09 +04:00
)
2013-02-28 23:50:02 +04:00
// Calculates the first and last IP addresses in an IPNet
2013-02-22 06:33:23 +04:00
func networkRange ( network * net . IPNet ) ( net . IP , net . IP ) {
netIP := network . IP . To4 ( )
firstIP := netIP . Mask ( network . Mask )
lastIP := net . IPv4 ( 0 , 0 , 0 , 0 ) . To4 ( )
for i := 0 ; i < len ( lastIP ) ; i ++ {
lastIP [ i ] = netIP [ i ] | ^ network . Mask [ i ]
}
return firstIP , lastIP
}
2013-04-04 01:53:54 +04:00
// Detects overlap between one IPNet and another
func networkOverlaps ( netX * net . IPNet , netY * net . IPNet ) bool {
firstIP , _ := networkRange ( netX )
if netY . Contains ( firstIP ) {
return true
}
firstIP , _ = networkRange ( netY )
if netX . Contains ( firstIP ) {
return true
}
return false
}
2013-02-28 23:50:02 +04:00
// Converts a 4 bytes IP into a 32 bit integer
2013-03-31 02:32:10 +04:00
func ipToInt ( ip net . IP ) int32 {
return int32 ( binary . BigEndian . Uint32 ( ip . To4 ( ) ) )
2013-02-22 06:33:23 +04:00
}
2013-02-28 23:50:02 +04:00
// Converts 32 bit integer into a 4 bytes IP address
2013-03-31 02:32:10 +04:00
func intToIp ( n int32 ) net . IP {
b := make ( [ ] byte , 4 )
binary . BigEndian . PutUint32 ( b , uint32 ( n ) )
return net . IP ( b )
2013-02-22 06:33:23 +04:00
}
2013-02-28 23:50:02 +04:00
// Given a netmask, calculates the number of available hosts
2013-03-31 02:32:10 +04:00
func networkSize ( mask net . IPMask ) int32 {
2013-02-26 02:06:22 +04:00
m := net . IPv4Mask ( 0 , 0 , 0 , 0 )
2013-02-22 06:33:23 +04:00
for i := 0 ; i < net . IPv4len ; i ++ {
2013-02-26 02:06:22 +04:00
m [ i ] = ^ mask [ i ]
2013-02-22 06:33:23 +04:00
}
2013-03-31 02:32:10 +04:00
return int32 ( binary . BigEndian . Uint32 ( m ) ) + 1
2013-02-22 06:33:23 +04:00
}
2013-04-04 02:57:57 +04:00
//Wrapper around the ip command
func ip ( args ... string ) ( string , error ) {
path , err := exec . LookPath ( "ip" )
if err != nil {
return "" , fmt . Errorf ( "command not found: ip" )
}
output , err := exec . Command ( path , args ... ) . CombinedOutput ( )
if err != nil {
return "" , fmt . Errorf ( "ip failed: ip %v" , strings . Join ( args , " " ) )
}
return string ( output ) , nil
}
2013-02-28 23:50:02 +04:00
// Wrapper around the iptables command
func iptables ( args ... string ) error {
2013-03-27 22:29:58 +04:00
path , err := exec . LookPath ( "iptables" )
if err != nil {
2013-03-28 23:30:56 +04:00
return fmt . Errorf ( "command not found: iptables" )
2013-03-27 22:29:58 +04:00
}
if err := exec . Command ( path , args ... ) . Run ( ) ; err != nil {
2013-02-28 23:50:02 +04:00
return fmt . Errorf ( "iptables failed: iptables %v" , strings . Join ( args , " " ) )
}
return nil
}
2013-04-04 02:57:57 +04:00
func checkRouteOverlaps ( dockerNetwork * net . IPNet ) error {
output , err := ip ( "route" )
if err != nil {
return err
}
Debugf ( "Routes:\n\n%s" , output )
for _ , line := range strings . Split ( output , "\n" ) {
if strings . Trim ( line , "\r\n\t " ) == "" || strings . Contains ( line , "default" ) {
continue
}
if _ , network , err := net . ParseCIDR ( strings . Split ( line , " " ) [ 0 ] ) ; err != nil {
return fmt . Errorf ( "Unexpected ip route output: %s (%s)" , err , line )
} else if networkOverlaps ( dockerNetwork , network ) {
return fmt . Errorf ( "Network %s is already routed: '%s'" , dockerNetwork . String ( ) , line )
}
}
return nil
}
func CreateBridgeIface ( ifaceName string ) error {
2013-04-09 05:16:58 +04:00
// FIXME: try more IP ranges
// FIXME: try bigger ranges! /24 is too small.
2013-04-04 02:57:57 +04:00
addrs := [ ] string { "172.16.42.1/24" , "10.0.42.1/24" , "192.168.42.1/24" }
var ifaceAddr string
for _ , addr := range addrs {
_ , dockerNetwork , err := net . ParseCIDR ( addr )
if err != nil {
return err
}
if err := checkRouteOverlaps ( dockerNetwork ) ; err == nil {
ifaceAddr = addr
break
} else {
Debugf ( "%s: %s" , addr , err )
}
}
if ifaceAddr == "" {
2013-04-09 05:16:58 +04:00
return fmt . Errorf ( "Could not find a free IP address range for interface '%s'. Please configure its address manually and run 'docker -b %s'" , ifaceName , ifaceName )
2013-04-04 02:57:57 +04:00
} else {
Debugf ( "Creating bridge %s with network %s" , ifaceName , ifaceAddr )
}
if output , err := ip ( "link" , "add" , ifaceName , "type" , "bridge" ) ; err != nil {
return fmt . Errorf ( "Error creating bridge: %s (output: %s)" , err , output )
}
if output , err := ip ( "addr" , "add" , ifaceAddr , "dev" , ifaceName ) ; err != nil {
return fmt . Errorf ( "Unable to add private network: %s (%s)" , err , output )
}
if output , err := ip ( "link" , "set" , ifaceName , "up" ) ; err != nil {
return fmt . Errorf ( "Unable to start network bridge: %s (%s)" , err , output )
}
if err := iptables ( "-t" , "nat" , "-A" , "POSTROUTING" , "-s" , ifaceAddr ,
"!" , "-d" , ifaceAddr , "-j" , "MASQUERADE" ) ; err != nil {
return fmt . Errorf ( "Unable to enable network bridge NAT: %s" , err )
}
return nil
}
2013-02-28 23:50:02 +04:00
// Return the IPv4 address of a network interface
2013-02-26 02:06:22 +04:00
func getIfaceAddr ( name string ) ( net . Addr , error ) {
2013-02-21 06:20:18 +04:00
iface , err := net . InterfaceByName ( name )
if err != nil {
return nil , err
}
addrs , err := iface . Addrs ( )
if err != nil {
return nil , err
}
var addrs4 [ ] net . Addr
for _ , addr := range addrs {
ip := ( addr . ( * net . IPNet ) ) . IP
if ip4 := ip . To4 ( ) ; len ( ip4 ) == net . IPv4len {
addrs4 = append ( addrs4 , addr )
}
}
switch {
case len ( addrs4 ) == 0 :
2013-02-28 23:50:02 +04:00
return nil , fmt . Errorf ( "Interface %v has no IP addresses" , name )
2013-02-21 06:20:18 +04:00
case len ( addrs4 ) > 1 :
2013-03-21 20:19:22 +04:00
fmt . Printf ( "Interface %v has more than 1 IPv4 address. Defaulting to using %v\n" ,
name , ( addrs4 [ 0 ] . ( * net . IPNet ) ) . IP )
2013-02-21 06:20:18 +04:00
}
return addrs4 [ 0 ] , nil
}
2013-02-28 23:50:02 +04:00
// Port mapper takes care of mapping external ports to containers by setting
// up iptables rules.
// It keeps track of all mappings and is able to unmap at will
type PortMapper struct {
mapping map [ int ] net . TCPAddr
2013-04-20 07:44:25 +04:00
proxies map [ int ] net . Listener
2013-02-28 23:50:02 +04:00
}
func ( mapper * PortMapper ) cleanup ( ) error {
// Ignore errors - This could mean the chains were never set up
2013-04-04 02:32:46 +04:00
iptables ( "-t" , "nat" , "-D" , "PREROUTING" , "-m" , "addrtype" , "--dst-type" , "LOCAL" , "-j" , "DOCKER" )
2013-04-20 06:32:32 +04:00
iptables ( "-t" , "nat" , "-D" , "OUTPUT" , "-m" , "addrtype" , "--dst-type" , "LOCAL" , "!" , "--dst" , "127.0.0.0/8" , "-j" , "DOCKER" )
iptables ( "-t" , "nat" , "-D" , "OUTPUT" , "-m" , "addrtype" , "--dst-type" , "LOCAL" , "-j" , "DOCKER" ) // Created in versions <= 0.1.6
2013-04-05 02:16:42 +04:00
// Also cleanup rules created by older versions, or -X might fail.
iptables ( "-t" , "nat" , "-D" , "PREROUTING" , "-j" , "DOCKER" )
iptables ( "-t" , "nat" , "-D" , "OUTPUT" , "-j" , "DOCKER" )
2013-02-28 23:50:02 +04:00
iptables ( "-t" , "nat" , "-F" , "DOCKER" )
iptables ( "-t" , "nat" , "-X" , "DOCKER" )
mapper . mapping = make ( map [ int ] net . TCPAddr )
2013-04-20 07:44:25 +04:00
mapper . proxies = make ( map [ int ] net . Listener )
2013-02-28 23:50:02 +04:00
return nil
}
func ( mapper * PortMapper ) setup ( ) error {
if err := iptables ( "-t" , "nat" , "-N" , "DOCKER" ) ; err != nil {
2013-03-28 23:44:54 +04:00
return fmt . Errorf ( "Failed to create DOCKER chain: %s" , err )
2013-02-21 06:20:18 +04:00
}
2013-04-04 02:32:46 +04:00
if err := iptables ( "-t" , "nat" , "-A" , "PREROUTING" , "-m" , "addrtype" , "--dst-type" , "LOCAL" , "-j" , "DOCKER" ) ; err != nil {
2013-03-28 23:44:54 +04:00
return fmt . Errorf ( "Failed to inject docker in PREROUTING chain: %s" , err )
2013-02-28 23:50:02 +04:00
}
2013-04-20 06:32:32 +04:00
if err := iptables ( "-t" , "nat" , "-A" , "OUTPUT" , "-m" , "addrtype" , "--dst-type" , "LOCAL" , "!" , "--dst" , "127.0.0.0/8" , "-j" , "DOCKER" ) ; err != nil {
2013-03-28 23:44:54 +04:00
return fmt . Errorf ( "Failed to inject docker in OUTPUT chain: %s" , err )
2013-03-22 18:06:14 +04:00
}
2013-02-28 23:50:02 +04:00
return nil
}
func ( mapper * PortMapper ) iptablesForward ( rule string , port int , dest net . TCPAddr ) error {
return iptables ( "-t" , "nat" , rule , "DOCKER" , "-p" , "tcp" , "--dport" , strconv . Itoa ( port ) ,
"-j" , "DNAT" , "--to-destination" , net . JoinHostPort ( dest . IP . String ( ) , strconv . Itoa ( dest . Port ) ) )
}
func ( mapper * PortMapper ) Map ( port int , dest net . TCPAddr ) error {
if err := mapper . iptablesForward ( "-A" , port , dest ) ; err != nil {
return err
}
2013-04-20 06:35:44 +04:00
2013-02-28 23:50:02 +04:00
mapper . mapping [ port ] = dest
2013-04-20 06:35:44 +04:00
listener , err := net . Listen ( "tcp" , fmt . Sprintf ( "127.0.0.1:%d" , port ) )
if err != nil {
mapper . Unmap ( port )
return err
}
2013-04-20 07:44:25 +04:00
mapper . proxies [ port ] = listener
2013-04-20 06:35:44 +04:00
go proxy ( listener , "tcp" , dest . String ( ) )
2013-02-28 23:50:02 +04:00
return nil
}
2013-02-26 02:06:22 +04:00
2013-04-20 06:35:44 +04:00
// proxy listens for socket connections on `listener`, and forwards them unmodified
// to `proto:address`
func proxy ( listener net . Listener , proto , address string ) error {
Debugf ( "proxying to %s:%s" , proto , address )
defer Debugf ( "Done proxying to %s:%s" , proto , address )
for {
Debugf ( "Listening on %s" , listener )
src , err := listener . Accept ( )
if err != nil {
return err
}
Debugf ( "Connecting to %s:%s" , proto , address )
dst , err := net . Dial ( proto , address )
if err != nil {
log . Printf ( "Error connecting to %s:%s: %s" , proto , address , err )
src . Close ( )
continue
}
Debugf ( "Connected to backend, splicing" )
splice ( src , dst )
}
return nil
}
func halfSplice ( dst , src net . Conn ) error {
_ , err := io . Copy ( dst , src )
// FIXME: on EOF from a tcp connection, pass WriteClose()
dst . Close ( )
src . Close ( )
return err
}
func splice ( a , b net . Conn ) {
go halfSplice ( a , b )
go halfSplice ( b , a )
}
2013-02-28 23:50:02 +04:00
func ( mapper * PortMapper ) Unmap ( port int ) error {
dest , ok := mapper . mapping [ port ]
if ! ok {
return errors . New ( "Port is not mapped" )
}
2013-04-20 07:44:25 +04:00
if proxy , exists := mapper . proxies [ port ] ; exists {
proxy . Close ( )
delete ( mapper . proxies , port )
}
2013-02-28 23:50:02 +04:00
if err := mapper . iptablesForward ( "-D" , port , dest ) ; err != nil {
return err
2013-02-22 06:33:23 +04:00
}
2013-02-28 23:50:02 +04:00
delete ( mapper . mapping , port )
return nil
}
func newPortMapper ( ) ( * PortMapper , error ) {
mapper := & PortMapper { }
if err := mapper . cleanup ( ) ; err != nil {
2013-02-26 02:06:22 +04:00
return nil , err
2013-02-21 05:47:09 +04:00
}
2013-02-28 23:50:02 +04:00
if err := mapper . setup ( ) ; err != nil {
return nil , err
}
return mapper , nil
2013-02-21 05:47:09 +04:00
}
2013-02-25 22:45:23 +04:00
2013-02-28 23:50:02 +04:00
// Port allocator: Atomatically allocate and release networking ports
type PortAllocator struct {
2013-04-05 09:58:01 +04:00
inUse map [ int ] struct { }
2013-04-05 09:56:12 +04:00
fountain chan ( int )
2013-04-05 09:58:01 +04:00
lock sync . Mutex
2013-02-25 22:45:23 +04:00
}
2013-04-05 09:56:12 +04:00
func ( alloc * PortAllocator ) runFountain ( ) {
for {
for port := portRangeStart ; port < portRangeEnd ; port ++ {
alloc . fountain <- port
}
2013-02-25 22:45:23 +04:00
}
}
2013-04-05 09:56:12 +04:00
// FIXME: Release can no longer fail, change its prototype to reflect that.
2013-02-28 23:50:02 +04:00
func ( alloc * PortAllocator ) Release ( port int ) error {
2013-04-06 00:03:24 +04:00
Debugf ( "Releasing %d" , port )
2013-04-05 09:56:12 +04:00
alloc . lock . Lock ( )
delete ( alloc . inUse , port )
alloc . lock . Unlock ( )
return nil
}
func ( alloc * PortAllocator ) Acquire ( port int ) ( int , error ) {
2013-04-06 00:03:24 +04:00
Debugf ( "Acquiring %d" , port )
2013-04-05 09:56:12 +04:00
if port == 0 {
// Allocate a port from the fountain
for port := range alloc . fountain {
if _ , err := alloc . Acquire ( port ) ; err == nil {
return port , nil
}
}
return - 1 , fmt . Errorf ( "Port generator ended unexpectedly" )
2013-02-25 22:45:23 +04:00
}
2013-04-05 09:56:12 +04:00
alloc . lock . Lock ( )
defer alloc . lock . Unlock ( )
if _ , inUse := alloc . inUse [ port ] ; inUse {
return - 1 , fmt . Errorf ( "Port already in use: %d" , port )
}
alloc . inUse [ port ] = struct { } { }
return port , nil
2013-02-25 22:45:23 +04:00
}
2013-04-05 09:56:12 +04:00
func newPortAllocator ( ) ( * PortAllocator , error ) {
allocator := & PortAllocator {
2013-04-12 18:37:24 +04:00
inUse : make ( map [ int ] struct { } ) ,
2013-04-06 00:03:04 +04:00
fountain : make ( chan int ) ,
2013-04-05 09:56:12 +04:00
}
go allocator . runFountain ( )
2013-02-28 23:50:02 +04:00
return allocator , nil
}
// IP allocator: Atomatically allocate and release networking ports
type IPAllocator struct {
2013-03-31 02:32:10 +04:00
network * net . IPNet
queueAlloc chan allocatedIP
queueReleased chan net . IP
inUse map [ int32 ] struct { }
}
type allocatedIP struct {
ip net . IP
err error
2013-02-28 23:50:02 +04:00
}
2013-03-31 02:32:10 +04:00
func ( alloc * IPAllocator ) run ( ) {
2013-02-28 23:50:02 +04:00
firstIP , _ := networkRange ( alloc . network )
2013-03-31 02:32:10 +04:00
ipNum := ipToInt ( firstIP )
ownIP := ipToInt ( alloc . network . IP )
size := networkSize ( alloc . network . Mask )
pos := int32 ( 1 )
max := size - 2 // -1 for the broadcast address, -1 for the gateway address
for {
var (
newNum int32
inUse bool
)
// Find first unused IP, give up after one whole round
for attempt := int32 ( 0 ) ; attempt < max ; attempt ++ {
newNum = ipNum + pos
pos = pos % max + 1
// The network's IP is never okay to use
if newNum == ownIP {
continue
}
if _ , inUse = alloc . inUse [ newNum ] ; ! inUse {
// We found an unused IP
break
}
2013-02-25 22:45:23 +04:00
}
2013-03-31 02:32:10 +04:00
ip := allocatedIP { ip : intToIp ( newNum ) }
if inUse {
ip . err = errors . New ( "No unallocated IP available" )
2013-02-25 22:45:23 +04:00
}
2013-03-31 02:32:10 +04:00
select {
case alloc . queueAlloc <- ip :
alloc . inUse [ newNum ] = struct { } { }
case released := <- alloc . queueReleased :
r := ipToInt ( released )
delete ( alloc . inUse , r )
if inUse {
// If we couldn't allocate a new IP, the released one
// will be the only free one now, so instantly use it
// next time
pos = r - ipNum
} else {
// Use same IP as last time
if pos == 1 {
pos = max
} else {
pos --
}
}
2013-02-25 22:45:23 +04:00
}
2013-02-28 23:50:02 +04:00
}
}
func ( alloc * IPAllocator ) Acquire ( ) ( net . IP , error ) {
2013-03-31 02:32:10 +04:00
ip := <- alloc . queueAlloc
return ip . ip , ip . err
2013-02-28 23:50:02 +04:00
}
2013-03-31 02:32:10 +04:00
func ( alloc * IPAllocator ) Release ( ip net . IP ) {
alloc . queueReleased <- ip
2013-02-25 22:45:23 +04:00
}
2013-03-31 02:32:10 +04:00
func newIPAllocator ( network * net . IPNet ) * IPAllocator {
2013-02-28 23:50:02 +04:00
alloc := & IPAllocator {
2013-03-31 02:32:10 +04:00
network : network ,
queueAlloc : make ( chan allocatedIP ) ,
queueReleased : make ( chan net . IP ) ,
inUse : make ( map [ int32 ] struct { } ) ,
2013-02-28 23:50:02 +04:00
}
2013-03-31 02:32:10 +04:00
go alloc . run ( )
return alloc
2013-02-28 23:50:02 +04:00
}
// Network interface represents the networking stack of a container
type NetworkInterface struct {
IPNet net . IPNet
Gateway net . IP
manager * NetworkManager
extPorts [ ] int
}
// Allocate an external TCP port and map it to the interface
2013-04-05 09:58:01 +04:00
func ( iface * NetworkInterface ) AllocatePort ( spec string ) ( * Nat , error ) {
nat , err := parseNat ( spec )
2013-02-28 23:50:02 +04:00
if err != nil {
2013-04-05 09:58:01 +04:00
return nil , err
}
// Allocate a random port if Frontend==0
if extPort , err := iface . manager . portAllocator . Acquire ( nat . Frontend ) ; err != nil {
return nil , err
} else {
nat . Frontend = extPort
}
if err := iface . manager . portMapper . Map ( nat . Frontend , net . TCPAddr { IP : iface . IPNet . IP , Port : nat . Backend } ) ; err != nil {
iface . manager . portAllocator . Release ( nat . Frontend )
return nil , err
}
iface . extPorts = append ( iface . extPorts , nat . Frontend )
return nat , nil
}
type Nat struct {
Proto string
Frontend int
Backend int
}
func parseNat ( spec string ) ( * Nat , error ) {
var nat Nat
// If spec starts with ':', external and internal ports must be the same.
// This might fail if the requested external port is not available.
var sameFrontend bool
if spec [ 0 ] == ':' {
sameFrontend = true
spec = spec [ 1 : ]
}
port , err := strconv . ParseUint ( spec , 10 , 16 )
if err != nil {
return nil , err
2013-02-28 23:50:02 +04:00
}
2013-04-05 09:58:01 +04:00
nat . Backend = int ( port )
if sameFrontend {
nat . Frontend = nat . Backend
2013-02-28 23:50:02 +04:00
}
2013-04-05 09:58:01 +04:00
nat . Proto = "tcp"
return & nat , nil
2013-02-28 23:50:02 +04:00
}
// Release: Network cleanup - release all resources
2013-03-31 02:32:10 +04:00
func ( iface * NetworkInterface ) Release ( ) {
2013-02-28 23:50:02 +04:00
for _ , port := range iface . extPorts {
if err := iface . manager . portMapper . Unmap ( port ) ; err != nil {
log . Printf ( "Unable to unmap port %v: %v" , port , err )
}
if err := iface . manager . portAllocator . Release ( port ) ; err != nil {
log . Printf ( "Unable to release port %v: %v" , port , err )
}
}
2013-03-31 02:32:10 +04:00
iface . manager . ipAllocator . Release ( iface . IPNet . IP )
2013-02-28 23:50:02 +04:00
}
// Network Manager manages a set of network interfaces
// Only *one* manager per host machine should be used
type NetworkManager struct {
bridgeIface string
bridgeNetwork * net . IPNet
ipAllocator * IPAllocator
portAllocator * PortAllocator
portMapper * PortMapper
}
// Allocate a network interface
func ( manager * NetworkManager ) Allocate ( ) ( * NetworkInterface , error ) {
ip , err := manager . ipAllocator . Acquire ( )
2013-02-25 22:45:23 +04:00
if err != nil {
2013-02-26 02:06:22 +04:00
return nil , err
2013-02-25 22:45:23 +04:00
}
2013-02-26 02:06:22 +04:00
iface := & NetworkInterface {
2013-03-20 17:02:25 +04:00
IPNet : net . IPNet { IP : ip , Mask : manager . bridgeNetwork . Mask } ,
2013-02-28 23:50:02 +04:00
Gateway : manager . bridgeNetwork . IP ,
manager : manager ,
2013-02-26 02:06:22 +04:00
}
return iface , nil
}
2013-02-28 23:50:02 +04:00
func newNetworkManager ( bridgeIface string ) ( * NetworkManager , error ) {
addr , err := getIfaceAddr ( bridgeIface )
if err != nil {
2013-04-04 02:57:57 +04:00
// If the iface is not found, try to create it
if err := CreateBridgeIface ( bridgeIface ) ; err != nil {
return nil , err
}
addr , err = getIfaceAddr ( bridgeIface )
if err != nil {
return nil , err
}
2013-02-28 23:50:02 +04:00
}
network := addr . ( * net . IPNet )
2013-03-31 02:32:10 +04:00
ipAllocator := newIPAllocator ( network )
2013-02-28 23:50:02 +04:00
2013-04-05 09:56:12 +04:00
portAllocator , err := newPortAllocator ( )
2013-02-28 23:50:02 +04:00
if err != nil {
return nil , err
}
portMapper , err := newPortMapper ( )
2013-03-23 08:43:31 +04:00
if err != nil {
return nil , err
}
2013-02-28 23:50:02 +04:00
manager := & NetworkManager {
bridgeIface : bridgeIface ,
bridgeNetwork : network ,
ipAllocator : ipAllocator ,
portAllocator : portAllocator ,
portMapper : portMapper ,
}
return manager , nil
2013-02-25 22:45:23 +04:00
}