2014-03-20 21:51:28 +00:00
package bridge
2014-01-29 16:59:21 -08:00
import (
2015-01-09 00:03:19 +01:00
"encoding/hex"
"errors"
2014-01-29 16:59:21 -08:00
"fmt"
2014-05-05 22:51:32 +00:00
"io/ioutil"
"net"
2014-10-05 00:21:59 -04:00
"os"
2015-04-14 15:02:02 -07:00
"os/exec"
2015-04-04 00:06:48 -04:00
"strconv"
2015-01-09 00:03:19 +01:00
"strings"
2014-05-29 16:28:06 +04:00
"sync"
2014-05-05 22:51:32 +00:00
2015-03-26 23:22:04 +01:00
"github.com/Sirupsen/logrus"
2015-04-04 00:06:48 -04:00
"github.com/docker/docker/daemon/network"
2014-07-24 22:19:50 +00:00
"github.com/docker/docker/daemon/networkdriver"
"github.com/docker/docker/daemon/networkdriver/ipallocator"
"github.com/docker/docker/daemon/networkdriver/portmapper"
2014-09-17 01:08:30 +00:00
"github.com/docker/docker/nat"
2014-07-24 22:19:50 +00:00
"github.com/docker/docker/pkg/iptables"
2014-07-28 17:23:38 -07:00
"github.com/docker/docker/pkg/parsers/kernel"
2015-03-22 23:27:04 -07:00
"github.com/docker/docker/pkg/resolvconf"
2014-07-24 22:25:29 +00:00
"github.com/docker/libcontainer/netlink"
2014-01-29 16:59:21 -08:00
)
const (
2014-06-26 00:09:19 -07:00
DefaultNetworkBridge = "docker0"
MaxAllocatedPortAttempts = 10
2014-01-29 16:59:21 -08:00
)
// Network interface represents the networking stack of a container
type networkInterface struct {
IP net . IP
2015-01-09 00:03:19 +01:00
IPv6 net . IP
2015-03-03 23:39:04 +08:00
PortMappings [ ] net . Addr // There are mappings to the host interfaces
2014-01-29 16:59:21 -08:00
}
2014-05-29 16:28:06 +04:00
type ifaces struct {
c map [ string ] * networkInterface
sync . Mutex
}
func ( i * ifaces ) Set ( key string , n * networkInterface ) {
i . Lock ( )
i . c [ key ] = n
i . Unlock ( )
}
func ( i * ifaces ) Get ( key string ) * networkInterface {
i . Lock ( )
res := i . c [ key ]
i . Unlock ( )
return res
}
2014-01-29 16:59:21 -08:00
var (
addrs = [ ] string {
// 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
2015-04-04 15:22:24 +02:00
// on the internal addressing or other things like that.
2014-04-15 17:35:36 -04:00
// They shouldn't, but hey, let's not break them unless we really have to.
2014-01-29 16:59:21 -08:00
"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" ,
}
2015-01-09 00:03:19 +01:00
bridgeIface string
bridgeIPv4Network * net . IPNet
2014-12-18 10:09:42 +01:00
gatewayIPv4 net . IP
2015-01-09 00:03:19 +01:00
bridgeIPv6Addr net . IP
globalIPv6Network * net . IPNet
2014-12-18 10:09:42 +01:00
gatewayIPv6 net . IP
2015-03-30 18:06:16 -07:00
portMapper * portmapper . PortMapper
2015-03-31 09:34:03 -07:00
once sync . Once
2014-01-29 16:59:21 -08:00
2014-01-30 14:52:59 -08:00
defaultBindingIP = net . ParseIP ( "0.0.0.0" )
2014-05-29 16:28:06 +04:00
currentInterfaces = ifaces { c : make ( map [ string ] * networkInterface ) }
2015-03-23 20:20:10 -07:00
ipAllocator = ipallocator . New ( )
2014-01-29 16:59:21 -08:00
)
2015-03-31 09:34:03 -07:00
func initPortMapper ( ) {
once . Do ( func ( ) {
portMapper = portmapper . New ( )
} )
}
2015-04-04 00:06:48 -04:00
type Config struct {
EnableIPv6 bool
EnableIptables bool
EnableIpForward bool
EnableIpMasq bool
DefaultIp net . IP
Iface string
IP string
FixedCIDR string
FixedCIDRv6 string
2014-12-18 10:09:42 +01:00
DefaultGatewayIPv4 string
DefaultGatewayIPv6 string
2015-04-04 00:06:48 -04:00
InterContainerCommunication bool
}
func InitDriver ( config * Config ) error {
2014-01-29 16:59:21 -08:00
var (
2015-04-04 00:06:48 -04:00
networkv4 * net . IPNet
networkv6 * net . IPNet
addrv4 net . Addr
addrsv6 [ ] net . Addr
bridgeIPv6 = "fe80::1/64"
2014-01-29 16:59:21 -08:00
)
2015-04-14 15:02:02 -07:00
// try to modprobe bridge first
// see gh#12177
if out , err := exec . Command ( "modprobe" , "-va" , "bridge" , "nf_nat" ) . Output ( ) ; err != nil {
logrus . Warnf ( "Running modprobe bridge nf_nat failed with message: %s, error: %v" , out , err )
}
2015-03-31 09:34:03 -07:00
initPortMapper ( )
2014-01-29 18:34:43 -08:00
2015-04-04 00:06:48 -04:00
if config . DefaultIp != nil {
defaultBindingIP = config . DefaultIp
2014-01-30 14:52:59 -08:00
}
2014-01-30 11:25:06 -08:00
2015-04-04 00:06:48 -04:00
bridgeIface = config . Iface
2014-04-08 14:07:02 -04:00
usingDefaultBridge := false
2014-01-29 18:34:43 -08:00
if bridgeIface == "" {
2014-04-08 14:07:02 -04:00
usingDefaultBridge = true
2014-01-29 18:34:43 -08:00
bridgeIface = DefaultNetworkBridge
}
2014-01-29 16:59:21 -08:00
2015-01-09 00:03:19 +01:00
addrv4 , addrsv6 , err := networkdriver . GetIfaceAddr ( bridgeIface )
2014-01-29 16:59:21 -08:00
if err != nil {
2015-03-03 00:52:53 +08:00
// No Bridge existent, create one
2014-04-08 14:07:02 -04:00
// If we're not using the default bridge, fail without trying to create it
if ! usingDefaultBridge {
2015-03-25 08:44:12 +01:00
return err
2014-04-08 14:07:02 -04:00
}
2015-01-09 00:03:19 +01:00
2015-04-12 15:49:29 -07:00
logrus . Info ( "Bridge interface not found, trying to create it" )
2015-04-11 10:40:37 -07:00
2015-01-09 00:03:19 +01:00
// If the iface is not found, try to create it
2015-04-04 00:06:48 -04:00
if err := configureBridge ( config . IP , bridgeIPv6 , config . EnableIPv6 ) ; err != nil {
2015-04-11 10:40:37 -07:00
logrus . Errorf ( "Could not configure Bridge: %s" , err )
2015-03-25 08:44:12 +01:00
return err
2014-01-29 16:59:21 -08:00
}
2015-01-09 00:03:19 +01:00
addrv4 , addrsv6 , err = networkdriver . GetIfaceAddr ( bridgeIface )
2014-01-29 16:59:21 -08:00
if err != nil {
2015-03-25 08:44:12 +01:00
return err
2014-01-29 16:59:21 -08:00
}
2015-01-09 00:03:19 +01:00
2015-04-04 00:06:48 -04:00
if config . FixedCIDRv6 != "" {
2015-01-09 00:03:19 +01:00
// Setting route to global IPv6 subnet
2015-04-04 00:06:48 -04:00
logrus . Infof ( "Adding route to IPv6 network %q via device %q" , config . FixedCIDRv6 , bridgeIface )
if err := netlink . AddRoute ( config . FixedCIDRv6 , "" , "" , bridgeIface ) ; err != nil {
logrus . Fatalf ( "Could not add route to IPv6 network %q via device %q" , config . FixedCIDRv6 , bridgeIface )
2015-01-09 00:03:19 +01:00
}
}
2014-01-29 16:59:21 -08:00
} else {
2015-03-03 00:52:53 +08:00
// Bridge exists already, getting info...
2015-03-03 23:39:04 +08:00
// Validate that the bridge ip matches the ip specified by BridgeIP
2015-04-04 00:06:48 -04:00
if config . IP != "" {
2015-01-09 00:03:19 +01:00
networkv4 = addrv4 . ( * net . IPNet )
2015-04-04 00:06:48 -04:00
bip , _ , err := net . ParseCIDR ( config . IP )
2014-04-28 17:04:56 -07:00
if err != nil {
2015-03-25 08:44:12 +01:00
return err
2014-04-28 17:04:56 -07:00
}
2015-01-09 00:03:19 +01:00
if ! networkv4 . IP . Equal ( bip ) {
2015-03-25 08:44:12 +01:00
return fmt . Errorf ( "Bridge ip (%s) does not match existing bridge configuration %s" , networkv4 . IP , bip )
2014-03-26 11:51:27 +00:00
}
}
2015-01-09 00:03:19 +01:00
2015-03-03 23:39:04 +08:00
// A bridge might exist but not have any IPv6 addr associated with it yet
2015-01-27 22:03:27 -05:00
// (for example, an existing Docker installation that has only been used
// with IPv4 and docker0 already is set up) In that case, we can perform
// the bridge init for IPv6 here, else we will error out below if --ipv6=true
2015-04-04 00:06:48 -04:00
if len ( addrsv6 ) == 0 && config . EnableIPv6 {
2015-01-27 22:03:27 -05:00
if err := setupIPv6Bridge ( bridgeIPv6 ) ; err != nil {
2015-03-25 08:44:12 +01:00
return err
2015-01-27 22:03:27 -05:00
}
2015-03-03 23:39:04 +08:00
// Recheck addresses now that IPv6 is setup on the bridge
2015-01-27 22:03:27 -05:00
addrv4 , addrsv6 , err = networkdriver . GetIfaceAddr ( bridgeIface )
if err != nil {
2015-03-25 08:44:12 +01:00
return err
2015-01-27 22:03:27 -05:00
}
}
2015-04-04 00:06:48 -04:00
// TODO: Check if route to config.FixedCIDRv6 is set
2015-01-09 00:03:19 +01:00
}
2015-04-04 00:06:48 -04:00
if config . EnableIPv6 {
2015-01-09 00:03:19 +01:00
bip6 , _ , err := net . ParseCIDR ( bridgeIPv6 )
if err != nil {
2015-03-25 08:44:12 +01:00
return err
2015-01-09 00:03:19 +01:00
}
found := false
for _ , addrv6 := range addrsv6 {
networkv6 = addrv6 . ( * net . IPNet )
if networkv6 . IP . Equal ( bip6 ) {
found = true
break
}
}
if ! found {
2015-03-25 08:44:12 +01:00
return fmt . Errorf ( "Bridge IPv6 does not match existing bridge configuration %s" , bip6 )
2015-01-09 00:03:19 +01:00
}
}
networkv4 = addrv4 . ( * net . IPNet )
2015-04-04 00:06:48 -04:00
if config . EnableIPv6 {
2015-01-09 00:03:19 +01:00
if len ( addrsv6 ) == 0 {
2015-03-25 08:44:12 +01:00
return errors . New ( "IPv6 enabled but no IPv6 detected" )
2015-01-09 00:03:19 +01:00
}
bridgeIPv6Addr = networkv6 . IP
2014-01-29 16:59:21 -08:00
}
2014-11-26 12:14:50 +01:00
if config . EnableIptables {
iptables . FirewalldInit ( )
}
2014-01-29 16:59:21 -08:00
// Configure iptables for link support
2015-04-04 00:06:48 -04:00
if config . EnableIptables {
if err := setupIPTables ( addrv4 , config . InterContainerCommunication , config . EnableIpMasq ) ; err != nil {
2015-04-24 16:52:32 -07:00
logrus . Errorf ( "Error configuring iptables: %s" , err )
2015-03-25 08:44:12 +01:00
return err
2014-01-29 16:59:21 -08:00
}
React to firewalld's reload/restart
When firewalld (or iptables service) restarts/reloads,
all previously added docker firewall rules are flushed.
With firewalld we can react to its Reloaded() [1]
D-Bus signal and recreate the firewall rules.
Also when firewalld gets restarted (stopped & started)
we can catch the NameOwnerChanged signal [2].
To specify which signals we want to react to we use AddMatch [3].
Libvirt has been doing this for quite a long time now.
Docker changes firewall rules on basically 3 places.
1) daemon/networkdriver/portmapper/mapper.go - port mappings
Portmapper fortunatelly keeps list of mapped ports,
so we can easily recreate firewall rules on firewalld restart/reload
New ReMapAll() function does that
2) daemon/networkdriver/bridge/driver.go
When setting a bridge, basic firewall rules are created.
This is done at once during start, it's parametrized and nowhere
tracked so how can one know what and how to set it again when
there's been firewalld restart/reload ?
The only solution that came to my mind is using of closures [4],
i.e. I keep list of references to closures (anonymous functions
together with a referencing environment) and when there's firewalld
restart/reload I re-call them in the same order.
3) links/links.go - linking containers
Link is added in Enable() and removed in Disable().
In Enable() we add a callback function, which creates the link,
that's OK so far.
It'd be ideal if we could remove the same function from
the list in Disable(). Unfortunatelly that's not possible AFAICT,
because we don't know the reference to that function
at that moment, so we can only add a reference to function,
which removes the link. That means that after creating and
removing a link there are 2 functions in the list,
one adding and one removing the link and after
firewalld restart/reload both are called.
It works, but it's far from ideal.
[1] https://jpopelka.fedorapeople.org/firewalld/doc/firewalld.dbus.html#FirewallD1.Signals.Reloaded
[2] http://dbus.freedesktop.org/doc/dbus-specification.html#bus-messages-name-owner-changed
[3] http://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-routing-match-rules
[4] https://en.wikipedia.org/wiki/Closure_%28computer_programming%29
Signed-off-by: Jiri Popelka <jpopelka@redhat.com>
2014-11-26 19:10:35 +01:00
// call this on Firewalld reload
iptables . OnReloaded ( func ( ) { setupIPTables ( addrv4 , config . InterContainerCommunication , config . EnableIpMasq ) } )
2014-01-29 16:59:21 -08:00
}
2015-04-04 00:06:48 -04:00
if config . EnableIpForward {
2014-01-29 16:59:21 -08:00
// Enable IPv4 forwarding
if err := ioutil . WriteFile ( "/proc/sys/net/ipv4/ip_forward" , [ ] byte { '1' , '\n' } , 0644 ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Warnf ( "WARNING: unable to enable IPv4 forwarding: %s\n" , err )
2014-01-29 16:59:21 -08:00
}
2015-01-09 00:03:19 +01:00
2015-04-04 00:06:48 -04:00
if config . FixedCIDRv6 != "" {
2015-01-09 00:03:19 +01:00
// Enable IPv6 forwarding
if err := ioutil . WriteFile ( "/proc/sys/net/ipv6/conf/default/forwarding" , [ ] byte { '1' , '\n' } , 0644 ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Warnf ( "WARNING: unable to enable IPv6 default forwarding: %s\n" , err )
2015-01-09 00:03:19 +01:00
}
if err := ioutil . WriteFile ( "/proc/sys/net/ipv6/conf/all/forwarding" , [ ] byte { '1' , '\n' } , 0644 ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Warnf ( "WARNING: unable to enable IPv6 all forwarding: %s\n" , err )
2015-01-09 00:03:19 +01:00
}
}
2014-01-29 16:59:21 -08:00
}
// We can always try removing the iptables
2014-07-16 22:03:01 +10:00
if err := iptables . RemoveExistingChain ( "DOCKER" , iptables . Nat ) ; err != nil {
2015-03-25 08:44:12 +01:00
return err
2014-01-29 16:59:21 -08:00
}
2015-04-04 00:06:48 -04:00
if config . EnableIptables {
2014-07-16 22:03:01 +10:00
_ , err := iptables . NewChain ( "DOCKER" , bridgeIface , iptables . Nat )
if err != nil {
2015-03-25 08:44:12 +01:00
return err
2014-07-16 22:03:01 +10:00
}
React to firewalld's reload/restart
When firewalld (or iptables service) restarts/reloads,
all previously added docker firewall rules are flushed.
With firewalld we can react to its Reloaded() [1]
D-Bus signal and recreate the firewall rules.
Also when firewalld gets restarted (stopped & started)
we can catch the NameOwnerChanged signal [2].
To specify which signals we want to react to we use AddMatch [3].
Libvirt has been doing this for quite a long time now.
Docker changes firewall rules on basically 3 places.
1) daemon/networkdriver/portmapper/mapper.go - port mappings
Portmapper fortunatelly keeps list of mapped ports,
so we can easily recreate firewall rules on firewalld restart/reload
New ReMapAll() function does that
2) daemon/networkdriver/bridge/driver.go
When setting a bridge, basic firewall rules are created.
This is done at once during start, it's parametrized and nowhere
tracked so how can one know what and how to set it again when
there's been firewalld restart/reload ?
The only solution that came to my mind is using of closures [4],
i.e. I keep list of references to closures (anonymous functions
together with a referencing environment) and when there's firewalld
restart/reload I re-call them in the same order.
3) links/links.go - linking containers
Link is added in Enable() and removed in Disable().
In Enable() we add a callback function, which creates the link,
that's OK so far.
It'd be ideal if we could remove the same function from
the list in Disable(). Unfortunatelly that's not possible AFAICT,
because we don't know the reference to that function
at that moment, so we can only add a reference to function,
which removes the link. That means that after creating and
removing a link there are 2 functions in the list,
one adding and one removing the link and after
firewalld restart/reload both are called.
It works, but it's far from ideal.
[1] https://jpopelka.fedorapeople.org/firewalld/doc/firewalld.dbus.html#FirewallD1.Signals.Reloaded
[2] http://dbus.freedesktop.org/doc/dbus-specification.html#bus-messages-name-owner-changed
[3] http://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-routing-match-rules
[4] https://en.wikipedia.org/wiki/Closure_%28computer_programming%29
Signed-off-by: Jiri Popelka <jpopelka@redhat.com>
2014-11-26 19:10:35 +01:00
// call this on Firewalld reload
iptables . OnReloaded ( func ( ) { iptables . NewChain ( "DOCKER" , bridgeIface , iptables . Nat ) } )
2014-07-16 22:03:01 +10:00
chain , err := iptables . NewChain ( "DOCKER" , bridgeIface , iptables . Filter )
2014-01-29 16:59:21 -08:00
if err != nil {
2015-03-25 08:44:12 +01:00
return err
2014-01-29 16:59:21 -08:00
}
React to firewalld's reload/restart
When firewalld (or iptables service) restarts/reloads,
all previously added docker firewall rules are flushed.
With firewalld we can react to its Reloaded() [1]
D-Bus signal and recreate the firewall rules.
Also when firewalld gets restarted (stopped & started)
we can catch the NameOwnerChanged signal [2].
To specify which signals we want to react to we use AddMatch [3].
Libvirt has been doing this for quite a long time now.
Docker changes firewall rules on basically 3 places.
1) daemon/networkdriver/portmapper/mapper.go - port mappings
Portmapper fortunatelly keeps list of mapped ports,
so we can easily recreate firewall rules on firewalld restart/reload
New ReMapAll() function does that
2) daemon/networkdriver/bridge/driver.go
When setting a bridge, basic firewall rules are created.
This is done at once during start, it's parametrized and nowhere
tracked so how can one know what and how to set it again when
there's been firewalld restart/reload ?
The only solution that came to my mind is using of closures [4],
i.e. I keep list of references to closures (anonymous functions
together with a referencing environment) and when there's firewalld
restart/reload I re-call them in the same order.
3) links/links.go - linking containers
Link is added in Enable() and removed in Disable().
In Enable() we add a callback function, which creates the link,
that's OK so far.
It'd be ideal if we could remove the same function from
the list in Disable(). Unfortunatelly that's not possible AFAICT,
because we don't know the reference to that function
at that moment, so we can only add a reference to function,
which removes the link. That means that after creating and
removing a link there are 2 functions in the list,
one adding and one removing the link and after
firewalld restart/reload both are called.
It works, but it's far from ideal.
[1] https://jpopelka.fedorapeople.org/firewalld/doc/firewalld.dbus.html#FirewallD1.Signals.Reloaded
[2] http://dbus.freedesktop.org/doc/dbus-specification.html#bus-messages-name-owner-changed
[3] http://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-routing-match-rules
[4] https://en.wikipedia.org/wiki/Closure_%28computer_programming%29
Signed-off-by: Jiri Popelka <jpopelka@redhat.com>
2014-11-26 19:10:35 +01:00
// call this on Firewalld reload
iptables . OnReloaded ( func ( ) { iptables . NewChain ( "DOCKER" , bridgeIface , iptables . Filter ) } )
2015-03-30 18:06:16 -07:00
portMapper . SetIptablesChain ( chain )
2014-01-29 16:59:21 -08:00
}
2015-01-09 00:03:19 +01:00
bridgeIPv4Network = networkv4
2015-04-04 00:06:48 -04:00
if config . FixedCIDR != "" {
_ , subnet , err := net . ParseCIDR ( config . FixedCIDR )
2014-05-29 00:06:23 +04:00
if err != nil {
2015-03-25 08:44:12 +01:00
return err
2014-05-29 00:06:23 +04:00
}
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Subnet: %v" , subnet )
2015-03-23 20:20:10 -07:00
if err := ipAllocator . RegisterSubnet ( bridgeIPv4Network , subnet ) ; err != nil {
2015-04-11 10:40:37 -07:00
logrus . Errorf ( "Error registering subnet for IPv4 bridge network: %s" , err )
2015-03-25 08:44:12 +01:00
return err
2015-01-09 00:03:19 +01:00
}
}
2014-12-18 10:09:42 +01:00
if gateway , err := requestDefaultGateway ( config . DefaultGatewayIPv4 , bridgeIPv4Network ) ; err != nil {
return err
} else {
gatewayIPv4 = gateway
}
2015-04-04 00:06:48 -04:00
if config . FixedCIDRv6 != "" {
_ , subnet , err := net . ParseCIDR ( config . FixedCIDRv6 )
2015-01-09 00:03:19 +01:00
if err != nil {
2015-03-25 08:44:12 +01:00
return err
2015-01-09 00:03:19 +01:00
}
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Subnet: %v" , subnet )
2015-03-23 20:20:10 -07:00
if err := ipAllocator . RegisterSubnet ( subnet , subnet ) ; err != nil {
2015-04-12 15:49:29 -07:00
logrus . Errorf ( "Error registering subnet for IPv6 bridge network: %s" , err )
2015-03-25 08:44:12 +01:00
return err
2014-05-29 00:06:23 +04:00
}
2015-01-09 00:03:19 +01:00
globalIPv6Network = subnet
2014-12-18 10:09:42 +01:00
if gateway , err := requestDefaultGateway ( config . DefaultGatewayIPv6 , globalIPv6Network ) ; err != nil {
return err
} else {
gatewayIPv6 = gateway
}
2014-05-29 00:06:23 +04:00
}
2014-01-29 18:34:43 -08:00
2015-01-08 16:21:01 +01:00
// Block BridgeIP in IP allocator
2015-03-23 20:20:10 -07:00
ipAllocator . RequestIP ( bridgeIPv4Network , bridgeIPv4Network . IP )
2015-01-08 16:21:01 +01:00
React to firewalld's reload/restart
When firewalld (or iptables service) restarts/reloads,
all previously added docker firewall rules are flushed.
With firewalld we can react to its Reloaded() [1]
D-Bus signal and recreate the firewall rules.
Also when firewalld gets restarted (stopped & started)
we can catch the NameOwnerChanged signal [2].
To specify which signals we want to react to we use AddMatch [3].
Libvirt has been doing this for quite a long time now.
Docker changes firewall rules on basically 3 places.
1) daemon/networkdriver/portmapper/mapper.go - port mappings
Portmapper fortunatelly keeps list of mapped ports,
so we can easily recreate firewall rules on firewalld restart/reload
New ReMapAll() function does that
2) daemon/networkdriver/bridge/driver.go
When setting a bridge, basic firewall rules are created.
This is done at once during start, it's parametrized and nowhere
tracked so how can one know what and how to set it again when
there's been firewalld restart/reload ?
The only solution that came to my mind is using of closures [4],
i.e. I keep list of references to closures (anonymous functions
together with a referencing environment) and when there's firewalld
restart/reload I re-call them in the same order.
3) links/links.go - linking containers
Link is added in Enable() and removed in Disable().
In Enable() we add a callback function, which creates the link,
that's OK so far.
It'd be ideal if we could remove the same function from
the list in Disable(). Unfortunatelly that's not possible AFAICT,
because we don't know the reference to that function
at that moment, so we can only add a reference to function,
which removes the link. That means that after creating and
removing a link there are 2 functions in the list,
one adding and one removing the link and after
firewalld restart/reload both are called.
It works, but it's far from ideal.
[1] https://jpopelka.fedorapeople.org/firewalld/doc/firewalld.dbus.html#FirewallD1.Signals.Reloaded
[2] http://dbus.freedesktop.org/doc/dbus-specification.html#bus-messages-name-owner-changed
[3] http://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-routing-match-rules
[4] https://en.wikipedia.org/wiki/Closure_%28computer_programming%29
Signed-off-by: Jiri Popelka <jpopelka@redhat.com>
2014-11-26 19:10:35 +01:00
if config . EnableIptables {
iptables . OnReloaded ( portMapper . ReMapAll ) // call this on Firewalld reload
}
2015-03-25 08:44:12 +01:00
return nil
2014-01-29 16:59:21 -08:00
}
2014-09-16 20:00:15 -07:00
func setupIPTables ( addr net . Addr , icc , ipmasq bool ) error {
2014-01-29 16:59:21 -08:00
// Enable NAT
2014-09-16 20:00:15 -07:00
if ipmasq {
2015-02-13 22:10:14 -05:00
natArgs := [ ] string { "-s" , addr . String ( ) , "!" , "-o" , bridgeIface , "-j" , "MASQUERADE" }
2014-09-16 20:00:15 -07:00
2015-02-13 22:10:14 -05:00
if ! iptables . Exists ( iptables . Nat , "POSTROUTING" , natArgs ... ) {
if output , err := iptables . Raw ( append ( [ ] string {
"-t" , string ( iptables . Nat ) , "-I" , "POSTROUTING" } , natArgs ... ) ... ) ; err != nil {
2014-09-16 20:00:15 -07:00
return fmt . Errorf ( "Unable to enable network bridge NAT: %s" , err )
} else if len ( output ) != 0 {
2015-04-16 21:22:32 +02:00
return iptables . ChainError { Chain : "POSTROUTING" , Output : output }
2014-09-16 20:00:15 -07:00
}
2014-01-29 16:59:21 -08:00
}
}
var (
2015-02-13 22:10:14 -05:00
args = [ ] string { "-i" , bridgeIface , "-o" , bridgeIface , "-j" }
2014-01-29 16:59:21 -08:00
acceptArgs = append ( args , "ACCEPT" )
dropArgs = append ( args , "DROP" )
)
if ! icc {
2015-02-13 22:10:14 -05:00
iptables . Raw ( append ( [ ] string { "-D" , "FORWARD" } , acceptArgs ... ) ... )
2014-01-29 16:59:21 -08:00
2015-02-13 22:10:14 -05:00
if ! iptables . Exists ( iptables . Filter , "FORWARD" , dropArgs ... ) {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Disable inter-container communication" )
2015-03-21 01:52:05 +09:00
if output , err := iptables . Raw ( append ( [ ] string { "-A" , "FORWARD" } , dropArgs ... ) ... ) ; err != nil {
2014-01-29 16:59:21 -08:00
return fmt . Errorf ( "Unable to prevent intercontainer communication: %s" , err )
} else if len ( output ) != 0 {
return fmt . Errorf ( "Error disabling intercontainer communication: %s" , output )
}
}
} else {
2015-02-13 22:10:14 -05:00
iptables . Raw ( append ( [ ] string { "-D" , "FORWARD" } , dropArgs ... ) ... )
2014-01-29 16:59:21 -08:00
2015-02-13 22:10:14 -05:00
if ! iptables . Exists ( iptables . Filter , "FORWARD" , acceptArgs ... ) {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Enable inter-container communication" )
2015-03-21 01:52:05 +09:00
if output , err := iptables . Raw ( append ( [ ] string { "-A" , "FORWARD" } , acceptArgs ... ) ... ) ; err != nil {
2014-01-29 16:59:21 -08:00
return fmt . Errorf ( "Unable to allow intercontainer communication: %s" , err )
} else if len ( output ) != 0 {
return fmt . Errorf ( "Error enabling intercontainer communication: %s" , output )
}
}
}
// Accept all non-intercontainer outgoing packets
2015-02-13 22:10:14 -05:00
outgoingArgs := [ ] string { "-i" , bridgeIface , "!" , "-o" , bridgeIface , "-j" , "ACCEPT" }
if ! iptables . Exists ( iptables . Filter , "FORWARD" , outgoingArgs ... ) {
if output , err := iptables . Raw ( append ( [ ] string { "-I" , "FORWARD" } , outgoingArgs ... ) ... ) ; err != nil {
2014-01-29 16:59:21 -08:00
return fmt . Errorf ( "Unable to allow outgoing packets: %s" , err )
} else if len ( output ) != 0 {
2015-04-16 21:22:32 +02:00
return iptables . ChainError { Chain : "FORWARD outgoing" , Output : output }
2014-01-29 16:59:21 -08:00
}
}
// Accept incoming packets for existing connections
2015-02-13 22:10:14 -05:00
existingArgs := [ ] string { "-o" , bridgeIface , "-m" , "conntrack" , "--ctstate" , "RELATED,ESTABLISHED" , "-j" , "ACCEPT" }
2014-01-29 16:59:21 -08:00
2015-02-13 22:10:14 -05:00
if ! iptables . Exists ( iptables . Filter , "FORWARD" , existingArgs ... ) {
if output , err := iptables . Raw ( append ( [ ] string { "-I" , "FORWARD" } , existingArgs ... ) ... ) ; err != nil {
2014-01-29 16:59:21 -08:00
return fmt . Errorf ( "Unable to allow incoming packets: %s" , err )
} else if len ( output ) != 0 {
2015-04-16 21:22:32 +02:00
return iptables . ChainError { Chain : "FORWARD incoming" , Output : output }
2014-01-29 16:59:21 -08:00
}
}
return nil
}
2015-03-30 18:06:16 -07:00
func RequestPort ( ip net . IP , proto string , port int ) ( int , error ) {
2015-03-31 09:34:03 -07:00
initPortMapper ( )
2015-03-30 18:06:16 -07:00
return portMapper . Allocator . RequestPort ( ip , proto , port )
}
2014-11-12 16:55:34 -05:00
// configureBridge attempts to create and configure a network bridge interface named `bridgeIface` on the host
2014-10-05 00:21:59 -04:00
// If bridgeIP is empty, it will try to find a non-conflicting IP from the Docker-specified private ranges
2014-11-12 16:55:34 -05:00
// If the bridge `bridgeIface` already exists, it will only perform the IP address association with the existing
2014-10-05 00:21:59 -04:00
// bridge (fixes issue #8444)
// If an address which doesn't conflict with existing interfaces can't be found, an error is returned.
2015-01-09 00:03:19 +01:00
func configureBridge ( bridgeIP string , bridgeIPv6 string , enableIPv6 bool ) error {
2014-01-29 16:59:21 -08:00
nameservers := [ ] string { }
2014-05-05 22:51:32 +00:00
resolvConf , _ := resolvconf . Get ( )
2015-03-03 23:39:04 +08:00
// We don't check for an error here, because we don't really care
2014-01-29 16:59:21 -08:00
// 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.
if resolvConf != nil {
2014-05-05 22:51:32 +00:00
nameservers = append ( nameservers , resolvconf . GetNameserversAsCIDR ( resolvConf ) ... )
2014-01-29 16:59:21 -08:00
}
var ifaceAddr string
if len ( bridgeIP ) != 0 {
_ , _ , err := net . ParseCIDR ( bridgeIP )
if err != nil {
return err
}
ifaceAddr = bridgeIP
} else {
for _ , addr := range addrs {
_ , dockerNetwork , err := net . ParseCIDR ( addr )
if err != nil {
return err
}
if err := networkdriver . CheckNameserverOverlaps ( nameservers , dockerNetwork ) ; err == nil {
if err := networkdriver . CheckRouteOverlaps ( dockerNetwork ) ; err == nil {
ifaceAddr = addr
break
} else {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "%s %s" , addr , err )
2014-01-29 16:59:21 -08:00
}
}
}
}
if ifaceAddr == "" {
return fmt . Errorf ( "Could not find a free IP address range for interface '%s'. Please configure its address manually and run 'docker -b %s'" , bridgeIface , bridgeIface )
}
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Creating bridge %s with network %s" , bridgeIface , ifaceAddr )
2014-01-29 16:59:21 -08:00
if err := createBridgeIface ( bridgeIface ) ; err != nil {
2015-03-03 23:39:04 +08:00
// The bridge may already exist, therefore we can ignore an "exists" error
2014-10-05 00:21:59 -04:00
if ! os . IsExist ( err ) {
return err
}
2014-01-29 16:59:21 -08:00
}
iface , err := net . InterfaceByName ( bridgeIface )
if err != nil {
return err
}
ipAddr , ipNet , err := net . ParseCIDR ( ifaceAddr )
if err != nil {
return err
}
2015-01-23 14:32:36 -08:00
if err := netlink . NetworkLinkAddIp ( iface , ipAddr , ipNet ) ; err != nil {
2014-01-29 16:59:21 -08:00
return fmt . Errorf ( "Unable to add private network: %s" , err )
}
2015-01-09 00:03:19 +01:00
if enableIPv6 {
2015-01-27 22:03:27 -05:00
if err := setupIPv6Bridge ( bridgeIPv6 ) ; err != nil {
2015-01-09 00:03:19 +01:00
return err
}
}
2014-01-29 16:59:21 -08:00
if err := netlink . NetworkLinkUp ( iface ) ; err != nil {
return fmt . Errorf ( "Unable to start network bridge: %s" , err )
}
return nil
}
2015-01-27 22:03:27 -05:00
func setupIPv6Bridge ( bridgeIPv6 string ) error {
iface , err := net . InterfaceByName ( bridgeIface )
if err != nil {
return err
}
// Enable IPv6 on the bridge
procFile := "/proc/sys/net/ipv6/conf/" + iface . Name + "/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 )
}
ipAddr6 , ipNet6 , err := net . ParseCIDR ( bridgeIPv6 )
if err != nil {
return fmt . Errorf ( "Unable to parse bridge IPv6 address: %q, error: %v" , bridgeIPv6 , err )
}
if err := netlink . NetworkLinkAddIp ( iface , ipAddr6 , ipNet6 ) ; err != nil {
return fmt . Errorf ( "Unable to add private IPv6 network: %v" , err )
}
return nil
}
2014-12-18 10:09:42 +01:00
func requestDefaultGateway ( requestedGateway string , network * net . IPNet ) ( gateway net . IP , err error ) {
if requestedGateway != "" {
gateway = net . ParseIP ( requestedGateway )
if gateway == nil {
return nil , fmt . Errorf ( "Bad parameter: invalid gateway ip %s" , requestedGateway )
}
if ! network . Contains ( gateway ) {
return nil , fmt . Errorf ( "Gateway ip %s must be part of the network %s" , requestedGateway , network . String ( ) )
}
ipAllocator . RequestIP ( network , gateway )
}
return gateway , nil
}
2014-01-29 16:59:21 -08:00
func createBridgeIface ( name string ) error {
2014-07-28 17:23:38 -07:00
kv , err := kernel . GetKernelVersion ( )
2015-03-03 23:39:04 +08:00
// Only set the bridge's mac address if the kernel version is > 3.3
2014-03-31 21:02:42 +00:00
// before that it was not supported
setBridgeMacAddr := err == nil && ( kv . Kernel >= 3 && kv . Major >= 3 )
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "setting bridge mac address = %v" , setBridgeMacAddr )
2014-03-31 21:02:42 +00:00
return netlink . CreateBridge ( name , setBridgeMacAddr )
2014-01-29 16:59:21 -08:00
}
2014-10-02 16:46:06 -07:00
// Generate a IEEE802 compliant MAC address from the given IP address.
//
// The generator is guaranteed to be consistent: the same IP will always yield the same
// MAC address. This is to avoid ARP cache issues.
func generateMacAddr ( ip net . IP ) net . HardwareAddr {
hw := make ( net . HardwareAddr , 6 )
// The first byte of the MAC address has to comply with these rules:
// 1. Unicast: Set the least-significant bit to 0.
// 2. Address is locally administered: Set the second-least-significant bit (U/L) to 1.
// 3. As "small" as possible: The veth address has to be "smaller" than the bridge address.
hw [ 0 ] = 0x02
// The first 24 bits of the MAC represent the Organizationally Unique Identifier (OUI).
// Since this address is locally administered, we can do whatever we want as long as
// it doesn't conflict with other addresses.
hw [ 1 ] = 0x42
// Insert the IP address into the last 32 bits of the MAC address.
// This is a simple way to guarantee the address will be consistent and unique.
copy ( hw [ 2 : ] , ip . To4 ( ) )
return hw
}
2015-01-09 00:03:19 +01:00
func linkLocalIPv6FromMac ( mac string ) ( string , error ) {
hx := strings . Replace ( mac , ":" , "" , - 1 )
hw , err := hex . DecodeString ( hx )
if err != nil {
return "" , errors . New ( "Could not parse MAC address " + mac )
}
hw [ 0 ] ^ = 0x2
return fmt . Sprintf ( "fe80::%x%x:%xff:fe%x:%x%x/64" , hw [ 0 ] , hw [ 1 ] , hw [ 2 ] , hw [ 3 ] , hw [ 4 ] , hw [ 5 ] ) , nil
}
2014-01-29 16:59:21 -08:00
// Allocate a network interface
2015-04-04 00:06:48 -04:00
func Allocate ( id , requestedMac , requestedIP , requestedIPv6 string ) ( * network . Settings , error ) {
2014-01-30 12:02:56 -08:00
var (
2014-12-18 10:09:42 +01:00
ip net . IP
mac net . HardwareAddr
err error
globalIPv6 net . IP
defaultGWIPv4 net . IP
defaultGWIPv6 net . IP
2014-01-30 12:02:56 -08:00
)
2014-01-29 16:59:21 -08:00
2015-04-04 00:06:48 -04:00
ip , err = ipAllocator . RequestIP ( bridgeIPv4Network , net . ParseIP ( requestedIP ) )
2014-01-29 16:59:21 -08:00
if err != nil {
2015-04-04 00:06:48 -04:00
return nil , err
2014-01-29 16:59:21 -08:00
}
2014-10-02 16:46:06 -07:00
// If no explicit mac address was given, generate a random one.
2015-04-04 00:06:48 -04:00
if mac , err = net . ParseMAC ( requestedMac ) ; err != nil {
2014-10-02 16:46:06 -07:00
mac = generateMacAddr ( ip )
}
2015-01-09 00:03:19 +01:00
if globalIPv6Network != nil {
2015-03-03 23:39:04 +08:00
// If globalIPv6Network Size is at least a /80 subnet generate IPv6 address from MAC address
2015-03-25 19:40:23 -06:00
netmaskOnes , _ := globalIPv6Network . Mask . Size ( )
2015-04-04 00:06:48 -04:00
ipv6 := net . ParseIP ( requestedIPv6 )
if ipv6 == nil && netmaskOnes <= 80 {
ipv6 = make ( net . IP , len ( globalIPv6Network . IP ) )
copy ( ipv6 , globalIPv6Network . IP )
2015-01-09 00:03:19 +01:00
for i , h := range mac {
2015-04-04 00:06:48 -04:00
ipv6 [ i + 10 ] = h
2015-01-09 00:03:19 +01:00
}
}
2015-04-04 00:06:48 -04:00
globalIPv6 , err = ipAllocator . RequestIP ( globalIPv6Network , ipv6 )
2015-01-09 00:03:19 +01:00
if err != nil {
2015-03-26 23:22:04 +01:00
logrus . Errorf ( "Allocator: RequestIP v6: %v" , err )
2015-04-04 00:06:48 -04:00
return nil , err
2015-01-09 00:03:19 +01:00
}
2015-03-26 23:22:04 +01:00
logrus . Infof ( "Allocated IPv6 %s" , globalIPv6 )
2015-01-09 00:03:19 +01:00
}
2015-04-04 00:06:48 -04:00
maskSize , _ := bridgeIPv4Network . Mask . Size ( )
2014-01-29 16:59:21 -08:00
2014-12-18 10:09:42 +01:00
if gatewayIPv4 != nil {
defaultGWIPv4 = gatewayIPv4
} else {
defaultGWIPv4 = bridgeIPv4Network . IP
}
if gatewayIPv6 != nil {
defaultGWIPv6 = gatewayIPv6
} else {
defaultGWIPv6 = bridgeIPv6Addr
}
2015-03-03 23:39:04 +08:00
// If linklocal IPv6
2015-01-09 00:03:19 +01:00
localIPv6Net , err := linkLocalIPv6FromMac ( mac . String ( ) )
if err != nil {
2015-04-04 00:06:48 -04:00
return nil , err
2015-01-09 00:03:19 +01:00
}
localIPv6 , _ , _ := net . ParseCIDR ( localIPv6Net )
2015-04-04 00:06:48 -04:00
networkSettings := & network . Settings {
IPAddress : ip . String ( ) ,
2014-12-18 10:09:42 +01:00
Gateway : defaultGWIPv4 . String ( ) ,
2015-04-04 00:06:48 -04:00
MacAddress : mac . String ( ) ,
Bridge : bridgeIface ,
IPPrefixLen : maskSize ,
LinkLocalIPv6Address : localIPv6 . String ( ) ,
}
2015-01-09 00:03:19 +01:00
if globalIPv6Network != nil {
2015-04-04 00:06:48 -04:00
networkSettings . GlobalIPv6Address = globalIPv6 . String ( )
maskV6Size , _ := globalIPv6Network . Mask . Size ( )
networkSettings . GlobalIPv6PrefixLen = maskV6Size
2014-12-18 10:09:42 +01:00
networkSettings . IPv6Gateway = defaultGWIPv6 . String ( )
2015-01-09 00:03:19 +01:00
}
2014-05-29 16:28:06 +04:00
currentInterfaces . Set ( id , & networkInterface {
2015-01-09 00:03:19 +01:00
IP : ip ,
IPv6 : globalIPv6 ,
2014-05-29 16:28:06 +04:00
} )
2014-01-29 16:59:21 -08:00
2015-04-04 00:06:48 -04:00
return networkSettings , nil
2014-01-29 16:59:21 -08:00
}
2015-03-03 23:39:04 +08:00
// Release an interface for a select ip
2015-04-04 00:06:48 -04:00
func Release ( id string ) {
var containerInterface = currentInterfaces . Get ( id )
2014-01-29 16:59:21 -08:00
2014-02-06 03:18:12 -08:00
if containerInterface == nil {
2015-04-04 00:06:48 -04:00
logrus . Warnf ( "No network information to release for %s" , id )
2015-04-13 20:24:10 +08:00
return
2014-02-06 03:18:12 -08:00
}
2014-01-29 16:59:21 -08:00
for _ , nat := range containerInterface . PortMappings {
2015-03-30 18:06:16 -07:00
if err := portMapper . Unmap ( nat ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Infof ( "Unable to unmap port %s: %s" , nat , err )
2014-01-29 16:59:21 -08:00
}
}
2015-03-23 20:20:10 -07:00
if err := ipAllocator . ReleaseIP ( bridgeIPv4Network , containerInterface . IP ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Infof ( "Unable to release IPv4 %s" , err )
2015-01-09 00:03:19 +01:00
}
if globalIPv6Network != nil {
2015-03-23 20:20:10 -07:00
if err := ipAllocator . ReleaseIP ( globalIPv6Network , containerInterface . IPv6 ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Infof ( "Unable to release IPv6 %s" , err )
2015-01-09 00:03:19 +01:00
}
2014-01-29 16:59:21 -08:00
}
}
// Allocate an external port and map it to the interface
2015-04-04 00:06:48 -04:00
func AllocatePort ( id string , port nat . Port , binding nat . PortBinding ) ( nat . PortBinding , error ) {
2014-01-29 16:59:21 -08:00
var (
ip = defaultBindingIP
2015-04-04 00:06:48 -04:00
proto = port . Proto ( )
containerPort = port . Int ( )
2014-05-29 16:28:06 +04:00
network = currentInterfaces . Get ( id )
2014-01-29 16:59:21 -08:00
)
2015-04-04 00:06:48 -04:00
if binding . HostIp != "" {
ip = net . ParseIP ( binding . HostIp )
2014-09-19 21:31:57 +09:00
if ip == nil {
2015-04-04 00:06:48 -04:00
return nat . PortBinding { } , fmt . Errorf ( "Bad parameter: invalid host ip %s" , binding . HostIp )
2014-09-19 21:31:57 +09:00
}
2014-01-29 16:59:21 -08:00
}
2014-06-19 23:33:51 +02:00
// host ip, proto, and host port
var container net . Addr
switch proto {
case "tcp" :
container = & net . TCPAddr { IP : network . IP , Port : containerPort }
case "udp" :
container = & net . UDPAddr { IP : network . IP , Port : containerPort }
default :
2015-04-04 00:06:48 -04:00
return nat . PortBinding { } , fmt . Errorf ( "unsupported address type %s" , proto )
2014-06-19 23:33:51 +02:00
}
2014-01-29 16:59:21 -08:00
2014-06-26 00:09:19 -07:00
//
// Try up to 10 times to get a port that's not already allocated.
//
// In the event of failure to bind, return the error that portmapper.Map
// yields.
//
2014-05-20 21:19:55 -07:00
2015-04-04 00:06:48 -04:00
var (
host net . Addr
err error
)
hostPort , err := nat . ParsePort ( binding . HostPort )
if err != nil {
return nat . PortBinding { } , err
}
2014-06-26 00:09:19 -07:00
for i := 0 ; i < MaxAllocatedPortAttempts ; i ++ {
2015-03-30 18:06:16 -07:00
if host , err = portMapper . Map ( container , ip , hostPort ) ; err == nil {
2014-06-19 23:33:51 +02:00
break
2014-05-20 21:19:55 -07:00
}
2014-12-01 16:24:43 -08:00
// There is no point in immediately retrying to map an explicitly
// chosen port.
if hostPort != 0 {
2015-03-26 23:22:04 +01:00
logrus . Warnf ( "Failed to allocate and map port %d: %s" , hostPort , err )
2014-05-20 21:19:55 -07:00
break
}
2015-03-26 23:22:04 +01:00
logrus . Warnf ( "Failed to allocate and map port: %s, retry: %d" , err , i + 1 )
2014-01-29 16:59:21 -08:00
}
2014-05-20 21:19:55 -07:00
if err != nil {
2015-04-04 00:06:48 -04:00
return nat . PortBinding { } , err
2014-01-29 16:59:21 -08:00
}
2014-05-20 21:19:55 -07:00
2014-01-29 16:59:21 -08:00
network . PortMappings = append ( network . PortMappings , host )
2014-06-19 23:33:51 +02:00
switch netAddr := host . ( type ) {
case * net . TCPAddr :
2015-04-04 00:06:48 -04:00
return nat . PortBinding { HostIp : netAddr . IP . String ( ) , HostPort : strconv . Itoa ( netAddr . Port ) } , nil
2014-06-19 23:33:51 +02:00
case * net . UDPAddr :
2015-04-04 00:06:48 -04:00
return nat . PortBinding { HostIp : netAddr . IP . String ( ) , HostPort : strconv . Itoa ( netAddr . Port ) } , nil
default :
return nat . PortBinding { } , fmt . Errorf ( "unsupported address type %T" , netAddr )
2014-01-30 14:52:59 -08:00
}
2014-01-29 16:59:21 -08:00
}
2014-01-30 12:43:49 -08:00
2015-04-04 00:06:48 -04:00
//TODO: should it return something more than just an error?
func LinkContainers ( action , parentIP , childIP string , ports [ ] nat . Port , ignoreErrors bool ) error {
var nfAction iptables . Action
2014-06-27 17:29:55 +10:00
switch action {
case "-A" :
nfAction = iptables . Append
case "-I" :
nfAction = iptables . Insert
case "-D" :
nfAction = iptables . Delete
default :
2015-03-25 08:44:12 +01:00
return fmt . Errorf ( "Invalid action '%s' specified" , action )
2014-06-27 17:29:55 +10:00
}
2014-02-09 01:43:46 -08:00
2014-06-27 17:29:55 +10:00
ip1 := net . ParseIP ( parentIP )
if ip1 == nil {
2015-03-25 08:44:12 +01:00
return fmt . Errorf ( "Parent IP '%s' is invalid" , parentIP )
2014-06-27 17:29:55 +10:00
}
ip2 := net . ParseIP ( childIP )
if ip2 == nil {
2015-03-25 08:44:12 +01:00
return fmt . Errorf ( "Child IP '%s' is invalid" , childIP )
2014-06-27 17:29:55 +10:00
}
2014-12-21 13:42:02 +10:00
chain := iptables . Chain { Name : "DOCKER" , Bridge : bridgeIface }
2015-04-04 00:06:48 -04:00
for _ , port := range ports {
2014-11-15 11:36:38 +10:00
if err := chain . Link ( nfAction , ip1 , ip2 , port . Int ( ) , port . Proto ( ) ) ; ! ignoreErrors && err != nil {
2015-03-25 08:44:12 +01:00
return err
2014-02-09 01:43:46 -08:00
}
2014-01-30 12:43:49 -08:00
}
2015-03-25 08:44:12 +01:00
return nil
2014-01-30 12:43:49 -08:00
}