package privileged import ( "fmt" "log" "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" ) func scanV6(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.OpenICMP6() defer func() { ps.CloseICMP6(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 := handlePacketICMP6(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 := sendICMP6(ps, discoverySession, stopChan); nil != err { log.Printf("sendICMP %v", err) return nil } select { case <-timerStopped: return nil case <-discoverySession.StopChan(): return nil } } func sendICMP6(ps pcap.PCapScanner, discoverySession session.DiscoverySession, stopChan chan struct{}) error { targetHosts := discoverySession.TargetHosts() icmp6Packet, err := makePacketICMPv6(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 { icmp6Packet.Eth.DstMAC = nil } else { icmp6Packet.Eth.DstMAC = mac } } if nil == icmp6Packet.Eth.DstMAC { icmp6Packet.Eth.DstMAC = net.HardwareAddr{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} } icmp6Packet.IP.DstIP = targetHost if err := gopacket.SerializeLayers(buf, icmp6Packet.Opts, icmp6Packet.Eth, icmp6Packet.IP, icmp6Packet.ICMPv6); 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 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, DiscoveredBy: "ICMP", DiscoveredDate: omu.NowPtr(), } hosts[ip.String()] = h return h } type PacketICMPv6 struct { Eth *layers.Ethernet IP *layers.IPv6 ICMPv6 *layers.ICMPv6 Opts gopacket.SerializeOptions } func makePacketICMPv6(zone *omd.Zone) (*PacketICMPv6, error) { packetICMPv6 := &PacketICMPv6{} 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 } packetICMPv6.Eth = &layers.Ethernet{ SrcMAC: srcMac, EthernetType: layers.EthernetTypeIPv6, } packetICMPv6.IP = &layers.IPv6{ Version: 6, TrafficClass: 0, FlowLabel: 0, Length: 24, NextHeader: layers.IPProtocolICMPv6, HopLimit: 255, SrcIP: srcIP, } packetICMPv6.ICMPv6 = &layers.ICMPv6{ TypeCode: layers.CreateICMPv6TypeCode(layers.ICMPv6TypeEchoRequest, 0), } packetICMPv6.Opts = gopacket.SerializeOptions{ ComputeChecksums: true, FixLengths: true, } return packetICMPv6, nil }