Merge pull request #353 from cf-container-networking/fix-hash-func
Increase IfbDeviceName size and refactor hashing functions, fix
This commit is contained in:
commit
7ba2bcfeab
@ -22,16 +22,22 @@ import (
|
|||||||
const (
|
const (
|
||||||
maxChainLength = 28
|
maxChainLength = 28
|
||||||
chainPrefix = "CNI-"
|
chainPrefix = "CNI-"
|
||||||
prefixLength = len(chainPrefix)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Generates a chain name to be used with iptables.
|
// FormatChainName generates a chain name to be used
|
||||||
// Ensures that the generated chain name is exactly
|
// with iptables. Ensures that the generated chain
|
||||||
// maxChainLength chars in length
|
// name is exactly maxChainLength chars in length.
|
||||||
func FormatChainName(name string, id string) string {
|
func FormatChainName(name string, id string) string {
|
||||||
chainBytes := sha512.Sum512([]byte(name + id))
|
return MustFormatChainNameWithPrefix(name, id, "")
|
||||||
chain := fmt.Sprintf("%s%x", chainPrefix, chainBytes)
|
}
|
||||||
return chain[:maxChainLength]
|
|
||||||
|
// MustFormatChainNameWithPrefix generates a chain name similar
|
||||||
|
// to FormatChainName, but adds a custom prefix between
|
||||||
|
// chainPrefix and unique identifier. Ensures that the
|
||||||
|
// generated chain name is exactly maxChainLength chars in length.
|
||||||
|
// Panics if the given prefix is too long.
|
||||||
|
func MustFormatChainNameWithPrefix(name string, id string, prefix string) string {
|
||||||
|
return MustFormatHashWithPrefix(maxChainLength, chainPrefix+prefix, name+id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FormatComment returns a comment used for easier
|
// FormatComment returns a comment used for easier
|
||||||
@ -39,3 +45,16 @@ func FormatChainName(name string, id string) string {
|
|||||||
func FormatComment(name string, id string) string {
|
func FormatComment(name string, id string) string {
|
||||||
return fmt.Sprintf("name: %q id: %q", name, id)
|
return fmt.Sprintf("name: %q id: %q", name, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MaxHashLen = sha512.Size * 2
|
||||||
|
|
||||||
|
// MustFormatHashWithPrefix returns a string of given length that begins with the
|
||||||
|
// given prefix. It is filled with entropy based on the given string toHash.
|
||||||
|
func MustFormatHashWithPrefix(length int, prefix string, toHash string) string {
|
||||||
|
if len(prefix) >= length || length > MaxHashLen {
|
||||||
|
panic("invalid length")
|
||||||
|
}
|
||||||
|
|
||||||
|
output := sha512.Sum512([]byte(toHash))
|
||||||
|
return fmt.Sprintf("%s%x", prefix, output)[:length]
|
||||||
|
}
|
||||||
|
@ -15,11 +15,15 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("Utils", func() {
|
var _ = Describe("Utils", func() {
|
||||||
|
Describe("FormatChainName", func() {
|
||||||
It("must format a short name", func() {
|
It("must format a short name", func() {
|
||||||
chain := FormatChainName("test", "1234")
|
chain := FormatChainName("test", "1234")
|
||||||
Expect(len(chain)).To(Equal(maxChainLength))
|
Expect(len(chain)).To(Equal(maxChainLength))
|
||||||
@ -49,3 +53,113 @@ var _ = Describe("Utils", func() {
|
|||||||
Expect(chain1).NotTo(Equal(chain2))
|
Expect(chain1).NotTo(Equal(chain2))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Describe("MustFormatChainNameWithPrefix", func() {
|
||||||
|
It("generates a chain name with a prefix", func() {
|
||||||
|
chain := MustFormatChainNameWithPrefix("test", "1234", "PREFIX-")
|
||||||
|
Expect(len(chain)).To(Equal(maxChainLength))
|
||||||
|
Expect(chain).To(Equal("CNI-PREFIX-2bbe0c48b91a7d1b8"))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("must format a short name", func() {
|
||||||
|
chain := MustFormatChainNameWithPrefix("test", "1234", "PREFIX-")
|
||||||
|
Expect(len(chain)).To(Equal(maxChainLength))
|
||||||
|
Expect(chain).To(Equal("CNI-PREFIX-2bbe0c48b91a7d1b8"))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("must truncate a long name", func() {
|
||||||
|
chain := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1234", "PREFIX-")
|
||||||
|
Expect(len(chain)).To(Equal(maxChainLength))
|
||||||
|
Expect(chain).To(Equal("CNI-PREFIX-374f33fe84ab0ed84"))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("must be predictable", func() {
|
||||||
|
chain1 := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1234", "PREFIX-")
|
||||||
|
chain2 := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1234", "PREFIX-")
|
||||||
|
Expect(len(chain1)).To(Equal(maxChainLength))
|
||||||
|
Expect(len(chain2)).To(Equal(maxChainLength))
|
||||||
|
Expect(chain1).To(Equal(chain2))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("must change when a character changes", func() {
|
||||||
|
chain1 := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1234", "PREFIX-")
|
||||||
|
chain2 := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1235", "PREFIX-")
|
||||||
|
Expect(len(chain1)).To(Equal(maxChainLength))
|
||||||
|
Expect(len(chain2)).To(Equal(maxChainLength))
|
||||||
|
Expect(chain1).To(Equal("CNI-PREFIX-374f33fe84ab0ed84"))
|
||||||
|
Expect(chain1).NotTo(Equal(chain2))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("panics when prefix is too large", func() {
|
||||||
|
longPrefix := strings.Repeat("PREFIX-", 4)
|
||||||
|
Expect(func() {
|
||||||
|
MustFormatChainNameWithPrefix("test", "1234", longPrefix)
|
||||||
|
}).To(Panic())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Describe("MustFormatHashWithPrefix", func() {
|
||||||
|
It("always returns a string with the given prefix", func() {
|
||||||
|
Expect(MustFormatHashWithPrefix(10, "AAA", "some string")).To(HavePrefix("AAA"))
|
||||||
|
Expect(MustFormatHashWithPrefix(10, "foo", "some string")).To(HavePrefix("foo"))
|
||||||
|
Expect(MustFormatHashWithPrefix(10, "bar", "some string")).To(HavePrefix("bar"))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("always returns a string of the given length", func() {
|
||||||
|
Expect(MustFormatHashWithPrefix(10, "AAA", "some string")).To(HaveLen(10))
|
||||||
|
Expect(MustFormatHashWithPrefix(15, "AAA", "some string")).To(HaveLen(15))
|
||||||
|
Expect(MustFormatHashWithPrefix(5, "AAA", "some string")).To(HaveLen(5))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("is deterministic", func() {
|
||||||
|
val1 := MustFormatHashWithPrefix(10, "AAA", "some string")
|
||||||
|
val2 := MustFormatHashWithPrefix(10, "AAA", "some string")
|
||||||
|
val3 := MustFormatHashWithPrefix(10, "AAA", "some string")
|
||||||
|
Expect(val1).To(Equal(val2))
|
||||||
|
Expect(val1).To(Equal(val3))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("is (nearly) perfect (injective function)", func() {
|
||||||
|
hashes := map[string]int{}
|
||||||
|
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
name := fmt.Sprintf("string %d", i)
|
||||||
|
hashes[MustFormatHashWithPrefix(8, "", name)]++
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, count := range hashes {
|
||||||
|
Expect(count).To(Equal(1), "for key "+key+" got non-unique correspondence")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
assertPanicWith := func(f func(), expectedErrorMessage string) {
|
||||||
|
defer func() {
|
||||||
|
Expect(recover()).To(Equal(expectedErrorMessage))
|
||||||
|
}()
|
||||||
|
f()
|
||||||
|
Fail("function should have panicked but did not")
|
||||||
|
}
|
||||||
|
|
||||||
|
It("panics when prefix is longer than the length", func() {
|
||||||
|
assertPanicWith(
|
||||||
|
func() { MustFormatHashWithPrefix(3, "AAA", "some string") },
|
||||||
|
"invalid length",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("panics when length is not positive", func() {
|
||||||
|
assertPanicWith(
|
||||||
|
func() { MustFormatHashWithPrefix(0, "", "some string") },
|
||||||
|
"invalid length",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("panics when length is larger than MaxLen", func() {
|
||||||
|
assertPanicWith(
|
||||||
|
func() { MustFormatHashWithPrefix(MaxHashLen+1, "", "some string") },
|
||||||
|
"invalid length",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
@ -105,7 +105,7 @@ var _ = Describe("bandwidth test", func() {
|
|||||||
hostIP = net.IP{169, 254, 0, 1}
|
hostIP = net.IP{169, 254, 0, 1}
|
||||||
containerIP = net.IP{10, 254, 0, 1}
|
containerIP = net.IP{10, 254, 0, 1}
|
||||||
hostIfaceMTU = 1024
|
hostIfaceMTU = 1024
|
||||||
ifbDeviceName = "5b6c"
|
ifbDeviceName = "bwpa8eda89404b7"
|
||||||
|
|
||||||
createVeth(hostNs.Path(), hostIfname, containerNs.Path(), containerIfname, hostIP, containerIP, hostIfaceMTU)
|
createVeth(hostNs.Path(), hostIfname, containerNs.Path(), containerIfname, hostIP, containerIP, hostIfaceMTU)
|
||||||
})
|
})
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha1"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
@ -28,9 +27,13 @@ import (
|
|||||||
|
|
||||||
"github.com/containernetworking/plugins/pkg/ip"
|
"github.com/containernetworking/plugins/pkg/ip"
|
||||||
"github.com/containernetworking/plugins/pkg/ns"
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
|
"github.com/containernetworking/plugins/pkg/utils"
|
||||||
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const maxIfbDeviceLength = 15
|
||||||
|
const ifbDevicePrefix = "bwp"
|
||||||
|
|
||||||
// BandwidthEntry corresponds to a single entry in the bandwidth argument,
|
// BandwidthEntry corresponds to a single entry in the bandwidth argument,
|
||||||
// see CONVENTIONS.md
|
// see CONVENTIONS.md
|
||||||
type BandwidthEntry struct {
|
type BandwidthEntry struct {
|
||||||
@ -111,14 +114,8 @@ func validateRateAndBurst(rate int, burst int) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getIfbDeviceName(networkName string, containerId string) (string, error) {
|
func getIfbDeviceName(networkName string, containerId string) string {
|
||||||
hash := sha1.New()
|
return utils.MustFormatHashWithPrefix(maxIfbDeviceLength, ifbDevicePrefix, networkName+containerId)
|
||||||
_, err := hash.Write([]byte(networkName + containerId))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("%x", hash.Sum(nil))[:4], nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMTU(deviceName string) (int, error) {
|
func getMTU(deviceName string) (int, error) {
|
||||||
@ -205,10 +202,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ifbDeviceName, err := getIfbDeviceName(conf.Name, args.ContainerID)
|
ifbDeviceName := getIfbDeviceName(conf.Name, args.ContainerID)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = CreateIfb(ifbDeviceName, mtu)
|
err = CreateIfb(ifbDeviceName, mtu)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -239,10 +233,7 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ifbDeviceName, err := getIfbDeviceName(conf.Name, args.ContainerID)
|
ifbDeviceName := getIfbDeviceName(conf.Name, args.ContainerID)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := TeardownIfb(ifbDeviceName); err != nil {
|
if err := TeardownIfb(ifbDeviceName); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -343,10 +334,7 @@ func cmdCheck(args *skel.CmdArgs) error {
|
|||||||
latency := latencyInUsec(latencyInMillis)
|
latency := latencyInUsec(latencyInMillis)
|
||||||
limitInBytes := limit(uint64(rateInBytes), latency, uint32(burstInBytes))
|
limitInBytes := limit(uint64(rateInBytes), latency, uint32(burstInBytes))
|
||||||
|
|
||||||
ifbDeviceName, err := getIfbDeviceName(bwConf.Name, args.ContainerID)
|
ifbDeviceName := getIfbDeviceName(bwConf.Name, args.ContainerID)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ifbDevice, err := netlink.LinkByName(ifbDeviceName)
|
ifbDevice, err := netlink.LinkByName(ifbDeviceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/containernetworking/plugins/pkg/utils"
|
||||||
"github.com/containernetworking/plugins/pkg/utils/sysctl"
|
"github.com/containernetworking/plugins/pkg/utils/sysctl"
|
||||||
"github.com/coreos/go-iptables/iptables"
|
"github.com/coreos/go-iptables/iptables"
|
||||||
)
|
)
|
||||||
@ -172,7 +173,7 @@ func genToplevelDnatChain() chain {
|
|||||||
func genDnatChain(netName, containerID string) chain {
|
func genDnatChain(netName, containerID string) chain {
|
||||||
return chain{
|
return chain{
|
||||||
table: "nat",
|
table: "nat",
|
||||||
name: formatChainName("DN-", netName, containerID),
|
name: utils.MustFormatChainNameWithPrefix(netName, containerID, "DN-"),
|
||||||
entryChains: []string{TopLevelDNATChainName},
|
entryChains: []string{TopLevelDNATChainName},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -323,11 +324,9 @@ func enableLocalnetRouting(ifName string) error {
|
|||||||
// genOldSnatChain is no longer used, but used to be created. We'll try and
|
// genOldSnatChain is no longer used, but used to be created. We'll try and
|
||||||
// tear it down in case the plugin version changed between ADD and DEL
|
// tear it down in case the plugin version changed between ADD and DEL
|
||||||
func genOldSnatChain(netName, containerID string) chain {
|
func genOldSnatChain(netName, containerID string) chain {
|
||||||
name := formatChainName("SN-", netName, containerID)
|
|
||||||
|
|
||||||
return chain{
|
return chain{
|
||||||
table: "nat",
|
table: "nat",
|
||||||
name: name,
|
name: utils.MustFormatChainNameWithPrefix(netName, containerID, "SN-"),
|
||||||
entryChains: []string{OldTopLevelSNATChainName},
|
entryChains: []string{OldTopLevelSNATChainName},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha512"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -24,8 +23,6 @@ import (
|
|||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
)
|
)
|
||||||
|
|
||||||
const maxChainNameLength = 28
|
|
||||||
|
|
||||||
// fmtIpPort correctly formats ip:port literals for iptables and ip6tables -
|
// fmtIpPort correctly formats ip:port literals for iptables and ip6tables -
|
||||||
// need to wrap v6 literals in a []
|
// need to wrap v6 literals in a []
|
||||||
func fmtIpPort(ip net.IP, port int) string {
|
func fmtIpPort(ip net.IP, port int) string {
|
||||||
@ -62,12 +59,6 @@ func getRoutableHostIF(containerIP net.IP) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatChainName(prefix, name, id string) string {
|
|
||||||
chainBytes := sha512.Sum512([]byte(name + id))
|
|
||||||
chain := fmt.Sprintf("CNI-%s%x", prefix, chainBytes)
|
|
||||||
return chain[:maxChainNameLength]
|
|
||||||
}
|
|
||||||
|
|
||||||
// groupByProto groups port numbers by protocol
|
// groupByProto groups port numbers by protocol
|
||||||
func groupByProto(entries []PortMapEntry) map[string][]int {
|
func groupByProto(entries []PortMapEntry) map[string][]int {
|
||||||
if len(entries) == 0 {
|
if len(entries) == 0 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user