Skip to content

Commit

Permalink
Merge pull request #217 from mccv1r0/dhcpif
Browse files Browse the repository at this point in the history
Add container ifName to the dhcp clientID, making the clientID value
  • Loading branch information
dcbw authored Nov 21, 2018
2 parents b75d14a + 0af31fc commit 72251a6
Show file tree
Hide file tree
Showing 17 changed files with 897 additions and 137 deletions.
121 changes: 121 additions & 0 deletions plugins/ipam/dhcp/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package main

import (
"github.com/d2g/dhcp4"
"github.com/d2g/dhcp4client"
)

const (
MaxDHCPLen = 576
)

//Send the Discovery Packet to the Broadcast Channel
func DhcpSendDiscoverPacket(c *dhcp4client.Client, options dhcp4.Options) (dhcp4.Packet, error) {
discoveryPacket := c.DiscoverPacket()

for opt, data := range options {
discoveryPacket.AddOption(opt, data)
}

discoveryPacket.PadToMinSize()
return discoveryPacket, c.SendPacket(discoveryPacket)
}

//Send Request Based On the offer Received.
func DhcpSendRequest(c *dhcp4client.Client, options dhcp4.Options, offerPacket *dhcp4.Packet) (dhcp4.Packet, error) {
requestPacket := c.RequestPacket(offerPacket)

for opt, data := range options {
requestPacket.AddOption(opt, data)
}

requestPacket.PadToMinSize()

return requestPacket, c.SendPacket(requestPacket)
}

//Send Decline to the received acknowledgement.
func DhcpSendDecline(c *dhcp4client.Client, acknowledgementPacket *dhcp4.Packet, options dhcp4.Options) (dhcp4.Packet, error) {
declinePacket := c.DeclinePacket(acknowledgementPacket)

for opt, data := range options {
declinePacket.AddOption(opt, data)
}

declinePacket.PadToMinSize()

return declinePacket, c.SendPacket(declinePacket)
}

//Lets do a Full DHCP Request.
func DhcpRequest(c *dhcp4client.Client, options dhcp4.Options) (bool, dhcp4.Packet, error) {
discoveryPacket, err := DhcpSendDiscoverPacket(c, options)
if err != nil {
return false, discoveryPacket, err
}

offerPacket, err := c.GetOffer(&discoveryPacket)
if err != nil {
return false, offerPacket, err
}

requestPacket, err := DhcpSendRequest(c, options, &offerPacket)
if err != nil {
return false, requestPacket, err
}

acknowledgement, err := c.GetAcknowledgement(&requestPacket)
if err != nil {
return false, acknowledgement, err
}

acknowledgementOptions := acknowledgement.ParseOptions()
if dhcp4.MessageType(acknowledgementOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.ACK {
return false, acknowledgement, nil
}

return true, acknowledgement, nil
}

//Renew a lease backed on the Acknowledgement Packet.
//Returns Sucessfull, The AcknoledgementPacket, Any Errors
func DhcpRenew(c *dhcp4client.Client, acknowledgement dhcp4.Packet, options dhcp4.Options) (bool, dhcp4.Packet, error) {
renewRequest := c.RenewalRequestPacket(&acknowledgement)

for opt, data := range options {
renewRequest.AddOption(opt, data)
}

renewRequest.PadToMinSize()

err := c.SendPacket(renewRequest)
if err != nil {
return false, renewRequest, err
}

newAcknowledgement, err := c.GetAcknowledgement(&renewRequest)
if err != nil {
return false, newAcknowledgement, err
}

newAcknowledgementOptions := newAcknowledgement.ParseOptions()
if dhcp4.MessageType(newAcknowledgementOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.ACK {
return false, newAcknowledgement, nil
}

return true, newAcknowledgement, nil
}

//Release a lease backed on the Acknowledgement Packet.
//Returns Any Errors
func DhcpRelease(c *dhcp4client.Client, acknowledgement dhcp4.Packet, options dhcp4.Options) error {
release := c.ReleasePacket(&acknowledgement)

for opt, data := range options {
release.AddOption(opt, data)
}

release.PadToMinSize()

return c.SendPacket(release)
}
26 changes: 16 additions & 10 deletions plugins/ipam/dhcp/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ func newDHCP() *DHCP {
}
}

func generateClientID(containerID string, netName string, ifName string) string {
return containerID + "/" + netName + "/" + ifName
}

// Allocate acquires an IP from a DHCP server for a specified container.
// The acquired lease will be maintained until Release() is called.
func (d *DHCP) Allocate(args *skel.CmdArgs, result *current.Result) error {
Expand All @@ -58,7 +62,7 @@ func (d *DHCP) Allocate(args *skel.CmdArgs, result *current.Result) error {
return fmt.Errorf("error parsing netconf: %v", err)
}

clientID := args.ContainerID + "/" + conf.Name
clientID := generateClientID(args.ContainerID, conf.Name, args.IfName)
hostNetns := d.hostNetnsPrefix + args.Netns
l, err := AcquireLease(clientID, hostNetns, args.IfName)
if err != nil {
Expand All @@ -71,7 +75,7 @@ func (d *DHCP) Allocate(args *skel.CmdArgs, result *current.Result) error {
return err
}

d.setLease(args.ContainerID, conf.Name, l)
d.setLease(clientID, l)

result.IPs = []*current.IPConfig{{
Version: "4",
Expand All @@ -91,40 +95,42 @@ func (d *DHCP) Release(args *skel.CmdArgs, reply *struct{}) error {
return fmt.Errorf("error parsing netconf: %v", err)
}

if l := d.getLease(args.ContainerID, conf.Name); l != nil {
clientID := generateClientID(args.ContainerID, conf.Name, args.IfName)
if l := d.getLease(clientID); l != nil {
l.Stop()
d.clearLease(args.ContainerID, conf.Name)
d.clearLease(clientID)
}

return nil
}

func (d *DHCP) getLease(contID, netName string) *DHCPLease {
func (d *DHCP) getLease(clientID string) *DHCPLease {
d.mux.Lock()
defer d.mux.Unlock()

// TODO(eyakubovich): hash it to avoid collisions
l, ok := d.leases[contID+netName]
l, ok := d.leases[clientID]
if !ok {
return nil
}
return l
}

func (d *DHCP) setLease(contID, netName string, l *DHCPLease) {
func (d *DHCP) setLease(clientID string, l *DHCPLease) {
d.mux.Lock()
defer d.mux.Unlock()

// TODO(eyakubovich): hash it to avoid collisions
d.leases[contID+netName] = l
d.leases[clientID] = l
}

func (d *DHCP) clearLease(contID, netName string) {
//func (d *DHCP) clearLease(contID, netName, ifName string) {
func (d *DHCP) clearLease(clientID string) {
d.mux.Lock()
defer d.mux.Unlock()

// TODO(eyakubovich): hash it to avoid collisions
delete(d.leases, contID+netName)
delete(d.leases, clientID)
}

func getListener(socketPath string) (net.Listener, error) {
Expand Down
183 changes: 183 additions & 0 deletions plugins/ipam/dhcp/dhcp2_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
// Copyright 2015-2018 CNI authors
//
// 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 main

import (
"fmt"
"net"
"os"
"os/exec"
"sync"
"time"

"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"

"github.com/vishvananda/netlink"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var _ = Describe("DHCP Multiple Lease Operations", func() {
var originalNS, targetNS ns.NetNS
var dhcpServerStopCh chan bool
var dhcpServerDone *sync.WaitGroup
var clientCmd *exec.Cmd
var socketPath string
var tmpDir string
var serverIP net.IPNet
var err error

BeforeEach(func() {
dhcpServerStopCh, serverIP, socketPath, originalNS, targetNS, err = dhcpSetupOriginalNS()
Expect(err).NotTo(HaveOccurred())

// Move the container side to the container's NS
err = targetNS.Do(func(_ ns.NetNS) error {
defer GinkgoRecover()

link, err := netlink.LinkByName(contVethName0)
Expect(err).NotTo(HaveOccurred())
err = netlink.LinkSetUp(link)
Expect(err).NotTo(HaveOccurred())

link1, err := netlink.LinkByName(contVethName1)
Expect(err).NotTo(HaveOccurred())
err = netlink.LinkSetUp(link1)
Expect(err).NotTo(HaveOccurred())
return nil
})

// Start the DHCP server
dhcpServerDone, err = dhcpServerStart(originalNS, net.IPv4(192, 168, 1, 5), serverIP.IP, 2, dhcpServerStopCh)
Expect(err).NotTo(HaveOccurred())

// Start the DHCP client daemon
dhcpPluginPath, err := exec.LookPath("dhcp")
Expect(err).NotTo(HaveOccurred())
clientCmd = exec.Command(dhcpPluginPath, "daemon", "-socketpath", socketPath)
err = clientCmd.Start()
Expect(err).NotTo(HaveOccurred())
Expect(clientCmd.Process).NotTo(BeNil())

// Wait up to 15 seconds for the client socket
Eventually(func() bool {
_, err := os.Stat(socketPath)
return err == nil
}, time.Second*15, time.Second/4).Should(BeTrue())
})

AfterEach(func() {
dhcpServerStopCh <- true
dhcpServerDone.Wait()
clientCmd.Process.Kill()
clientCmd.Wait()

Expect(originalNS.Close()).To(Succeed())
Expect(targetNS.Close()).To(Succeed())
defer os.RemoveAll(tmpDir)
})

It("configures multiple links with multiple ADD/DEL", func() {
conf := fmt.Sprintf(`{
"cniVersion": "0.3.1",
"name": "mynet",
"type": "bridge",
"bridge": "%s",
"ipam": {
"type": "dhcp",
"daemonSocketPath": "%s"
}
}`, hostBridgeName, socketPath)

args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: contVethName0,
StdinData: []byte(conf),
}

var addResult *current.Result
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()

r, _, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())

addResult, err = current.GetResult(r)
Expect(err).NotTo(HaveOccurred())
Expect(len(addResult.IPs)).To(Equal(1))
Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.5/24"))
return nil
})
Expect(err).NotTo(HaveOccurred())

args = &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: contVethName1,
StdinData: []byte(conf),
}

err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()

r, _, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())

addResult, err = current.GetResult(r)
Expect(err).NotTo(HaveOccurred())
Expect(len(addResult.IPs)).To(Equal(1))
Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.6/24"))
return nil
})
Expect(err).NotTo(HaveOccurred())

args = &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: contVethName1,
StdinData: []byte(conf),
}

err = originalNS.Do(func(ns.NetNS) error {
return testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
})
Expect(err).NotTo(HaveOccurred())

args = &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: contVethName0,
StdinData: []byte(conf),
}

err = originalNS.Do(func(ns.NetNS) error {
return testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
})
Expect(err).NotTo(HaveOccurred())
})
})
Loading

0 comments on commit 72251a6

Please sign in to comment.