Opening a pfring from Go: pfring NewRing error: no such device - go

I want to call pf_ring from Go code using the github.com/google/gopacket/pfring package and cannot get it working on a Debian 11 (my code is working on Debian 10).
This is my Go code:
package main
import (
"github.com/google/gopacket/pfring"
"log"
)
func main() {
_, err := pfring.NewRing("eno1#0", 1574, pfring.FlagPromisc|pfring.Flag(1<<14))
if err == nil {
log.Printf("Success!")
return
}
log.Fatalf("Failure: %s", err)
}
And when I run it:
# ./test-go
2023/01/24 10:12:25 Failure: pfring NewRing error: no such device
Obviously the eno1 interface exists:
# pf_ringcfg --list-interfaces
Name: eno1 Driver: i40e RSS: 12 [Supported by ZC]
Name: enp3s0f1 Driver: i40e RSS: 12 [Supported by ZC]
Name: enx0a229512eeb9 Driver: cdc_ether RSS: 1 [Linux Driver]
The odd thing is that the same code written in C works:
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <pfring.h>
int main() {
pfring* ring = pfring_open("eno1#0", 1574, PF_RING_PROMISC | PF_RING_ZC_NOT_REPROGRAM_RSS);
if (ring != NULL) {
printf("Success!\n");
exit(0);
}
int e = errno;
char* msg = strerror(e);
printf("Failure %d: %s\n", e, msg);
exit(1);
}
# ./test-c
Success!
Any idea?

It turns out this is a bug in gopacket/pfring, see issue #147 and the fix. The call to pfring_open made by this library reported no error, but the library misinterpreted the return code.

Related

Segmentation violation error when calling fts_open via cgo

I'm testing cgo and every simple hello world like code works well.
but i have a problem with C code below.
The C code is that traverse a directory tree and sums file size.
if i build with go command, then the build is OK with no error.
but when running, there is a "segmentation violation" error occurred
bash$./walkdir
fatal error: unexpected signal during runtime execution
[signal SIGSEGV: segmentation violation code=0x1 addr=0x1 pc=0x7f631e077c1a]
. . . .
-------------------------------------------------------------
package main
/*
#include <stdint.h>
#include <fts.h>
#include <sys/stat.h>
uintmax_t get_total_size(char *path)
{
uintmax_t total_size = 0;
FTS *fts = fts_open(&path, FTS_PHYSICAL, NULL);
FTSENT *fent;
while ((fent = fts_read(fts)) != NULL)
if (fent->fts_info == FTS_F)
total_size += fent->fts_statp->st_size;
fts_close(fts);
return total_size;
}
*/
import "C"
import "fmt"
func main() {
fmt.Println(C.get_total_size(C.CString("/usr")))
}
fts_open is defined like this:
fts_open()
The fts_open() function takes a pointer to an array of character
pointers naming one or more paths which make up a logical file
hierarchy to be traversed. The array must be terminated by a
null pointer.
C does not have direct support for arrays; it only has pointers.
In your case you pass fts_open a single valid pointer but it is not located in an array which has a NULL pointer as the immediately following element, so fts_open continues to scan the memory past &path — looking for a NULL pointer, — and eventually tries to read memory at some address it is forbidden to do so (usually because the page at that address was not allocated).
A way to fix it is to create that array and initialize it on the C side.
Looks like you're using a reasonably up-to-date standard of C, so let's just use direct literal to initialize the array:
package main
/*
#include <stddef.h> // for NULL
#include <stdint.h>
#include <stdlib.h> // for C.free
#include <fts.h>
#include <sys/stat.h>
uintmax_t get_total_size(char *path)
{
uintmax_t total_size = 0;
char * path_argv[2] = {path, NULL};
FTS *fts = fts_open(path_argv, FTS_PHYSICAL, NULL);
FTSENT *fent;
while ((fent = fts_read(fts)) != NULL)
if (fent->fts_info == FTS_F)
total_size += fent->fts_statp->st_size;
fts_close(fts);
return total_size;
}
*/
import "C"
import (
"fmt"
"unsafe"
)
func main() {
cpath := C.CString("/usr")
defer C.free(unsafe.Pointer(cpath))
fmt.Println(C.get_total_size(cpath))
}
Note that your program has one bug and one possible problem:
A bug is that the call C.CString allocates a chunk of memory by performing a call to malloc(3) from the linked C library, and you did not free that memory block.
The symbol NULL is defined in "stddef.h"; you might or might not get an error when compiling.
I've fixed both problems in my example.
A further improvement over our example might be leveraging the ability of fts_* functions to scan multiple paths in a single run; if we were to implement that, it would have more sense to allocate the array for the 1st argument of fts_open on the Go's side:
package main
/*
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <fts.h>
#include <sys/stat.h>
uintmax_t get_total_size(char * const *path_argv)
{
uintmax_t total_size = 0;
FTS *fts = fts_open(path_argv, FTS_PHYSICAL, NULL);
FTSENT *fent;
while ((fent = fts_read(fts)) != NULL)
if (fent->fts_info == FTS_F)
total_size += fent->fts_statp->st_size;
fts_close(fts);
return total_size;
}
*/
import "C"
import (
"fmt"
"unsafe"
)
func main() {
fmt.Println(getTotalSize("/usr", "/etc"))
}
func getTotalSize(paths ...string) uint64 {
argv := make([]*C.char, len(paths)+1)
for i, path := range paths {
argv[i] = C.CString(path)
defer C.free(unsafe.Pointer(argv[i]))
}
return uint64(C.get_total_size(&argv[0]))
}
Note that here we did not explicitly zero out the last argument of argv because — contrary to C, — Go initializes each allocated memory block with zeroes, so once argv is allocated, all its memory is already zeroed.
you are getting the error cause "fts_open" requires a character pointer to an array which is NULL terminating like char *argv[] = { path, NULL };..(https://linux.die.net/man/3/fts_open)
package main
/*
#include <stdint.h>
#include <fts.h>
#include <sys/stat.h>
uintmax_t get_total_size(char *path)
{
uintmax_t total_size = 0;
char *argv[] = { path, NULL };
FTS *fts = fts_open(argv, FTS_PHYSICAL, NULL);
if (fts == NULL)
return 0;
FTSENT *fent;
while ((fent = fts_read(fts)) != NULL)
if (fent->fts_info == FTS_F)
total_size += fent->fts_statp->st_size;
fts_close(fts);
return total_size;
}
*/
import "C"
import "fmt"
func main() {
fmt.Println(C.get_total_size(C.CString("/usr")))
}
so adding the array pointer will fix the code.
The same code works when compiled with GCC but fts_open returns NULL.I am guessing that there is some difference in optimization between gcc and cgo(not very sure)
I tried some test results and was able to find that when compiling with GCC the char **pointer is getting NULL-terminated but in the case of cgo it was not getting NULL-terminated so you were getting "SIGSEGV" as your code is reading invalid memory reference
#include <stdio.h>
#include <string.h>
void try(char **p)
{
while (*p != NULL)
{
printf("%zu\n", strlen(*p));
++p;
}
}
void get_total_size(char *path)
{
try(&path);
}
int main()
{
get_total_size("/usr");
}
c code (which works)
package main
/*
#include <stdio.h>
#include <string.h>
void try(char **p)
{
while (*p != NULL)
{
printf("%zu\n", strlen(*p));
++p;
}
}
void get_total_size(char *path)
{
try(&path);
}
*/
import "C"
func main() {
C.get_total_size(C.CString("/usr"))
}
same go code you will face error

How to return go (array/slice/ist) to a C function

I have C code in which I am calling golang functions. I am able to do it for primitives data types (int/float etc.) but I want to return some other data structure like array/list/slice.
I could not find any solution on internet.
Looking for help.
Want to return a array/slice/list of string data type.
It would be helpful if you provide additional information, i.e. example code you are currently working on.
As stated from the Cgo documentation page:
Go array types are not supported; use a C pointer
To do so
hello.go
package main
// #include <stdlib.h>
import "C"
import "unsafe"
// StringSlice is a wrapper arround GoStringSlice to make it usable in C.
//export StringSlice
func StringSlice() **C.char {
x := GoStringSlice()
ret := C.malloc(C.size_t(len(x)) * C.size_t(unsafe.Sizeof(uintptr(0))))
// convert to usable format so we are able to fill it with data
pRet := (*[1<<30 - 1]*C.char)(ret)
for i, item := range x {
pRet[i] = C.CString(item)
}
return (**C.char)(ret)
}
func GoStringSlice() []string {
return []string{
"Hello",
"World",
}
}
func main() {}
hello.c
#include <stdio.h>
#include "hello.h"
int main() {
printf("Hello from C!\n");
char **slice = StringSlice();
int numItems = sizeof(slice) / sizeof(char *);
printf("Number of items: %d\n", numItems+1);
printf("String #0: %s\n", *slice);
slice++;
printf("String #1: %s\n", *slice);
return 0;
}
You have to execute go build -buildmode=c-archive hello.go which will generate a hello.h and hello.a.
The hello.a has to be compiled with your C code: gcc -pthread hello.c hello.a -o hello.

cgo calling share library: cannot find lib or function?

I'm using the sample code of chapter 13 of The Go Programming Language as below:
$ cat bzip2.c
#include <bzlib.h>
int bz2compress(bz_stream *s, int action,
char *in, unsigned *inlen, char *out, unsigned *outlen) {
s->next_in = in;
s->avail_in = *inlen;
s->next_out = out;
s->avail_out = *outlen;
int r = BZ2_bzCompress(s, action);
*inlen -= s->avail_in;
*outlen -= s->avail_out;
s->next_in = s->next_out = NULL;
return r;
}
$ cat usebzip2.go
// Package bzip provides a writer that uses bzip2 compression (bzip.org).
package main
import "C"
import (
"io"
"log"
"os"
"testing"
"unsafe"
)
type writer struct {
w io.Writer // underlying output stream
stream *C.bz_stream
outbuf [64 * 1024]byte
}
// Close flushes the compressed data and closes the stream.
// It does not close the underlying io.Writer.
func (w *writer) Close() error {
if w.stream == nil {
panic("closed")
}
defer func() {
C.BZ2_bzCompressEnd(w.stream)
C.bz2free(w.stream)
w.stream = nil
}()
for {
inlen, outlen := C.uint(0), C.uint(cap(w.outbuf))
r := C.bz2compress(w.stream, C.BZ_FINISH, nil, &inlen,
(*C.char)(unsafe.Pointer(&w.outbuf)), &outlen)
if _, err := w.w.Write(w.outbuf[:outlen]); err != nil {
return err
}
if r == C.BZ_STREAM_END {
return nil
}
}
}
// NewWriter returns a writer for bzip2-compressed streams.
func NewWriter(out io.Writer) io.WriteCloser {
const blockSize = 9
const verbosity = 0
const workFactor = 30
w := &writer{w: out, stream: C.bz2alloc()}
C.BZ2_bzCompressInit(w.stream, blockSize, verbosity, workFactor)
return w
}
func main() {
w := NewWriter(os.Stdout)
if _, err := io.Copy(w, os.Stdin); err != nil {
log.Fatalf("bzipper: %v\n", err)
}
if err := w.Close(); err != nil {
log.Fatalf("bzipper: close: %v\n", err)
}
}
First I compile the .c file:
gcc -I/usr/include -L/usr/lib -lbz2 --shared bzip2.c -fPIC -o libbzip2.so
The linux environment LD_LIBRARY_PATH contains ".", and then go build fails:
go build usebzip2.go
# command-line-arguments
/tmp/go-build677611698/b001/_x002.o: In function `_cgo_22d5d7fabfe4_Cfunc_bz2compress':
/tmp/go-build/cgo-gcc-prolog:118: undefined reference to `bz2compress'
collect2: error: ld returned 1 exit status
So how to fix it? I'm using ubuntu 18.04 LTS. Thanks a lot.
Don't run:
go build usebzip2.go
but rather:
go build
(and you don't need to invoke gcc directly on bzip2.c). When you use this process, you'll get many more (but different) errors because you have not put in the right directives before the:
import "C"
line. You need a comment (or series of comments) telling cgo about the functions you intend to provide, or providing those functions inline, and to direct the link phase to use -lbz2. In particular, you will need to:
#include <bzlib.h>
provide a bz2alloc function
provide a bz2free function
provide a declaration for your bz2compress function
set the LDFLAGS to include -lbz2
The actual bz2alloc and bz2free are short and simple and therefore can be included directly in this header block:
package main
/*
#cgo LDFLAGS: -lbz2
#include <bzlib.h>
#include <stdlib.h>
bz_stream *bz2alloc() { return calloc(1, sizeof(bz_stream)); }
int bz2compress(bz_stream *s, int action,
char *in, unsigned *intlen, char *out, unsigned *outlen);
void bz2free(bz_stream* s) { free(s); }
*/
import "C"
If you insert this and run go build you will now see a different and more useful error:
./usebzip2.go:60:2: cannot use w (type *writer) as type io.WriteCloser in return argument:
*writer does not implement io.WriteCloser (missing Write method)
which is of course because type writer does not implement Write.
(There's a completed version of exercise 13.3—not mine—at https://github.com/torbiak/gopl/tree/master/ex13.3. Note that they have augmented theirs to use locking as well, making it safe to call the write function from multiple goroutines simultaneously.)

Failed to load dll. Specified module could not be found

I have some code that I wrote in golang (on ubuntu) and tried to package as a windows exe but unfortunately, due to some cgo dependencies from a github project, I ended up having to build my package as a dll as per this answer https://stackoverflow.com/a/49079049/4750381 because it would not compile as a runnable exe file for windows (even using MinGw).
My compile line was:
GOOS=windows GOARCH=386 CGO_ENABLED=1 CC=i686-w64-mingw32-gcc go build -buildmode=c-shared -o main.dll main.go
My main package code looks like this:
package main
import (
"C"
"fmt"
console "github.com/AsynkronIT/goconsole"
"github.com/AsynkronIT/protoactor-go/actor"
"path/to/repo"
)
const cfgPath string = "./config.json"
func main() {
fmt.Println("from main")
}
func dllRun() {
// Used for running the test and various other operations, thus generally all lines except 1 will be commented out
ctx := actor.EmptyRootContext
props := actor.PropsFromProducer(testmachine.NewTestMachine(cfgPath))
pid, err := ctx.SpawnNamed(props, "tm")
if err != nil {
panic(err)
}
defer func() { // run after the read line fucntion executes and terminates the program
ctx.Poison(pid)
}()
console.ReadLine()
}
I wrote another go script (using windows this time) to try to load and read that DLL file:
import (
"syscall"
)
func main() {
myDLL := syscall.NewLazyDLL("C:/Users/konyenso/Documents/DLLOpener/main.dll")
mainCall := myDLL.NewProc("dllRun")
ret, _, err := mainCall.Call()
if err != nil {
panic(err) // calling myDLL.mainCall failed
}
if ret == 0 {
print("Could not set the desired attributes")
// TODO: call GetLastError to get more information
}
print("OK")
}
But now I always get the error below even though my file path is ok:
panic: Failed to load C:/Users/konyenso/Documents/DLLOpener/main.dll: The specified module could not be found.
goroutine 1 [running]:
syscall.(*LazyProc).mustFind(0x13019400)
c:/go/src/syscall/dll_windows.go:311 +0x42
syscall.(*LazyProc).Call(0x13019400, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4623e0)
c:/go/src/syscall/dll_windows.go:327 +0x21
main.main()
C:/Users/konyenso/Documents/DLLOpener/main.go:11 +0xa5
Please could someone tell me what I'm doing wrong? I've been tweaking this all day with little success. Ideally I would want to build an exe straight from ubuntu without the dll but if that's not feasible, I would at least like to be able to run my dll from another exe file.
Thanks for any assistance.
********** EDIT****************************
So I wrote some C++ code to try to open the dll file (made 64 and 32 bit versions of both)
#define UNICODE
#include <windows.h>
#include <iostream>
/* Define a function pointer for our imported
* function.
* This reads as "introduce the new type f_funci as the type:
* pointer to a function returning an int and
* taking no arguments.
*
* Make sure to use matching calling convention (__cdecl, __stdcall, ...)
* with the exported function. __stdcall is the convention used by the WinAPI
*/
typedef int (__stdcall *f_funci)();
int main()
{
HINSTANCE hGetProcIDDLL = LoadLibrary((LPCWSTR)"C:\\Users\\konyenso\\Documents\\DLLOpener\\main.dll");
if (!hGetProcIDDLL) {
std::cout << "could not load the dynamic library" << std::endl;
return EXIT_FAILURE;
}
// resolve function address here
f_funci funci = (f_funci)GetProcAddress(hGetProcIDDLL, "dllRun");
if (!funci) {
std::cout << "could not locate the function" << std::endl;
return EXIT_FAILURE;
}
std::cout << "funci() returned " << funci() << std::endl;
return EXIT_SUCCESS;
}
Same issue:
Screenshot showing could not load
As you can see from this below screenshot, the file path matches so I have no idea what's going on.
Screenshot showing paths confirmed
First,you need export dll method like this:
// export dllRun
func dllRun() {
There is a simple example,
https://github.com/whtiehack/checkdll_log
Second,
Go main program could not load Go dll correct because
https://github.com/golang/go/issues/34168
https://github.com/golang/go/issues/22192
In summary, in Windows, there can not have more than two go runtimes in a main program.

GDB can't debug the go program within cgo code

example files
src/test.go
package main
import (
. "clib"
)
func main() {
a := "123";
b := "456";
c := "789";
println(a,b,c);
Output("ABC");
}
src/clib/clib.h
#ifndef CLIB
void output(char* str);
#endif
src/clib/clib.c
#include "clib.h"
#include <stdio.h>
void output(char* str)
{
printf("%s\n", str);
}
src/clib/clib.go
package clib
/*
#cgo CFLAGS:-g
#include "clib.h"
*/
import "C"
func Output(s string) {
p := C.CString(s);
C.output(p);
}
exec code
go build -gcflags "-N -l" test.go
gdb ./test
b 10
r
info locals // <- every variable's value is wrong!
Who can help me solve this problem, thank you very much.
My Environment:
ubuntu 11.04 i386
gdb 7.6
go 1.1
There is currently an open bug regarding this: https://code.google.com/p/go/issues/detail?id=5221
Debugging cgo with gdb worked in 1.0 but is currently broken in 1.1. It's being worked on.

Resources