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/ipv6" ) func scanV6(discoverySession session.DiscoverySession) error { targetHosts := discoverySession.TargetHosts() if nil == targetHosts || 0 == len(targetHosts) { return nil } zone := discoverySession.Zone() ps, err := pcap.RetainScanner(zone) if nil != err { return fmt.Errorf("Cannot retain pcap instance %v", err) } defer func() { pcap.ReleaseScanner(zone) }() icmpChan := ps.OpenICMP6() defer func() { ps.CloseICMP6(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 := handlePacketICMP6(zone, targetHosts, hosts, packet); nil != h { if h != nil { discoverySession.AddHost(h) } // resultChan <- h } case <-ticker.C: if false == delay.Load().(bool) { ticker.Stop() timerStopped <- struct{}{} return } delay.Store(false) } } }() if err := sendICMP6(ps, zone, targetHosts); nil != err { log.Printf("sendICMP %v", err) return nil } select { case <-timerStopped: return nil } } func sendICMP6(ps pcap.PCapScanner, zone *omd.Zone, targetHosts []net.IP) error { conn, err := icmp.ListenPacket("ip6:ipv6-icmp", "") if nil != err { return err } defer conn.Close() msg, err := makeMessageICMP6() 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 } timer := time.NewTimer(time.Microsecond * 100) select { case <-timer.C: } } return nil } func handlePacketICMP6(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) _ip6Layer := packet.Layer(layers.LayerTypeIPv6) ip6Layer := _ip6Layer.(*layers.IPv6) _icmp6Layer := packet.Layer(layers.LayerTypeICMPv6) icmp6Layer := _icmp6Layer.(*layers.ICMPv6) if icmp6Layer.TypeCode != layers.ICMPv6TypeEchoReply { return nil } ip := ip6Layer.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.MetaIPTypeEnumV6), Address: ip.String(), Mac: net.HardwareAddr(ethLayer.SrcMAC).String(), Zone: zone, DiscoveredDate: omu.NowPtr(), } hosts[ip.String()] = h return h } func makeMessageICMP6() ([]byte, error) { id := rand.Intn(0xffff) seq := rand.Intn(0xffff) return (&icmp.Message{ Type: ipv6.ICMPTypeEchoRequest, Code: 0, Body: &icmp.Echo{ ID: id, Seq: seq, Data: []byte("PING by overFlow"), }, }).Marshal(nil) }