summaryrefslogtreecommitdiff
path: root/vendor/github.com/containernetworking/plugins
diff options
context:
space:
mode:
authorbaude <bbaude@redhat.com>2019-08-08 06:01:00 -0500
committerbaude <bbaude@redhat.com>2019-09-09 09:32:43 -0500
commitee432cf2792c5dbe81953007f1fd5c87beb3ebd5 (patch)
treedc0646e4b2faeadf9cca58bf80f1e90d98c50165 /vendor/github.com/containernetworking/plugins
parent30cbb0091515a7f802f0f3f3ee486be6ff98f645 (diff)
downloadpodman-ee432cf2792c5dbe81953007f1fd5c87beb3ebd5.tar.gz
podman-ee432cf2792c5dbe81953007f1fd5c87beb3ebd5.tar.bz2
podman-ee432cf2792c5dbe81953007f1fd5c87beb3ebd5.zip
podman network create
initial implementation of network create. we only support bridging networks with this first pass. Signed-off-by: baude <bbaude@redhat.com>
Diffstat (limited to 'vendor/github.com/containernetworking/plugins')
-rw-r--r--vendor/github.com/containernetworking/plugins/pkg/ip/addr_linux.go68
-rw-r--r--vendor/github.com/containernetworking/plugins/pkg/ip/cidr.go61
-rw-r--r--vendor/github.com/containernetworking/plugins/pkg/ip/ipforward_linux.go61
-rw-r--r--vendor/github.com/containernetworking/plugins/pkg/ip/ipmasq_linux.go126
-rw-r--r--vendor/github.com/containernetworking/plugins/pkg/ip/link_linux.go275
-rw-r--r--vendor/github.com/containernetworking/plugins/pkg/ip/route_linux.go47
-rw-r--r--vendor/github.com/containernetworking/plugins/pkg/ip/utils_linux.go120
-rw-r--r--vendor/github.com/containernetworking/plugins/pkg/utils/hwaddr/hwaddr.go63
-rw-r--r--vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/allocator.go217
-rw-r--r--vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/config.go160
-rw-r--r--vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/range.go166
-rw-r--r--vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/range_set.go97
-rw-r--r--vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/store.go27
13 files changed, 1488 insertions, 0 deletions
diff --git a/vendor/github.com/containernetworking/plugins/pkg/ip/addr_linux.go b/vendor/github.com/containernetworking/plugins/pkg/ip/addr_linux.go
new file mode 100644
index 000000000..b4db50b9a
--- /dev/null
+++ b/vendor/github.com/containernetworking/plugins/pkg/ip/addr_linux.go
@@ -0,0 +1,68 @@
+// Copyright 2017 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package ip
+
+import (
+ "fmt"
+ "syscall"
+ "time"
+
+ "github.com/vishvananda/netlink"
+)
+
+const SETTLE_INTERVAL = 50 * time.Millisecond
+
+// SettleAddresses waits for all addresses on a link to leave tentative state.
+// This is particularly useful for ipv6, where all addresses need to do DAD.
+// There is no easy way to wait for this as an event, so just loop until the
+// addresses are no longer tentative.
+// If any addresses are still tentative after timeout seconds, then error.
+func SettleAddresses(ifName string, timeout int) error {
+ link, err := netlink.LinkByName(ifName)
+ if err != nil {
+ return fmt.Errorf("failed to retrieve link: %v", err)
+ }
+
+ deadline := time.Now().Add(time.Duration(timeout) * time.Second)
+ for {
+ addrs, err := netlink.AddrList(link, netlink.FAMILY_ALL)
+ if err != nil {
+ return fmt.Errorf("could not list addresses: %v", err)
+ }
+
+ if len(addrs) == 0 {
+ return nil
+ }
+
+ ok := true
+ for _, addr := range addrs {
+ if addr.Flags&(syscall.IFA_F_TENTATIVE|syscall.IFA_F_DADFAILED) > 0 {
+ ok = false
+ break // Break out of the `range addrs`, not the `for`
+ }
+ }
+
+ if ok {
+ return nil
+ }
+ if time.Now().After(deadline) {
+ return fmt.Errorf("link %s still has tentative addresses after %d seconds",
+ ifName,
+ timeout)
+ }
+
+ time.Sleep(SETTLE_INTERVAL)
+ }
+}
diff --git a/vendor/github.com/containernetworking/plugins/pkg/ip/cidr.go b/vendor/github.com/containernetworking/plugins/pkg/ip/cidr.go
new file mode 100644
index 000000000..7acc2d47c
--- /dev/null
+++ b/vendor/github.com/containernetworking/plugins/pkg/ip/cidr.go
@@ -0,0 +1,61 @@
+// Copyright 2015 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package ip
+
+import (
+ "math/big"
+ "net"
+)
+
+// NextIP returns IP incremented by 1
+func NextIP(ip net.IP) net.IP {
+ i := ipToInt(ip)
+ return intToIP(i.Add(i, big.NewInt(1)))
+}
+
+// PrevIP returns IP decremented by 1
+func PrevIP(ip net.IP) net.IP {
+ i := ipToInt(ip)
+ return intToIP(i.Sub(i, big.NewInt(1)))
+}
+
+// Cmp compares two IPs, returning the usual ordering:
+// a < b : -1
+// a == b : 0
+// a > b : 1
+func Cmp(a, b net.IP) int {
+ aa := ipToInt(a)
+ bb := ipToInt(b)
+ return aa.Cmp(bb)
+}
+
+func ipToInt(ip net.IP) *big.Int {
+ if v := ip.To4(); v != nil {
+ return big.NewInt(0).SetBytes(v)
+ }
+ return big.NewInt(0).SetBytes(ip.To16())
+}
+
+func intToIP(i *big.Int) net.IP {
+ return net.IP(i.Bytes())
+}
+
+// Network masks off the host portion of the IP
+func Network(ipn *net.IPNet) *net.IPNet {
+ return &net.IPNet{
+ IP: ipn.IP.Mask(ipn.Mask),
+ Mask: ipn.Mask,
+ }
+}
diff --git a/vendor/github.com/containernetworking/plugins/pkg/ip/ipforward_linux.go b/vendor/github.com/containernetworking/plugins/pkg/ip/ipforward_linux.go
new file mode 100644
index 000000000..8216a2c38
--- /dev/null
+++ b/vendor/github.com/containernetworking/plugins/pkg/ip/ipforward_linux.go
@@ -0,0 +1,61 @@
+// Copyright 2015 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package ip
+
+import (
+ "bytes"
+ "io/ioutil"
+
+ "github.com/containernetworking/cni/pkg/types/current"
+)
+
+func EnableIP4Forward() error {
+ return echo1("/proc/sys/net/ipv4/ip_forward")
+}
+
+func EnableIP6Forward() error {
+ return echo1("/proc/sys/net/ipv6/conf/all/forwarding")
+}
+
+// EnableForward will enable forwarding for all configured
+// address families
+func EnableForward(ips []*current.IPConfig) error {
+ v4 := false
+ v6 := false
+
+ for _, ip := range ips {
+ if ip.Version == "4" && !v4 {
+ if err := EnableIP4Forward(); err != nil {
+ return err
+ }
+ v4 = true
+ } else if ip.Version == "6" && !v6 {
+ if err := EnableIP6Forward(); err != nil {
+ return err
+ }
+ v6 = true
+ }
+ }
+ return nil
+}
+
+func echo1(f string) error {
+ if content, err := ioutil.ReadFile(f); err == nil {
+ if bytes.Equal(bytes.TrimSpace(content), []byte("1")) {
+ return nil
+ }
+ }
+ return ioutil.WriteFile(f, []byte("1"), 0644)
+}
diff --git a/vendor/github.com/containernetworking/plugins/pkg/ip/ipmasq_linux.go b/vendor/github.com/containernetworking/plugins/pkg/ip/ipmasq_linux.go
new file mode 100644
index 000000000..cc640a605
--- /dev/null
+++ b/vendor/github.com/containernetworking/plugins/pkg/ip/ipmasq_linux.go
@@ -0,0 +1,126 @@
+// Copyright 2015 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package ip
+
+import (
+ "fmt"
+ "net"
+
+ "github.com/coreos/go-iptables/iptables"
+)
+
+// SetupIPMasq installs iptables rules to masquerade traffic
+// coming from ip of ipn and going outside of ipn
+func SetupIPMasq(ipn *net.IPNet, chain string, comment string) error {
+ isV6 := ipn.IP.To4() == nil
+
+ var ipt *iptables.IPTables
+ var err error
+ var multicastNet string
+
+ if isV6 {
+ ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv6)
+ multicastNet = "ff00::/8"
+ } else {
+ ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv4)
+ multicastNet = "224.0.0.0/4"
+ }
+ if err != nil {
+ return fmt.Errorf("failed to locate iptables: %v", err)
+ }
+
+ // Create chain if doesn't exist
+ exists := false
+ chains, err := ipt.ListChains("nat")
+ if err != nil {
+ return fmt.Errorf("failed to list chains: %v", err)
+ }
+ for _, ch := range chains {
+ if ch == chain {
+ exists = true
+ break
+ }
+ }
+ if !exists {
+ if err = ipt.NewChain("nat", chain); err != nil {
+ return err
+ }
+ }
+
+ // Packets to this network should not be touched
+ if err := ipt.AppendUnique("nat", chain, "-d", ipn.String(), "-j", "ACCEPT", "-m", "comment", "--comment", comment); err != nil {
+ return err
+ }
+
+ // Don't masquerade multicast - pods should be able to talk to other pods
+ // on the local network via multicast.
+ if err := ipt.AppendUnique("nat", chain, "!", "-d", multicastNet, "-j", "MASQUERADE", "-m", "comment", "--comment", comment); err != nil {
+ return err
+ }
+
+ // Packets from the specific IP of this network will hit the chain
+ return ipt.AppendUnique("nat", "POSTROUTING", "-s", ipn.IP.String(), "-j", chain, "-m", "comment", "--comment", comment)
+}
+
+// TeardownIPMasq undoes the effects of SetupIPMasq
+func TeardownIPMasq(ipn *net.IPNet, chain string, comment string) error {
+ isV6 := ipn.IP.To4() == nil
+
+ var ipt *iptables.IPTables
+ var err error
+
+ if isV6 {
+ ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv6)
+ } else {
+ ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv4)
+ }
+ if err != nil {
+ return fmt.Errorf("failed to locate iptables: %v", err)
+ }
+
+ err = ipt.Delete("nat", "POSTROUTING", "-s", ipn.IP.String(), "-j", chain, "-m", "comment", "--comment", comment)
+ if err != nil && !isNotExist(err) {
+ return err
+ }
+
+ // for downward compatibility
+ err = ipt.Delete("nat", "POSTROUTING", "-s", ipn.String(), "-j", chain, "-m", "comment", "--comment", comment)
+ if err != nil && !isNotExist(err) {
+ return err
+ }
+
+ err = ipt.ClearChain("nat", chain)
+ if err != nil && !isNotExist(err) {
+ return err
+
+ }
+
+ err = ipt.DeleteChain("nat", chain)
+ if err != nil && !isNotExist(err) {
+ return err
+ }
+
+ return nil
+}
+
+// isNotExist returnst true if the error is from iptables indicating
+// that the target does not exist.
+func isNotExist(err error) bool {
+ e, ok := err.(*iptables.Error)
+ if !ok {
+ return false
+ }
+ return e.IsNotExist()
+}
diff --git a/vendor/github.com/containernetworking/plugins/pkg/ip/link_linux.go b/vendor/github.com/containernetworking/plugins/pkg/ip/link_linux.go
new file mode 100644
index 000000000..909afd04e
--- /dev/null
+++ b/vendor/github.com/containernetworking/plugins/pkg/ip/link_linux.go
@@ -0,0 +1,275 @@
+// Copyright 2015 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package ip
+
+import (
+ "crypto/rand"
+ "errors"
+ "fmt"
+ "net"
+ "os"
+
+ "github.com/containernetworking/plugins/pkg/ns"
+ "github.com/containernetworking/plugins/pkg/utils/hwaddr"
+ "github.com/safchain/ethtool"
+ "github.com/vishvananda/netlink"
+)
+
+var (
+ ErrLinkNotFound = errors.New("link not found")
+)
+
+func makeVethPair(name, peer string, mtu int) (netlink.Link, error) {
+ veth := &netlink.Veth{
+ LinkAttrs: netlink.LinkAttrs{
+ Name: name,
+ Flags: net.FlagUp,
+ MTU: mtu,
+ },
+ PeerName: peer,
+ }
+ if err := netlink.LinkAdd(veth); err != nil {
+ return nil, err
+ }
+ // Re-fetch the link to get its creation-time parameters, e.g. index and mac
+ veth2, err := netlink.LinkByName(name)
+ if err != nil {
+ netlink.LinkDel(veth) // try and clean up the link if possible.
+ return nil, err
+ }
+
+ return veth2, nil
+}
+
+func peerExists(name string) bool {
+ if _, err := netlink.LinkByName(name); err != nil {
+ return false
+ }
+ return true
+}
+
+func makeVeth(name string, mtu int) (peerName string, veth netlink.Link, err error) {
+ for i := 0; i < 10; i++ {
+ peerName, err = RandomVethName()
+ if err != nil {
+ return
+ }
+
+ veth, err = makeVethPair(name, peerName, mtu)
+ switch {
+ case err == nil:
+ return
+
+ case os.IsExist(err):
+ if peerExists(peerName) {
+ continue
+ }
+ err = fmt.Errorf("container veth name provided (%v) already exists", name)
+ return
+
+ default:
+ err = fmt.Errorf("failed to make veth pair: %v", err)
+ return
+ }
+ }
+
+ // should really never be hit
+ err = fmt.Errorf("failed to find a unique veth name")
+ return
+}
+
+// RandomVethName returns string "veth" with random prefix (hashed from entropy)
+func RandomVethName() (string, error) {
+ entropy := make([]byte, 4)
+ _, err := rand.Reader.Read(entropy)
+ if err != nil {
+ return "", fmt.Errorf("failed to generate random veth name: %v", err)
+ }
+
+ // NetworkManager (recent versions) will ignore veth devices that start with "veth"
+ return fmt.Sprintf("veth%x", entropy), nil
+}
+
+func RenameLink(curName, newName string) error {
+ link, err := netlink.LinkByName(curName)
+ if err == nil {
+ err = netlink.LinkSetName(link, newName)
+ }
+ return err
+}
+
+func ifaceFromNetlinkLink(l netlink.Link) net.Interface {
+ a := l.Attrs()
+ return net.Interface{
+ Index: a.Index,
+ MTU: a.MTU,
+ Name: a.Name,
+ HardwareAddr: a.HardwareAddr,
+ Flags: a.Flags,
+ }
+}
+
+// SetupVeth sets up a pair of virtual ethernet devices.
+// Call SetupVeth from inside the container netns. It will create both veth
+// devices and move the host-side veth into the provided hostNS namespace.
+// On success, SetupVeth returns (hostVeth, containerVeth, nil)
+func SetupVeth(contVethName string, mtu int, hostNS ns.NetNS) (net.Interface, net.Interface, error) {
+ hostVethName, contVeth, err := makeVeth(contVethName, mtu)
+ if err != nil {
+ return net.Interface{}, net.Interface{}, err
+ }
+
+ if err = netlink.LinkSetUp(contVeth); err != nil {
+ return net.Interface{}, net.Interface{}, fmt.Errorf("failed to set %q up: %v", contVethName, err)
+ }
+
+ hostVeth, err := netlink.LinkByName(hostVethName)
+ if err != nil {
+ return net.Interface{}, net.Interface{}, fmt.Errorf("failed to lookup %q: %v", hostVethName, err)
+ }
+
+ if err = netlink.LinkSetNsFd(hostVeth, int(hostNS.Fd())); err != nil {
+ return net.Interface{}, net.Interface{}, fmt.Errorf("failed to move veth to host netns: %v", err)
+ }
+
+ err = hostNS.Do(func(_ ns.NetNS) error {
+ hostVeth, err = netlink.LinkByName(hostVethName)
+ if err != nil {
+ return fmt.Errorf("failed to lookup %q in %q: %v", hostVethName, hostNS.Path(), err)
+ }
+
+ if err = netlink.LinkSetUp(hostVeth); err != nil {
+ return fmt.Errorf("failed to set %q up: %v", hostVethName, err)
+ }
+ return nil
+ })
+ if err != nil {
+ return net.Interface{}, net.Interface{}, err
+ }
+ return ifaceFromNetlinkLink(hostVeth), ifaceFromNetlinkLink(contVeth), nil
+}
+
+// DelLinkByName removes an interface link.
+func DelLinkByName(ifName string) error {
+ iface, err := netlink.LinkByName(ifName)
+ if err != nil {
+ if err.Error() == "Link not found" {
+ return ErrLinkNotFound
+ }
+ return fmt.Errorf("failed to lookup %q: %v", ifName, err)
+ }
+
+ if err = netlink.LinkDel(iface); err != nil {
+ return fmt.Errorf("failed to delete %q: %v", ifName, err)
+ }
+
+ return nil
+}
+
+// DelLinkByNameAddr remove an interface and returns its addresses
+func DelLinkByNameAddr(ifName string) ([]*net.IPNet, error) {
+ iface, err := netlink.LinkByName(ifName)
+ if err != nil {
+ if err != nil && err.Error() == "Link not found" {
+ return nil, ErrLinkNotFound
+ }
+ return nil, fmt.Errorf("failed to lookup %q: %v", ifName, err)
+ }
+
+ addrs, err := netlink.AddrList(iface, netlink.FAMILY_ALL)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get IP addresses for %q: %v", ifName, err)
+ }
+
+ if err = netlink.LinkDel(iface); err != nil {
+ return nil, fmt.Errorf("failed to delete %q: %v", ifName, err)
+ }
+
+ out := []*net.IPNet{}
+ for _, addr := range addrs {
+ if addr.IP.IsGlobalUnicast() {
+ out = append(out, addr.IPNet)
+ }
+ }
+
+ return out, nil
+}
+
+func SetHWAddrByIP(ifName string, ip4 net.IP, ip6 net.IP) error {
+ iface, err := netlink.LinkByName(ifName)
+ if err != nil {
+ return fmt.Errorf("failed to lookup %q: %v", ifName, err)
+ }
+
+ switch {
+ case ip4 == nil && ip6 == nil:
+ return fmt.Errorf("neither ip4 or ip6 specified")
+
+ case ip4 != nil:
+ {
+ hwAddr, err := hwaddr.GenerateHardwareAddr4(ip4, hwaddr.PrivateMACPrefix)
+ if err != nil {
+ return fmt.Errorf("failed to generate hardware addr: %v", err)
+ }
+ if err = netlink.LinkSetHardwareAddr(iface, hwAddr); err != nil {
+ return fmt.Errorf("failed to add hardware addr to %q: %v", ifName, err)
+ }
+ }
+ case ip6 != nil:
+ // TODO: IPv6
+ }
+
+ return nil
+}
+
+// GetVethPeerIfindex returns the veth link object, the peer ifindex of the
+// veth, or an error. This peer ifindex will only be valid in the peer's
+// network namespace.
+func GetVethPeerIfindex(ifName string) (netlink.Link, int, error) {
+ link, err := netlink.LinkByName(ifName)
+ if err != nil {
+ return nil, -1, fmt.Errorf("could not look up %q: %v", ifName, err)
+ }
+ if _, ok := link.(*netlink.Veth); !ok {
+ return nil, -1, fmt.Errorf("interface %q was not a veth interface", ifName)
+ }
+
+ // veth supports IFLA_LINK (what vishvananda/netlink calls ParentIndex)
+ // on 4.1 and higher kernels
+ peerIndex := link.Attrs().ParentIndex
+ if peerIndex <= 0 {
+ // Fall back to ethtool for 4.0 and earlier kernels
+ e, err := ethtool.NewEthtool()
+ if err != nil {
+ return nil, -1, fmt.Errorf("failed to initialize ethtool: %v", err)
+ }
+ defer e.Close()
+
+ stats, err := e.Stats(link.Attrs().Name)
+ if err != nil {
+ return nil, -1, fmt.Errorf("failed to request ethtool stats: %v", err)
+ }
+ n, ok := stats["peer_ifindex"]
+ if !ok {
+ return nil, -1, fmt.Errorf("failed to find 'peer_ifindex' in ethtool stats")
+ }
+ if n > 32767 || n == 0 {
+ return nil, -1, fmt.Errorf("invalid 'peer_ifindex' %d", n)
+ }
+ peerIndex = int(n)
+ }
+
+ return link, peerIndex, nil
+}
diff --git a/vendor/github.com/containernetworking/plugins/pkg/ip/route_linux.go b/vendor/github.com/containernetworking/plugins/pkg/ip/route_linux.go
new file mode 100644
index 000000000..f5c0d0803
--- /dev/null
+++ b/vendor/github.com/containernetworking/plugins/pkg/ip/route_linux.go
@@ -0,0 +1,47 @@
+// Copyright 2015-2017 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package ip
+
+import (
+ "net"
+
+ "github.com/vishvananda/netlink"
+)
+
+// AddRoute adds a universally-scoped route to a device.
+func AddRoute(ipn *net.IPNet, gw net.IP, dev netlink.Link) error {
+ return netlink.RouteAdd(&netlink.Route{
+ LinkIndex: dev.Attrs().Index,
+ Scope: netlink.SCOPE_UNIVERSE,
+ Dst: ipn,
+ Gw: gw,
+ })
+}
+
+// AddHostRoute adds a host-scoped route to a device.
+func AddHostRoute(ipn *net.IPNet, gw net.IP, dev netlink.Link) error {
+ return netlink.RouteAdd(&netlink.Route{
+ LinkIndex: dev.Attrs().Index,
+ Scope: netlink.SCOPE_HOST,
+ Dst: ipn,
+ Gw: gw,
+ })
+}
+
+// AddDefaultRoute sets the default route on the given gateway.
+func AddDefaultRoute(gw net.IP, dev netlink.Link) error {
+ _, defNet, _ := net.ParseCIDR("0.0.0.0/0")
+ return AddRoute(defNet, gw, dev)
+}
diff --git a/vendor/github.com/containernetworking/plugins/pkg/ip/utils_linux.go b/vendor/github.com/containernetworking/plugins/pkg/ip/utils_linux.go
new file mode 100644
index 000000000..7623c5e13
--- /dev/null
+++ b/vendor/github.com/containernetworking/plugins/pkg/ip/utils_linux.go
@@ -0,0 +1,120 @@
+// +build linux
+
+// Copyright 2016 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package ip
+
+import (
+ "fmt"
+ "net"
+
+ "github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/current"
+ "github.com/vishvananda/netlink"
+)
+
+func ValidateExpectedInterfaceIPs(ifName string, resultIPs []*current.IPConfig) error {
+
+ // Ensure ips
+ for _, ips := range resultIPs {
+ ourAddr := netlink.Addr{IPNet: &ips.Address}
+ match := false
+
+ link, err := netlink.LinkByName(ifName)
+ if err != nil {
+ return fmt.Errorf("Cannot find container link %v", ifName)
+ }
+
+ addrList, err := netlink.AddrList(link, netlink.FAMILY_ALL)
+ if err != nil {
+ return fmt.Errorf("Cannot obtain List of IP Addresses")
+ }
+
+ for _, addr := range addrList {
+ if addr.Equal(ourAddr) {
+ match = true
+ break
+ }
+ }
+ if match == false {
+ return fmt.Errorf("Failed to match addr %v on interface %v", ourAddr, ifName)
+ }
+
+ // Convert the host/prefixlen to just prefix for route lookup.
+ _, ourPrefix, err := net.ParseCIDR(ourAddr.String())
+
+ findGwy := &netlink.Route{Dst: ourPrefix}
+ routeFilter := netlink.RT_FILTER_DST
+ var family int
+
+ switch {
+ case ips.Version == "4":
+ family = netlink.FAMILY_V4
+ case ips.Version == "6":
+ family = netlink.FAMILY_V6
+ default:
+ return fmt.Errorf("Invalid IP Version %v for interface %v", ips.Version, ifName)
+ }
+
+ gwy, err := netlink.RouteListFiltered(family, findGwy, routeFilter)
+ if err != nil {
+ return fmt.Errorf("Error %v trying to find Gateway %v for interface %v", err, ips.Gateway, ifName)
+ }
+ if gwy == nil {
+ return fmt.Errorf("Failed to find Gateway %v for interface %v", ips.Gateway, ifName)
+ }
+ }
+
+ return nil
+}
+
+func ValidateExpectedRoute(resultRoutes []*types.Route) error {
+
+ // Ensure that each static route in prevResults is found in the routing table
+ for _, route := range resultRoutes {
+ find := &netlink.Route{Dst: &route.Dst, Gw: route.GW}
+ routeFilter := netlink.RT_FILTER_DST | netlink.RT_FILTER_GW
+ var family int
+
+ switch {
+ case route.Dst.IP.To4() != nil:
+ family = netlink.FAMILY_V4
+ // Default route needs Dst set to nil
+ if route.Dst.String() == "0.0.0.0/0" {
+ find = &netlink.Route{Dst: nil, Gw: route.GW}
+ routeFilter = netlink.RT_FILTER_DST
+ }
+ case len(route.Dst.IP) == net.IPv6len:
+ family = netlink.FAMILY_V6
+ // Default route needs Dst set to nil
+ if route.Dst.String() == "::/0" {
+ find = &netlink.Route{Dst: nil, Gw: route.GW}
+ routeFilter = netlink.RT_FILTER_DST
+ }
+ default:
+ return fmt.Errorf("Invalid static route found %v", route)
+ }
+
+ wasFound, err := netlink.RouteListFiltered(family, find, routeFilter)
+ if err != nil {
+ return fmt.Errorf("Expected Route %v not route table lookup error %v", route, err)
+ }
+ if wasFound == nil {
+ return fmt.Errorf("Expected Route %v not found in routing table", route)
+ }
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/containernetworking/plugins/pkg/utils/hwaddr/hwaddr.go b/vendor/github.com/containernetworking/plugins/pkg/utils/hwaddr/hwaddr.go
new file mode 100644
index 000000000..aaf3b8a02
--- /dev/null
+++ b/vendor/github.com/containernetworking/plugins/pkg/utils/hwaddr/hwaddr.go
@@ -0,0 +1,63 @@
+// Copyright 2016 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package hwaddr
+
+import (
+ "fmt"
+ "net"
+)
+
+const (
+ ipRelevantByteLen = 4
+ PrivateMACPrefixString = "0a:58"
+)
+
+var (
+ // private mac prefix safe to use
+ PrivateMACPrefix = []byte{0x0a, 0x58}
+)
+
+type SupportIp4OnlyErr struct{ msg string }
+
+func (e SupportIp4OnlyErr) Error() string { return e.msg }
+
+type MacParseErr struct{ msg string }
+
+func (e MacParseErr) Error() string { return e.msg }
+
+type InvalidPrefixLengthErr struct{ msg string }
+
+func (e InvalidPrefixLengthErr) Error() string { return e.msg }
+
+// GenerateHardwareAddr4 generates 48 bit virtual mac addresses based on the IP4 input.
+func GenerateHardwareAddr4(ip net.IP, prefix []byte) (net.HardwareAddr, error) {
+ switch {
+
+ case ip.To4() == nil:
+ return nil, SupportIp4OnlyErr{msg: "GenerateHardwareAddr4 only supports valid IPv4 address as input"}
+
+ case len(prefix) != len(PrivateMACPrefix):
+ return nil, InvalidPrefixLengthErr{msg: fmt.Sprintf(
+ "Prefix has length %d instead of %d", len(prefix), len(PrivateMACPrefix)),
+ }
+ }
+
+ ipByteLen := len(ip)
+ return (net.HardwareAddr)(
+ append(
+ prefix,
+ ip[ipByteLen-ipRelevantByteLen:ipByteLen]...),
+ ), nil
+}
diff --git a/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/allocator.go b/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/allocator.go
new file mode 100644
index 000000000..d1c2b1018
--- /dev/null
+++ b/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/allocator.go
@@ -0,0 +1,217 @@
+// Copyright 2015 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package allocator
+
+import (
+ "fmt"
+ "log"
+ "net"
+ "os"
+ "strconv"
+
+ "github.com/containernetworking/cni/pkg/types/current"
+ "github.com/containernetworking/plugins/pkg/ip"
+ "github.com/containernetworking/plugins/plugins/ipam/host-local/backend"
+)
+
+type IPAllocator struct {
+ rangeset *RangeSet
+ store backend.Store
+ rangeID string // Used for tracking last reserved ip
+}
+
+func NewIPAllocator(s *RangeSet, store backend.Store, id int) *IPAllocator {
+ return &IPAllocator{
+ rangeset: s,
+ store: store,
+ rangeID: strconv.Itoa(id),
+ }
+}
+
+// Get alocates an IP
+func (a *IPAllocator) Get(id string, ifname string, requestedIP net.IP) (*current.IPConfig, error) {
+ a.store.Lock()
+ defer a.store.Unlock()
+
+ var reservedIP *net.IPNet
+ var gw net.IP
+
+ if requestedIP != nil {
+ if err := canonicalizeIP(&requestedIP); err != nil {
+ return nil, err
+ }
+
+ r, err := a.rangeset.RangeFor(requestedIP)
+ if err != nil {
+ return nil, err
+ }
+
+ if requestedIP.Equal(r.Gateway) {
+ return nil, fmt.Errorf("requested ip %s is subnet's gateway", requestedIP.String())
+ }
+
+ reserved, err := a.store.Reserve(id, ifname, requestedIP, a.rangeID)
+ if err != nil {
+ return nil, err
+ }
+ if !reserved {
+ return nil, fmt.Errorf("requested IP address %s is not available in range set %s", requestedIP, a.rangeset.String())
+ }
+ reservedIP = &net.IPNet{IP: requestedIP, Mask: r.Subnet.Mask}
+ gw = r.Gateway
+
+ } else {
+ iter, err := a.GetIter()
+ if err != nil {
+ return nil, err
+ }
+ for {
+ reservedIP, gw = iter.Next()
+ if reservedIP == nil {
+ break
+ }
+
+ reserved, err := a.store.Reserve(id, ifname, reservedIP.IP, a.rangeID)
+ if err != nil {
+ return nil, err
+ }
+
+ if reserved {
+ break
+ }
+ }
+ }
+
+ if reservedIP == nil {
+ return nil, fmt.Errorf("no IP addresses available in range set: %s", a.rangeset.String())
+ }
+ version := "4"
+ if reservedIP.IP.To4() == nil {
+ version = "6"
+ }
+
+ return &current.IPConfig{
+ Version: version,
+ Address: *reservedIP,
+ Gateway: gw,
+ }, nil
+}
+
+// Release clears all IPs allocated for the container with given ID
+func (a *IPAllocator) Release(id string, ifname string) error {
+ a.store.Lock()
+ defer a.store.Unlock()
+
+ return a.store.ReleaseByID(id, ifname)
+}
+
+type RangeIter struct {
+ rangeset *RangeSet
+
+ // The current range id
+ rangeIdx int
+
+ // Our current position
+ cur net.IP
+
+ // The IP and range index where we started iterating; if we hit this again, we're done.
+ startIP net.IP
+ startRange int
+}
+
+// GetIter encapsulates the strategy for this allocator.
+// We use a round-robin strategy, attempting to evenly use the whole set.
+// More specifically, a crash-looping container will not see the same IP until
+// the entire range has been run through.
+// We may wish to consider avoiding recently-released IPs in the future.
+func (a *IPAllocator) GetIter() (*RangeIter, error) {
+ iter := RangeIter{
+ rangeset: a.rangeset,
+ }
+
+ // Round-robin by trying to allocate from the last reserved IP + 1
+ startFromLastReservedIP := false
+
+ // We might get a last reserved IP that is wrong if the range indexes changed.
+ // This is not critical, we just lose round-robin this one time.
+ lastReservedIP, err := a.store.LastReservedIP(a.rangeID)
+ if err != nil && !os.IsNotExist(err) {
+ log.Printf("Error retrieving last reserved ip: %v", err)
+ } else if lastReservedIP != nil {
+ startFromLastReservedIP = a.rangeset.Contains(lastReservedIP)
+ }
+
+ // Find the range in the set with this IP
+ if startFromLastReservedIP {
+ for i, r := range *a.rangeset {
+ if r.Contains(lastReservedIP) {
+ iter.rangeIdx = i
+ iter.startRange = i
+
+ // We advance the cursor on every Next(), so the first call
+ // to next() will return lastReservedIP + 1
+ iter.cur = lastReservedIP
+ break
+ }
+ }
+ } else {
+ iter.rangeIdx = 0
+ iter.startRange = 0
+ iter.startIP = (*a.rangeset)[0].RangeStart
+ }
+ return &iter, nil
+}
+
+// Next returns the next IP, its mask, and its gateway. Returns nil
+// if the iterator has been exhausted
+func (i *RangeIter) Next() (*net.IPNet, net.IP) {
+ r := (*i.rangeset)[i.rangeIdx]
+
+ // If this is the first time iterating and we're not starting in the middle
+ // of the range, then start at rangeStart, which is inclusive
+ if i.cur == nil {
+ i.cur = r.RangeStart
+ i.startIP = i.cur
+ if i.cur.Equal(r.Gateway) {
+ return i.Next()
+ }
+ return &net.IPNet{IP: i.cur, Mask: r.Subnet.Mask}, r.Gateway
+ }
+
+ // If we've reached the end of this range, we need to advance the range
+ // RangeEnd is inclusive as well
+ if i.cur.Equal(r.RangeEnd) {
+ i.rangeIdx += 1
+ i.rangeIdx %= len(*i.rangeset)
+ r = (*i.rangeset)[i.rangeIdx]
+
+ i.cur = r.RangeStart
+ } else {
+ i.cur = ip.NextIP(i.cur)
+ }
+
+ if i.startIP == nil {
+ i.startIP = i.cur
+ } else if i.rangeIdx == i.startRange && i.cur.Equal(i.startIP) {
+ // IF we've looped back to where we started, give up
+ return nil, nil
+ }
+
+ if i.cur.Equal(r.Gateway) {
+ return i.Next()
+ }
+
+ return &net.IPNet{IP: i.cur, Mask: r.Subnet.Mask}, r.Gateway
+}
diff --git a/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/config.go b/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/config.go
new file mode 100644
index 000000000..c8cb2a746
--- /dev/null
+++ b/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/config.go
@@ -0,0 +1,160 @@
+// Copyright 2015 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package allocator
+
+import (
+ "encoding/json"
+ "fmt"
+ "net"
+
+ "github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/020"
+)
+
+// The top-level network config - IPAM plugins are passed the full configuration
+// of the calling plugin, not just the IPAM section.
+type Net struct {
+ Name string `json:"name"`
+ CNIVersion string `json:"cniVersion"`
+ IPAM *IPAMConfig `json:"ipam"`
+ RuntimeConfig struct { // The capability arg
+ IPRanges []RangeSet `json:"ipRanges,omitempty"`
+ } `json:"runtimeConfig,omitempty"`
+ Args *struct {
+ A *IPAMArgs `json:"cni"`
+ } `json:"args"`
+}
+
+// IPAMConfig represents the IP related network configuration.
+// This nests Range because we initially only supported a single
+// range directly, and wish to preserve backwards compatability
+type IPAMConfig struct {
+ *Range
+ Name string
+ Type string `json:"type"`
+ Routes []*types.Route `json:"routes"`
+ DataDir string `json:"dataDir"`
+ ResolvConf string `json:"resolvConf"`
+ Ranges []RangeSet `json:"ranges"`
+ IPArgs []net.IP `json:"-"` // Requested IPs from CNI_ARGS and args
+}
+
+type IPAMEnvArgs struct {
+ types.CommonArgs
+ IP net.IP `json:"ip,omitempty"`
+}
+
+type IPAMArgs struct {
+ IPs []net.IP `json:"ips"`
+}
+
+type RangeSet []Range
+
+type Range struct {
+ RangeStart net.IP `json:"rangeStart,omitempty"` // The first ip, inclusive
+ RangeEnd net.IP `json:"rangeEnd,omitempty"` // The last ip, inclusive
+ Subnet types.IPNet `json:"subnet"`
+ Gateway net.IP `json:"gateway,omitempty"`
+}
+
+// NewIPAMConfig creates a NetworkConfig from the given network name.
+func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) {
+ n := Net{}
+ if err := json.Unmarshal(bytes, &n); err != nil {
+ return nil, "", err
+ }
+
+ if n.IPAM == nil {
+ return nil, "", fmt.Errorf("IPAM config missing 'ipam' key")
+ }
+
+ // Parse custom IP from both env args *and* the top-level args config
+ if envArgs != "" {
+ e := IPAMEnvArgs{}
+ err := types.LoadArgs(envArgs, &e)
+ if err != nil {
+ return nil, "", err
+ }
+
+ if e.IP != nil {
+ n.IPAM.IPArgs = []net.IP{e.IP}
+ }
+ }
+
+ if n.Args != nil && n.Args.A != nil && len(n.Args.A.IPs) != 0 {
+ n.IPAM.IPArgs = append(n.IPAM.IPArgs, n.Args.A.IPs...)
+ }
+
+ for idx := range n.IPAM.IPArgs {
+ if err := canonicalizeIP(&n.IPAM.IPArgs[idx]); err != nil {
+ return nil, "", fmt.Errorf("cannot understand ip: %v", err)
+ }
+ }
+
+ // If a single range (old-style config) is specified, prepend it to
+ // the Ranges array
+ if n.IPAM.Range != nil && n.IPAM.Range.Subnet.IP != nil {
+ n.IPAM.Ranges = append([]RangeSet{{*n.IPAM.Range}}, n.IPAM.Ranges...)
+ }
+ n.IPAM.Range = nil
+
+ // If a range is supplied as a runtime config, prepend it to the Ranges
+ if len(n.RuntimeConfig.IPRanges) > 0 {
+ n.IPAM.Ranges = append(n.RuntimeConfig.IPRanges, n.IPAM.Ranges...)
+ }
+
+ if len(n.IPAM.Ranges) == 0 {
+ return nil, "", fmt.Errorf("no IP ranges specified")
+ }
+
+ // Validate all ranges
+ numV4 := 0
+ numV6 := 0
+ for i := range n.IPAM.Ranges {
+ if err := n.IPAM.Ranges[i].Canonicalize(); err != nil {
+ return nil, "", fmt.Errorf("invalid range set %d: %s", i, err)
+ }
+
+ if n.IPAM.Ranges[i][0].RangeStart.To4() != nil {
+ numV4++
+ } else {
+ numV6++
+ }
+ }
+
+ // CNI spec 0.2.0 and below supported only one v4 and v6 address
+ if numV4 > 1 || numV6 > 1 {
+ for _, v := range types020.SupportedVersions {
+ if n.CNIVersion == v {
+ return nil, "", fmt.Errorf("CNI version %v does not support more than 1 address per family", n.CNIVersion)
+ }
+ }
+ }
+
+ // Check for overlaps
+ l := len(n.IPAM.Ranges)
+ for i, p1 := range n.IPAM.Ranges[:l-1] {
+ for j, p2 := range n.IPAM.Ranges[i+1:] {
+ if p1.Overlaps(&p2) {
+ return nil, "", fmt.Errorf("range set %d overlaps with %d", i, (i + j + 1))
+ }
+ }
+ }
+
+ // Copy net name into IPAM so not to drag Net struct around
+ n.IPAM.Name = n.Name
+
+ return n.IPAM, n.CNIVersion, nil
+}
diff --git a/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/range.go b/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/range.go
new file mode 100644
index 000000000..9bf389e80
--- /dev/null
+++ b/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/range.go
@@ -0,0 +1,166 @@
+// Copyright 2017 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package allocator
+
+import (
+ "fmt"
+ "net"
+
+ "github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/plugins/pkg/ip"
+)
+
+// Canonicalize takes a given range and ensures that all information is consistent,
+// filling out Start, End, and Gateway with sane values if missing
+func (r *Range) Canonicalize() error {
+ if err := canonicalizeIP(&r.Subnet.IP); err != nil {
+ return err
+ }
+
+ // Can't create an allocator for a network with no addresses, eg
+ // a /32 or /31
+ ones, masklen := r.Subnet.Mask.Size()
+ if ones > masklen-2 {
+ return fmt.Errorf("Network %s too small to allocate from", (*net.IPNet)(&r.Subnet).String())
+ }
+
+ if len(r.Subnet.IP) != len(r.Subnet.Mask) {
+ return fmt.Errorf("IPNet IP and Mask version mismatch")
+ }
+
+ // Ensure Subnet IP is the network address, not some other address
+ networkIP := r.Subnet.IP.Mask(r.Subnet.Mask)
+ if !r.Subnet.IP.Equal(networkIP) {
+ return fmt.Errorf("Network has host bits set. For a subnet mask of length %d the network address is %s", ones, networkIP.String())
+ }
+
+ // If the gateway is nil, claim .1
+ if r.Gateway == nil {
+ r.Gateway = ip.NextIP(r.Subnet.IP)
+ } else {
+ if err := canonicalizeIP(&r.Gateway); err != nil {
+ return err
+ }
+ }
+
+ // RangeStart: If specified, make sure it's sane (inside the subnet),
+ // otherwise use the first free IP (i.e. .1) - this will conflict with the
+ // gateway but we skip it in the iterator
+ if r.RangeStart != nil {
+ if err := canonicalizeIP(&r.RangeStart); err != nil {
+ return err
+ }
+
+ if !r.Contains(r.RangeStart) {
+ return fmt.Errorf("RangeStart %s not in network %s", r.RangeStart.String(), (*net.IPNet)(&r.Subnet).String())
+ }
+ } else {
+ r.RangeStart = ip.NextIP(r.Subnet.IP)
+ }
+
+ // RangeEnd: If specified, verify sanity. Otherwise, add a sensible default
+ // (e.g. for a /24: .254 if IPv4, ::255 if IPv6)
+ if r.RangeEnd != nil {
+ if err := canonicalizeIP(&r.RangeEnd); err != nil {
+ return err
+ }
+
+ if !r.Contains(r.RangeEnd) {
+ return fmt.Errorf("RangeEnd %s not in network %s", r.RangeEnd.String(), (*net.IPNet)(&r.Subnet).String())
+ }
+ } else {
+ r.RangeEnd = lastIP(r.Subnet)
+ }
+
+ return nil
+}
+
+// IsValidIP checks if a given ip is a valid, allocatable address in a given Range
+func (r *Range) Contains(addr net.IP) bool {
+ if err := canonicalizeIP(&addr); err != nil {
+ return false
+ }
+
+ subnet := (net.IPNet)(r.Subnet)
+
+ // Not the same address family
+ if len(addr) != len(r.Subnet.IP) {
+ return false
+ }
+
+ // Not in network
+ if !subnet.Contains(addr) {
+ return false
+ }
+
+ // We ignore nils here so we can use this function as we initialize the range.
+ if r.RangeStart != nil {
+ // Before the range start
+ if ip.Cmp(addr, r.RangeStart) < 0 {
+ return false
+ }
+ }
+
+ if r.RangeEnd != nil {
+ if ip.Cmp(addr, r.RangeEnd) > 0 {
+ // After the range end
+ return false
+ }
+ }
+
+ return true
+}
+
+// Overlaps returns true if there is any overlap between ranges
+func (r *Range) Overlaps(r1 *Range) bool {
+ // different familes
+ if len(r.RangeStart) != len(r1.RangeStart) {
+ return false
+ }
+
+ return r.Contains(r1.RangeStart) ||
+ r.Contains(r1.RangeEnd) ||
+ r1.Contains(r.RangeStart) ||
+ r1.Contains(r.RangeEnd)
+}
+
+func (r *Range) String() string {
+ return fmt.Sprintf("%s-%s", r.RangeStart.String(), r.RangeEnd.String())
+}
+
+// canonicalizeIP makes sure a provided ip is in standard form
+func canonicalizeIP(ip *net.IP) error {
+ if ip.To4() != nil {
+ *ip = ip.To4()
+ return nil
+ } else if ip.To16() != nil {
+ *ip = ip.To16()
+ return nil
+ }
+ return fmt.Errorf("IP %s not v4 nor v6", *ip)
+}
+
+// Determine the last IP of a subnet, excluding the broadcast if IPv4
+func lastIP(subnet types.IPNet) net.IP {
+ var end net.IP
+ for i := 0; i < len(subnet.IP); i++ {
+ end = append(end, subnet.IP[i]|^subnet.Mask[i])
+ }
+ if subnet.IP.To4() != nil {
+ end[3]--
+ }
+
+ return end
+}
diff --git a/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/range_set.go b/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/range_set.go
new file mode 100644
index 000000000..da957f535
--- /dev/null
+++ b/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/range_set.go
@@ -0,0 +1,97 @@
+// Copyright 2017 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package allocator
+
+import (
+ "fmt"
+ "net"
+ "strings"
+)
+
+// Contains returns true if any range in this set contains an IP
+func (s *RangeSet) Contains(addr net.IP) bool {
+ r, _ := s.RangeFor(addr)
+ return r != nil
+}
+
+// RangeFor finds the range that contains an IP, or nil if not found
+func (s *RangeSet) RangeFor(addr net.IP) (*Range, error) {
+ if err := canonicalizeIP(&addr); err != nil {
+ return nil, err
+ }
+
+ for _, r := range *s {
+ if r.Contains(addr) {
+ return &r, nil
+ }
+ }
+
+ return nil, fmt.Errorf("%s not in range set %s", addr.String(), s.String())
+}
+
+// Overlaps returns true if any ranges in any set overlap with this one
+func (s *RangeSet) Overlaps(p1 *RangeSet) bool {
+ for _, r := range *s {
+ for _, r1 := range *p1 {
+ if r.Overlaps(&r1) {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+// Canonicalize ensures the RangeSet is in a standard form, and detects any
+// invalid input. Call Range.Canonicalize() on every Range in the set
+func (s *RangeSet) Canonicalize() error {
+ if len(*s) == 0 {
+ return fmt.Errorf("empty range set")
+ }
+
+ fam := 0
+ for i := range *s {
+ if err := (*s)[i].Canonicalize(); err != nil {
+ return err
+ }
+ if i == 0 {
+ fam = len((*s)[i].RangeStart)
+ } else {
+ if fam != len((*s)[i].RangeStart) {
+ return fmt.Errorf("mixed address families")
+ }
+ }
+ }
+
+ // Make sure none of the ranges in the set overlap
+ l := len(*s)
+ for i, r1 := range (*s)[:l-1] {
+ for _, r2 := range (*s)[i+1:] {
+ if r1.Overlaps(&r2) {
+ return fmt.Errorf("subnets %s and %s overlap", r1.String(), r2.String())
+ }
+ }
+ }
+
+ return nil
+}
+
+func (s *RangeSet) String() string {
+ out := []string{}
+ for _, r := range *s {
+ out = append(out, r.String())
+ }
+
+ return strings.Join(out, ",")
+}
diff --git a/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/store.go b/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/store.go
new file mode 100644
index 000000000..4ea845da7
--- /dev/null
+++ b/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/store.go
@@ -0,0 +1,27 @@
+// Copyright 2015 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package backend
+
+import "net"
+
+type Store interface {
+ Lock() error
+ Unlock() error
+ Close() error
+ Reserve(id string, ifname string, ip net.IP, rangeID string) (bool, error)
+ LastReservedIP(rangeID string) (net.IP, error)
+ Release(ip net.IP) error
+ ReleaseByID(id string, ifname string) error
+}