golang 实现简单的tcp流量监听器

作用

  • 可以用golang实现本机的tcp抓包
  • 可以用来做tcp流量复制工具
  • 在用户态实现异常流量监控

demo源码

import (
    "bytes"
    "encoding/binary"
    "fmt"
    "log"
    "net"
    "strings"
)

func main() {
    netaddr, _ := net.ResolveIPAddr("ip4", interfaceAddress("eth0"))
    conn, _ := net.ListenIP("ip4:tcp", netaddr)
    for {
        buf := make([]byte, 1480)
        n, addr, _ := conn.ReadFrom(buf)
        tcpheader := NewTCPHeader(buf[0:n])
        log.Println(n, addr, tcpheader)
    }
}

func interfaceAddress(ifaceName string) string {
    iface, err := net.InterfaceByName(ifaceName)
    if err != nil {
        panic(err)
    }
    addr, err := iface.Addrs()
    if err != nil {
        panic(err)
    }
    addrStr := strings.Split(addr[0].String(), "/")[0]
    return addrStr
}

const (
    FIN = 1  // 00 0001
    SYN = 2  // 00 0010
    RST = 4  // 00 0100
    PSH = 8  // 00 1000
    ACK = 16 // 01 0000
    URG = 32 // 10 0000
)

type TCPHeader struct {
    Source      uint16
    Destination uint16
    SeqNum      uint32
    AckNum      uint32
    DataOffset  uint8 // 4 bits
    Reserved    uint8 // 3 bits
    ECN         uint8 // 3 bits
    Ctrl        uint8 // 6 bits
    Window      uint16
    Checksum    uint16 // 如果它为0内核将会自动设置
    Urgent      uint16
    Options     []TCPOption
}

type TCPOption struct {
    Kind   uint8
    Length uint8
    Data   []byte
}

// Parse packet into TCPHeader structure
func NewTCPHeader(data []byte) *TCPHeader {
    var tcp TCPHeader
    r := bytes.NewReader(data)
    binary.Read(r, binary.BigEndian, &tcp.Source)
    binary.Read(r, binary.BigEndian, &tcp.Destination)
    binary.Read(r, binary.BigEndian, &tcp.SeqNum)
    binary.Read(r, binary.BigEndian, &tcp.AckNum)

    var mix uint16
    binary.Read(r, binary.BigEndian, &mix)
    tcp.DataOffset = byte(mix >> 12)  // top 4 bits
    tcp.Reserved = byte(mix >> 9 & 7) // 3 bits
    tcp.ECN = byte(mix >> 6 & 7)      // 3 bits
    tcp.Ctrl = byte(mix & 0x3f)       // bottom 6 bits

    binary.Read(r, binary.BigEndian, &tcp.Window)
    binary.Read(r, binary.BigEndian, &tcp.Checksum)
    binary.Read(r, binary.BigEndian, &tcp.Urgent)

    return &tcp
}

func (tcp *TCPHeader) HasFlag(flagBit byte) bool {
    return tcp.Ctrl&flagBit != 0
}
func (tcp *TCPHeader) String() string {
    if tcp == nil {
        return "<nil>"
    }
    return fmt.Sprintf("Source=%v Destination=%v SeqNum=%v AckNum=%v DataOffset=%v Reserved=%v ECN=%v Ctrl=%v Window=%v Checksum=%v Urgent=%v", tcp.Source, tcp.Destination, tcp.SeqNum, tcp.AckNum, tcp.DataOffset, tcp.Reserved, tcp.ECN, tcp.Ctrl, tcp.Window, tcp.Checksum, tcp.Urgent)
}

func (tcp *TCPHeader) Marshal() []byte {

    buf := new(bytes.Buffer)
    binary.Write(buf, binary.BigEndian, tcp.Source)
    binary.Write(buf, binary.BigEndian, tcp.Destination)
    binary.Write(buf, binary.BigEndian, tcp.SeqNum)
    binary.Write(buf, binary.BigEndian, tcp.AckNum)

    var mix uint16
    mix = uint16(tcp.DataOffset)<<12 | // top 4 bits
        uint16(tcp.Reserved)<<9 | // 3 bits
        uint16(tcp.ECN)<<6 | // 3 bits
        uint16(tcp.Ctrl) // bottom 6 bits
    binary.Write(buf, binary.BigEndian, mix)

    binary.Write(buf, binary.BigEndian, tcp.Window)
    binary.Write(buf, binary.BigEndian, tcp.Checksum)
    binary.Write(buf, binary.BigEndian, tcp.Urgent)

    for _, option := range tcp.Options {
        binary.Write(buf, binary.BigEndian, option.Kind)
        if option.Length > 1 {
            binary.Write(buf, binary.BigEndian, option.Length)
            binary.Write(buf, binary.BigEndian, option.Data)
        }
    }

    out := buf.Bytes()

    // Pad to min tcp header size, which is 20 bytes (5 32-bit words)
    pad := 20 - len(out)
    for i := 0; i < pad; i++ {
        out = append(out, 0)
    }

    return out
}

// Csum TCP Checksum
func Csum(data []byte, srcip, dstip [4]byte) uint16 {

    pseudoHeader := []byte{
        srcip[0], srcip[1], srcip[2], srcip[3],
        dstip[0], dstip[1], dstip[2], dstip[3],
        0,                  // zero
        6,                  // protocol number (6 == TCP)
        0, byte(len(data)), // TCP length (16 bits), not inc pseudo header
    }

    sumThis := make([]byte, 0, len(pseudoHeader)+len(data))
    sumThis = append(sumThis, pseudoHeader...)
    sumThis = append(sumThis, data...)
    //fmt.Printf("% x\n", sumThis)

    lenSumThis := len(sumThis)
    var nextWord uint16
    var sum uint32
    for i := 0; i+1 < lenSumThis; i += 2 {
        nextWord = uint16(sumThis[i])<<8 | uint16(sumThis[i+1])
        sum += uint32(nextWord)
    }
    if lenSumThis%2 != 0 {
        //fmt.Println("Odd byte")
        sum += uint32(sumThis[len(sumThis)-1])
    }

    // Add back any carry, and any carry from adding the carry
    sum = (sum >> 16) + (sum & 0xffff)
    sum = sum + (sum >> 16)

    // Bitwise complement
    return uint16(^sum)
}
添加新评论