fix: add dhcp discover packet to linux swiftv2 to ensure mappings exist for dns (#2989)
* add dhcp client * address linter issues * issue dhcp discover request without libraries * remove library references * add build tag and remove rand dependency * address linter issues * update logging * fix logging * move unix sockets to interface to address feedback * address linter issues * prevent leaking sockets on error * remove reuse address option * add comments * make dhcp client
This commit is contained in:
Родитель
0c48fa06da
Коммит
47b4329b06
|
@ -21,6 +21,7 @@ import (
|
||||||
"github.com/Azure/azure-container-networking/cns"
|
"github.com/Azure/azure-container-networking/cns"
|
||||||
cnscli "github.com/Azure/azure-container-networking/cns/client"
|
cnscli "github.com/Azure/azure-container-networking/cns/client"
|
||||||
"github.com/Azure/azure-container-networking/common"
|
"github.com/Azure/azure-container-networking/common"
|
||||||
|
"github.com/Azure/azure-container-networking/dhcp"
|
||||||
"github.com/Azure/azure-container-networking/iptables"
|
"github.com/Azure/azure-container-networking/iptables"
|
||||||
"github.com/Azure/azure-container-networking/netio"
|
"github.com/Azure/azure-container-networking/netio"
|
||||||
"github.com/Azure/azure-container-networking/netlink"
|
"github.com/Azure/azure-container-networking/netlink"
|
||||||
|
@ -130,7 +131,7 @@ func NewPlugin(name string,
|
||||||
|
|
||||||
nl := netlink.NewNetlink()
|
nl := netlink.NewNetlink()
|
||||||
// Setup network manager.
|
// Setup network manager.
|
||||||
nm, err := network.NewNetworkManager(nl, platform.NewExecClient(logger), &netio.NetIO{}, network.NewNamespaceClient(), iptables.NewClient())
|
nm, err := network.NewNetworkManager(nl, platform.NewExecClient(logger), &netio.NetIO{}, network.NewNamespaceClient(), iptables.NewClient(), dhcp.New(logger))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,461 @@
|
||||||
|
//go:build linux
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package dhcp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"golang.org/x/net/ipv4"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
dhcpDiscover = 1
|
||||||
|
bootRequest = 1
|
||||||
|
ethPAll = 0x0003
|
||||||
|
MaxUDPReceivedPacketSize = 8192
|
||||||
|
dhcpServerPort = 67
|
||||||
|
dhcpClientPort = 68
|
||||||
|
dhcpOpCodeReply = 2
|
||||||
|
bootpMinLen = 300
|
||||||
|
bytesInAddress = 4 // bytes in an ip address
|
||||||
|
macBytes = 6 // bytes in a mac address
|
||||||
|
udpProtocol = 17
|
||||||
|
|
||||||
|
opRequest = 1
|
||||||
|
htypeEthernet = 1
|
||||||
|
hlenEthernet = 6
|
||||||
|
hops = 0
|
||||||
|
secs = 0
|
||||||
|
flags = 0x8000 // Broadcast flag
|
||||||
|
)
|
||||||
|
|
||||||
|
// TransactionID represents a 4-byte DHCP transaction ID as defined in RFC 951,
|
||||||
|
// Section 3.
|
||||||
|
//
|
||||||
|
// The TransactionID is used to match DHCP replies to their original request.
|
||||||
|
type TransactionID [4]byte
|
||||||
|
|
||||||
|
var (
|
||||||
|
magicCookie = []byte{0x63, 0x82, 0x53, 0x63} // DHCP magic cookie
|
||||||
|
DefaultReadTimeout = 3 * time.Second
|
||||||
|
DefaultTimeout = 3 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
type DHCP struct {
|
||||||
|
logger *zap.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(logger *zap.Logger) *DHCP {
|
||||||
|
return &DHCP{
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Socket struct {
|
||||||
|
fd int
|
||||||
|
remoteAddr unix.SockaddrInet4
|
||||||
|
}
|
||||||
|
|
||||||
|
// Linux specific
|
||||||
|
// returns a writer which should always be closed, even if we return an error
|
||||||
|
func NewWriteSocket(ifname string, remoteAddr unix.SockaddrInet4) (io.WriteCloser, error) {
|
||||||
|
fd, err := MakeBroadcastSocket(ifname)
|
||||||
|
ret := &Socket{
|
||||||
|
fd: fd,
|
||||||
|
remoteAddr: remoteAddr,
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return ret, errors.Wrap(err, "could not make dhcp write socket")
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Socket) Write(packetBytes []byte) (int, error) {
|
||||||
|
err := unix.Sendto(s.fd, packetBytes, 0, &s.remoteAddr)
|
||||||
|
if err != nil {
|
||||||
|
return 0, errors.Wrap(err, "failed unix send to")
|
||||||
|
}
|
||||||
|
return len(packetBytes), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a reader which should always be closed, even if we return an error
|
||||||
|
func NewReadSocket(ifname string, timeout time.Duration) (io.ReadCloser, error) {
|
||||||
|
fd, err := makeListeningSocket(ifname, timeout)
|
||||||
|
ret := &Socket{
|
||||||
|
fd: fd,
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return ret, errors.Wrap(err, "could not make dhcp read socket")
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Socket) Read(p []byte) (n int, err error) {
|
||||||
|
n, _, innerErr := unix.Recvfrom(s.fd, p, 0)
|
||||||
|
if innerErr != nil {
|
||||||
|
return 0, errors.Wrap(err, "failed unix recv from")
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Socket) Close() error {
|
||||||
|
// do not attempt to close fd with -1 as they are not valid
|
||||||
|
if s.fd == -1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Ensure the file descriptor is closed when done
|
||||||
|
if err := unix.Close(s.fd); err != nil {
|
||||||
|
return errors.Wrap(err, "error closing dhcp unix socket")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateTransactionID generates a random 32-bits number suitable for use as TransactionID
|
||||||
|
func GenerateTransactionID() (TransactionID, error) {
|
||||||
|
var xid TransactionID
|
||||||
|
_, err := rand.Read(xid[:])
|
||||||
|
if err != nil {
|
||||||
|
return xid, errors.Errorf("could not get random number: %v", err)
|
||||||
|
}
|
||||||
|
return xid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeListeningSocket(ifname string, timeout time.Duration) (int, error) {
|
||||||
|
// reference: https://manned.org/packet.7
|
||||||
|
// starts listening to the specified protocol, or none if zero
|
||||||
|
// the SockaddrLinkLayer also ensures packets for the htons(unix.ETH_P_IP) prot are received
|
||||||
|
fd, err := unix.Socket(unix.AF_PACKET, unix.SOCK_DGRAM, int(htons(unix.ETH_P_IP)))
|
||||||
|
if err != nil {
|
||||||
|
return fd, errors.Wrap(err, "dhcp socket creation failure")
|
||||||
|
}
|
||||||
|
iface, err := net.InterfaceByName(ifname)
|
||||||
|
if err != nil {
|
||||||
|
return fd, errors.Wrap(err, "dhcp failed to get interface")
|
||||||
|
}
|
||||||
|
llAddr := unix.SockaddrLinklayer{
|
||||||
|
Ifindex: iface.Index,
|
||||||
|
Protocol: htons(unix.ETH_P_IP),
|
||||||
|
}
|
||||||
|
err = unix.Bind(fd, &llAddr)
|
||||||
|
|
||||||
|
// set max time waiting without any data received
|
||||||
|
timeval := unix.NsecToTimeval(timeout.Nanoseconds())
|
||||||
|
if innerErr := unix.SetsockoptTimeval(fd, unix.SOL_SOCKET, unix.SO_RCVTIMEO, &timeval); innerErr != nil {
|
||||||
|
return fd, errors.Wrap(innerErr, "could not set timeout on socket")
|
||||||
|
}
|
||||||
|
|
||||||
|
return fd, errors.Wrap(err, "dhcp failed to bind")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeBroadcastSocket creates a socket that can be passed to unix.Sendto
|
||||||
|
// that will send packets out to the broadcast address.
|
||||||
|
func MakeBroadcastSocket(ifname string) (int, error) {
|
||||||
|
fd, err := makeRawSocket(ifname)
|
||||||
|
if err != nil {
|
||||||
|
return fd, err
|
||||||
|
}
|
||||||
|
// enables broadcast (disabled by default)
|
||||||
|
err = unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_BROADCAST, 1)
|
||||||
|
if err != nil {
|
||||||
|
return fd, errors.Wrap(err, "dhcp failed to set sockopt")
|
||||||
|
}
|
||||||
|
return fd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// conversion between host and network byte order
|
||||||
|
func htons(v uint16) uint16 {
|
||||||
|
var tmp [2]byte
|
||||||
|
binary.BigEndian.PutUint16(tmp[:], v)
|
||||||
|
return binary.LittleEndian.Uint16(tmp[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func BindToInterface(fd int, ifname string) error {
|
||||||
|
return errors.Wrap(unix.BindToDevice(fd, ifname), "failed to bind to device")
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeRawSocket creates a socket that can be passed to unix.Sendto.
|
||||||
|
func makeRawSocket(ifname string) (int, error) {
|
||||||
|
// AF_INET sends via IPv4, SOCK_RAW means create an ip datagram socket (skips udp transport layer, see below)
|
||||||
|
fd, err := unix.Socket(unix.AF_INET, unix.SOCK_RAW, unix.IPPROTO_RAW)
|
||||||
|
if err != nil {
|
||||||
|
return fd, errors.Wrap(err, "dhcp raw socket creation failure")
|
||||||
|
}
|
||||||
|
// Later on when we write to this socket, our packet already contains the header (we create it with MakeRawUDPPacket).
|
||||||
|
err = unix.SetsockoptInt(fd, unix.IPPROTO_IP, unix.IP_HDRINCL, 1)
|
||||||
|
if err != nil {
|
||||||
|
return fd, errors.Wrap(err, "dhcp failed to set hdrincl raw sockopt")
|
||||||
|
}
|
||||||
|
err = BindToInterface(fd, ifname)
|
||||||
|
if err != nil {
|
||||||
|
return fd, errors.Wrap(err, "dhcp failed to bind to interface")
|
||||||
|
}
|
||||||
|
return fd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build DHCP Discover Packet
|
||||||
|
func buildDHCPDiscover(mac net.HardwareAddr, txid TransactionID) ([]byte, error) {
|
||||||
|
if len(mac) != macBytes {
|
||||||
|
return nil, errors.Errorf("invalid MAC address length")
|
||||||
|
}
|
||||||
|
|
||||||
|
var packet bytes.Buffer
|
||||||
|
|
||||||
|
// BOOTP header
|
||||||
|
packet.WriteByte(opRequest) // op: BOOTREQUEST (1)
|
||||||
|
packet.WriteByte(htypeEthernet) // htype: Ethernet (1)
|
||||||
|
packet.WriteByte(hlenEthernet) // hlen: MAC address length (6)
|
||||||
|
packet.WriteByte(hops) // hops: 0
|
||||||
|
packet.Write(txid[:]) // xid: Transaction ID (4 bytes)
|
||||||
|
err := binary.Write(&packet, binary.BigEndian, uint16(secs)) // secs: Seconds elapsed
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to write seconds elapsed")
|
||||||
|
}
|
||||||
|
err = binary.Write(&packet, binary.BigEndian, uint16(flags)) // flags: Broadcast flag
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to write broadcast flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client IP address (0.0.0.0)
|
||||||
|
packet.Write(make([]byte, bytesInAddress))
|
||||||
|
// Your IP address (0.0.0.0)
|
||||||
|
packet.Write(make([]byte, bytesInAddress))
|
||||||
|
// Server IP address (0.0.0.0)
|
||||||
|
packet.Write(make([]byte, bytesInAddress))
|
||||||
|
// Gateway IP address (0.0.0.0)
|
||||||
|
packet.Write(make([]byte, bytesInAddress))
|
||||||
|
|
||||||
|
// chaddr: Client hardware address (MAC address)
|
||||||
|
paddingBytes := 10
|
||||||
|
packet.Write(mac) // MAC address (6 bytes)
|
||||||
|
packet.Write(make([]byte, paddingBytes)) // Padding to 16 bytes
|
||||||
|
|
||||||
|
// sname: Server host name (64 bytes)
|
||||||
|
serverHostNameBytes := 64
|
||||||
|
packet.Write(make([]byte, serverHostNameBytes))
|
||||||
|
// file: Boot file name (128 bytes)
|
||||||
|
bootFileNameBytes := 128
|
||||||
|
packet.Write(make([]byte, bootFileNameBytes))
|
||||||
|
|
||||||
|
// Magic cookie (DHCP)
|
||||||
|
err = binary.Write(&packet, binary.BigEndian, magicCookie)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to write magic cookie")
|
||||||
|
}
|
||||||
|
|
||||||
|
// DHCP options (minimal required options for DISCOVER)
|
||||||
|
packet.Write([]byte{
|
||||||
|
53, 1, 1, // Option 53: DHCP Message Type (1 = DHCP Discover)
|
||||||
|
55, 3, 1, 3, 6, // Option 55: Parameter Request List (1 = Subnet Mask, 3 = Router, 6 = DNS)
|
||||||
|
255, // End option
|
||||||
|
})
|
||||||
|
|
||||||
|
// padding length to 300 bytes
|
||||||
|
var value uint8 // default is zero
|
||||||
|
if packet.Len() < bootpMinLen {
|
||||||
|
packet.Write(bytes.Repeat([]byte{value}, bootpMinLen-packet.Len()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return packet.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRawUDPPacket converts a payload (a serialized packet) into a
|
||||||
|
// raw UDP packet for the specified serverAddr from the specified clientAddr.
|
||||||
|
func MakeRawUDPPacket(payload []byte, serverAddr, clientAddr net.UDPAddr) ([]byte, error) {
|
||||||
|
udpBytes := 8
|
||||||
|
udp := make([]byte, udpBytes)
|
||||||
|
binary.BigEndian.PutUint16(udp[:2], uint16(clientAddr.Port))
|
||||||
|
binary.BigEndian.PutUint16(udp[2:4], uint16(serverAddr.Port))
|
||||||
|
totalLen := uint16(udpBytes + len(payload))
|
||||||
|
binary.BigEndian.PutUint16(udp[4:6], totalLen)
|
||||||
|
binary.BigEndian.PutUint16(udp[6:8], 0) // try to offload the checksum
|
||||||
|
|
||||||
|
headerVersion := 4
|
||||||
|
headerLen := 20
|
||||||
|
headerTTL := 64
|
||||||
|
|
||||||
|
h := ipv4.Header{
|
||||||
|
Version: headerVersion, // nolint
|
||||||
|
Len: headerLen, // nolint
|
||||||
|
TotalLen: headerLen + len(udp) + len(payload),
|
||||||
|
TTL: headerTTL,
|
||||||
|
Protocol: udpProtocol, // UDP
|
||||||
|
Dst: serverAddr.IP,
|
||||||
|
Src: clientAddr.IP,
|
||||||
|
}
|
||||||
|
ret, err := h.Marshal()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to marshal when making udp packet")
|
||||||
|
}
|
||||||
|
ret = append(ret, udp...)
|
||||||
|
ret = append(ret, payload...)
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receive DHCP response packet using reader
|
||||||
|
func (c *DHCP) receiveDHCPResponse(ctx context.Context, reader io.ReadCloser, xid TransactionID) error {
|
||||||
|
recvErrors := make(chan error, 1)
|
||||||
|
// Recvfrom is a blocking call, so if something goes wrong with its timeout it won't return.
|
||||||
|
|
||||||
|
// Additionally, the timeout on the socket (on the Read(...)) call is how long until the socket times out and gives an error,
|
||||||
|
// but it won't error if we do get some sort of data within the time out period.
|
||||||
|
|
||||||
|
// If we get some data (even if it is not the packet we are looking for, like wrong txid, wrong response opcode etc.)
|
||||||
|
// then we continue in the for loop. We then call recvfrom again which will reset the timeout period
|
||||||
|
// Without the secondary timeout at the bottom of the function, we could stay stuck in the for loop as long as we receive packets.
|
||||||
|
go func(errs chan<- error) {
|
||||||
|
// loop will only exit if there is an error, context canceled, or we find our reply packet
|
||||||
|
for {
|
||||||
|
if ctx.Err() != nil {
|
||||||
|
errs <- ctx.Err()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, MaxUDPReceivedPacketSize)
|
||||||
|
// Blocks until data received or timeout period is reached
|
||||||
|
n, innerErr := reader.Read(buf)
|
||||||
|
if innerErr != nil {
|
||||||
|
errs <- innerErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// check header
|
||||||
|
var iph ipv4.Header
|
||||||
|
if err := iph.Parse(buf[:n]); err != nil {
|
||||||
|
// skip non-IP data
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if iph.Protocol != udpProtocol {
|
||||||
|
// skip non-UDP packets
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
udph := buf[iph.Len:n]
|
||||||
|
// source is from dhcp server if receiving
|
||||||
|
srcPort := int(binary.BigEndian.Uint16(udph[0:2]))
|
||||||
|
if srcPort != dhcpServerPort {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// client is to dhcp client if receiving
|
||||||
|
dstPort := int(binary.BigEndian.Uint16(udph[2:4]))
|
||||||
|
if dstPort != dhcpClientPort {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// check payload
|
||||||
|
pLen := int(binary.BigEndian.Uint16(udph[4:6]))
|
||||||
|
payload := buf[iph.Len+8 : iph.Len+pLen]
|
||||||
|
|
||||||
|
// retrieve opcode from payload
|
||||||
|
opcode := payload[0] // opcode is first byte
|
||||||
|
// retrieve txid from payload
|
||||||
|
txidOffset := 4 // after 4 bytes, the txid starts
|
||||||
|
// the txid is 4 bytes, so we take four bytes after the offset
|
||||||
|
txid := payload[txidOffset : txidOffset+4]
|
||||||
|
|
||||||
|
c.logger.Info("Received packet", zap.Int("opCode", int(opcode)), zap.Any("transactionID", TransactionID(txid)))
|
||||||
|
if opcode != dhcpOpCodeReply {
|
||||||
|
continue // opcode is not a reply, so continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if TransactionID(txid) == xid {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// only occurs if we find our reply packet successfully
|
||||||
|
// a nil error means a reply was found for this txid
|
||||||
|
recvErrors <- nil
|
||||||
|
}(recvErrors)
|
||||||
|
|
||||||
|
// sends a message on repeat after timeout, but only the first one matters
|
||||||
|
ticker := time.NewTicker(DefaultReadTimeout)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-recvErrors:
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "error during receiving")
|
||||||
|
}
|
||||||
|
case <-ticker.C:
|
||||||
|
return errors.New("timed out waiting for replies")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issues a DHCP Discover packet from the nic specified by mac and name ifname
|
||||||
|
// Returns nil if a reply to the transaction was received, or error if time out
|
||||||
|
// Does not return the DHCP Offer that was received from the DHCP server
|
||||||
|
func (c *DHCP) DiscoverRequest(ctx context.Context, mac net.HardwareAddr, ifname string) error {
|
||||||
|
txid, err := GenerateTransactionID()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to generate random transaction id")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used in later steps
|
||||||
|
raddr := &net.UDPAddr{IP: net.IPv4bcast, Port: dhcpServerPort}
|
||||||
|
laddr := &net.UDPAddr{IP: net.IPv4zero, Port: dhcpClientPort}
|
||||||
|
var destination [net.IPv4len]byte
|
||||||
|
copy(destination[:], raddr.IP.To4())
|
||||||
|
|
||||||
|
// Build a DHCP discover packet
|
||||||
|
dhcpPacket, err := buildDHCPDiscover(mac, txid)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to build dhcp discover packet")
|
||||||
|
}
|
||||||
|
// Make UDP packet from dhcp packet in previous steps
|
||||||
|
packetToSendBytes, err := MakeRawUDPPacket(dhcpPacket, *raddr, *laddr)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "error making raw udp packet")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make writer
|
||||||
|
remoteAddr := unix.SockaddrInet4{Port: laddr.Port, Addr: destination}
|
||||||
|
writer, err := NewWriteSocket(ifname, remoteAddr)
|
||||||
|
defer func() {
|
||||||
|
// Ensure the file descriptor is closed when done
|
||||||
|
if err = writer.Close(); err != nil {
|
||||||
|
c.logger.Error("Error closing dhcp writer socket:", zap.Error(err))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to make broadcast socket")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make reader
|
||||||
|
deadline, ok := ctx.Deadline()
|
||||||
|
if !ok {
|
||||||
|
return errors.New("no deadline for passed in context")
|
||||||
|
}
|
||||||
|
timeout := time.Until(deadline)
|
||||||
|
// note: if the write/send takes a long time DiscoverRequest might take a bit longer than the deadline
|
||||||
|
reader, err := NewReadSocket(ifname, timeout)
|
||||||
|
defer func() {
|
||||||
|
// Ensure the file descriptor is closed when done
|
||||||
|
if err = reader.Close(); err != nil {
|
||||||
|
c.logger.Error("Error closing dhcp reader socket:", zap.Error(err))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to make listening socket")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once writer and reader created, start sending and receiving
|
||||||
|
_, err = writer.Write(packetToSendBytes)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to send dhcp discover packet")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.logger.Info("DHCP Discover packet was sent successfully", zap.Any("transactionID", txid))
|
||||||
|
|
||||||
|
// Wait for DHCP response (Offer)
|
||||||
|
res := c.receiveDHCPResponse(ctx, reader, txid)
|
||||||
|
return res
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package dhcp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DHCP struct {
|
||||||
|
logger *zap.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(logger *zap.Logger) *DHCP {
|
||||||
|
return &DHCP{
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DHCP) DiscoverRequest(_ context.Context, _ net.HardwareAddr, _ string) error {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package network
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dhcpClient interface {
|
||||||
|
DiscoverRequest(context.Context, net.HardwareAddr, string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockDHCP struct{}
|
||||||
|
|
||||||
|
func (netns *mockDHCP) DiscoverRequest(context.Context, net.HardwareAddr, string) error {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -169,6 +169,7 @@ func (nw *network) newEndpoint(
|
||||||
netioCli netio.NetIOInterface,
|
netioCli netio.NetIOInterface,
|
||||||
nsc NamespaceClientInterface,
|
nsc NamespaceClientInterface,
|
||||||
iptc ipTablesClient,
|
iptc ipTablesClient,
|
||||||
|
dhcpc dhcpClient,
|
||||||
epInfo *EndpointInfo,
|
epInfo *EndpointInfo,
|
||||||
) (*endpoint, error) {
|
) (*endpoint, error) {
|
||||||
var ep *endpoint
|
var ep *endpoint
|
||||||
|
@ -182,7 +183,7 @@ func (nw *network) newEndpoint(
|
||||||
|
|
||||||
// Call the platform implementation.
|
// Call the platform implementation.
|
||||||
// Pass nil for epClient and will be initialized in newendpointImpl
|
// Pass nil for epClient and will be initialized in newendpointImpl
|
||||||
ep, err = nw.newEndpointImpl(apipaCli, nl, plc, netioCli, nil, nsc, iptc, epInfo)
|
ep, err = nw.newEndpointImpl(apipaCli, nl, plc, netioCli, nil, nsc, iptc, dhcpc, epInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -195,7 +196,7 @@ func (nw *network) newEndpoint(
|
||||||
|
|
||||||
// DeleteEndpoint deletes an existing endpoint from the network.
|
// DeleteEndpoint deletes an existing endpoint from the network.
|
||||||
func (nw *network) deleteEndpoint(nl netlink.NetlinkInterface, plc platform.ExecClient, nioc netio.NetIOInterface, nsc NamespaceClientInterface,
|
func (nw *network) deleteEndpoint(nl netlink.NetlinkInterface, plc platform.ExecClient, nioc netio.NetIOInterface, nsc NamespaceClientInterface,
|
||||||
iptc ipTablesClient, endpointID string,
|
iptc ipTablesClient, dhcpc dhcpClient, endpointID string,
|
||||||
) error {
|
) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
@ -215,7 +216,7 @@ func (nw *network) deleteEndpoint(nl netlink.NetlinkInterface, plc platform.Exec
|
||||||
|
|
||||||
// Call the platform implementation.
|
// Call the platform implementation.
|
||||||
// Pass nil for epClient and will be initialized in deleteEndpointImpl
|
// Pass nil for epClient and will be initialized in deleteEndpointImpl
|
||||||
err = nw.deleteEndpointImpl(nl, plc, nil, nioc, nsc, iptc, ep)
|
err = nw.deleteEndpointImpl(nl, plc, nil, nioc, nsc, iptc, dhcpc, ep)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,7 @@ func (nw *network) newEndpointImpl(
|
||||||
testEpClient EndpointClient,
|
testEpClient EndpointClient,
|
||||||
nsc NamespaceClientInterface,
|
nsc NamespaceClientInterface,
|
||||||
iptc ipTablesClient,
|
iptc ipTablesClient,
|
||||||
|
dhcpclient dhcpClient,
|
||||||
epInfo *EndpointInfo,
|
epInfo *EndpointInfo,
|
||||||
) (*endpoint, error) {
|
) (*endpoint, error) {
|
||||||
var (
|
var (
|
||||||
|
@ -167,7 +168,7 @@ func (nw *network) newEndpointImpl(
|
||||||
epClient = NewLinuxBridgeEndpointClient(nw.extIf, hostIfName, contIfName, nw.Mode, nl, plc)
|
epClient = NewLinuxBridgeEndpointClient(nw.extIf, hostIfName, contIfName, nw.Mode, nl, plc)
|
||||||
} else if epInfo.NICType == cns.NodeNetworkInterfaceFrontendNIC {
|
} else if epInfo.NICType == cns.NodeNetworkInterfaceFrontendNIC {
|
||||||
logger.Info("Secondary client")
|
logger.Info("Secondary client")
|
||||||
epClient = NewSecondaryEndpointClient(nl, netioCli, plc, nsc, ep)
|
epClient = NewSecondaryEndpointClient(nl, netioCli, plc, nsc, dhcpclient, ep)
|
||||||
} else {
|
} else {
|
||||||
logger.Info("Transparent client")
|
logger.Info("Transparent client")
|
||||||
epClient = NewTransparentEndpointClient(nw.extIf, hostIfName, contIfName, nw.Mode, nl, netioCli, plc)
|
epClient = NewTransparentEndpointClient(nw.extIf, hostIfName, contIfName, nw.Mode, nl, netioCli, plc)
|
||||||
|
@ -265,7 +266,7 @@ func (nw *network) newEndpointImpl(
|
||||||
|
|
||||||
// deleteEndpointImpl deletes an existing endpoint from the network.
|
// deleteEndpointImpl deletes an existing endpoint from the network.
|
||||||
func (nw *network) deleteEndpointImpl(nl netlink.NetlinkInterface, plc platform.ExecClient, epClient EndpointClient, nioc netio.NetIOInterface, nsc NamespaceClientInterface,
|
func (nw *network) deleteEndpointImpl(nl netlink.NetlinkInterface, plc platform.ExecClient, epClient EndpointClient, nioc netio.NetIOInterface, nsc NamespaceClientInterface,
|
||||||
iptc ipTablesClient, ep *endpoint,
|
iptc ipTablesClient, dhcpc dhcpClient, ep *endpoint,
|
||||||
) error {
|
) error {
|
||||||
// Delete the veth pair by deleting one of the peer interfaces.
|
// Delete the veth pair by deleting one of the peer interfaces.
|
||||||
// Deleting the host interface is more convenient since it does not require
|
// Deleting the host interface is more convenient since it does not require
|
||||||
|
@ -287,7 +288,7 @@ func (nw *network) deleteEndpointImpl(nl netlink.NetlinkInterface, plc platform.
|
||||||
} else {
|
} else {
|
||||||
// delete if secondary interfaces populated or endpoint of type delegated (new way)
|
// delete if secondary interfaces populated or endpoint of type delegated (new way)
|
||||||
if len(ep.SecondaryInterfaces) > 0 || ep.NICType == cns.NodeNetworkInterfaceFrontendNIC {
|
if len(ep.SecondaryInterfaces) > 0 || ep.NICType == cns.NodeNetworkInterfaceFrontendNIC {
|
||||||
epClient = NewSecondaryEndpointClient(nl, nioc, plc, nsc, ep)
|
epClient = NewSecondaryEndpointClient(nl, nioc, plc, nsc, dhcpc, ep)
|
||||||
epClient.DeleteEndpointRules(ep)
|
epClient.DeleteEndpointRules(ep)
|
||||||
//nolint:errcheck // ignore error
|
//nolint:errcheck // ignore error
|
||||||
epClient.DeleteEndpoints(ep)
|
epClient.DeleteEndpoints(ep)
|
||||||
|
|
|
@ -186,7 +186,7 @@ var _ = Describe("Test Endpoint", func() {
|
||||||
It("Should be added", func() {
|
It("Should be added", func() {
|
||||||
// Add endpoint with valid id
|
// Add endpoint with valid id
|
||||||
ep, err := nw.newEndpointImpl(nil, netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false),
|
ep, err := nw.newEndpointImpl(nil, netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false),
|
||||||
netio.NewMockNetIO(false, 0), NewMockEndpointClient(nil), NewMockNamespaceClient(), iptables.NewClient(), epInfo)
|
netio.NewMockNetIO(false, 0), NewMockEndpointClient(nil), NewMockNamespaceClient(), iptables.NewClient(), &mockDHCP{}, epInfo)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(ep).NotTo(BeNil())
|
Expect(ep).NotTo(BeNil())
|
||||||
Expect(ep.Id).To(Equal(epInfo.EndpointID))
|
Expect(ep.Id).To(Equal(epInfo.EndpointID))
|
||||||
|
@ -198,7 +198,7 @@ var _ = Describe("Test Endpoint", func() {
|
||||||
extIf: &externalInterface{IPv4Gateway: net.ParseIP("192.168.0.1")},
|
extIf: &externalInterface{IPv4Gateway: net.ParseIP("192.168.0.1")},
|
||||||
}
|
}
|
||||||
ep, err := nw2.newEndpointImpl(nil, netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false),
|
ep, err := nw2.newEndpointImpl(nil, netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false),
|
||||||
netio.NewMockNetIO(false, 0), NewMockEndpointClient(nil), NewMockNamespaceClient(), iptables.NewClient(), epInfo)
|
netio.NewMockNetIO(false, 0), NewMockEndpointClient(nil), NewMockNamespaceClient(), iptables.NewClient(), &mockDHCP{}, epInfo)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(ep).NotTo(BeNil())
|
Expect(ep).NotTo(BeNil())
|
||||||
Expect(ep.Id).To(Equal(epInfo.EndpointID))
|
Expect(ep.Id).To(Equal(epInfo.EndpointID))
|
||||||
|
@ -216,7 +216,7 @@ var _ = Describe("Test Endpoint", func() {
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
// Adding endpoint with same id should fail and delete should cleanup the state
|
// Adding endpoint with same id should fail and delete should cleanup the state
|
||||||
ep2, err := nw.newEndpointImpl(nil, netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false),
|
ep2, err := nw.newEndpointImpl(nil, netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false),
|
||||||
netio.NewMockNetIO(false, 0), mockCli, NewMockNamespaceClient(), iptables.NewClient(), epInfo)
|
netio.NewMockNetIO(false, 0), mockCli, NewMockNamespaceClient(), iptables.NewClient(), &mockDHCP{}, epInfo)
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
Expect(ep2).To(BeNil())
|
Expect(ep2).To(BeNil())
|
||||||
assert.Contains(GinkgoT(), err.Error(), "Endpoint already exists")
|
assert.Contains(GinkgoT(), err.Error(), "Endpoint already exists")
|
||||||
|
@ -226,17 +226,17 @@ var _ = Describe("Test Endpoint", func() {
|
||||||
// Adding an endpoint with an id.
|
// Adding an endpoint with an id.
|
||||||
mockCli := NewMockEndpointClient(nil)
|
mockCli := NewMockEndpointClient(nil)
|
||||||
ep2, err := nw.newEndpointImpl(nil, netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false),
|
ep2, err := nw.newEndpointImpl(nil, netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false),
|
||||||
netio.NewMockNetIO(false, 0), mockCli, NewMockNamespaceClient(), iptables.NewClient(), epInfo)
|
netio.NewMockNetIO(false, 0), mockCli, NewMockNamespaceClient(), iptables.NewClient(), &mockDHCP{}, epInfo)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(ep2).ToNot(BeNil())
|
Expect(ep2).ToNot(BeNil())
|
||||||
Expect(len(mockCli.endpoints)).To(Equal(1))
|
Expect(len(mockCli.endpoints)).To(Equal(1))
|
||||||
// Deleting the endpoint
|
// Deleting the endpoint
|
||||||
//nolint:errcheck // ignore error
|
//nolint:errcheck // ignore error
|
||||||
nw.deleteEndpointImpl(netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false), mockCli, netio.NewMockNetIO(false, 0), NewMockNamespaceClient(), iptables.NewClient(), ep2)
|
nw.deleteEndpointImpl(netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false), mockCli, netio.NewMockNetIO(false, 0), NewMockNamespaceClient(), iptables.NewClient(), &mockDHCP{}, ep2)
|
||||||
Expect(len(mockCli.endpoints)).To(Equal(0))
|
Expect(len(mockCli.endpoints)).To(Equal(0))
|
||||||
// Deleting same endpoint with same id should not fail
|
// Deleting same endpoint with same id should not fail
|
||||||
//nolint:errcheck // ignore error
|
//nolint:errcheck // ignore error
|
||||||
nw.deleteEndpointImpl(netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false), mockCli, netio.NewMockNetIO(false, 0), NewMockNamespaceClient(), iptables.NewClient(), ep2)
|
nw.deleteEndpointImpl(netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false), mockCli, netio.NewMockNetIO(false, 0), NewMockNamespaceClient(), iptables.NewClient(), &mockDHCP{}, ep2)
|
||||||
Expect(len(mockCli.endpoints)).To(Equal(0))
|
Expect(len(mockCli.endpoints)).To(Equal(0))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -256,7 +256,7 @@ var _ = Describe("Test Endpoint", func() {
|
||||||
extIf: &externalInterface{IPv4Gateway: net.ParseIP("192.168.0.1")},
|
extIf: &externalInterface{IPv4Gateway: net.ParseIP("192.168.0.1")},
|
||||||
}
|
}
|
||||||
ep, err := nw2.newEndpointImpl(nil, netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false),
|
ep, err := nw2.newEndpointImpl(nil, netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false),
|
||||||
netio.NewMockNetIO(false, 0), NewMockEndpointClient(nil), NewMockNamespaceClient(), iptables.NewClient(), epInfo)
|
netio.NewMockNetIO(false, 0), NewMockEndpointClient(nil), NewMockNamespaceClient(), iptables.NewClient(), &mockDHCP{}, epInfo)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(ep).NotTo(BeNil())
|
Expect(ep).NotTo(BeNil())
|
||||||
Expect(ep.Id).To(Equal(epInfo.EndpointID))
|
Expect(ep.Id).To(Equal(epInfo.EndpointID))
|
||||||
|
@ -282,7 +282,7 @@ var _ = Describe("Test Endpoint", func() {
|
||||||
extIf: &externalInterface{IPv4Gateway: net.ParseIP("192.168.0.1")},
|
extIf: &externalInterface{IPv4Gateway: net.ParseIP("192.168.0.1")},
|
||||||
}
|
}
|
||||||
ep, err := nw2.newEndpointImpl(nil, netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false),
|
ep, err := nw2.newEndpointImpl(nil, netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false),
|
||||||
netio.NewMockNetIO(false, 0), NewMockEndpointClient(nil), NewMockNamespaceClient(), iptables.NewClient(), epInfo)
|
netio.NewMockNetIO(false, 0), NewMockEndpointClient(nil), NewMockNamespaceClient(), iptables.NewClient(), &mockDHCP{}, epInfo)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(ep).NotTo(BeNil())
|
Expect(ep).NotTo(BeNil())
|
||||||
Expect(ep.Id).To(Equal(epInfo.EndpointID))
|
Expect(ep.Id).To(Equal(epInfo.EndpointID))
|
||||||
|
@ -309,11 +309,11 @@ var _ = Describe("Test Endpoint", func() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}), NewMockNamespaceClient(), iptables.NewClient(), epInfo)
|
}), NewMockNamespaceClient(), iptables.NewClient(), &mockDHCP{}, epInfo)
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
Expect(ep).To(BeNil())
|
Expect(ep).To(BeNil())
|
||||||
ep, err = nw.newEndpointImpl(nil, netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false),
|
ep, err = nw.newEndpointImpl(nil, netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false),
|
||||||
netio.NewMockNetIO(false, 0), NewMockEndpointClient(nil), NewMockNamespaceClient(), iptables.NewClient(), epInfo)
|
netio.NewMockNetIO(false, 0), NewMockEndpointClient(nil), NewMockNamespaceClient(), iptables.NewClient(), &mockDHCP{}, epInfo)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(ep).NotTo(BeNil())
|
Expect(ep).NotTo(BeNil())
|
||||||
Expect(ep.Id).To(Equal(epInfo.EndpointID))
|
Expect(ep.Id).To(Equal(epInfo.EndpointID))
|
||||||
|
@ -342,14 +342,14 @@ var _ = Describe("Test Endpoint", func() {
|
||||||
It("Should not add endpoint to the network when there is an error", func() {
|
It("Should not add endpoint to the network when there is an error", func() {
|
||||||
secondaryEpInfo.MacAddress = netio.BadHwAddr // mock netlink will fail to set link state on bad eth
|
secondaryEpInfo.MacAddress = netio.BadHwAddr // mock netlink will fail to set link state on bad eth
|
||||||
ep, err := nw.newEndpointImpl(nil, netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false),
|
ep, err := nw.newEndpointImpl(nil, netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false),
|
||||||
netio.NewMockNetIO(false, 0), nil, NewMockNamespaceClient(), iptables.NewClient(), secondaryEpInfo)
|
netio.NewMockNetIO(false, 0), nil, NewMockNamespaceClient(), iptables.NewClient(), &mockDHCP{}, secondaryEpInfo)
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
Expect(err.Error()).To(Equal("SecondaryEndpointClient Error: " + netlink.ErrorMockNetlink.Error()))
|
Expect(err.Error()).To(Equal("SecondaryEndpointClient Error: " + netlink.ErrorMockNetlink.Error()))
|
||||||
Expect(ep).To(BeNil())
|
Expect(ep).To(BeNil())
|
||||||
// should not panic or error when going through the unified endpoint impl flow with only the delegated nic type fields
|
// should not panic or error when going through the unified endpoint impl flow with only the delegated nic type fields
|
||||||
secondaryEpInfo.MacAddress = netio.HwAddr
|
secondaryEpInfo.MacAddress = netio.HwAddr
|
||||||
ep, err = nw.newEndpointImpl(nil, netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false),
|
ep, err = nw.newEndpointImpl(nil, netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false),
|
||||||
netio.NewMockNetIO(false, 0), nil, NewMockNamespaceClient(), iptables.NewClient(), secondaryEpInfo)
|
netio.NewMockNetIO(false, 0), nil, NewMockNamespaceClient(), iptables.NewClient(), &mockDHCP{}, secondaryEpInfo)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(ep.Id).To(Equal(epInfo.EndpointID))
|
Expect(ep.Id).To(Equal(epInfo.EndpointID))
|
||||||
})
|
})
|
||||||
|
@ -357,12 +357,12 @@ var _ = Describe("Test Endpoint", func() {
|
||||||
It("Should add endpoint when there are no errors", func() {
|
It("Should add endpoint when there are no errors", func() {
|
||||||
secondaryEpInfo.MacAddress = netio.HwAddr
|
secondaryEpInfo.MacAddress = netio.HwAddr
|
||||||
ep, err := nw.newEndpointImpl(nil, netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false),
|
ep, err := nw.newEndpointImpl(nil, netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false),
|
||||||
netio.NewMockNetIO(false, 0), nil, NewMockNamespaceClient(), iptables.NewClient(), secondaryEpInfo)
|
netio.NewMockNetIO(false, 0), nil, NewMockNamespaceClient(), iptables.NewClient(), &mockDHCP{}, secondaryEpInfo)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(ep.Id).To(Equal(epInfo.EndpointID))
|
Expect(ep.Id).To(Equal(epInfo.EndpointID))
|
||||||
|
|
||||||
ep, err = nw.newEndpointImpl(nil, netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false),
|
ep, err = nw.newEndpointImpl(nil, netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false),
|
||||||
netio.NewMockNetIO(false, 0), nil, NewMockNamespaceClient(), iptables.NewClient(), epInfo)
|
netio.NewMockNetIO(false, 0), nil, NewMockNamespaceClient(), iptables.NewClient(), &mockDHCP{}, epInfo)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(ep.Id).To(Equal(epInfo.EndpointID))
|
Expect(ep.Id).To(Equal(epInfo.EndpointID))
|
||||||
})
|
})
|
||||||
|
|
|
@ -150,6 +150,7 @@ func (nw *network) newEndpointImpl(
|
||||||
_ EndpointClient,
|
_ EndpointClient,
|
||||||
_ NamespaceClientInterface,
|
_ NamespaceClientInterface,
|
||||||
_ ipTablesClient,
|
_ ipTablesClient,
|
||||||
|
_ dhcpClient,
|
||||||
epInfo *EndpointInfo,
|
epInfo *EndpointInfo,
|
||||||
) (*endpoint, error) {
|
) (*endpoint, error) {
|
||||||
if epInfo.NICType == cns.BackendNIC {
|
if epInfo.NICType == cns.BackendNIC {
|
||||||
|
@ -521,7 +522,7 @@ func (nw *network) newEndpointImplHnsV2(cli apipaClient, epInfo *EndpointInfo) (
|
||||||
|
|
||||||
// deleteEndpointImpl deletes an existing endpoint from the network.
|
// deleteEndpointImpl deletes an existing endpoint from the network.
|
||||||
func (nw *network) deleteEndpointImpl(_ netlink.NetlinkInterface, _ platform.ExecClient, _ EndpointClient, _ netio.NetIOInterface, _ NamespaceClientInterface,
|
func (nw *network) deleteEndpointImpl(_ netlink.NetlinkInterface, _ platform.ExecClient, _ EndpointClient, _ netio.NetIOInterface, _ NamespaceClientInterface,
|
||||||
_ ipTablesClient, ep *endpoint,
|
_ ipTablesClient, _ dhcpClient, ep *endpoint,
|
||||||
) error {
|
) error {
|
||||||
// endpoint deletion is not required for IB
|
// endpoint deletion is not required for IB
|
||||||
if ep.NICType == cns.BackendNIC {
|
if ep.NICType == cns.BackendNIC {
|
||||||
|
|
|
@ -107,7 +107,8 @@ func TestDeleteEndpointImplHnsV2ForIB(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
mockCli := NewMockEndpointClient(nil)
|
mockCli := NewMockEndpointClient(nil)
|
||||||
err := nw.deleteEndpointImpl(netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false), mockCli, netio.NewMockNetIO(false, 0), NewMockNamespaceClient(), iptables.NewClient(), &ep)
|
err := nw.deleteEndpointImpl(netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false), mockCli,
|
||||||
|
netio.NewMockNetIO(false, 0), NewMockNamespaceClient(), iptables.NewClient(), &mockDHCP{}, &ep)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("endpoint deletion for IB is executed")
|
t.Fatal("endpoint deletion for IB is executed")
|
||||||
}
|
}
|
||||||
|
@ -134,7 +135,8 @@ func TestDeleteEndpointImplHnsV2WithEmptyHNSID(t *testing.T) {
|
||||||
|
|
||||||
// should return nil because HnsID is empty
|
// should return nil because HnsID is empty
|
||||||
mockCli := NewMockEndpointClient(nil)
|
mockCli := NewMockEndpointClient(nil)
|
||||||
err := nw.deleteEndpointImpl(netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false), mockCli, netio.NewMockNetIO(false, 0), NewMockNamespaceClient(), iptables.NewClient(), &ep)
|
err := nw.deleteEndpointImpl(netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false), mockCli,
|
||||||
|
netio.NewMockNetIO(false, 0), NewMockNamespaceClient(), iptables.NewClient(), &mockDHCP{}, &ep)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("endpoint deletion gets executed")
|
t.Fatal("endpoint deletion gets executed")
|
||||||
}
|
}
|
||||||
|
@ -492,7 +494,7 @@ func TestNewEndpointImplHnsv2ForIBHappyPath(t *testing.T) {
|
||||||
|
|
||||||
// Happy Path
|
// Happy Path
|
||||||
endpoint, err := nw.newEndpointImpl(nil, netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false),
|
endpoint, err := nw.newEndpointImpl(nil, netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false),
|
||||||
netio.NewMockNetIO(false, 0), NewMockEndpointClient(nil), NewMockNamespaceClient(), iptables.NewClient(), epInfo)
|
netio.NewMockNetIO(false, 0), NewMockEndpointClient(nil), NewMockNamespaceClient(), iptables.NewClient(), &mockDHCP{}, epInfo)
|
||||||
|
|
||||||
if endpoint != nil || err != nil {
|
if endpoint != nil || err != nil {
|
||||||
t.Fatalf("Endpoint is created for IB due to %v", err)
|
t.Fatalf("Endpoint is created for IB due to %v", err)
|
||||||
|
@ -522,7 +524,7 @@ func TestNewEndpointImplHnsv2ForIBUnHappyPath(t *testing.T) {
|
||||||
|
|
||||||
// Set UnHappy Path
|
// Set UnHappy Path
|
||||||
_, err := nw.newEndpointImpl(nil, netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(true),
|
_, err := nw.newEndpointImpl(nil, netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(true),
|
||||||
netio.NewMockNetIO(false, 0), NewMockEndpointClient(nil), NewMockNamespaceClient(), iptables.NewClient(), epInfo)
|
netio.NewMockNetIO(false, 0), NewMockEndpointClient(nil), NewMockNamespaceClient(), iptables.NewClient(), &mockDHCP{}, epInfo)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Failed to test Endpoint creation for IB with unhappy path")
|
t.Fatal("Failed to test Endpoint creation for IB with unhappy path")
|
||||||
|
@ -562,7 +564,8 @@ func TestCreateAndDeleteEndpointImplHnsv2ForDelegatedHappyPath(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
mockCli := NewMockEndpointClient(nil)
|
mockCli := NewMockEndpointClient(nil)
|
||||||
err = nw.deleteEndpointImpl(netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false), mockCli, netio.NewMockNetIO(false, 0), NewMockNamespaceClient(), iptables.NewClient(), ep)
|
err = nw.deleteEndpointImpl(netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false), mockCli,
|
||||||
|
netio.NewMockNetIO(false, 0), NewMockNamespaceClient(), iptables.NewClient(), &mockDHCP{}, ep)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to delete endpoint for Delegated NIC due to %v", err)
|
t.Fatalf("Failed to delete endpoint for Delegated NIC due to %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,6 +86,7 @@ type networkManager struct {
|
||||||
plClient platform.ExecClient
|
plClient platform.ExecClient
|
||||||
nsClient NamespaceClientInterface
|
nsClient NamespaceClientInterface
|
||||||
iptablesClient ipTablesClient
|
iptablesClient ipTablesClient
|
||||||
|
dhcpClient dhcpClient
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +124,7 @@ type NetworkManager interface {
|
||||||
|
|
||||||
// Creates a new network manager.
|
// Creates a new network manager.
|
||||||
func NewNetworkManager(nl netlink.NetlinkInterface, plc platform.ExecClient, netioCli netio.NetIOInterface, nsc NamespaceClientInterface,
|
func NewNetworkManager(nl netlink.NetlinkInterface, plc platform.ExecClient, netioCli netio.NetIOInterface, nsc NamespaceClientInterface,
|
||||||
iptc ipTablesClient,
|
iptc ipTablesClient, dhcpc dhcpClient,
|
||||||
) (NetworkManager, error) {
|
) (NetworkManager, error) {
|
||||||
nm := &networkManager{
|
nm := &networkManager{
|
||||||
ExternalInterfaces: make(map[string]*externalInterface),
|
ExternalInterfaces: make(map[string]*externalInterface),
|
||||||
|
@ -132,6 +133,7 @@ func NewNetworkManager(nl netlink.NetlinkInterface, plc platform.ExecClient, net
|
||||||
netio: netioCli,
|
netio: netioCli,
|
||||||
nsClient: nsc,
|
nsClient: nsc,
|
||||||
iptablesClient: iptc,
|
iptablesClient: iptc,
|
||||||
|
dhcpClient: dhcpc,
|
||||||
}
|
}
|
||||||
|
|
||||||
return nm, nil
|
return nm, nil
|
||||||
|
@ -386,7 +388,7 @@ func (nm *networkManager) createEndpoint(cli apipaClient, networkID string, epIn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ep, err := nw.newEndpoint(cli, nm.netlink, nm.plClient, nm.netio, nm.nsClient, nm.iptablesClient, epInfo)
|
ep, err := nw.newEndpoint(cli, nm.netlink, nm.plClient, nm.netio, nm.nsClient, nm.iptablesClient, nm.dhcpClient, epInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -395,7 +397,7 @@ func (nm *networkManager) createEndpoint(cli apipaClient, networkID string, epIn
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Create endpoint failure", zap.Error(err))
|
logger.Error("Create endpoint failure", zap.Error(err))
|
||||||
logger.Info("Cleanup resources")
|
logger.Info("Cleanup resources")
|
||||||
delErr := nw.deleteEndpoint(nm.netlink, nm.plClient, nm.netio, nm.nsClient, nm.iptablesClient, ep.Id)
|
delErr := nw.deleteEndpoint(nm.netlink, nm.plClient, nm.netio, nm.nsClient, nm.iptablesClient, nm.dhcpClient, ep.Id)
|
||||||
if delErr != nil {
|
if delErr != nil {
|
||||||
logger.Error("Deleting endpoint after create endpoint failure failed with", zap.Error(delErr))
|
logger.Error("Deleting endpoint after create endpoint failure failed with", zap.Error(delErr))
|
||||||
}
|
}
|
||||||
|
@ -489,7 +491,7 @@ func (nm *networkManager) DeleteEndpoint(networkID, endpointID string, epInfo *E
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = nw.deleteEndpoint(nm.netlink, nm.plClient, nm.netio, nm.nsClient, nm.iptablesClient, endpointID)
|
err = nw.deleteEndpoint(nm.netlink, nm.plClient, nm.netio, nm.nsClient, nm.iptablesClient, nm.dhcpClient, endpointID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -531,7 +533,7 @@ func (nm *networkManager) DeleteEndpointState(networkID string, epInfo *Endpoint
|
||||||
}
|
}
|
||||||
logger.Info("Deleting endpoint with", zap.String("Endpoint Info: ", epInfo.PrettyString()), zap.String("HNISID : ", ep.HnsId))
|
logger.Info("Deleting endpoint with", zap.String("Endpoint Info: ", epInfo.PrettyString()), zap.String("HNISID : ", ep.HnsId))
|
||||||
|
|
||||||
err := nw.deleteEndpointImpl(netlink.NewNetlink(), platform.NewExecClient(logger), nil, nil, nil, nil, ep)
|
err := nw.deleteEndpointImpl(netlink.NewNetlink(), platform.NewExecClient(logger), nil, nil, nil, nil, nil, ep)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package network
|
package network
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/Azure/azure-container-networking/netio"
|
"github.com/Azure/azure-container-networking/netio"
|
||||||
"github.com/Azure/azure-container-networking/netlink"
|
"github.com/Azure/azure-container-networking/netlink"
|
||||||
|
@ -25,6 +27,7 @@ type SecondaryEndpointClient struct {
|
||||||
plClient platform.ExecClient
|
plClient platform.ExecClient
|
||||||
netUtilsClient networkutils.NetworkUtils
|
netUtilsClient networkutils.NetworkUtils
|
||||||
nsClient NamespaceClientInterface
|
nsClient NamespaceClientInterface
|
||||||
|
dhcpClient dhcpClient
|
||||||
ep *endpoint
|
ep *endpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +36,7 @@ func NewSecondaryEndpointClient(
|
||||||
nioc netio.NetIOInterface,
|
nioc netio.NetIOInterface,
|
||||||
plc platform.ExecClient,
|
plc platform.ExecClient,
|
||||||
nsc NamespaceClientInterface,
|
nsc NamespaceClientInterface,
|
||||||
|
dhcpClient dhcpClient,
|
||||||
endpoint *endpoint,
|
endpoint *endpoint,
|
||||||
) *SecondaryEndpointClient {
|
) *SecondaryEndpointClient {
|
||||||
client := &SecondaryEndpointClient{
|
client := &SecondaryEndpointClient{
|
||||||
|
@ -41,6 +45,7 @@ func NewSecondaryEndpointClient(
|
||||||
plClient: plc,
|
plClient: plc,
|
||||||
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
|
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
|
||||||
nsClient: nsc,
|
nsClient: nsc,
|
||||||
|
dhcpClient: dhcpClient,
|
||||||
ep: endpoint,
|
ep: endpoint,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,6 +132,19 @@ func (client *SecondaryEndpointClient) ConfigureContainerInterfacesAndRoutes(epI
|
||||||
|
|
||||||
ifInfo.Routes = append(ifInfo.Routes, epInfo.Routes...)
|
ifInfo.Routes = append(ifInfo.Routes, epInfo.Routes...)
|
||||||
|
|
||||||
|
// issue dhcp discover packet to ensure mapping created for dns via wireserver to work
|
||||||
|
// we do not use the response for anything
|
||||||
|
numSecs := 3
|
||||||
|
timeout := time.Duration(numSecs) * time.Second
|
||||||
|
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(timeout))
|
||||||
|
defer cancel()
|
||||||
|
logger.Info("Sending DHCP packet", zap.Any("macAddress", epInfo.MacAddress), zap.String("ifName", epInfo.IfName))
|
||||||
|
err := client.dhcpClient.DiscoverRequest(ctx, epInfo.MacAddress, epInfo.IfName)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to issue dhcp discover packet to create mapping in host")
|
||||||
|
}
|
||||||
|
logger.Info("Finished configuring container interfaces and routes for secondary endpoint client")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ func TestSecondaryAddEndpoints(t *testing.T) {
|
||||||
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
|
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
|
||||||
netioshim: netio.NewMockNetIO(false, 0),
|
netioshim: netio.NewMockNetIO(false, 0),
|
||||||
ep: &endpoint{SecondaryInterfaces: make(map[string]*InterfaceInfo)},
|
ep: &endpoint{SecondaryInterfaces: make(map[string]*InterfaceInfo)},
|
||||||
|
dhcpClient: &mockDHCP{},
|
||||||
},
|
},
|
||||||
epInfo: &EndpointInfo{MacAddress: mac},
|
epInfo: &EndpointInfo{MacAddress: mac},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
|
@ -255,6 +256,7 @@ func TestSecondaryConfigureContainerInterfacesAndRoutes(t *testing.T) {
|
||||||
plClient: platform.NewMockExecClient(false),
|
plClient: platform.NewMockExecClient(false),
|
||||||
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
|
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
|
||||||
netioshim: netio.NewMockNetIO(false, 0),
|
netioshim: netio.NewMockNetIO(false, 0),
|
||||||
|
dhcpClient: &mockDHCP{},
|
||||||
ep: &endpoint{SecondaryInterfaces: map[string]*InterfaceInfo{"eth1": {Name: "eth1"}}},
|
ep: &endpoint{SecondaryInterfaces: map[string]*InterfaceInfo{"eth1": {Name: "eth1"}}},
|
||||||
},
|
},
|
||||||
epInfo: &EndpointInfo{
|
epInfo: &EndpointInfo{
|
||||||
|
@ -280,6 +282,7 @@ func TestSecondaryConfigureContainerInterfacesAndRoutes(t *testing.T) {
|
||||||
plClient: platform.NewMockExecClient(false),
|
plClient: platform.NewMockExecClient(false),
|
||||||
netUtilsClient: networkutils.NewNetworkUtils(netlink.NewMockNetlink(true, ""), plc),
|
netUtilsClient: networkutils.NewNetworkUtils(netlink.NewMockNetlink(true, ""), plc),
|
||||||
netioshim: netio.NewMockNetIO(false, 0),
|
netioshim: netio.NewMockNetIO(false, 0),
|
||||||
|
dhcpClient: &mockDHCP{},
|
||||||
ep: &endpoint{SecondaryInterfaces: map[string]*InterfaceInfo{"eth1": {Name: "eth1"}}},
|
ep: &endpoint{SecondaryInterfaces: map[string]*InterfaceInfo{"eth1": {Name: "eth1"}}},
|
||||||
},
|
},
|
||||||
epInfo: &EndpointInfo{
|
epInfo: &EndpointInfo{
|
||||||
|
@ -301,6 +304,7 @@ func TestSecondaryConfigureContainerInterfacesAndRoutes(t *testing.T) {
|
||||||
plClient: platform.NewMockExecClient(false),
|
plClient: platform.NewMockExecClient(false),
|
||||||
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
|
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
|
||||||
netioshim: netio.NewMockNetIO(true, 1),
|
netioshim: netio.NewMockNetIO(true, 1),
|
||||||
|
dhcpClient: &mockDHCP{},
|
||||||
ep: &endpoint{SecondaryInterfaces: map[string]*InterfaceInfo{"eth1": {Name: "eth1"}}},
|
ep: &endpoint{SecondaryInterfaces: map[string]*InterfaceInfo{"eth1": {Name: "eth1"}}},
|
||||||
},
|
},
|
||||||
epInfo: &EndpointInfo{
|
epInfo: &EndpointInfo{
|
||||||
|
@ -327,6 +331,7 @@ func TestSecondaryConfigureContainerInterfacesAndRoutes(t *testing.T) {
|
||||||
plClient: platform.NewMockExecClient(false),
|
plClient: platform.NewMockExecClient(false),
|
||||||
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
|
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
|
||||||
netioshim: netio.NewMockNetIO(false, 0),
|
netioshim: netio.NewMockNetIO(false, 0),
|
||||||
|
dhcpClient: &mockDHCP{},
|
||||||
ep: &endpoint{SecondaryInterfaces: map[string]*InterfaceInfo{"eth1": {Name: "eth1"}}},
|
ep: &endpoint{SecondaryInterfaces: map[string]*InterfaceInfo{"eth1": {Name: "eth1"}}},
|
||||||
},
|
},
|
||||||
epInfo: &EndpointInfo{
|
epInfo: &EndpointInfo{
|
||||||
|
@ -348,6 +353,7 @@ func TestSecondaryConfigureContainerInterfacesAndRoutes(t *testing.T) {
|
||||||
plClient: platform.NewMockExecClient(false),
|
plClient: platform.NewMockExecClient(false),
|
||||||
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
|
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
|
||||||
netioshim: netio.NewMockNetIO(false, 0),
|
netioshim: netio.NewMockNetIO(false, 0),
|
||||||
|
dhcpClient: &mockDHCP{},
|
||||||
ep: &endpoint{SecondaryInterfaces: map[string]*InterfaceInfo{"eth1": {Name: "eth1"}}},
|
ep: &endpoint{SecondaryInterfaces: map[string]*InterfaceInfo{"eth1": {Name: "eth1"}}},
|
||||||
},
|
},
|
||||||
epInfo: &EndpointInfo{
|
epInfo: &EndpointInfo{
|
||||||
|
|
Загрузка…
Ссылка в новой задаче