| package osl | |
| import ( | |
| "bytes" | |
| "context" | |
| "errors" | |
| "fmt" | |
| "net" | |
| "os" | |
| "github.com/containerd/log" | |
| "github.com/vishvananda/netlink" | |
| ) | |
| // NeighborSearchError indicates that the neighbor is already present | |
| type NeighborSearchError struct { | |
| ip net.IP | |
| mac net.HardwareAddr | |
| present bool | |
| } | |
| func (n NeighborSearchError) Error() string { | |
| return fmt.Sprintf("Search neighbor failed for IP %v, mac %v, present in db:%t", n.ip, n.mac, n.present) | |
| } | |
| type neigh struct { | |
| dstIP net.IP | |
| dstMac net.HardwareAddr | |
| linkName string | |
| linkDst string | |
| family int | |
| } | |
| func (n *Namespace) findNeighbor(dstIP net.IP, dstMac net.HardwareAddr) *neigh { | |
| n.mu.Lock() | |
| defer n.mu.Unlock() | |
| for _, nh := range n.neighbors { | |
| if nh.dstIP.Equal(dstIP) && bytes.Equal(nh.dstMac, dstMac) { | |
| return nh | |
| } | |
| } | |
| return nil | |
| } | |
| // DeleteNeighbor deletes neighbor entry from the sandbox. | |
| func (n *Namespace) DeleteNeighbor(dstIP net.IP, dstMac net.HardwareAddr) error { | |
| nh := n.findNeighbor(dstIP, dstMac) | |
| if nh == nil { | |
| return NeighborSearchError{dstIP, dstMac, false} | |
| } | |
| n.mu.Lock() | |
| nlh := n.nlHandle | |
| n.mu.Unlock() | |
| var linkIndex int | |
| if nh.linkDst != "" { | |
| iface, err := nlh.LinkByName(nh.linkDst) | |
| if err != nil { | |
| return fmt.Errorf("could not find interface with destination name %s: %v", nh.linkDst, err) | |
| } | |
| linkIndex = iface.Attrs().Index | |
| } | |
| nlnh := &netlink.Neigh{ | |
| LinkIndex: linkIndex, | |
| IP: dstIP, | |
| State: netlink.NUD_PERMANENT, | |
| Family: nh.family, | |
| } | |
| if nh.family > 0 { | |
| nlnh.HardwareAddr = dstMac | |
| nlnh.Flags = netlink.NTF_SELF | |
| } | |
| // If the kernel deletion fails for the neighbor entry still remove it | |
| // from the namespace cache, otherwise kernel update can fail if the | |
| // neighbor moves back to the same host again. | |
| if err := nlh.NeighDel(nlnh); err != nil && !errors.Is(err, os.ErrNotExist) { | |
| log.G(context.TODO()).Warnf("Deleting neighbor IP %s, mac %s failed, %v", dstIP, dstMac, err) | |
| } | |
| // Delete the dynamic entry in the bridge | |
| if nh.family > 0 { | |
| if err := nlh.NeighDel(&netlink.Neigh{ | |
| LinkIndex: linkIndex, | |
| IP: dstIP, | |
| Family: nh.family, | |
| HardwareAddr: dstMac, | |
| Flags: netlink.NTF_MASTER, | |
| }); err != nil && !errors.Is(err, os.ErrNotExist) { | |
| log.G(context.TODO()).WithError(err).Warn("error while deleting neighbor entry") | |
| } | |
| } | |
| n.mu.Lock() | |
| for i, neighbor := range n.neighbors { | |
| if neighbor.dstIP.Equal(dstIP) && bytes.Equal(neighbor.dstMac, dstMac) { | |
| n.neighbors = append(n.neighbors[:i], n.neighbors[i+1:]...) | |
| break | |
| } | |
| } | |
| n.mu.Unlock() | |
| log.G(context.TODO()).Debugf("Neighbor entry deleted for IP %v, mac %v", dstIP, dstMac) | |
| return nil | |
| } | |
| // AddNeighbor adds a neighbor entry into the sandbox. | |
| func (n *Namespace) AddNeighbor(dstIP net.IP, dstMac net.HardwareAddr, force bool, options ...NeighOption) error { | |
| var ( | |
| iface netlink.Link | |
| err error | |
| neighborAlreadyPresent bool | |
| ) | |
| // If the namespace already has the neighbor entry but the AddNeighbor is called | |
| // because of a miss notification (force flag) program the kernel anyway. | |
| nh := n.findNeighbor(dstIP, dstMac) | |
| if nh != nil { | |
| neighborAlreadyPresent = true | |
| log.G(context.TODO()).Warnf("Neighbor entry already present for IP %v, mac %v neighbor:%+v forceUpdate:%t", dstIP, dstMac, nh, force) | |
| if !force { | |
| return NeighborSearchError{dstIP, dstMac, true} | |
| } | |
| } | |
| 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) | |
| } | |
| } | |
| n.mu.Lock() | |
| nlh := n.nlHandle | |
| n.mu.Unlock() | |
| if nh.linkDst != "" { | |
| iface, err = nlh.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 := nlh.NeighSet(nlnh); err != nil { | |
| return fmt.Errorf("could not add neighbor entry:%+v error:%v", nlnh, err) | |
| } | |
| if neighborAlreadyPresent { | |
| return nil | |
| } | |
| n.mu.Lock() | |
| n.neighbors = append(n.neighbors, nh) | |
| n.mu.Unlock() | |
| log.G(context.TODO()).Debugf("Neighbor entry added for IP:%v, mac:%v on ifc:%s", dstIP, dstMac, nh.linkName) | |
| return nil | |
| } |