diff --git a/plugins/ipam/host-local/backend/allocator/config_test.go b/plugins/ipam/host-local/backend/allocator/config_test.go index cbae3d15..fc3793f7 100644 --- a/plugins/ipam/host-local/backend/allocator/config_test.go +++ b/plugins/ipam/host-local/backend/allocator/config_test.go @@ -372,7 +372,7 @@ var _ = Describe("IPAM config", func() { "type": "host-local", "ranges": [ [{"subnet": "10.1.2.0/24"}], - [{"subnet": "2001:db8:1::/24"}] + [{"subnet": "2001:db8:1::/48"}] ] } }` diff --git a/plugins/ipam/host-local/backend/allocator/range.go b/plugins/ipam/host-local/backend/allocator/range.go index e696b024..515afd0d 100644 --- a/plugins/ipam/host-local/backend/allocator/range.go +++ b/plugins/ipam/host-local/backend/allocator/range.go @@ -40,6 +40,12 @@ func (r *Range) Canonicalize() error { 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) diff --git a/plugins/ipam/host-local/backend/allocator/range_test.go b/plugins/ipam/host-local/backend/allocator/range_test.go index cb8ca01b..9cdb5383 100644 --- a/plugins/ipam/host-local/backend/allocator/range_test.go +++ b/plugins/ipam/host-local/backend/allocator/range_test.go @@ -25,7 +25,7 @@ import ( ) var _ = Describe("IP ranges", func() { - It("should generate sane defaults for ipv4", func() { + It("should generate sane defaults for ipv4 with a clean prefix", func() { snstr := "192.0.2.0/24" r := Range{Subnet: mustSubnet(snstr)} @@ -33,7 +33,7 @@ var _ = Describe("IP ranges", func() { Expect(err).NotTo(HaveOccurred()) Expect(r).To(Equal(Range{ - Subnet: mustSubnet(snstr), + Subnet: networkSubnet(snstr), RangeStart: net.IP{192, 0, 2, 1}, RangeEnd: net.IP{192, 0, 2, 254}, Gateway: net.IP{192, 0, 2, 1}, @@ -47,13 +47,41 @@ var _ = Describe("IP ranges", func() { Expect(err).NotTo(HaveOccurred()) Expect(r).To(Equal(Range{ - Subnet: mustSubnet(snstr), + Subnet: networkSubnet(snstr), RangeStart: net.IP{192, 0, 2, 1}, RangeEnd: net.IP{192, 0, 2, 126}, Gateway: net.IP{192, 0, 2, 1}, })) }) - It("should generate sane defaults for ipv6", func() { + It("should reject ipv4 subnet using a masked address", func() { + snstr := "192.0.2.12/24" + r := Range{Subnet: mustSubnet(snstr)} + + err := r.Canonicalize() + Expect(err).Should(MatchError("Network has host bits set. For a subnet mask of length 24 the network address is 192.0.2.0")) + }) + It("should reject ipv6 subnet using a masked address", func() { + snstr := "2001:DB8:1::24:19ff:fee1:c44a/64" + r := Range{Subnet: mustSubnet(snstr)} + + err := r.Canonicalize() + Expect(err).Should(MatchError("Network has host bits set. For a subnet mask of length 64 the network address is 2001:db8:1::")) + }) + It("should reject ipv6 prefix with host bit set", func() { + snstr := "2001:DB8:24:19ff::/63" + r := Range{Subnet: mustSubnet(snstr)} + + err := r.Canonicalize() + Expect(err).Should(MatchError("Network has host bits set. For a subnet mask of length 63 the network address is 2001:db8:24:19fe::")) + }) + It("should reject ipv4 network with host bit set", func() { + snstr := "192.168.127.0/23" + r := Range{Subnet: mustSubnet(snstr)} + + err := r.Canonicalize() + Expect(err).Should(MatchError("Network has host bits set. For a subnet mask of length 23 the network address is 192.168.126.0")) + }) + It("should generate sane defaults for ipv6 with a clean prefix", func() { snstr := "2001:DB8:1::/64" r := Range{Subnet: mustSubnet(snstr)} @@ -61,7 +89,7 @@ var _ = Describe("IP ranges", func() { Expect(err).NotTo(HaveOccurred()) Expect(r).To(Equal(Range{ - Subnet: mustSubnet(snstr), + Subnet: networkSubnet(snstr), RangeStart: net.ParseIP("2001:DB8:1::1"), RangeEnd: net.ParseIP("2001:DB8:1::ffff:ffff:ffff:ffff"), Gateway: net.ParseIP("2001:DB8:1::1"), @@ -75,16 +103,17 @@ var _ = Describe("IP ranges", func() { }) It("should reject invalid RangeStart and RangeEnd specifications", func() { - r := Range{Subnet: mustSubnet("192.0.2.0/24"), RangeStart: net.ParseIP("192.0.3.0")} + snstr := "192.0.2.0/24" + r := Range{Subnet: mustSubnet(snstr), RangeStart: net.ParseIP("192.0.3.0")} err := r.Canonicalize() Expect(err).Should(MatchError("RangeStart 192.0.3.0 not in network 192.0.2.0/24")) - r = Range{Subnet: mustSubnet("192.0.2.0/24"), RangeEnd: net.ParseIP("192.0.4.0")} + r = Range{Subnet: mustSubnet(snstr), RangeEnd: net.ParseIP("192.0.4.0")} err = r.Canonicalize() Expect(err).Should(MatchError("RangeEnd 192.0.4.0 not in network 192.0.2.0/24")) r = Range{ - Subnet: mustSubnet("192.0.2.0/24"), + Subnet: networkSubnet(snstr), RangeStart: net.ParseIP("192.0.2.50"), RangeEnd: net.ParseIP("192.0.2.40"), } @@ -99,8 +128,9 @@ var _ = Describe("IP ranges", func() { }) It("should parse all fields correctly", func() { + snstr := "192.0.2.0/24" r := Range{ - Subnet: mustSubnet("192.0.2.0/24"), + Subnet: mustSubnet(snstr), RangeStart: net.ParseIP("192.0.2.40"), RangeEnd: net.ParseIP("192.0.2.50"), Gateway: net.ParseIP("192.0.2.254"), @@ -109,7 +139,7 @@ var _ = Describe("IP ranges", func() { Expect(err).NotTo(HaveOccurred()) Expect(r).To(Equal(Range{ - Subnet: mustSubnet("192.0.2.0/24"), + Subnet: networkSubnet(snstr), RangeStart: net.IP{192, 0, 2, 40}, RangeEnd: net.IP{192, 0, 2, 50}, Gateway: net.IP{192, 0, 2, 254}, @@ -207,3 +237,9 @@ func mustSubnet(s string) types.IPNet { canonicalizeIP(&n.IP) return types.IPNet(*n) } + +func networkSubnet(s string) types.IPNet { + net := mustSubnet(s) + net.IP = net.IP.Mask(net.Mask) + return net +} diff --git a/plugins/ipam/host-local/host_local_test.go b/plugins/ipam/host-local/host_local_test.go index cf5a39b2..653d6175 100644 --- a/plugins/ipam/host-local/host_local_test.go +++ b/plugins/ipam/host-local/host_local_test.go @@ -444,7 +444,7 @@ var _ = Describe("host-local Operations", func() { "dataDir": "%s", "ranges": [ [{"subnet":"172.16.1.0/24"}, { "subnet": "10.1.2.0/24" }], - [{ "subnet": "2001:db8:1::/24" }] + [{ "subnet": "2001:db8:1::/48" }] ] }, "args": {