From 2cfb5ab1606d1b24b5b914c7776bceb156dd8da4 Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Wed, 4 Mar 2020 12:51:54 +0100 Subject: [PATCH] Implement IP 4 to 6 mapping on HostIP listen addresses In some cases, the docker container can specify a HostIP listen address to listen to. It might be desirable to allow converting containers listening on IPv4 only addresses to IPv6 by using a network mapping. The IPv4 address is mapped to IPv6 by matching it to an IPv4 CIDR provided via a command-line flag. Multiple mapping is possible. --- cmd/docker-ipv6nat/main.go | 30 +++++++++++++++++++++++++++++- state.go | 15 ++++++++++++++- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/cmd/docker-ipv6nat/main.go b/cmd/docker-ipv6nat/main.go index 54040e6..c3a73f3 100644 --- a/cmd/docker-ipv6nat/main.go +++ b/cmd/docker-ipv6nat/main.go @@ -5,6 +5,8 @@ import ( "fmt" "log" "os" + "strings" + "net" "github.com/fsouza/go-dockerclient" "github.com/robbertkl/docker-ipv6nat" @@ -18,6 +20,7 @@ var ( userlandProxy bool version bool debug bool + mapIpv4 string ) func usage() { @@ -43,6 +46,7 @@ func initFlags() { flag.BoolVar(&retry, "retry", false, "keep retrying to reconnect after a disconnect") flag.BoolVar(&version, "version", false, "show version") flag.BoolVar(&debug, "debug", false, "log ruleset changes to stdout") + flag.StringVar(&mapIpv4, "map-ipv4", "", "IPv4 listen address mapping (IPV4/CIDR=IPV4,...)") flag.Usage = usage flag.Parse() @@ -66,6 +70,25 @@ func main() { } } +func parseIpMapping(mapIpv4 string) (map[*net.IPNet]net.IP, error) { + res := map[*net.IPNet]net.IP{} + for _, map46 := range strings.Split(mapIpv4, ",") { + s := strings.Split(map46, "=") + if len(s)==2 && s[0] != "" && s[1] != "" { + _, ip4, err := net.ParseCIDR(s[0]) + if err != nil { + return nil, fmt.Errorf("Cannot parse %+v IPv4, %e", map46, err) + } + ip6 := net.ParseIP(s[1]) + if ip6 == nil { + return nil, fmt.Errorf("Cannot parse %+v IPv6, %e", map46) + } + res[ip4] = ip6 + } + } + return res, nil +} + func run() error { if debug { log.Println("docker-ipv6nat is running in debug mode") @@ -76,7 +99,12 @@ func run() error { return err } - state, err := dockeripv6nat.NewState(debug) + ipMap, err := parseIpMapping(mapIpv4) + if err != nil { + return err + } + + state, err := dockeripv6nat.NewState(debug, ipMap) if err != nil { return err } diff --git a/state.go b/state.go index 5ef7287..112805e 100644 --- a/state.go +++ b/state.go @@ -13,6 +13,8 @@ type State struct { manager *Manager networks map[string]*managedNetwork containers map[string]*managedContainer + mapIpv4 map[*net.IPNet]net.IP + debug bool } // fc00::/7, Unique Local IPv6 Unicast Addresses, see RFC 4193 @@ -22,7 +24,7 @@ var ulaCIDR = net.IPNet{ } // NewState constructs a new state -func NewState(debug bool) (*State, error) { +func NewState(debug bool, mapIpv4 map[*net.IPNet]net.IP) (*State, error) { manager, err := NewManager(debug) if err != nil { return nil, err @@ -32,6 +34,7 @@ func NewState(debug bool) (*State, error) { manager: manager, networks: make(map[string]*managedNetwork), containers: make(map[string]*managedContainer), + mapIpv4: mapIpv4, }, nil } @@ -236,6 +239,16 @@ func (s *State) parseContainer(container *docker.Container) *managedContainer { if binding.HostIP != "" && binding.HostIP != "0.0.0.0" { ip := net.ParseIP(binding.HostIP) + if ip != nil && ip.To4() != nil { + // Try map Ipv4 to IPv6 + for ip4net, ip6 := range s.mapIpv4 { + if ip4net.Contains(ip) { + log.Printf("Converting listen IP %v to %v (matching %v)", ip, ip6, ip4net) + ip = ip6 + break + } + } + } if ip == nil || ip.To4() != nil { // Skip bindings to IPv4. continue