package privileged import ( "fmt" "log" "math/rand" "net" "sync/atomic" "time" omd "git.loafle.net/overflow/model/discovery" omm "git.loafle.net/overflow/model/meta" omu "git.loafle.net/overflow/model/util" "git.loafle.net/overflow_scanner/probe/discovery/session" "git.loafle.net/overflow_scanner/probe/internal/pcap" "github.com/google/gopacket" "github.com/google/gopacket/layers" "golang.org/x/net/icmp" "golang.org/x/net/ipv4" ) func scanV4(discoverySession session.DiscoverySession) error { targetHosts := discoverySession.TargetHosts() if nil == targetHosts || 0 == len(targetHosts) { return nil } zone := discoverySession.Zone() ps := discoverySession.PCapScanner() if nil == ps { return fmt.Errorf("Cannot retain pcap instance") } icmpChan := ps.OpenICMP4() defer func() { ps.CloseICMP4(icmpChan) }() timerStopped := make(chan struct{}) go func() { hosts := make(map[string]*omd.Host) var delay atomic.Value delay.Store(false) ticker := time.NewTicker(time.Millisecond * 500) for { select { case packet, ok := <-icmpChan: if !ok { // logging.Logger().Debugf("icmp channel is closed") return } delay.Store(true) if h := handlePacketICMP4(zone, targetHosts, hosts, packet); nil != h { go discoverySession.AddHost(h) } case <-ticker.C: if false == delay.Load().(bool) { ticker.Stop() timerStopped <- struct{}{} return } delay.Store(false) } } }() if err := sendICMP4(ps, zone, targetHosts); nil != err { log.Printf("sendICMP %v", err) return nil } select { case <-timerStopped: return nil case <-discoverySession.StopChan(): return nil } } func sendICMP4(ps pcap.PCapScanner, zone *omd.Zone, targetHosts []net.IP) error { conn, err := icmp.ListenPacket("ip4:icmp", "") if nil != err { return err } defer conn.Close() msg, err := makeMessageICMP4() if nil != err { return err } var dst net.IPAddr LOOP: for _, targetHost := range targetHosts { dst.IP = targetHost if _, err := conn.WriteTo(msg, &dst); nil != err { continue LOOP } select { case <-time.After(time.Microsecond * 100): } } return nil } func handlePacketICMP4(zone *omd.Zone, targetHosts []net.IP, hosts map[string]*omd.Host, packet gopacket.Packet) *omd.Host { _ethLayer := packet.Layer(layers.LayerTypeEthernet) ethLayer := _ethLayer.(*layers.Ethernet) _ip4Layer := packet.Layer(layers.LayerTypeIPv4) ip4Layer := _ip4Layer.(*layers.IPv4) _icmp4Layer := packet.Layer(layers.LayerTypeICMPv4) icmp4Layer := _icmp4Layer.(*layers.ICMPv4) if icmp4Layer.TypeCode != layers.ICMPv4TypeEchoReply { return nil } ip := ip4Layer.SrcIP if _, ok := hosts[ip.String()]; ok { return nil } isTarget := false for _, h := range targetHosts { if h.Equal(ip) { isTarget = true break } } if !isTarget { return nil } h := &omd.Host{ MetaIPType: omm.ToMetaIPType(omm.MetaIPTypeEnumV4), Address: ip.String(), Mac: net.HardwareAddr(ethLayer.SrcMAC).String(), Zone: zone, DiscoveredBy: "ICMP", DiscoveredDate: omu.NowPtr(), } hosts[ip.String()] = h return h } func makeMessageICMP4() ([]byte, error) { id := rand.Intn(0xffff) seq := rand.Intn(0xffff) return (&icmp.Message{ Type: ipv4.ICMPTypeEcho, Code: 0, Body: &icmp.Echo{ ID: id, Seq: seq, Data: []byte("PING by overFlow"), }, }).Marshal(nil) }