github.com/equinix-metal/virtlet@v1.5.2-0.20191204181327-1659b8a48e9b/tests/network/dhcp_test.go (about)

     1  /*
     2  Copyright 2016 Mirantis
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package network
    18  
    19  import (
    20  	"fmt"
    21  	"net"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/containernetworking/cni/pkg/ns"
    26  	cnitypes "github.com/containernetworking/cni/pkg/types"
    27  	cnicurrent "github.com/containernetworking/cni/pkg/types/current"
    28  	"github.com/vishvananda/netlink"
    29  
    30  	"github.com/Mirantis/virtlet/pkg/nettools"
    31  	"github.com/Mirantis/virtlet/pkg/network"
    32  )
    33  
    34  func TestDhcpServer(t *testing.T) {
    35  	clientMac, _ := net.ParseMAC(clientMacAddrs[0])
    36  	for _, tc := range []struct {
    37  		name               string
    38  		csn                network.ContainerSideNetwork
    39  		expectedSubstrings []string
    40  	}{
    41  		{
    42  			name: "with classless routes",
    43  			csn: network.ContainerSideNetwork{
    44  				Result: &cnicurrent.Result{
    45  					Interfaces: []*cnicurrent.Interface{
    46  						{
    47  							Name: "eth0",
    48  							Mac:  clientMacAddrs[0],
    49  							// Sandbox is clientNS dependent
    50  							// so it must be set in runtime
    51  						},
    52  					},
    53  					IPs: []*cnicurrent.IPConfig{
    54  						{
    55  							Version:   "4",
    56  							Interface: 0,
    57  							Address: net.IPNet{
    58  								IP:   net.IP{10, 1, 90, 5},
    59  								Mask: net.IPMask{255, 255, 255, 0},
    60  							},
    61  							Gateway: net.IP{10, 1, 90, 1},
    62  						},
    63  					},
    64  					Routes: []*cnitypes.Route{
    65  						{
    66  							Dst: net.IPNet{
    67  								IP:   net.IP{0, 0, 0, 0},
    68  								Mask: net.IPMask{0, 0, 0, 0},
    69  							},
    70  							GW: net.IP{10, 1, 90, 1},
    71  						},
    72  						{
    73  							Dst: net.IPNet{
    74  								IP:   net.IP{10, 10, 42, 0},
    75  								Mask: net.IPMask{255, 255, 255, 0},
    76  							},
    77  							GW: net.IP{10, 1, 90, 90},
    78  						},
    79  					},
    80  				},
    81  				Interfaces: []*network.InterfaceDescription{
    82  					{
    83  						HardwareAddr: clientMac,
    84  						MTU:          9000,
    85  					},
    86  				},
    87  			},
    88  			expectedSubstrings: []string{
    89  				"new_broadcast_address='10.1.90.255'",
    90  				"new_classless_static_routes='10.10.42.0/24 10.1.90.90 0.0.0.0/0 10.1.90.1'",
    91  				"new_dhcp_lease_time='86400'",
    92  				"new_dhcp_rebinding_time='64800'",
    93  				"new_dhcp_renewal_time='43200'",
    94  				"new_dhcp_server_identifier='169.254.254.2'",
    95  				"new_domain_name_servers='8.8.8.8'",
    96  				"new_ip_address='10.1.90.5'",
    97  				"new_interface_mtu='9000'",
    98  				"new_network_number='10.1.90.0'",
    99  				"new_subnet_cidr='24'",
   100  				"new_subnet_mask='255.255.255.0'",
   101  				"veth0: offered 10.1.90.5 from 169.254.254.2",
   102  			},
   103  		},
   104  		{
   105  			name: "without classless routes",
   106  			csn: network.ContainerSideNetwork{
   107  				Result: &cnicurrent.Result{
   108  					Interfaces: []*cnicurrent.Interface{
   109  						{
   110  							Name: "eth0",
   111  							Mac:  clientMacAddrs[0],
   112  							// Sandbox is clientNS dependent
   113  							// so it must be set in runtime
   114  						},
   115  					},
   116  					IPs: []*cnicurrent.IPConfig{
   117  						{
   118  							Version:   "4",
   119  							Interface: 0,
   120  							Address: net.IPNet{
   121  								IP:   net.IP{10, 1, 90, 5},
   122  								Mask: net.IPMask{255, 255, 255, 0},
   123  							},
   124  							Gateway: net.IP{10, 1, 90, 1},
   125  						},
   126  					},
   127  					Routes: []*cnitypes.Route{
   128  						{
   129  							Dst: net.IPNet{
   130  								IP:   net.IP{0, 0, 0, 0},
   131  								Mask: net.IPMask{0, 0, 0, 0},
   132  							},
   133  							GW: net.IP{10, 1, 90, 1},
   134  						},
   135  					},
   136  				},
   137  				Interfaces: []*network.InterfaceDescription{
   138  					{
   139  						HardwareAddr: clientMac,
   140  						MTU:          9000,
   141  					},
   142  				},
   143  			},
   144  			expectedSubstrings: []string{
   145  				"new_broadcast_address='10.1.90.255'",
   146  				"new_dhcp_lease_time='86400'",
   147  				"new_dhcp_rebinding_time='64800'",
   148  				"new_dhcp_renewal_time='43200'",
   149  				"new_dhcp_server_identifier='169.254.254.2'",
   150  				"new_domain_name_servers='8.8.8.8'",
   151  				"new_ip_address='10.1.90.5'",
   152  				"new_interface_mtu='9000'",
   153  				"new_network_number='10.1.90.0'",
   154  				"new_routers='10.1.90.1'",
   155  				"new_subnet_cidr='24'",
   156  				"new_subnet_mask='255.255.255.0'",
   157  				"veth0: offered 10.1.90.5 from 169.254.254.2",
   158  			},
   159  		},
   160  		// TODO: add dns test case here
   161  	} {
   162  		t.Run(tc.name, func(t *testing.T) {
   163  			serverNS, err := ns.NewNS()
   164  			if err != nil {
   165  				t.Fatalf("Failed to create ns for dhcp server: %v", err)
   166  			}
   167  			defer serverNS.Close()
   168  			clientNS, err := ns.NewNS()
   169  			if err != nil {
   170  				t.Fatalf("Failed to create ns for dhcp client: %v", err)
   171  			}
   172  			defer clientNS.Close()
   173  
   174  			// Sandbox is clientNS dependent so it needs to be set there on all interfaces
   175  			for _, iface := range tc.csn.Result.Interfaces {
   176  				iface.Sandbox = clientNS.Path()
   177  			}
   178  
   179  			var clientVeth, serverVeth netlink.Link
   180  			if err := serverNS.Do(func(ns.NetNS) error {
   181  				serverVeth, clientVeth, err = nettools.CreateEscapeVethPair(clientNS, "veth0", 1500)
   182  				if err != nil {
   183  					return fmt.Errorf("failed to create escape veth pair: %v", err)
   184  				}
   185  				addr, err := netlink.ParseAddr("169.254.254.2/24")
   186  				if err != nil {
   187  					return fmt.Errorf("failed to parse dhcp interface address: %v", err)
   188  				}
   189  				if err = netlink.AddrAdd(serverVeth, addr); err != nil {
   190  					return fmt.Errorf("failed to add addr for serverVeth: %v", err)
   191  				}
   192  
   193  				return nil
   194  			}); err != nil {
   195  				t.Fatal(err)
   196  			}
   197  
   198  			if err := clientNS.Do(func(ns.NetNS) error {
   199  				mac, _ := net.ParseMAC(clientMacAddrs[0])
   200  				if err = nettools.SetHardwareAddr(clientVeth, mac); err != nil {
   201  					return fmt.Errorf("can't set MAC address on the client interface: %v", err)
   202  				}
   203  
   204  				return nil
   205  			}); err != nil {
   206  				t.Fatal(err)
   207  			}
   208  
   209  			g := NewNetTestGroup(t, 15*time.Second)
   210  			defer g.Stop()
   211  			g.Add(serverNS, NewDhcpServerTester(&tc.csn))
   212  
   213  			g.Add(clientNS, NewDhcpClient("veth0", tc.expectedSubstrings))
   214  			g.Wait()
   215  		})
   216  	}
   217  }