gopacket学习

1. 使用示例

https://www.cnblogs.com/rsapaper/p/15493262.html

 http://timd.cn/go/gopacket/

2. 使用gopacket发送dns数据包

 

package main

import (
    "net"

    "github.com/google/gopacket"
    "github.com/google/gopacket/layers"
    "github.com/google/gopacket/pcap"
)

func main() {
    handle, err := pcap.OpenLive("lo", 1500, false, pcap.BlockForever)
    if err != nil {
        panic(err)
    }

    // Create ethernet layer
    eth := layers.Ethernet{
        SrcMAC:       net.HardwareAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
        DstMAC:       net.HardwareAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
        EthernetType: layers.EthernetTypeIPv4,
    }

    // Create ip layer
    ip := layers.IPv4{
        Version:  4,
        TTL:      64,
        SrcIP:    net.IP{1, 3, 3, 7},
        DstIP:    net.IP{127, 0, 0, 1},
        Protocol: layers.IPProtocolUDP,
    }

    // Create udp layer
    udp := layers.UDP{
        SrcPort: 62003,
        DstPort: 53,
    }
    udp.SetNetworkLayerForChecksum(&ip)

    qst := layers.DNSQuestion{
        Name:  []byte{'w', 'w', 'w', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'c', 'o', 'm', '.'},
        Type:  layers.DNSTypeCNAME,
        Class: layers.DNSClassIN,
    }

    dns := layers.DNS{
        BaseLayer:    layers.BaseLayer{},
        ID:           0,
        QR:           true,
        OpCode:       0,
        AA:           false,
        TC:           false,
        RD:           true,
        RA:           true,
        Z:            0,
        ResponseCode: 0,
        QDCount:      1,
        ANCount:      1,
        NSCount:      0,
        ARCount:      0,
        Questions:    []layers.DNSQuestion{qst},
    }

    buffer := gopacket.NewSerializeBuffer()
    options := gopacket.SerializeOptions{
        ComputeChecksums: true,
        FixLengths:       true,
    }

    if err = gopacket.SerializeLayers(buffer, options,
        &eth,
        &ip,
        &udp,
        &dns,
    ); err != nil {
        panic(err)
    }
    outgoingPacket := buffer.Bytes()

    if err = handle.WritePacketData(outgoingPacket); err != nil {
        panic(err)
    }
}

 

3.使用gopacket被动分析dns数据包(10个消费者同时分析)

package main

import (
    "fmt"
    "net"
    "sync"
    "time"

    "github.com/google/gopacket"
    "github.com/google/gopacket/layers"
    _ "github.com/google/gopacket/layers"
    "github.com/google/gopacket/pcap"
    "github.com/pkg/errors"

    "github.com/akitasoftware/akita-cli/printer"
    "github.com/petermattis/goid"
)

const (
    // The same default as tcpdump.
    defaultSnapLen = 262144
)

var wg sync.WaitGroup

type pcapWrapper interface {
    capturePackets(done <-chan struct{}, interfaceName, bpfFilter string) (<-chan gopacket.Packet, error)
}

type pcapImpl struct{}

func (p *pcapImpl) capturePackets(wrappedChan chan gopacket.Packet, done <-chan struct{}, interfaceName, bpfFilter string) error {
    handle, err := pcap.OpenLive(interfaceName, defaultSnapLen, true, pcap.BlockForever)
    if err != nil {
        return errors.Wrapf(err, "failed to open pcap to %s", interfaceName)
    }
    if bpfFilter != "" {
        if err := handle.SetBPFFilter(bpfFilter); err != nil {
            handle.Close()
            return errors.Wrap(err, "failed to set BPF filter")
        }
    }

    // Creating the packet source takes some time - do it here so the caller can
    // be confident that pakcets are being watched after this function returns.
    packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
    pktChan := packetSource.Packets()

    // TODO: tune the packet channel buffer
    // wrappedChan := make(chan gopacket.Packet, 10)
    go func() {
        // Closing the handle can take a long time, so we close wrappedChan first to
        // allow the packet consumer to advance with its processing logic while we
        // wait for the handle to close in this goroutine.
        defer func() {
            handle.Close()
        }()

        startTime := time.Now()
        count := 0
        for {
            select {
            case <-done:
                return
            case pkt, ok := <-pktChan:
                if ok {
                    wrappedChan <- pkt

                    if count == 0 {
                        ttfp := time.Now().Sub(startTime)
                        printer.Debugf("Time to first packet on %s: %s\n", interfaceName, ttfp)
                    }
                    count += 1
                } else {
                    return
                }
            }
        }
    }()
    return nil
}

func nextIP(ip net.IP) {
    for i := len(ip) - 1; i >= 0; i-- {
        ip[i]++
        if ip[i] > 0 {
            break
        }
    }
}

func consumer(wrappedChan chan gopacket.Packet) {

    defer wg.Done()
    var eth layers.Ethernet
    var ip4 layers.IPv4
    var udp layers.UDP
    var dns layers.DNS

    parser := gopacket.NewDecodingLayerParser(
        layers.LayerTypeEthernet, &eth, &ip4, &udp, &dns)
    decodedLayers := []gopacket.LayerType{}
    for pk := range wrappedChan {
        fmt.Printf("协程ID:%d \n", goid.Get())
        parser.DecodeLayers(pk.Data(), &decodedLayers)
        fmt.Printf("dnsID:%d, answers count: %d,  是否是回应包:%t, Queries: %s \n", dns.ID, dns.ANCount, dns.QR, string(dns.Questions[0].Name))
        //Answers
        for _, v := range dns.Answers {
            fmt.Printf("Answers-qname:%s, Answers: %s, Answers-type: %s, Answers-class: %s \n", string(v.Name), v.String(), v.Type.String(), v.Class.String())
        }
        fmt.Println("===========")
    }

}

func main() {

    p := pcapImpl{}
    var wrappedChan = make(chan gopacket.Packet, 10000)
    var doneCh = make(chan struct{})

    defer close(wrappedChan)

    err := p.capturePackets(wrappedChan, doneCh, "ens33", "port 53")
    if err != nil {
        fmt.Println(err)
    }

    // 10个消费者从同一队列取数据,分析dns数据
    for i := 1; i <= 10; i++ {
        wg.Add(1)
        go consumer(wrappedChan)
    }
    wg.Wait()

}

 

4. 使用gopacket被动分析dns数据包(10个消费者同时分析,增加定时统计)

package main

import (
    "fmt"
    "sync"
    "time"

    "github.com/google/gopacket"
    "github.com/google/gopacket/layers"
    _ "github.com/google/gopacket/layers"
    "github.com/google/gopacket/pcap"
    "github.com/pkg/errors"

    "github.com/akitasoftware/akita-cli/printer"
)

const (
    // The same default as tcpdump.
    defaultSnapLen = 262144
)

var wg sync.WaitGroup
var query int64
var response int64

type pcapWrapper interface {
    capturePackets(done <-chan struct{}, interfaceName, bpfFilter string) (<-chan gopacket.Packet, error)
}

type pcapImpl struct{}

type Result struct {
    Query    int64
    Response int64
}

func (p *pcapImpl) capturePackets(wrappedChan chan gopacket.Packet, done <-chan struct{}, interfaceName, bpfFilter string) error {
    handle, err := pcap.OpenLive(interfaceName, defaultSnapLen, true, pcap.BlockForever)
    if err != nil {
        return errors.Wrapf(err, "failed to open pcap to %s", interfaceName)
    }
    if bpfFilter != "" {
        if err := handle.SetBPFFilter(bpfFilter); err != nil {
            handle.Close()
            return errors.Wrap(err, "failed to set BPF filter")
        }
    }

    // Creating the packet source takes some time - do it here so the caller can
    // be confident that pakcets are being watched after this function returns.
    packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
    pktChan := packetSource.Packets()

    // TODO: tune the packet channel buffer
    // wrappedChan := make(chan gopacket.Packet, 10)
    go func() {
        // Closing the handle can take a long time, so we close wrappedChan first to
        // allow the packet consumer to advance with its processing logic while we
        // wait for the handle to close in this goroutine.
        defer func() {
            handle.Close()
        }()

        startTime := time.Now()
        count := 0
        for {
            select {
            case <-done:
                return
            case pkt, ok := <-pktChan:
                if ok {
                    wrappedChan <- pkt

                    if count == 0 {
                        ttfp := time.Now().Sub(startTime)
                        printer.Debugf("Time to first packet on %s: %s\n", interfaceName, ttfp)
                    }
                    count += 1
                } else {
                    return
                }
            }
        }
    }()
    return nil
}

func consumer(wrappedChan chan gopacket.Packet, resultChan chan Result) {

    defer wg.Done()
    var result Result
    var eth layers.Ethernet
    var ip4 layers.IPv4
    var udp layers.UDP
    var dns layers.DNS

    parser := gopacket.NewDecodingLayerParser(
        layers.LayerTypeEthernet, &eth, &ip4, &udp, &dns)
    decodedLayers := []gopacket.LayerType{}
    for pk := range wrappedChan {
        // fmt.Printf("协程ID:%d \n", goid.Get())
        parser.DecodeLayers(pk.Data(), &decodedLayers)
        if dns.ID == 0 {
            continue
        }
        fmt.Printf("dnsID:%d, answers count: %d,  是否是回应包:%t, Queries: %s \n", dns.ID, dns.ANCount, dns.QR, string(dns.Questions[0].Name))

        // //Answers
        // for _, v := range dns.Answers {
        //     fmt.Printf("Answers-qname:%s, Answers: %s, Answers-type: %s, Answers-class: %s \n", string(v.Name), v.String(), v.Type.String(), v.Class.String())
        // }
        if dns.QR {
            result.Response = 1
        } else {
            result.Query = 1
        }
        resultChan <- result
    }

}

func recount(sig chan struct{}, resultChan chan Result, query *int64, response *int64) {
    go func() {
        for {
            select {

            case <-sig:
                *query = 0
                *response = 0
            case r := <-resultChan:
                *query += r.Query
                *response += r.Response
            }
        }
    }()
}

func main() {

    p := pcapImpl{}
    var wrappedChan = make(chan gopacket.Packet, 10000)
    var resultChan = make(chan Result, 10000)
    var doneCh = make(chan struct{})
    var sigChan = make(chan struct{})
    var (
        query    *int64
        response *int64
    )
    query = new(int64)
    response = new(int64)

    defer close(wrappedChan)

    err := p.capturePackets(wrappedChan, doneCh, "enp4s0", "port 53")
    if err != nil {
        fmt.Println(err)
    }

    // 10个消费者从同一队列取数据,分析dns数据
    for i := 1; i <= 10; i++ {
        wg.Add(1)
        go consumer(wrappedChan, resultChan)
    }

    go recount(sigChan, resultChan, query, response)

    ticker := time.NewTicker(time.Duration(5) * time.Second)
    defer ticker.Stop()
    for range ticker.C {
        fmt.Printf("Query: %d, Response: %d \n", *query, *response)
        sigChan <- struct{}{}
    }
    wg.Wait()
}
View Code

 

package main

import (
"fmt"
"sync"
"time"

"github.com/google/gopacket"
"github.com/google/gopacket/layers"
_ "github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
"github.com/pkg/errors"

"github.com/akitasoftware/akita-cli/printer"
)

const (
// The same default as tcpdump.
defaultSnapLen = 262144
)

var wg sync.WaitGroup
var query int64
var response int64

type pcapWrapper interface {
capturePackets(done <-chan struct{}, interfaceName, bpfFilter string) (<-chan gopacket.Packet, error)
}

type pcapImpl struct{}

type Result struct {
Query int64
Response int64
}

func (p *pcapImpl) capturePackets(wrappedChan chan gopacket.Packet, done <-chan struct{}, interfaceName, bpfFilter string) error {
handle, err := pcap.OpenLive(interfaceName, defaultSnapLen, true, pcap.BlockForever)
if err != nil {
return errors.Wrapf(err, "failed to open pcap to %s", interfaceName)
}
if bpfFilter != "" {
if err := handle.SetBPFFilter(bpfFilter); err != nil {
handle.Close()
return errors.Wrap(err, "failed to set BPF filter")
}
}

// Creating the packet source takes some time - do it here so the caller can
// be confident that pakcets are being watched after this function returns.
packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
pktChan := packetSource.Packets()

// TODO: tune the packet channel buffer
// wrappedChan := make(chan gopacket.Packet, 10)
go func() {
// Closing the handle can take a long time, so we close wrappedChan first to
// allow the packet consumer to advance with its processing logic while we
// wait for the handle to close in this goroutine.
defer func() {
handle.Close()
}()

startTime := time.Now()
count := 0
for {
select {
case <-done:
return
case pkt, ok := <-pktChan:
if ok {
wrappedChan <- pkt

if count == 0 {
ttfp := time.Now().Sub(startTime)
printer.Debugf("Time to first packet on %s: %s\n", interfaceName, ttfp)
}
count += 1
} else {
return
}
}
}
}()
return nil
}

func consumer(wrappedChan chan gopacket.Packet, resultChan chan Result) {

defer wg.Done()
var result Result
var eth layers.Ethernet
var ip4 layers.IPv4
var udp layers.UDP
var dns layers.DNS

parser := gopacket.NewDecodingLayerParser(
layers.LayerTypeEthernet, &eth, &ip4, &udp, &dns)
decodedLayers := []gopacket.LayerType{}
for pk := range wrappedChan {
// fmt.Printf("协程ID:%d \n", goid.Get())
parser.DecodeLayers(pk.Data(), &decodedLayers)
if dns.ID == 0 {
continue
}
fmt.Printf("dnsID:%d, answers count: %d, 是否是回应包:%t, Queries: %s\n", dns.ID, dns.ANCount, dns.QR, string(dns.Questions[0].Name))

// //Answers
// for _, v := range dns.Answers {
// fmt.Printf("Answers-qname:%s, Answers: %s, Answers-type: %s, Answers-class: %s \n", string(v.Name), v.String(), v.Type.String(), v.Class.String())
// }
if dns.QR {
result.Response = 1
} else {
result.Query = 1
}
resultChan <- result
}

}

func recount(sig chan struct{}, resultChan chan Result, query *int64, response *int64) {
go func() {
for {
select {

case <-sig:
*query = 0
*response = 0
case r := <-resultChan:
*query += r.Query
*response += r.Response
}
}
}()
}

func main() {

p := pcapImpl{}
var wrappedChan = make(chan gopacket.Packet, 10000)
var resultChan = make(chan Result, 10000)
var doneCh = make(chan struct{})
var sigChan = make(chan struct{})
var (
query *int64
response *int64
)
query = new(int64)
response = new(int64)

defer close(wrappedChan)

err := p.capturePackets(wrappedChan, doneCh, "enp4s0", "port 53")
if err != nil {
fmt.Println(err)
}

// 10个消费者从同一队列取数据,分析dns数据
for i := 1; i <= 10; i++ {
wg.Add(1)
go consumer(wrappedChan, resultChan)
}

go recount(sigChan, resultChan, query, response)

ticker := time.NewTicker(time.Duration(5) * time.Second)
defer ticker.Stop()
for range ticker.C {
fmt.Printf("Query: %d, Response: %d\n", *query, *response)
sigChan <- struct{}{}
}
wg.Wait()
}
posted @ 2022-03-01 23:02  明天OoO你好  阅读(713)  评论(0编辑  收藏  举报