package main import ( "fmt" "os" "syscall" "time" "github.com/pivotal-golang/lager" "github.com/vishvananda/netlink" "github.com/vishvananda/netlink/nl" "github.com/cloudfoundry-incubator/ducati-daemon/lib/namespace" ) func main() { logger := lager.NewLogger("ducati-d") logger = logger.Session("", lager.Data{"pid": os.Getpid()}) logger.RegisterSink(lager.NewWriterSink(os.Stdout, lager.DEBUG)) namespacePath := os.Args[1] ns := namespace.NewNamespace(namespacePath) misses := make(chan *netlink.Neigh, 100) logger.Debug("Creating netlink socket") var sock *nl.NetlinkSocket ns.Execute(func(_ *os.File) error { logger.Debug("Subscribing to socket") sock = subscribe(logger) return nil }) ch := make(chan struct{}, 1) logger.Debug("Monitoring misses", lager.Data{"socket": fmt.Sprintf("%+v", sock)}) go monitorMisses(logger, misses, sock) <-ch } func subscribe(logger lager.Logger) *nl.NetlinkSocket { nlsock, err := nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_NEIGH) if err != nil { logger.Error("Failed to subscribe to netlink RTNLGRP_NEIGH messages", err) return nil } return nlsock } func monitorMisses(logger lager.Logger, misses chan *netlink.Neigh, nlsock *nl.NetlinkSocket) { for { msgs, err := nlsock.Receive() if err != nil { logger.Error("Failed to receive from netlink", err) time.Sleep(1 * time.Second) continue } for _, msg := range msgs { processNeighMsg(logger, msg, misses) } } } func isNeighResolving(state int) bool { return (state & (netlink.NUD_INCOMPLETE | netlink.NUD_STALE | netlink.NUD_DELAY | netlink.NUD_PROBE)) != 0 } type neigh netlink.Neigh func (n *neigh) String() string { var readableState string if n.State&netlink.NUD_INCOMPLETE != 0 { readableState = " | " + "INCOMPLETE" } if n.State&netlink.NUD_REACHABLE != 0 { readableState = " | " + "REACHABLE" } if n.State&netlink.NUD_STALE != 0 { readableState = " | " + "STALE" } if n.State&netlink.NUD_DELAY != 0 { readableState = " | " + "DELAY" } if n.State&netlink.NUD_PROBE != 0 { readableState = " | " + "PROBE" } if n.State&netlink.NUD_FAILED != 0 { readableState = " | " + "FAILED" } if n.State&netlink.NUD_NOARP != 0 { readableState = " | " + "NOARP" } if n.State&netlink.NUD_PERMANENT != 0 { readableState = " | " + "PERMANENT" } return fmt.Sprintf( "LinkIndex: %d, Family: %d, State: %s, Type: %d, Flags: %d, IP: %s, HardwareAddr: %s", int(n.LinkIndex), int(n.Family), readableState, int(n.Type), int(n.Flags), n.IP.String(), n.HardwareAddr.String(), ) } func processNeighMsg(logger lager.Logger, msg syscall.NetlinkMessage, misses chan *netlink.Neigh) { n, err := netlink.NeighDeserialize(msg.Data) if err != nil { logger.Error("Failed to deserialize netlink ndmsg", err) return } myNeigh := neigh(*n) logger.Debug("netlink-neighbor-message", lager.Data{"msg": fmt.Sprintf("%s", myNeigh.String())}) if msg.Header.Type != syscall.RTM_GETNEIGH && msg.Header.Type != syscall.RTM_NEWNEIGH { return } if !isNeighResolving(n.State) { // misses come with NUD_STALE bit set return } misses <- n }