golang的原始套接字

RawSocket是原始套接字,即可以接受并处理到网卡上的原始数据帧或数据包,而不经过内核的TCP/IP层的处理。
在Go语言的net库里,我们可以使用iprawsocket里的ListenIP来获得创建原生Socket的能力。注意这个是网络层的原生socket,即TCP/UDP层的数据信息需要我们手动解析。

监听数据包

package main

import (
    "fmt"
    "net"
)

func main() {
    protocol := "icmp"
    netaddr, _ := net.ResolveIPAddr("ip4", "127.0.0.1") 
    //如果第二个string参数设置成零值,那么就监控所有源的网络包
    conn, err := net.ListenIP("ip4:"+protocol, netaddr)
    if err != nil {
        fmt.Println(err)
        return
    }
    buf := make([]byte, 1024)
    numRead, _, _ := conn.ReadFrom(buf)
    fmt.Printf("% X\n", buf[:numRead])
}

这样就可以实现监听从127.0.0.1发送过来的icmp包。如果在本地调用ping 127.0.0.1就可以出发这个ReadFrom获得IP包的具体信息。注意只是监听到,但并不拦截。

发送数据包

我们可以直接通过net.Dial来创建一个Dialer变量来连接到已命名的网络,并返回一个Conn变量。 具体信息可以看net/dial.go这个文件里的Dial方法。

conn, err := net.Dial("ip4:tcp", raddr) //这里就指定了是tcp
if err != nil {
    log.Fatalf("Dial: %s\n", err)
}
conn.Write(data)

第一个参数是你选择建立连接的网络类型,想要使用RawSocket的话,必须是ipip4ip6。如果是ip的话,第一个参数的形式必须是[ network ]:[ protocol ]的形式,protocol是你选择在IP网络之上的协议。你可以选择tcpicmp等,对应协议号你可以在/etc/protocols文件中找到。你当然可以自定义协议,比如自己选个协议号。注意当你使用了这个RawSocket,像TCPICMP这种包头信息都需要你自己手动写。

更底层的原生Socket

golang现在不提供对底层的设备直接读写或在数据链路层读写的功能,具体可以看下面的讨论链接
https://github.com/golang/go/issues/15985
但是我们可以通过golang绑定C语言来获得这种能力。大致的示例代码如下:

//gobindc.h
#ifndef GOBINDC_LIBRARY_H
#define GOBINDC_LIBRARY_H
int socketInit(const char *ifname); //初始化socket
int socketOnRecv(void* buf, int size);  //接受数据
int sendPacket(void* buf, int size);    //发送数据
void socketEnd();
#endif

编译C程序为C库的配置

#CMakeLists.txt
cmake_minimum_required(VERSION 3.2)
project(gobindc)
set(CMAKE_C_STANDARD 11)
set(SOURCE_FILES gobindc.c gobindc.h)
add_library(gobindc ${SOURCE_FILES})

Go语言使用C库函数获得原生socket能力

func main() {
    //设置 rx 端口名称
    rxifname := C.CString("ens33")
    defer C.free(unsafe.Pointer(rxifname))
    ...
    //初始化监听Socket
    ret := C.socketInit(rxifname)
    if ret < 0 {
        fmt.Println("Error in init listen socket")
        return
    }
    ...
    var cbuf [2048]C.uint8_t
    for {
        //接收数据
        rxlen := C.socketOnRecv(unsafe.Pointer(&cbuf), 2048)
        //转化为Go语言类型
        gBuf := C.GoBytes(unsafe.Pointer(&cbuf), 2048)[0:rxlen]
        //并行处理
        go func(buf []byte) {
            //对buf数据手动进行解析
            ...
            //示例转发
            txLen := len(buf)
            cTxBuf := C.CBytes(buf)
            C.sendPacket(cTxBuf, C.int(txLen))
            ...
        }(gBuf)
    }
    c.socketEnd()
}

你可能感兴趣的:(golang,tcp-ip)