Skip to content

Commit f4f1dff

Browse files
authored
Merge pull request #61 from plumgrid/arping
Arping capabilities for CNM
2 parents 1194521 + 9ed9b63 commit f4f1dff

3 files changed

Lines changed: 187 additions & 6 deletions

File tree

plugin/driver/config.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,18 @@
1515
package driver
1616

1717
import (
18-
"github.com/alyu/configparser"
1918
"log"
19+
20+
"github.com/alyu/configparser"
2021
)
2122

2223
var (
23-
vip string
24-
username string
25-
password string
26-
default_vd string
27-
scope string
24+
vip string
25+
username string
26+
password string
27+
default_vd string
28+
scope string
29+
auto_arp bool = false
2830
)
2931

3032
// Read configuration file
@@ -43,6 +45,7 @@ func ReadConfig() {
4345
username = pg_section.ValueOf("pg_username")
4446
password = pg_section.ValueOf("pg_password")
4547
default_vd = pg_section.ValueOf("default_domain")
48+
4649
}
4750

4851
// get Libnetwork config
@@ -51,5 +54,6 @@ func ReadConfig() {
5154
log.Fatal(lib_err)
5255
} else {
5356
scope = lib_section.ValueOf("scope")
57+
auto_arp = lib_section.Exists("auto_arp")
5458
}
5559
}

plugin/driver/driver.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"net"
2424
"net/http"
2525
"os/exec"
26+
"runtime"
2627

2728
Log "github.com/Sirupsen/logrus"
2829
"github.com/docker/libnetwork/drivers/remote/api"
@@ -343,6 +344,23 @@ func (driver *driver) joinEndpoint(w http.ResponseWriter, r *http.Request) {
343344

344345
objectResponse(w, res)
345346
Log.Infof("Join endpoint %s:%s to %s", j.NetworkID, j.EndpointID, j.SandboxKey)
347+
348+
if auto_arp {
349+
go func(net_ns_path string, pg_ifc_prefix string) {
350+
351+
// Disallow this goroutine to work on any other thread than this one
352+
// since namespace ops (unshare, setns) are done for a single thread, we
353+
// must ensure that the goroutine does not jump from OS thread to thread
354+
runtime.LockOSThread()
355+
defer runtime.UnlockOSThread()
356+
357+
err := RunContainerArping(net_ns_path, pg_ifc_prefix)
358+
if err != nil {
359+
Log.Printf("Error while running arping : %v", err)
360+
}
361+
362+
}(j.SandboxKey, ifname.DstPrefix)
363+
}
346364
}
347365

348366
// leave call

plugin/driver/network_utils.go

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
// Copyright 2016 PLUMgrid
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.package main
14+
15+
package driver
16+
17+
import (
18+
"fmt"
19+
"net"
20+
"strings"
21+
"time"
22+
23+
"github.com/google/gopacket"
24+
"github.com/google/gopacket/layers"
25+
"github.com/google/gopacket/pcap"
26+
27+
Log "github.com/Sirupsen/logrus"
28+
"github.com/eduser25/cni/pkg/ns"
29+
)
30+
31+
// Plumgrid just specifies the prefix, so we will arping on all ifcs
32+
// that match with the prefix given to libnetwork
33+
func RunContainerArping(net_ns_path string, pg_ifc_prefix string) error {
34+
35+
Log.Printf("Attempting to acces the container %s NetNS", net_ns_path)
36+
var netns ns.NetNS
37+
var err error
38+
success := false
39+
40+
for attempts := 0; attempts <= 5; attempts++ {
41+
42+
netns, err = ns.GetNS(net_ns_path)
43+
44+
if err != nil {
45+
Log.Printf("Attempt %d: failed to open netns %s: %v", attempts, net_ns_path, err)
46+
time.Sleep(1 * time.Second)
47+
} else {
48+
success = true
49+
break
50+
}
51+
attempts += 1
52+
}
53+
54+
if !success {
55+
return fmt.Errorf("Attempts exhaused, could not access %s, exiting", net_ns_path)
56+
}
57+
defer netns.Close()
58+
59+
// Let's go in
60+
err = netns.Do(func(_ ns.NetNS) error {
61+
62+
// Get Ifcs on container
63+
ifc_list, err := net.Interfaces()
64+
if err != nil {
65+
return fmt.Errorf("Failed to get interfaces in Netns: %v", err)
66+
}
67+
68+
// Iterate Ifcs
69+
for _, ifc := range ifc_list {
70+
if strings.HasPrefix(ifc.Name, pg_ifc_prefix) {
71+
// Valid PG-created ifc
72+
73+
// TODO we might want to add a retry here to make sure,
74+
// in case the IFC is up but still has no IP, we retry
75+
address_slice, err := ifc.Addrs()
76+
if err != nil {
77+
Log.Errorf("Failed to get Address slice for ifc %s: %v", ifc.Name, err)
78+
continue
79+
}
80+
81+
if len(address_slice) == 0 {
82+
Log.Errorf("Interface %s had no IP address at the moment we checked", ifc.Name)
83+
continue
84+
}
85+
86+
Log.Printf("Attempting raw socket open on %s", ifc.Name)
87+
handle, err := pcap.OpenLive(ifc.Name, 65536, true, pcap.BlockForever)
88+
if err != nil {
89+
return fmt.Errorf("Failed to open raw socket %s : %v", ifc.Name, err)
90+
}
91+
92+
// We will just arping for all IPv4 addresses on this ifc, just in case
93+
for _, ifc_addr := range address_slice {
94+
ip, _, err := net.ParseCIDR(ifc_addr.String())
95+
if err != nil {
96+
Log.Printf("Failed to parse network %s : %v", ifc_addr.String(), err)
97+
continue
98+
}
99+
100+
if ip.To4() == nil {
101+
// ignoring IPV6 addr
102+
continue
103+
}
104+
105+
// Get ARP packet
106+
arp_pkt := getGratuitousArp(ifc.HardwareAddr, ip)
107+
108+
for i := 0; i <= 3; i++ {
109+
Log.Printf("Sending %d GARP on %s : %s - %s",
110+
i, ifc.Name, ifc.HardwareAddr.String(), ip.String())
111+
112+
if err := handle.WritePacketData(arp_pkt); err != nil {
113+
handle.Close()
114+
return fmt.Errorf("Failed to write ARP packet data on %s : %v", ifc.Name, err)
115+
}
116+
time.Sleep(750 * time.Millisecond)
117+
}
118+
}
119+
120+
// Close the socket
121+
handle.Close()
122+
}
123+
}
124+
return nil
125+
})
126+
127+
return err
128+
}
129+
130+
func getGratuitousArp(mac net.HardwareAddr, ip net.IP) []byte {
131+
// Set up all the layers' fields we can.
132+
eth := layers.Ethernet{
133+
SrcMAC: mac,
134+
DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
135+
EthernetType: layers.EthernetTypeARP,
136+
}
137+
arp := layers.ARP{
138+
AddrType: layers.LinkTypeEthernet,
139+
Protocol: layers.EthernetTypeIPv4,
140+
HwAddressSize: 6,
141+
ProtAddressSize: 4,
142+
Operation: layers.ARPReply,
143+
SourceHwAddress: mac,
144+
SourceProtAddress: ip.To4(),
145+
DstHwAddress: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
146+
DstProtAddress: ip.To4(),
147+
}
148+
// Set up buffer and options for serialization.
149+
buf := gopacket.NewSerializeBuffer()
150+
151+
opts := gopacket.SerializeOptions{
152+
FixLengths: true,
153+
ComputeChecksums: true,
154+
}
155+
// Send one packet for every address.
156+
gopacket.SerializeLayers(buf, opts, &eth, &arp)
157+
158+
return buf.Bytes()
159+
}

0 commit comments

Comments
 (0)