diff --git a/README-zh.md b/README-zh.md index c9781a2c..4a086029 100644 --- a/README-zh.md +++ b/README-zh.md @@ -844,7 +844,7 @@ apt install bison - [x] 离线数据包文件解析并输出 JSON 格式结果 - [x] 离线数据包解析获取16进制相关数据 - [x] 实时监听接口并捕获数据包 -- [x] 封装 go 调用实时解析的逻辑——通过 Unix 域套接字(AF_UNIX)将实时解析结果传输到 golang +- [x] 封装 go 调用实时解析的逻辑——通过回调函数将实时解析结果传输到 golang - [x] 封装 go 对收到的 Golang 调用实时数据包解析结果的处理 - [x] 优化代码并解决内存泄漏问题,使实时接口可以长时间运行 - [x] 支持停止实时接口 diff --git a/README.md b/README.md index 1f188d33..b9d440fc 100644 --- a/README.md +++ b/README.md @@ -850,7 +850,7 @@ apt install bison - [x] Offline packet files parse and output JSON format - [x] Offline packet parsing to obtain base-16 related data - [x] Listen to interfaces in real time and capture packets -- [x] Encapsulates the logic for go to invoke real-time parsing - transmits real-time parsing results to golang via Unix domain sockets (AF_UNIX) +- [x] Encapsulates the logic for go to invoke real-time parsing - transmits real-time parsing results to golang - [x] Encapsulates Golang's processing of the received real-time packet parsing results for Golang calling - [x] Optimize code to resolve memory leaks - [x] Stop real-time packet capture parsing diff --git a/gowireshark.go b/gowireshark.go index 4ee1800f..b55e7699 100644 --- a/gowireshark.go +++ b/gowireshark.go @@ -16,10 +16,8 @@ import "C" import ( "encoding/json" "log/slog" - "net" "os" "strconv" - "syscall" "unsafe" "github.com/pkg/errors" @@ -41,6 +39,9 @@ var ( // result of a single data packet, which is convenient for converting c char to go string const SINGLEPKTMAXLEN = 6553500 +// DissectResChans dissect result chan map +var DissectResChans = make(map[string]chan FrameDissectRes) + // Init policies、WTAP mod、EPAN mod. func init() { initEnvRes := C.init_env() @@ -525,78 +526,49 @@ func SetIfaceNonblockStatus(deviceName string, isNonblock bool) (status bool, er return } -// RunSock Unix domain socket(AF_UNIX) server: start socket and read data. -func RunSock(sockServerPath string, sockBuffSize int, listener *net.UnixConn, pkgChan chan FrameDissectRes) (err error) { - if sockServerPath == "" { - err = errors.Wrap(err, "sockServerPath is blank") - return +//export GoDataCallback +func GoDataCallback(data *C.char, length C.int, deviceName *C.char) { + goPacket := "" + if data != nil { + goPacket = C.GoStringN(data, length) } - addr, err := net.ResolveUnixAddr("unixgram", sockServerPath) - if err != nil { - err = errors.Wrap(err, "fail to resolve UnixAddr") - panic(err) + deviceNameStr := "" + if deviceName != nil { + deviceNameStr = C.GoString(deviceName) } - syscall.Unlink(sockServerPath) - if listener == nil { - listener, err = net.ListenUnixgram("unixgram", addr) - if err != nil { - err = errors.Wrap(err, "fail to start Unix domain socket(AF_UNIX) server") - return - } + // unmarshal each pkg dissect result + singleFrameData, err := UnmarshalDissectResult(string(goPacket)) + if err != nil { + err = errors.Wrap(ErrUnmarshalObj, "WsIndex: "+singleFrameData.WsIndex) + slog.Warn(err.Error()) } - // read data from c client - go readSock(listener, pkgChan, sockBuffSize) - - return -} - -// readSock Unix domain socket(AF_UNIX) server: read data func -func readSock(listener *net.UnixConn, pkgChan chan FrameDissectRes, sockBuffSize int) { - for { - buf := make([]byte, sockBuffSize) - size, _, err := listener.ReadFromUnix(buf) - if err != nil { - err = errors.Wrap(err, "fail to read from Unix domain socket(AF_UNIX) client") - panic(err) - } - - // handle each pkg - // unmarshal dissect result - singleFrameData, err := UnmarshalDissectResult(string(buf[:size])) - if err != nil { - err = errors.Wrap(ErrUnmarshalObj, "WsIndex: "+singleFrameData.WsIndex) - slog.Warn(err.Error()) - } - - // write packet dissect result to go pipe - pkgChan <- singleFrameData + // write packet dissect result to go pipe + if ch, ok := DissectResChans[deviceNameStr]; ok { + ch <- singleFrameData } } // DissectPktLive // -// @Description: Start up Unix domain socket(AF_UNIX) client, capture and dissect packet. +// @Description: Set up callback function, capture and dissect packet. // @param deviceName // @param bpfFilter bpf filter -// @param sockServerPath // @param num // @param promisc: 0 indicates a non-promiscuous mode, and any other value indicates a promiscuous mode // @param timeout -func DissectPktLive(deviceName, bpfFilter, sockServerPath string, num, promisc, timeout int) (err error) { +func DissectPktLive(deviceName, bpfFilter string, num, promisc, timeout int) (err error) { + // Set up callback function + C.setDataCallback((C.DataCallback)(C.GoDataCallback)) + if deviceName == "" { err = errors.Wrap(err, "device name is blank") return } - if sockServerPath == "" { - err = errors.Wrap(err, "sockServerPath is blank") - return - } - - errMsg := C.handle_packet(C.CString(deviceName), C.CString(bpfFilter), C.CString(sockServerPath), C.int(num), C.int(promisc), C.int(timeout)) + errMsg := C.handle_packet(C.CString(deviceName), C.CString(bpfFilter), C.int(num), C.int(promisc), C.int(timeout)) if C.strlen(errMsg) != 0 { // transfer c char to go string errMsgStr := CChar2GoStr(errMsg) @@ -607,7 +579,7 @@ func DissectPktLive(deviceName, bpfFilter, sockServerPath string, num, promisc, return } -// StopDissectPktLive Stop capture packet live、 free all memory allocated、close socket. +// StopDissectPktLive Stop capture packet live、 free all memory allocated. func StopDissectPktLive(deviceName string) (err error) { if deviceName == "" { err = errors.Wrap(err, "device name is blank") diff --git a/include/online.h b/include/online.h index 44b8ca44..9d37eb03 100644 --- a/include/online.h +++ b/include/online.h @@ -8,7 +8,12 @@ int get_if_nonblock_status(char *device_name); // Set interface nonblock status int set_if_nonblock_status(char *device_name, int nonblock); // Capture and dissect packet in real time -char *handle_packet(char *device_name, char *bpf_expr, char *sock_server_path, - int num, int promisc, int to_ms); -// Stop capture packet live、 free all memory allocated、close socket. -char *stop_dissect_capture_pkg(char *device_name); \ No newline at end of file +char *handle_packet(char *device_name, char *bpf_expr, int num, int promisc, + int to_ms); +// Stop capture packet live、 free all memory allocated +char *stop_dissect_capture_pkg(char *device_name); + +// Set up callback function for send packet to Go +typedef void (*DataCallback)(const char *, int, const char *); +void GoDataCallback(char *data, int length, char *device_name); +void setDataCallback(DataCallback callback); \ No newline at end of file diff --git a/online.c b/online.c index 1d32ce73..ce058008 100644 --- a/online.c +++ b/online.c @@ -1,26 +1,13 @@ -#include #include #include -#include +#include #include #include #include -#include -#include -#include -#include - -#define SOCKBUFFSIZE 655350 +#include // device_content Contains the information needed for each device typedef struct device_content { - char *sock_server_path; - int sockfd; - char sock_buf[SOCKBUFFSIZE]; - struct sockaddr_un serveraddr; - struct sockaddr_un clientaddr; - socklen_t addrlen; - char *device; int num; int promisc; @@ -43,13 +30,9 @@ struct device_map { // global map to restore device info struct device_map *devices = NULL; -char *add_device(char *device_name, char *sock_server_path, int num, - int promisc, int to_ms); +char *add_device(char *device_name, int num, int promisc, int to_ms); struct device_map *find_device(char *device_name); -void *init_sock_send(void *arg); -char *init_sock(char *device_name); - void cap_file_init(capture_file *cf); char *init_cf_live(capture_file *cf_live); void close_cf_live(capture_file *cf_live); @@ -64,16 +47,17 @@ static gboolean process_packet(struct device_map *device, gint64 offset, void before_callback_init(struct device_map *device); void process_packet_callback(u_char *arg, const struct pcap_pkthdr *pkthdr, const u_char *packet); -char *handle_pkt_live(char *device_name, char *sock_server_path, int num, - int promisc, int to_ms); +char *handle_pkt_live(char *device_name, int num, int promisc, int to_ms); char *stop_dissect_capture_pkg(char *device_name); +// Set up callback function for send packet to Go +static DataCallback dataCallback; +void setDataCallback(DataCallback callback) { dataCallback = callback; } /* PART0. Use uthash to implement the logic related to the map of the device */ -char *add_device(char *device_name, char *sock_server_path, int num, - int promisc, int to_ms) { +char *add_device(char *device_name, int num, int promisc, int to_ms) { struct device_map *s; capture_file *cf_tmp; @@ -87,11 +71,9 @@ char *add_device(char *device_name, char *sock_server_path, int num, cap_file_init(cf_tmp); s->device_name = device_name; - s->content.sock_server_path = sock_server_path; s->content.num = num; s->content.promisc = promisc; s->content.to_ms = to_ms; - s->content.addrlen = sizeof(s->content.clientaddr); s->content.cf_live = cf_tmp; // init capture_file @@ -122,54 +104,7 @@ struct device_map *find_device(char *device_name) { } /* -PART1. Unix domain socket(AF_UNIX) -*/ - -// init Unix domain socket(AF_UNIX) send data func -void *init_sock_send(void *arg) { - char *device_name = (char *)arg; - struct device_map *device = find_device(device_name); - if (!device) { - printf("device unknown: %s\n", device_name); - exit(1); - } - - if ((device->content.sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) { - printf("fail to init socket: %s\n", device_name); - exit(1); - } - device->content.serveraddr.sun_family = AF_UNIX; - strcpy(device->content.serveraddr.sun_path, device->content.sock_server_path); -} - -// init Unix domain socket(AF_UNIX) -char *init_sock(char *device_name) { - int ret; - pthread_t send; - ret = pthread_create(&send, NULL, init_sock_send, device_name); - if (ret != 0) { - return "fail to create pthread send"; - } - - // wait for send thread's end - ret = pthread_join(send, NULL); - if (ret != 0) { - return "fail to recollect send"; - } - - return NULL; -} - -// safety memcpy -void *memcpy_safe(void *dest, const void *src, size_t n, size_t left_buf_size) { - if (n > left_buf_size) { - n = left_buf_size; - } - return memcpy(dest, src, n); -} - -/* -PART2. libpcap +PART1. libpcap */ #define SNAP_LEN 65535 @@ -273,7 +208,7 @@ int set_if_nonblock_status(char *device_name, int nonblock) { } /* -PART3. wireshark +PART2. wireshark */ static const nstime_t *raw_get_frame_ts(struct packet_provider_data *prov, @@ -481,7 +416,7 @@ static gboolean prepare_data(wtap_rec *rec, const struct pcap_pkthdr *pkthdr) { } /** - * Use socket to transfer data to the Go program. + * Use callback to transfer data to the Go program. * * @param device: a device in global device map * @return gboolean: true or false @@ -495,20 +430,11 @@ static gboolean send_data_to_go(struct device_map *device) { proto_node_group_children_by_json_key, proto_tree_json, 1); char *proto_tree_json_str = cJSON_PrintUnformatted(proto_tree_json); - int len = strlen(proto_tree_json_str); - memset(device->content.sock_buf, 0, len); - memcpy_safe(device->content.sock_buf, proto_tree_json_str, len, SOCKBUFFSIZE); - if (sendto(device->content.sockfd, device->content.sock_buf, len, 0, - (struct sockaddr *)&device->content.serveraddr, - device->content.addrlen) < 0) { - cJSON_free(proto_tree_json_str); - cJSON_Delete(proto_tree_json); - epan_dissect_cleanup(&device->content.edt); - wtap_rec_cleanup(&device->content.rec); - // printf("socket err, fail to sendto, frame Num:%lu\n", - // (unsigned long int)device->content.cf_live->count); - return FALSE; + + // send data to Go + if (dataCallback != NULL) { + dataCallback(proto_tree_json_str, len, device->device_name); } cJSON_free(proto_tree_json_str); @@ -616,11 +542,10 @@ void process_packet_callback(u_char *arg, const struct pcap_pkthdr *pkthdr, } /** - * Add a device to global device map、listen to this device、init a socket for - * this device and capture packet from this device. + * Add a device to global device map、listen to this device、 + * capture packet from this device. * * @param device_name: the name of interface device - * @param sock_server_path: device's socket server path * @param num: the number of packet you want to capture and dissect * @param promisc: 0 indicates a non-promiscuous mode, and any other value * indicates a promiscuous mode @@ -628,10 +553,10 @@ void process_packet_callback(u_char *arg, const struct pcap_pkthdr *pkthdr, * device * @return char: error message */ -char *handle_packet(char *device_name, char *bpf_expr, char *sock_server_path, int num, - int promisc, int to_ms) { +char *handle_packet(char *device_name, char *bpf_expr, int num, int promisc, + int to_ms) { // add a device to global device map - err_msg = add_device(device_name, sock_server_path, num, promisc, to_ms); + err_msg = add_device(device_name, num, promisc, to_ms); if (err_msg != NULL) { if (strlen(err_msg) != 0) { return err_msg; @@ -661,48 +586,43 @@ char *handle_packet(char *device_name, char *bpf_expr, char *sock_server_path, i bpf_u_int32 mask; bpf_u_int32 net; - if (pcap_lookupnet((const char *) device->device_name, &net, &mask, errbuf) == -1) { - fprintf(stderr, "Could not get netmask for device %s: %s\n", device->device_name, errbuf); - net = 0; - mask = 0; + if (pcap_lookupnet((const char *)device->device_name, &net, &mask, errbuf) != + 0) { + fprintf(stderr, "Could not get netmask for device %s: %s\n", + device->device_name, errbuf); + net = 0; + mask = 0; } - if (pcap_compile(device->content.handle, &fp, bpf_expr, 0, net) == -1) { - fprintf(stderr, "Could not parse filter %s: %s\n", bpf_expr, pcap_geterr(device->content.handle)); - return "Could not parse filter"; + if (pcap_compile(device->content.handle, &fp, bpf_expr, 0, net) != 0) { + fprintf(stderr, "Could not parse filter %s: %s\n", bpf_expr, + pcap_geterr(device->content.handle)); + return "Could not parse filter"; } - if (pcap_setfilter(device->content.handle, &fp) == -1) { - fprintf(stderr, "Could not set filter %s: %s\n", bpf_expr, pcap_geterr(device->content.handle)); - return "Could not set filter"; + if (pcap_setfilter(device->content.handle, &fp) != 0) { + fprintf(stderr, "Could not set filter %s: %s\n", bpf_expr, + pcap_geterr(device->content.handle)); + return "Could not set filter"; } - printf("Start capture packet live sucess on device:%s bpf: %s \n", device->device_name, bpf_expr); - - // start Unix domain socket(AF_UNIX) to send data to golang - err_msg = init_sock(device->device_name); - if (err_msg != NULL) { - if (strlen(err_msg) != 0) { - return err_msg; - } - } + printf("Start capture packet on device:%s bpf: %s \n", device->device_name, + bpf_expr); // loop and dissect pkg before_callback_init(device); pcap_loop(device->content.handle, num, process_packet_callback, - device->device_name); + (u_char *)device->device_name); // close libpcap device handler pcap_close(device->content.handle); // close cf file for live capture close_cf_live(device->content.cf_live); - // close socket - close(device->content.sockfd); return ""; } /** - * Stop capture packet live、 free all memory allocated、close socket. + * Stop capture packet live、 free all memory allocated. * * @param device: a device in global device map * @return char: err message @@ -722,12 +642,5 @@ char *stop_dissect_capture_pkg(char *device_name) { // close cf file for live capture close_cf_live(device->content.cf_live); - // shutdown socket's write function and close socket - int shut_wr_res = shutdown(device->content.sockfd, SHUT_WR); - if (shut_wr_res != 0) { - printf("SHUT_WR: %s\n", "close socket write failed!"); - } - close(device->content.sockfd); - return ""; } \ No newline at end of file diff --git a/tests/gowireshark_test.go b/tests/gowireshark_test.go index 3c1da706..5e9fef30 100644 --- a/tests/gowireshark_test.go +++ b/tests/gowireshark_test.go @@ -3,7 +3,6 @@ package tests import ( "encoding/json" "fmt" - "net" "testing" "time" @@ -92,7 +91,7 @@ func TestGetSpecificFrameHexData(t *testing.T) { } func TestGetSpecificFrameProtoTreeInJson(t *testing.T) { - frameData, err := gowireshark.GetSpecificFrameProtoTreeInJson(inputFilepath, 65, false, true) + frameData, err := gowireshark.GetSpecificFrameProtoTreeInJson(inputFilepath, 65, true, true) if err != nil { t.Fatal(err) } @@ -120,15 +119,35 @@ func TestGetSpecificFrameProtoTreeInJson(t *testing.T) { } func TestGetAllFrameProtoTreeInJson(t *testing.T) { - res, err := gowireshark.GetAllFrameProtoTreeInJson(inputFilepath, true, true) + res, err := gowireshark.GetAllFrameProtoTreeInJson(inputFilepath, true, false) if err != nil { t.Fatal(err) } // read frame from channel - for frame := range res { - fmt.Println("## Frame:", frame) + for _, frameData := range res { + fmt.Println("## Frame:", frameData) fmt.Println("======================================================") + colSrc := frameData.WsSource.Layers["_ws.col"] + col, err := gowireshark.UnmarshalWsCol(colSrc) + if err != nil { + t.Fatal(err) + } + + frameSrc := frameData.WsSource.Layers["frame"] + frame, err := gowireshark.UnmarshalFrame(frameSrc) + if err != nil { + t.Fatal(err) + } + + fmt.Println("# Frame index:", col.Num) + fmt.Println("## WsIndex:", frameData.WsIndex) + fmt.Println("## Offset:", frameData.Offset) + fmt.Println("## Hex:", frameData.Hex) + fmt.Println("## Ascii:", frameData.Ascii) + + fmt.Println("【layer _ws.col】:", col) + fmt.Println("【layer frame】:", frame) } } @@ -183,47 +202,58 @@ Set the num parameter of the DissectPktLive function to -1 to process packets in an infinite loop. */ func TestDissectPktLiveInfinite(t *testing.T) { - sockServerPath := "/tmp/gsocket" - // sockBuffSize The maximum length of packet detail data transmitted by the Unix domain socket; - // Beyond this length will be safely truncated at c; The truncated data will not be properly deserialized into a golang struct. - sockBuffSize := 655350 - ifName := "ens33" + ifName := "en7" filter := "tcp and port 3306" pktNum := -1 promisc := 1 timeout := 20 livePkgCount := 0 - // UnixListener socket server listener - var UnixListener *net.UnixConn - // PkgDetailLiveChan put pkg detail struct into go pipe - var PkgDetailLiveChan = make(chan gowireshark.FrameDissectRes, 1000) - - // socket server: start socket server and wait data to come - err := gowireshark.RunSock(sockServerPath, sockBuffSize, UnixListener, PkgDetailLiveChan) - if err != nil { - t.Fatal(err) - } + gowireshark.DissectResChans[ifName] = make(chan gowireshark.FrameDissectRes, 100) // user read unmarshal data from go channel go func() { for { select { - case pkg := <-PkgDetailLiveChan: + case frameData := <-gowireshark.DissectResChans[ifName]: livePkgCount++ - pkgByte, _ := json.Marshal(pkg) + //fmt.Print(pkg) + pkgByte, _ := json.Marshal(frameData) + fmt.Printf("Processed pkg:【%d】pkg len:【%d】\n", livePkgCount, len(pkgByte)) + //fmt.Println(string(pkgByte)) //fmt.Println(pkg) + colSrc := frameData.WsSource.Layers["_ws.col"] + col, err := gowireshark.UnmarshalWsCol(colSrc) + if err != nil { + fmt.Println(err) + } + + frameSrc := frameData.WsSource.Layers["frame"] + frame, err := gowireshark.UnmarshalFrame(frameSrc) + if err != nil { + fmt.Println(err) + } + + fmt.Println("# Frame index:", col.Num) + fmt.Println("## WsIndex:", frameData.WsIndex) + fmt.Println("## Offset:", frameData.Offset) + fmt.Println("## Hex:", frameData.Hex) + fmt.Println("## Ascii:", frameData.Ascii) + + fmt.Println("【layer _ws.col】:", col) + fmt.Println("【layer frame】:", frame) + default: } } }() - // start socket client, capture and dissect packet. - err = gowireshark.DissectPktLive(ifName, filter, sockServerPath, pktNum, promisc, timeout) + // capture and dissect packet. + err := gowireshark.DissectPktLive(ifName, filter, pktNum, promisc, timeout) if err != nil { - t.Fatal(err) + fmt.Println(err) } select {} @@ -234,34 +264,21 @@ Set the num parameter of the DissectPktLive function to a specific num like 20 to process packets in a limited loop. */ func TestDissectPktLiveSpecificNum(t *testing.T) { - sockServerPath := "/tmp/gsocket" - // sockBuffSize The maximum length of packet detail data transmitted by the Unix domain socket; - // Beyond this length will be safely truncated at c; The truncated data will not be properly deserialized into a golang struct. - sockBuffSize := 655350 - ifName := "en0" - filter := "tcp and port 3306" + ifName := "en7" + filter := "" pktNum := 20 promisc := 1 timeout := 20 livePkgCount := 0 - // UnixListener socket server listener - var UnixListener *net.UnixConn - // PkgDetailLiveChan put pkg detail struct into go pipe - var PkgDetailLiveChan = make(chan gowireshark.FrameDissectRes, 1000) - - // socket server: start socket server and wait data to come - err := gowireshark.RunSock(sockServerPath, sockBuffSize, UnixListener, PkgDetailLiveChan) - if err != nil { - t.Fatal(err) - } + gowireshark.DissectResChans[ifName] = make(chan gowireshark.FrameDissectRes, 100) // user read unmarshal data from go channel go func() { for { select { - case pkg := <-PkgDetailLiveChan: + case pkg := <-gowireshark.DissectResChans[ifName]: livePkgCount++ pkgByte, _ := json.Marshal(pkg) fmt.Printf("Processed pkg:【%d】pkg len:【%d】\n", livePkgCount, len(pkgByte)) @@ -271,8 +288,8 @@ func TestDissectPktLiveSpecificNum(t *testing.T) { } }() - // start socket client, capture and dissect packet. - err = gowireshark.DissectPktLive(ifName, filter, sockServerPath, pktNum, promisc, timeout) + // capture and dissect packet. + err := gowireshark.DissectPktLive(ifName, filter, pktNum, promisc, timeout) if err != nil { t.Fatal(err) } @@ -281,36 +298,21 @@ func TestDissectPktLiveSpecificNum(t *testing.T) { } func TestStopDissectPktLive(t *testing.T) { - sockServerPath := "/tmp/gsocket" - ifName := "ens33" - filter := "tcp and port 3306" - // sockBuffSize The maximum length of packet detail data transmitted by the Unix domain socket; - // Beyond this length will be safely truncated at c; The truncated data will not be properly deserialized into a golang struct. - sockBuffSize := 655350 + ifName := "en7" + filter := "" pktNum := -1 promisc := 1 timeout := 20 livePkgCount := 0 - // UnixListener socket server listener - var UnixListener *net.UnixConn - // PkgDetailLiveChan put pkg detail struct into go pipe - var PkgDetailLiveChan = make(chan gowireshark.FrameDissectRes, 1000) - - // socket server: start socket server and wait data to come - err := gowireshark.RunSock(sockServerPath, sockBuffSize, UnixListener, PkgDetailLiveChan) - if err != nil { - t.Fatal(err) - } - - fmt.Println("socket server started!") + gowireshark.DissectResChans[ifName] = make(chan gowireshark.FrameDissectRes, 100) // user read unmarshal data from go channel go func() { for { select { - case pkg := <-PkgDetailLiveChan: + case pkg := <-gowireshark.DissectResChans[ifName]: //time.Sleep(time.Millisecond * 200) livePkgCount++ pkgByte, _ := json.Marshal(pkg) @@ -333,8 +335,8 @@ func TestStopDissectPktLive(t *testing.T) { }() fmt.Println("start c client, start capture function") - // start socket client, capture and dissect packet. - err = gowireshark.DissectPktLive(ifName, filter, sockServerPath, pktNum, promisc, timeout) + // capture and dissect packet. + err := gowireshark.DissectPktLive(ifName, filter, pktNum, promisc, timeout) if err != nil { t.Fatal(err) }