114 lines
3.7 KiB
Go
114 lines
3.7 KiB
Go
package net
|
|
|
|
import (
|
|
"net"
|
|
"syscall"
|
|
|
|
"github.com/coreos/go-iptables/iptables"
|
|
"github.com/j-keck/arping"
|
|
"github.com/pkg/errors"
|
|
"github.com/vishvananda/netlink"
|
|
)
|
|
|
|
// Expose makes the network accessible from a host by assigning a given IP address
|
|
// to the weave bridge.
|
|
//
|
|
// List of params:
|
|
// * "bridgeName" - a name of the weave bridge.
|
|
// * "ipAddr" - IP addr to be assigned to the bridge.
|
|
// * "removeDefaultRoute" - whether to remove a default route installed by the kernel (used only in the AWSVPC mode).
|
|
// * "npc" - whether is Weave NPC running.
|
|
// * "skipNAT" - whether to skip adding iptables NAT rules
|
|
func Expose(bridgeName string, ipAddr *net.IPNet, removeDefaultRoute, npc bool, skipNAT bool) error {
|
|
ipt, err := iptables.New()
|
|
if err != nil {
|
|
return errors.Wrap(err, "iptables.New")
|
|
}
|
|
cidr := ipAddr.String()
|
|
|
|
if err := addBridgeIPAddr(bridgeName, ipAddr, removeDefaultRoute); err != nil {
|
|
return errors.Wrap(err, "addBridgeIPAddr")
|
|
}
|
|
|
|
if !skipNAT {
|
|
if err := exposeNAT(ipt, cidr); err != nil {
|
|
return errors.Wrap(err, "exposeNAT")
|
|
}
|
|
}
|
|
|
|
if !npc {
|
|
// Docker 1.13 has changed a default policy of FORWARD chain to DROP
|
|
// (https://github.com/moby/moby/pull/28257) which makes containers
|
|
// inaccessible from a remote host when the bridge is exposed.
|
|
//
|
|
// The change breaks e.g. the AWSVPC mode. To overcome this we install
|
|
// an explicit rule for accepting forwarded ingress traffic to an
|
|
// exposed subnet.
|
|
if err := ipt.AppendUnique("filter", "WEAVE-EXPOSE", "-d", cidr, "-j", "ACCEPT"); err != nil {
|
|
return errors.Wrap(err, "ipt.AppendUnique")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func addBridgeIPAddr(bridgeName string, addr *net.IPNet, removeDefaultRoute bool) error {
|
|
link, err := netlink.LinkByName(bridgeName)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "addBridgeIPAddr finding bridge %q", bridgeName)
|
|
}
|
|
err = netlink.AddrAdd(link, &netlink.Addr{IPNet: addr})
|
|
// The IP addr might have been already set by a concurrent request, just ignore then
|
|
if err != nil && err != syscall.Errno(syscall.EEXIST) {
|
|
return errors.Wrapf(err, "adding address %v to %q", addr, bridgeName)
|
|
}
|
|
|
|
// Sending multiple ARP REQUESTs in the case of EEXIST above does not hurt
|
|
arping.GratuitousArpOverIfaceByName(addr.IP, bridgeName)
|
|
|
|
// Remove a default route installed by the kernel. Required by the AWSVPC mode.
|
|
if removeDefaultRoute {
|
|
routeFilter := &netlink.Route{
|
|
LinkIndex: link.Attrs().Index,
|
|
Dst: &net.IPNet{IP: addr.IP.Mask(addr.Mask), Mask: addr.Mask},
|
|
Protocol: 2, // RTPROT_KERNEL
|
|
}
|
|
filterMask := netlink.RT_FILTER_OIF | netlink.RT_FILTER_DST | netlink.RT_FILTER_PROTOCOL
|
|
routes, err := netlink.RouteListFiltered(netlink.FAMILY_V4, routeFilter, filterMask)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "failed to get route list for bridge %q", bridgeName)
|
|
}
|
|
for _, r := range routes {
|
|
err = netlink.RouteDel(&r)
|
|
// Again, there might be a concurrent request for removing routes
|
|
if err != nil && err != syscall.Errno(syscall.ESRCH) {
|
|
return errors.Wrapf(err, "failed to delete default route %+v", r)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func exposeNAT(ipt *iptables.IPTables, cidr string) error {
|
|
if err := addNatRule(ipt, "-s", cidr, "-d", "224.0.0.0/4", "-j", "RETURN"); err != nil {
|
|
return err
|
|
}
|
|
if err := addNatRule(ipt, "-d", cidr, "!", "-s", cidr, "-j", "MASQUERADE"); err != nil {
|
|
return err
|
|
}
|
|
return addNatRule(ipt, "-s", cidr, "!", "-d", cidr, "-j", "MASQUERADE")
|
|
}
|
|
|
|
func addNatRule(ipt *iptables.IPTables, rulespec ...string) error {
|
|
// Loop until we get an exit code other than "temporarily unavailable"
|
|
for {
|
|
if err := ipt.AppendUnique("nat", "WEAVE", rulespec...); err != nil {
|
|
if isResourceError(err) {
|
|
continue
|
|
}
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
}
|