|
@@ -0,0 +1,138 @@
|
|
|
+package sandbox
|
|
|
+
|
|
|
+import (
|
|
|
+ "bytes"
|
|
|
+ "fmt"
|
|
|
+ "net"
|
|
|
+
|
|
|
+ "github.com/vishvananda/netlink"
|
|
|
+)
|
|
|
+
|
|
|
+// NeighOption is a function option type to set interface options
|
|
|
+type NeighOption func(nh *neigh)
|
|
|
+
|
|
|
+type neigh struct {
|
|
|
+ dstIP net.IP
|
|
|
+ dstMac net.HardwareAddr
|
|
|
+ linkName string
|
|
|
+ linkDst string
|
|
|
+ family int
|
|
|
+}
|
|
|
+
|
|
|
+func (n *networkNamespace) findNeighbor(dstIP net.IP, dstMac net.HardwareAddr) *neigh {
|
|
|
+ n.Lock()
|
|
|
+ defer n.Unlock()
|
|
|
+
|
|
|
+ for _, nh := range n.neighbors {
|
|
|
+ if nh.dstIP.Equal(dstIP) && bytes.Equal(nh.dstMac, dstMac) {
|
|
|
+ return nh
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (n *networkNamespace) DeleteNeighbor(dstIP net.IP, dstMac net.HardwareAddr) error {
|
|
|
+ nh := n.findNeighbor(dstIP, dstMac)
|
|
|
+ if nh == nil {
|
|
|
+ return fmt.Errorf("could not find the neighbor entry to delete")
|
|
|
+ }
|
|
|
+
|
|
|
+ return nsInvoke(n.nsPath(), func(nsFD int) error { return nil }, func(callerFD int) error {
|
|
|
+ var iface netlink.Link
|
|
|
+
|
|
|
+ if nh.linkDst != "" {
|
|
|
+ var err error
|
|
|
+ iface, err = netlink.LinkByName(nh.linkDst)
|
|
|
+ if err != nil {
|
|
|
+ return fmt.Errorf("could not find interface with destination name %s: %v",
|
|
|
+ nh.linkDst, err)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ nlnh := &netlink.Neigh{
|
|
|
+ IP: dstIP,
|
|
|
+ State: netlink.NUD_PERMANENT,
|
|
|
+ Family: nh.family,
|
|
|
+ }
|
|
|
+
|
|
|
+ if nlnh.Family > 0 {
|
|
|
+ nlnh.HardwareAddr = dstMac
|
|
|
+ nlnh.Flags = netlink.NTF_SELF
|
|
|
+ }
|
|
|
+
|
|
|
+ if nh.linkDst != "" {
|
|
|
+ nlnh.LinkIndex = iface.Attrs().Index
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := netlink.NeighDel(nlnh); err != nil {
|
|
|
+ return fmt.Errorf("could not delete neighbor entry: %v", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ for i, nh := range n.neighbors {
|
|
|
+ if nh.dstIP.Equal(dstIP) && bytes.Equal(nh.dstMac, dstMac) {
|
|
|
+ n.neighbors = append(n.neighbors[:i], n.neighbors[i+1:]...)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+func (n *networkNamespace) AddNeighbor(dstIP net.IP, dstMac net.HardwareAddr, options ...NeighOption) error {
|
|
|
+ nh := n.findNeighbor(dstIP, dstMac)
|
|
|
+ if nh != nil {
|
|
|
+ // If it exists silently return
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ nh = &neigh{
|
|
|
+ dstIP: dstIP,
|
|
|
+ dstMac: dstMac,
|
|
|
+ }
|
|
|
+
|
|
|
+ nh.processNeighOptions(options...)
|
|
|
+
|
|
|
+ if nh.linkName != "" {
|
|
|
+ nh.linkDst = n.findDst(nh.linkName, false)
|
|
|
+ if nh.linkDst == "" {
|
|
|
+ return fmt.Errorf("could not find the interface with name %s", nh.linkName)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return nsInvoke(n.nsPath(), func(nsFD int) error { return nil }, func(callerFD int) error {
|
|
|
+ var iface netlink.Link
|
|
|
+
|
|
|
+ if nh.linkDst != "" {
|
|
|
+ var err error
|
|
|
+ iface, err = netlink.LinkByName(nh.linkDst)
|
|
|
+ if err != nil {
|
|
|
+ return fmt.Errorf("could not find interface with destination name %s: %v",
|
|
|
+ nh.linkDst, err)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ nlnh := &netlink.Neigh{
|
|
|
+ IP: dstIP,
|
|
|
+ HardwareAddr: dstMac,
|
|
|
+ State: netlink.NUD_PERMANENT,
|
|
|
+ Family: nh.family,
|
|
|
+ }
|
|
|
+
|
|
|
+ if nlnh.Family > 0 {
|
|
|
+ nlnh.Flags = netlink.NTF_SELF
|
|
|
+ }
|
|
|
+
|
|
|
+ if nh.linkDst != "" {
|
|
|
+ nlnh.LinkIndex = iface.Attrs().Index
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := netlink.NeighSet(nlnh); err != nil {
|
|
|
+ return fmt.Errorf("could not add neighbor entry: %v", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ n.neighbors = append(n.neighbors, nh)
|
|
|
+
|
|
|
+ return nil
|
|
|
+ })
|
|
|
+}
|