golang 监听ip数据包(golang纯享版)
【注】本机编译运行平台为linux,如需测试代码请移至linux平台进行代码测试
本文以ip4 作为案例进行包抓取示范,ip6抓取与ip4方式异曲同工,可自行举一反三得出
第一步,通过wireshark抓包拿到ip4下的tcp/udp包,通过wireshark可视化我们可以很容易找到我们需要的源/目的地址信息所在ip包字节数. 这里两张截图,一张ip4,一张ip6的
第二步,编写我们秘制的简易抓包工具,此处以直接输出来源和去向地址为例,自己可以根据需求做更改
package inetimport ("encoding/binary""fmt""strconv""syscall""unsafe"
)func reverse(b []byte) {var (mid uint8blen = len(b))if blen > 1 {for i := 0; i < blen/2; i++ {mid = b[i]b[i] = b[blen-i-1]b[blen-i-1] = mid}}
}
//主机字节序变网络字节序
func Htons[T uint | uint16 | uint32 | uint64](t T) T {var (b []byte = make([]byte, unsafe.Sizeof(t))ptr *T = (*T)(unsafe.Pointer(&b[0])))*ptr = treverse(b)return *ptr
}//ip包必选,ip6自行根据wireshark进行编写,此处ip4为例
type IPHeader struct {Version_And_Len uint8//前4个bit为version(4 ip4,6 ip6),后bit个字节为首部length xxxx xxxxDiffernetialtedService uint8Tot_Len uint16Id uint16Flag_And_Seek uint16//前3bit 为flag后面13bit为seekTTL uint8Protocol uint8CheckSum uint16Source uint32Dest uint32
}
type PortInfo struct {Source uint16Dest uint16
}var (__IP_DEFAULT IPHeaderIPHEADER_SIZE = int(unsafe.Sizeof(__IP_DEFAULT))
)func Watch(watcher func([]byte, int)) error {//socket af_packet 会抓取全部网卡的IP数据包,如需监听特定的网卡请自行判断fd, _, err_ := syscall.Syscall(syscall.SYS_SOCKET, syscall.AF_PACKET, syscall.SOCK_DGRAM, uintptr(Htons[uint16](syscall.ETH_P_IP)))if int(fd) < 0 {return err_}ifd := int(fd)var (buff []byte = make([]byte, 512)lang interr error)defer syscall.Close(ifd)fmt.Println("start watch raw stream", ifd)for {lang, _, err = syscall.Recvfrom(ifd, buff, 0)if lang <= 0 {break}watcher(buff, lang)}return err
}
//输出所有来源去向
func print_info(info []byte, size int) {if size <= IPHEADER_SIZE {return}var (ipheader *IPHeader = (*IPHeader)(unsafe.Pointer(&info[0]))portinfo *PortInfo)if size > IPHEADER_SIZE+4 {portinfo = (*PortInfo)(unsafe.Pointer(&info[IPHEADER_SIZE]))fmt.Printf("src %s:%d dst %s:%d\n", Raw2String(ipheader.Source), portinfo.Source, Raw2String(ipheader.Dest), portinfo.Dest)} else {fmt.Printf("src %s dst %s\n", Raw2String(ipheader.Source), Raw2String(ipheader.Dest))}}
func Raw2String(src uint32) string {raw := make([]byte, 4)binary.LittleEndian.PutUint32(raw, src)return strconv.FormatUint(uint64(raw[0]), 10) + "." + strconv.FormatUint(uint64(raw[1]), 10) + "." + strconv.FormatUint(uint64(raw[2]), 10) + "." + strconv.FormatUint(uint64(raw[3]), 10)
}
func Print_Info() func([]byte, int) {return print_info
}
测试主函数
func main() {fmt.Fprintln(os.Stderr, inet.Watch(inet.Print_Info()))
}