How to cast bytes to struct(c struct) in go? - c

package main
/*
#define _GNU_SOURCE 1
#include <stdio.h>
#include <stdlib.h>
#include <utmpx.h>
#include <fcntl.h>
#include <unistd.h>
char *path_utmpx = _PATH_UTMPX;
typedef struct utmpx utmpx;
*/
import "C"
import (
"fmt"
"io/ioutil"
)
type Record C.utmpx
func main() {
path := C.GoString(C.path_utmpx)
content, err := ioutil.ReadFile(path)
handleError(err)
var records []Record
// now we have the bytes(content), the struct(Record/C.utmpx)
// how can I cast bytes to struct ?
}
func handleError(err error) {
if err != nil {
panic("bad")
}
}
I'm trying to read content into Record
I have asked a few related questions.
Cannot access c variables in cgo
Can not read utmpx file in go
I have read some articles and posts but still cannot figure out a way to do this.

I think you're going about this the wrong way. If you were wanting to use the C library, you would use the C library to read the file.
Don't use cgo purely to have struct definitions, you should create these in Go yourself. You could then write the appropriate marshal / unmarshal code to read from the raw bytes.
A quick Google shows that someone has already done the work required to convert a look of the relevant C library to Go. See the utmp repository.
A short example of how this could be used is:
package main
import (
"bytes"
"fmt"
"log"
"github.com/ericlagergren/go-gnulib/utmp"
)
func handleError(err error) {
if err != nil {
log.Fatal(err)
}
}
func byteToStr(b []byte) string {
i := bytes.IndexByte(b, 0)
if i == -1 {
i = len(b)
}
return string(b[:i])
}
func main() {
list, err := utmp.ReadUtmp(utmp.UtmpxFile, 0)
handleError(err)
for _, u := range list {
fmt.Println(byteToStr(u.User[:]))
}
}
You can view the GoDoc for the utmp package for more information.

Related

Header file not found in XDP source compiled in Go program

My XDP module compiles by itself, but compilation within the context of a Go program fails because it can't find bpf/bpf_helpers.h file. Here's the code leading up the problem:
package main
import (
"fmt"
bpf "github.com/iovisor/gobpf/bcc"
log "github.com/sirupsen/logrus"
"io/ioutil"
"os"
)
/*
#cgo CFLAGS: -I/usr/include/bcc/compat
#cgo LDFLAGS: -lbcc
#include <bcc/bcc_common.h>
#include <bcc/libbpf.h>
void perf_reader_free(void *ptr);
*/
import "C"
func main() {
// Get the source code from disk
source, err := ioutil.ReadFile("xdp/collect_ips.c")
if err != nil {
log.Fatalln("Cannot read collect_ips.c")
}
// Compile module
module := bpf.NewModule(string(source), []string{
"-Wall",
"-O2",
})
defer module.Close()
// Load module
fn, err := module.Load("collect_ips", C.BPF_PROG_TYPE_XDP, 1, 65536) // Problem happens here
// ...
The Go program compiles fine, but I when I run the program, I get this:
/virtual/main.c:2:10: fatal error: 'bpf/bpf_helpers.h' file not found
#include <bpf/bpf_helpers.h>
^~~~~~~~~~~~~~~~~~~
9 warnings and 1 error generated.
panic: runtime error: invalid memory address or nil pointer dereference
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x4b23d4]
goroutine 1 [running]:
github.com/iovisor/gobpf/bcc.(*Module).Close.func1(0x203000)
/home/me/go/pkg/mod/github.com/iovisor/gobpf#v0.2.0/bcc/module.go:132 +0x14
github.com/iovisor/gobpf/bcc.(*Module).Close(0x0)
/home/me/go/pkg/mod/github.com/iovisor/gobpf#v0.2.0/bcc/module.go:132 +0x36
panic({0x4da720, 0x59ec60})
/usr/lib/go-1.17/src/runtime/panic.go:1038 +0x215
github.com/iovisor/gobpf/bcc.(*Module).Load(0x0, {0x4ef9f2, 0xb}, 0x2, 0x2, 0x8001)
/home/me/go/pkg/mod/github.com/iovisor/gobpf#v0.2.0/bcc/module.go:202 +0x36
main.main()
/home/me/go/src/code.squarespace.net/net/prism-stream/cmd/server/main.go:35 +0x1b7
This problem is happening because of my XDP module because if I comment out this header file in the C source, the error moves to a different header file.
I think this is happening because of bpf_helpers.h does not exist here https://github.com/iovisor/gobpf/tree/master/elf/include. If this is the issue, is there a way to use the header file from /usr/include/bpf?
If I take out bpf_helpers.h from the XDP code, I get an error complaining about the use of SEC in my code:
struct bpf_map_def SEC("maps") addr_map = {
.type = BPF_MAP_TYPE_LRU_HASH,
.key_size = sizeof(struct addr_desc_struct),
.value_size = sizeof(long),
.max_entries = 4096
};
I copied the macro for SEC from bpf_helpers.h to my code, but I then get error: variable has incomplete type 'struct bpf_map_def'. I also use bpf_map_lookup_elem() and bpf_map_update_elem(), which are defined in the bpf/ directory.
I used the eBPF library from Cilium. Here's an example program that loads .o file to an interface, waits for 10 seconds, and prints whatever is in the BPF map.
You have to make sure your XDP function has SEC("xdp") before it. Whereas if you load the program using xdp-loader, you can pass whatever you want to SEC as long as it's different from the name of the function.
package main
import (
"bytes"
"fmt"
"github.com/cilium/ebpf"
log "github.com/sirupsen/logrus"
"github.com/vishvananda/netlink"
"time"
)
func main() {
var objects struct {
XCProg *ebpf.Program `ebpf:"collect_ips"` // Name of function containing the XDP program
XCMap *ebpf.Map `ebpf:"addr_map"` // Name of the map
}
spec, err := ebpf.LoadCollectionSpec("dist/collect_ips.o")
if err != nil {
log.Fatalln("ebpf.LoadCollectionSpec", err)
}
if err := spec.LoadAndAssign(&objects, nil); err != nil {
log.Fatalln("ebpf.LoadAndAssign", err)
}
link, err := netlink.LinkByName("enp0s8")
if err != nil {
log.Fatalln("netlink.LinkByName", err)
}
err = netlink.LinkSetXdpFdWithFlags(link, objects.XCProg.FD(), 2)
if err != nil {
log.Fatalln("netlink.LinkSetXdpFdWithFlags")
}
time.Sleep(time.Second*10)
var keyBytes, valueBytes []byte
iterator := objects.XCMap.Iterate()
if iterator == nil {
log.Fatalln("nil iterator")
}
for iterator.Next(&keyBytes, &valueBytes) != false {
fmt.Printf("%x: %x", keyBytes, valueBytes)
}
defer objects.XCProg.Close()
defer objects.XCMap.Close()
}

How to get values from a C Struct in python?

So I created golang code to pass a value to a C struct and then return it into a pointer so that I can get the value in py. But am I able to get the values from the C struct in py? If so, how can I? My main goal is to pass multiple values from go to the C struct then get those values in py. Also what is the right way to do this without any memory leaks?
Heres my go code:
package main
/*
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
typedef struct Resp {
char *response;
char *responsetwo;
} Resp;
void fill(Resp *r) {
printf("Original value: %s\n", r->response);
}
*/
import "C"
import (
"unsafe"
)
func body_string(body string, body_two string) unsafe.Pointer {
sb := C.CString(body)
sb_two := C.CString(body_two)
// adding it to the C struct
return &C.Resp{
response: sb,
responsetwo: sb_two,
}
}
I am not sure if this is the most efficient way, but is this okay for large text values? Thank you.

CGO: How do you use pointers in Golang to access data from an array in C

I'm writing an app for the windows platform using FFmpeg and it's golang wrapper goav, but I'm having trouble understanding how to use the C pointers to gain access to the data array they point to.
I'm trying to get the data stored in the AVFrame class and use Go to write it to a file, and eventually a texture in OpenGl to make a video player with cool transitions.
I think understanding how to cast and access the C data will make coding this a lot easier.
I've stripped out all the relevant parts of the C code, the wrapper and my code, shown below:
C code - libavutil/frame.h
#include <stdint.h>
typedef struct AVFrame {
#define AV_NUM_DATA_POINTERS 8
uint8_t *data[AV_NUM_DATA_POINTERS];
}
Golang goav wrapper - I don't really know whats going on here with the unsafe.Pointers and casting but it gives me access to the underlying C code
package avutil
/*
#cgo pkg-config: libavutil
#include <libavutil/frame.h>
#include <stdlib.h>
*/
import "C"
import (
"unsafe"
)
type Frame C.struct_AVFrame
func AvFrameAlloc() *Frame {
return (*Frame)(unsafe.Pointer(C.av_frame_alloc()))
}
func Data(f *Frame) *uint8 {
return (*uint8)(unsafe.Pointer((*C.uint8_t)(unsafe.Pointer(&f.data))))
}
My Golang code
package main
import "github.com/giorgisio/goav/avutil"
func main() {
videoFrame := avutil.AvFrameAlloc()
data := avutil.Data(videoFrame)
fmt.Println(data) // here i want the values from data[0] to data[7], but how?
}
Since the library author did not construct a slice header for you to work with you will instead need to cast the return value you get to an unsafe.Pointer and then to a uintptr this will allow you to perform pointer arithmetic on it to get elements later in memory.
Here's some example code that should run as-is on the go playground.
package main
import (
"fmt"
"unsafe"
)
func main() {
nums := []uint8{1, 2, 3, 4, 5, 6, 7, 8}
val := &nums[0] // val is the equivalent of the *uint8 the Data function returns
ptr := unsafe.Pointer(val)
sixthVal := (*uint8)(unsafe.Pointer(uintptr(ptr) + 5*unsafe.Sizeof(*val)))
fmt.Println("Sixth element:", *sixthVal)
}
Of course, you will need to be very certain you know how many elements there are so that you do not access invalid memory.

Invalid argument (EINVAL) when trying to load a BPF program

I am trying to load a BPF program using the bpf syscall but I am receiving invalid argument (EINVAL) on return. From the man page, the possible reasons for this are:
EINVAL
For BPF_PROG_LOAD, indicates an attempt to load an invalid program.
eBPF programs can be deemed invalid due to unrecognized instructions,
the use of reserved fields, jumps out of range, infinite loops or calls
of unknown functions.
So it seems there is something wrong with my BPF program. My BPF program is as follows:
#include <uapi/linux/bpf.h>
int prog(struct pt_regs *ctx)
{
return 0;
}
Which surely cannot have anything wrong with it.
I am compiling with the Makefile here (I removed most of the code from test_overhead_kprobe_kern.c to give a very simple program for testing).
What could be wrong with my program that caused it to get rejected?
uname -a: Linux ubuntu1710 4.13.0-32-generic #35-Ubuntu SMP Thu Jan 25 09:13:46 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
My full userspace code (in Go) is as follows:
package main
/*
#include <stdio.h>
#include <stdlib.h>
void print(char* s) {
printf("%s\n", s);
}
*/
import "C"
import (
"unsafe"
"golang.org/x/sys/unix"
"github.com/cilium/cilium/pkg/bpf"
)
import (
"fmt"
"io/ioutil"
)
const (
bufferSize = 256
sessionIDHTTPHeader = "X-Session-ID"
defaultServerAddress = "localhost"
defaultPort = 5050
)
const (
BPF_PROG_TYPE_UNSPEC = 0
BPF_PROG_TYPE_SOCKET_FILTER = 1
BPF_PROG_TYPE_KPROBE = 2
BPF_PROG_TYPE_SCHED_CLS = 3
BPF_PROG_TYPE_SCHED_ACT = 4
)
type ttyWrite struct {
Count int32
Buf [bufferSize]byte
SessionID int32
}
func main() {
//for i := 0; i < 6; i++ {
//b, err := ioutil.ReadFile(fmt.Sprintf("bpf/test%d.o", i))
b, err := ioutil.ReadFile("bpf/bpf_tty.o")
if err != nil {
fmt.Print(err)
}
err = loadProgram(BPF_PROG_TYPE_KPROBE, unsafe.Pointer(&b), len(b))
if err != nil {
fmt.Printf("%s\n", err)
}
//}
}
func loadProgram(progType int, insns unsafe.Pointer, insnCnt int) error {
licenseBuf := "GPL"
licenseStr := C.CString(licenseBuf)
defer C.free(unsafe.Pointer(licenseStr))
logStr := C.CString("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
defer C.free(unsafe.Pointer(logStr))
lba := struct {
progType uint32
//pad0 [4]byte
insnCnt uint32
//pad1 [4]byte
insns uint64
license uint64
logLevel uint32
//pad2 [4]byte
logSize uint32
//pad3 [4]byte
logBuf uint64
kernVersion uint32
//pad4 [4]byte
}{
progType: uint32(progType),
insnCnt: uint32(insnCnt),
insns: uint64(uintptr(insns)),
license: uint64(uintptr(unsafe.Pointer(licenseStr))),
logLevel: uint32(1),
logSize: uint32(50),
logBuf: uint64(uintptr(unsafe.Pointer(logStr))),
//logBuf: uint64(uintptr(unsafe.Pointer(bufStr))),
// /usr/src/linux-headers-4.13.0-32-generic/include/generated/uapi/linux/version.h
kernVersion: uint32(265485),
}
ret, _, err := unix.Syscall(
unix.SYS_BPF,
bpf.BPF_PROG_LOAD,
uintptr(unsafe.Pointer(&lba)),
unsafe.Sizeof(lba),
)
//fmt.Printf("%s\n", logBuf)
//cs := C.CString("XXXXXXXXXX")
C.print(logStr)
//fmt.Printf("%c\n", *logStr)
if ret != 0 || err != 0 {
//fmt.Printf("%#v %d\n", logBuf, unsafe.Sizeof(lba))
return fmt.Errorf("Unable to load program: ret: %d: %s", int(ret), err)
}
return nil
}
As Qeole pointed out in your previous question, your userspace Go program needs to extract the BPF instructions (.text section) from the object file. Otherwise, the kernel will try to interpret the binary content as BPF instructions and inevitably fail.

Parse post data with punctuation incorrectly in golang

I know how to parse post data in golang
r.ParseForm()
pid := r.PostFormValue("pid")
code := r.PostFormValue("code")
lang := r.PostFormValue("lang")
author := r.PostFormValue("author")
But the post data is pid=1&code=#include <stdio.h>\x0Aint main()\x0A{\x0A\x09printf(\x223\x5Cn\x22);\x0A\x09return 0;\x0A}&lang=c&author=11(this is obtained from nginx log)
So when I parse the data, it could be wrong. The parsed data of code is
#include <stdio.h>
int main()
{
printf("3\n")
instead of
#include <stdio.h>
int main()
{
printf("3\n");
return 0;
}
So how can I fix this problem?
You're passing the raw code, which could be unsafe, your problem is because of this:
https://golang.org/src/net/url/url.go?s=21047:21092#L761
I would suggest that you base64encode your log code and then decode it in your handler.
import "encoding/base64"
...
code, err := base64.RawURLEncoding.DecodeString(r.PostFormValue("code"))
if err != nil {
// handle error
}
Then your request should look like this:
curl --data "pid=1&code=I2luY2x1ZGUgPHN0ZGlvLmg-XHgwQWludCBtYWluKClceDBBe1x4MEFceDA5cHJpbnRmKFx4MjIzXHg1Q25ceDIyKTtceDBBXHgwOXJldHVybiAwO1x4MEF9&lang=c&author=11" http://localhost:8080

Resources