| // Copyright 2020 The Monogon Project Authors. |
| // |
| // SPDX-License-Identifier: Apache-2.0 |
| // |
| // 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 dhcp4c |
| |
| import ( |
| "bytes" |
| "net" |
| "testing" |
| |
| "github.com/insomniacslk/dhcp/dhcpv4" |
| "github.com/stretchr/testify/assert" |
| ) |
| |
| func TestLeaseDHCPServers(t *testing.T) { |
| var tests = []struct { |
| name string |
| lease *Lease |
| expected DNSServers |
| }{{ |
| name: "ReturnsNilWithNoLease", |
| lease: nil, |
| expected: nil, |
| }, { |
| name: "DiscardsInvalidIPs", |
| lease: &Lease{ |
| Options: dhcpv4.OptionsFromList(dhcpv4.OptDNS(net.IP{0, 0, 0, 0})), |
| }, |
| expected: nil, |
| }, { |
| name: "DeduplicatesIPs", |
| lease: &Lease{ |
| Options: dhcpv4.OptionsFromList(dhcpv4.OptDNS(net.IP{192, 0, 2, 1}, net.IP{192, 0, 2, 2}, net.IP{192, 0, 2, 1})), |
| }, |
| expected: DNSServers{net.IP{192, 0, 2, 1}, net.IP{192, 0, 2, 2}}, |
| }} |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| res := test.lease.DNSServers() |
| assert.Equal(t, test.expected, res) |
| }) |
| } |
| } |
| |
| func makeIPNet(cidr string) *net.IPNet { |
| _, n, err := net.ParseCIDR(cidr) |
| if err != nil { |
| panic(err) |
| } |
| return n |
| } |
| |
| type testRoute struct { |
| dest string |
| via string |
| expected bool |
| } |
| |
| func (t testRoute) toRealRoute() *dhcpv4.Route { |
| ip, n, err := net.ParseCIDR(t.dest) |
| if err != nil { |
| panic(err) |
| } |
| if !ip.Equal(n.IP) { |
| panic("testRoute is not aligned to route boundary") |
| } |
| routerIP := net.ParseIP(t.via) |
| if routerIP == nil && t.via != "" { |
| panic("routerIP is not valid") |
| } |
| return &dhcpv4.Route{ |
| Dest: n, |
| Router: routerIP, |
| } |
| } |
| |
| func TestSanitizeRoutes(t *testing.T) { |
| var tests = []struct { |
| name string |
| assignedNet *net.IPNet |
| routes []testRoute |
| }{{ |
| name: "SimpleAdditionalRoute", |
| assignedNet: makeIPNet("10.0.5.0/24"), |
| routes: []testRoute{ |
| {"10.0.7.0/24", "10.0.5.1", true}, |
| }, |
| }, { |
| name: "OutOfNetworkGateway", |
| assignedNet: makeIPNet("10.5.0.2/32"), |
| routes: []testRoute{ |
| {"10.0.7.1/32", "", true}, |
| {"0.0.0.0/0", "10.0.7.1", true}, |
| }, |
| }, { |
| name: "InvalidRouter", |
| assignedNet: makeIPNet("10.0.5.0/24"), |
| routes: []testRoute{ |
| // Router is localhost |
| {"10.0.7.0/24", "127.0.0.1", false}, |
| // Router is unreachable |
| {"10.0.8.0/24", "10.254.0.1", false}, |
| }, |
| }, { |
| name: "SameDestinationRoutes", |
| assignedNet: makeIPNet("10.0.5.0/24"), |
| routes: []testRoute{ |
| {"0.0.0.0/0", "10.0.5.1", true}, |
| {"10.0.7.0/24", "10.0.5.1", true}, |
| {"0.0.0.0/0", "10.0.7.1", false}, |
| }, |
| }, { |
| name: "RoutesShadowingLoopback", |
| assignedNet: makeIPNet("10.0.5.0/24"), |
| routes: []testRoute{ |
| // Default route, technically covers 127.0.0.0/8, but less-specific |
| {"0.0.0.0/0", "10.0.5.1", true}, |
| // 127.0.0.0/8 is still more-specific |
| {"126.0.0.0/7", "10.0.5.1", true}, |
| // Duplicate of 127.0.0.0/8, behavior undefined, disallowed |
| {"127.0.0.0/8", "10.0.5.1", false}, |
| // Shadows localhost, disallowed |
| {"127.0.0.1/32", "10.0.5.1", false}, |
| }, |
| }} |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| var routes []*dhcpv4.Route |
| var expectedRoutes []*dhcpv4.Route |
| for _, r := range test.routes { |
| routes = append(routes, r.toRealRoute()) |
| if r.expected { |
| expectedRoutes = append(expectedRoutes, r.toRealRoute()) |
| } |
| } |
| out := sanitizeRoutes(routes, test.assignedNet) |
| if len(out) != len(expectedRoutes) { |
| t.Errorf("expected %d routes, got %d", len(expectedRoutes), len(out)) |
| t.Error("Expected:") |
| for _, r := range expectedRoutes { |
| t.Errorf("\t%s via %s", r.Dest, r.Router) |
| } |
| t.Error("Actual:") |
| for _, r := range out { |
| t.Errorf("\t%s via %s", r.Dest, r.Router) |
| } |
| return |
| } |
| for i, r := range expectedRoutes { |
| if !out[i].Router.Equal(r.Router) || !out[i].Dest.IP.Equal(r.Dest.IP) || !bytes.Equal(out[i].Dest.Mask, r.Dest.Mask) { |
| t.Errorf("expected %s via %s, got %s via %s", r.Dest, r.Router, out[i].Dest, out[i].Router) |
| } |
| } |
| }) |
| } |
| } |