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" ) const ( // IP flag DF = 0x02 // // IP header len // IPHLEN = 40 // // SIOCGIFMTU = 0x8921 UINT16 = 65535 ) 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{}) stopChan := make(chan struct{}) defer close(stopChan) go func() { hosts := make(map[string]*omd.Host) var delay atomic.Value delay.Store(false) ticker := time.NewTicker(time.Millisecond * 1000) 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(omm.ToMetaDiscovererType(omm.MetaDiscovererTypeEnumICMP), h, nil) } case <-ticker.C: if false == delay.Load().(bool) { ticker.Stop() timerStopped <- struct{}{} return } delay.Store(false) } } }() if err := sendICMP4(ps, discoverySession, stopChan); nil != err { log.Printf("sendICMP %v", err) return nil } select { case <-timerStopped: return nil case <-discoverySession.StopChan(): return nil } } func sendICMP4(ps pcap.PCapScanner, discoverySession session.DiscoverySession, stopChan chan struct{}) error { targetHosts := discoverySession.TargetHosts() icmp4Packet, err := makePacketICMPv4(discoverySession.Zone()) if nil != err { return err } buf := gopacket.NewSerializeBuffer() LOOP: for _, targetHost := range targetHosts { h := discoverySession.DiscoveredHost(targetHost.String()) if nil != h && "" != h.Mac { mac, err := net.ParseMAC(h.Mac) if nil != err { icmp4Packet.Eth.DstMAC = nil } else { icmp4Packet.Eth.DstMAC = mac } } if nil == icmp4Packet.Eth.DstMAC { icmp4Packet.Eth.DstMAC = net.HardwareAddr{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} } icmp4Packet.IP.DstIP = targetHost icmp4Packet.IP.Id = uint16(rand.Intn(UINT16)) icmp4Packet.ICMPv4.Id = uint16(rand.Intn(0xffff)) icmp4Packet.ICMPv4.Seq = uint16(rand.Intn(0xffff)) if err := gopacket.SerializeLayers(buf, icmp4Packet.Opts, icmp4Packet.Eth, icmp4Packet.IP, icmp4Packet.ICMPv4); err != nil { log.Print(err) continue LOOP } if err := ps.WritePacketData(buf.Bytes()); err != nil { log.Print(err) continue LOOP } select { case <-time.After(time.Microsecond * 100): case <-stopChan: return nil } } 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.NewHost( zone, omm.ToMetaIPType(omm.MetaIPTypeEnumV4), ip.String(), ) h.Mac = ethLayer.SrcMAC.String() h.DiscoveredDate = omu.NowPtr() hosts[ip.String()] = h return h } type PacketICMPv4 struct { Eth *layers.Ethernet IP *layers.IPv4 ICMPv4 *layers.ICMPv4 Opts gopacket.SerializeOptions } func makePacketICMPv4(zone *omd.Zone) (*PacketICMPv4, error) { packetICMPv4 := &PacketICMPv4{} srcIP := net.ParseIP(zone.Address) if nil == srcIP { return nil, fmt.Errorf("IP(%s) of zone is not valid", zone.Address) } srcMac, err := net.ParseMAC(zone.Mac) if nil != err { return nil, err } packetICMPv4.Eth = &layers.Ethernet{ SrcMAC: srcMac, EthernetType: layers.EthernetTypeIPv4, } packetICMPv4.IP = &layers.IPv4{ SrcIP: srcIP, Flags: DF, Version: 4, TTL: 64, Protocol: layers.IPProtocolICMPv4, } packetICMPv4.ICMPv4 = &layers.ICMPv4{ TypeCode: layers.CreateICMPv4TypeCode(layers.ICMPv4TypeEchoRequest, 0), } packetICMPv4.Opts = gopacket.SerializeOptions{ ComputeChecksums: true, FixLengths: true, } return packetICMPv4, nil }