| 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 |
| } |