dotcloud/docker

View on GitHub
libnetwork/osl/route_linux.go

Summary

Maintainability
A
3 hrs
Test Coverage
package osl

import (
    "fmt"
    "net"

    "github.com/docker/docker/libnetwork/types"
    "github.com/vishvananda/netlink"
)

// Gateway returns the IPv4 gateway for the sandbox.
func (n *Namespace) Gateway() net.IP {
    n.mu.Lock()
    defer n.mu.Unlock()

    return n.gw
}

// GatewayIPv6 returns the IPv6 gateway for the sandbox.
func (n *Namespace) GatewayIPv6() net.IP {
    n.mu.Lock()
    defer n.mu.Unlock()

    return n.gwv6
}

// StaticRoutes returns additional static routes for the sandbox. Note that
// directly connected routes are stored on the particular interface they
// refer to.
func (n *Namespace) StaticRoutes() []*types.StaticRoute {
    n.mu.Lock()
    defer n.mu.Unlock()

    routes := make([]*types.StaticRoute, len(n.staticRoutes))
    for i, route := range n.staticRoutes {
        r := route.GetCopy()
        routes[i] = r
    }

    return routes
}

// SetGateway sets the default IPv4 gateway for the sandbox. It is a no-op
// if the given gateway is empty.
func (n *Namespace) SetGateway(gw net.IP) error {
    if len(gw) == 0 {
        return nil
    }

    if err := n.programGateway(gw, true); err != nil {
        return err
    }
    n.mu.Lock()
    n.gw = gw
    n.mu.Unlock()
    return nil
}

// UnsetGateway the previously set default IPv4 gateway in the sandbox.
// It is a no-op if no gateway was set.
func (n *Namespace) UnsetGateway() error {
    gw := n.Gateway()
    if len(gw) == 0 {
        return nil
    }

    if err := n.programGateway(gw, false); err != nil {
        return err
    }
    n.mu.Lock()
    n.gw = net.IP{}
    n.mu.Unlock()
    return nil
}

func (n *Namespace) programGateway(gw net.IP, isAdd bool) error {
    gwRoutes, err := n.nlHandle.RouteGet(gw)
    if err != nil {
        return fmt.Errorf("route for the gateway %s could not be found: %v", gw, err)
    }

    var linkIndex int
    for _, gwRoute := range gwRoutes {
        if gwRoute.Gw == nil {
            linkIndex = gwRoute.LinkIndex
            break
        }
    }

    if linkIndex == 0 {
        return fmt.Errorf("direct route for the gateway %s could not be found", gw)
    }

    if isAdd {
        return n.nlHandle.RouteAdd(&netlink.Route{
            Scope:     netlink.SCOPE_UNIVERSE,
            LinkIndex: linkIndex,
            Gw:        gw,
        })
    }

    return n.nlHandle.RouteDel(&netlink.Route{
        Scope:     netlink.SCOPE_UNIVERSE,
        LinkIndex: linkIndex,
        Gw:        gw,
    })
}

// Program a route in to the namespace routing table.
func (n *Namespace) programRoute(dest *net.IPNet, nh net.IP) error {
    gwRoutes, err := n.nlHandle.RouteGet(nh)
    if err != nil {
        return fmt.Errorf("route for the next hop %s could not be found: %v", nh, err)
    }

    return n.nlHandle.RouteAdd(&netlink.Route{
        Scope:     netlink.SCOPE_UNIVERSE,
        LinkIndex: gwRoutes[0].LinkIndex,
        Gw:        nh,
        Dst:       dest,
    })
}

// Delete a route from the namespace routing table.
func (n *Namespace) removeRoute(dest *net.IPNet, nh net.IP) error {
    gwRoutes, err := n.nlHandle.RouteGet(nh)
    if err != nil {
        return fmt.Errorf("route for the next hop could not be found: %v", err)
    }

    return n.nlHandle.RouteDel(&netlink.Route{
        Scope:     netlink.SCOPE_UNIVERSE,
        LinkIndex: gwRoutes[0].LinkIndex,
        Gw:        nh,
        Dst:       dest,
    })
}

// SetGatewayIPv6 sets the default IPv6 gateway for the sandbox. It is a no-op
// if the given gateway is empty.
func (n *Namespace) SetGatewayIPv6(gwv6 net.IP) error {
    if len(gwv6) == 0 {
        return nil
    }

    if err := n.programGateway(gwv6, true); err != nil {
        return err
    }

    n.mu.Lock()
    n.gwv6 = gwv6
    n.mu.Unlock()
    return nil
}

// UnsetGatewayIPv6 unsets the previously set default IPv6 gateway in the sandbox.
// It is a no-op if no gateway was set.
func (n *Namespace) UnsetGatewayIPv6() error {
    gwv6 := n.GatewayIPv6()
    if len(gwv6) == 0 {
        return nil
    }

    if err := n.programGateway(gwv6, false); err != nil {
        return err
    }

    n.mu.Lock()
    n.gwv6 = net.IP{}
    n.mu.Unlock()
    return nil
}

// AddStaticRoute adds a static route to the sandbox.
func (n *Namespace) AddStaticRoute(r *types.StaticRoute) error {
    if err := n.programRoute(r.Destination, r.NextHop); err != nil {
        return err
    }

    n.mu.Lock()
    n.staticRoutes = append(n.staticRoutes, r)
    n.mu.Unlock()
    return nil
}

// RemoveStaticRoute removes a static route from the sandbox.
func (n *Namespace) RemoveStaticRoute(r *types.StaticRoute) error {
    if err := n.removeRoute(r.Destination, r.NextHop); err != nil {
        return err
    }

    n.mu.Lock()
    lastIndex := len(n.staticRoutes) - 1
    for i, v := range n.staticRoutes {
        if v == r {
            // Overwrite the route we're removing with the last element
            n.staticRoutes[i] = n.staticRoutes[lastIndex]
            // Shorten the slice to trim the extra element
            n.staticRoutes = n.staticRoutes[:lastIndex]
            break
        }
    }
    n.mu.Unlock()
    return nil
}