probe/discovery/protocol/icmp/privileged/icmpv4.go
crusader 97184ea723 ing
2018-09-12 13:55:51 +09:00

231 lines
4.8 KiB
Go

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("ICMP", 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
}