Skip to content

Commit 53f6653

Browse files
committed
Merge pull request #1 from AkihiroSuda/suda/wip
Avoid golang/go#14210 (Go 1.6 panic: runtime error: cgo argument has Go pointer to Go pointer)
2 parents 402cdf1 + f2d32d4 commit 53f6653

File tree

4 files changed

+72
-7
lines changed

4 files changed

+72
-7
lines changed

.travis.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
language: go
22

33
go:
4+
- tip
5+
- 1.6
46
- 1.5
57

68
before_install:

netfilter.go

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,12 @@ package netfilter
3232
import "C"
3333

3434
import (
35+
"fmt"
3536
"github.com/google/gopacket"
3637
"github.com/google/gopacket/layers"
37-
"fmt"
38+
"os"
39+
"sync"
40+
"time"
3841
"unsafe"
3942
)
4043

@@ -61,6 +64,7 @@ type NFQueue struct {
6164
qh *C.struct_nfq_q_handle
6265
fd C.int
6366
packets chan NFPacket
67+
idx uint32
6468
}
6569

6670
//Verdict for a packet
@@ -79,6 +83,9 @@ const (
7983
NF_DEFAULT_PACKET_SIZE uint32 = 0xffff
8084
)
8185

86+
var theTable = make(map[uint32]*chan NFPacket, 0)
87+
var theTabeLock sync.RWMutex
88+
8289
//Create and bind to queue specified by queueId
8390
func NewNFQueue(queueId uint16, maxPacketsInQueue uint32, packetSize uint32) (*NFQueue, error) {
8491
var nfq = NFQueue{}
@@ -98,7 +105,11 @@ func NewNFQueue(queueId uint16, maxPacketsInQueue uint32, packetSize uint32) (*N
98105
}
99106

100107
nfq.packets = make(chan NFPacket)
101-
if nfq.qh, err = C.CreateQueue(nfq.h, C.u_int16_t(queueId), unsafe.Pointer(&nfq.packets)); err != nil || nfq.qh == nil {
108+
nfq.idx = uint32(time.Now().UnixNano())
109+
theTabeLock.Lock()
110+
theTable[nfq.idx] = &nfq.packets
111+
theTabeLock.Unlock()
112+
if nfq.qh, err = C.CreateQueue(nfq.h, C.u_int16_t(queueId), C.u_int32_t(nfq.idx)); err != nil || nfq.qh == nil {
102113
C.nfq_close(nfq.h)
103114
return nil, fmt.Errorf("Error binding to queue: %v\n", err)
104115
}
@@ -130,6 +141,9 @@ func NewNFQueue(queueId uint16, maxPacketsInQueue uint32, packetSize uint32) (*N
130141
func (nfq *NFQueue) Close() {
131142
C.nfq_destroy_queue(nfq.qh)
132143
C.nfq_close(nfq.h)
144+
theTabeLock.Lock()
145+
delete(theTable, nfq.idx)
146+
theTabeLock.Unlock()
133147
}
134148

135149
//Get the channel for packets
@@ -142,15 +156,23 @@ func (nfq *NFQueue) run() {
142156
}
143157

144158
//export go_callback
145-
func go_callback(queueId C.int, data *C.uchar, len C.int, cb *chan NFPacket) Verdict {
159+
func go_callback(queueId C.int, data *C.uchar, len C.int, idx uint32) Verdict {
146160
xdata := C.GoBytes(unsafe.Pointer(data), len)
147161
packet := gopacket.NewPacket(xdata, layers.LayerTypeIPv4, gopacket.DecodeOptions{Lazy: true, NoCopy: true})
148162
p := NFPacket{verdictChannel: make(chan Verdict), Packet: packet}
163+
theTabeLock.RLock()
164+
cb, ok := theTable[idx]
165+
theTabeLock.RUnlock()
166+
if !ok {
167+
fmt.Fprintf(os.Stderr, "Dropping, unexpectedly due to bad idx=%d\n", idx)
168+
return NF_DROP
169+
}
149170
select {
150171
case (*cb) <- p:
151172
v := <-p.verdictChannel
152173
return v
153174
default:
175+
fmt.Fprintf(os.Stderr, "Dropping, unexpectedly due to no recv, idx=%d\n", idx)
154176
return NF_DROP
155177
}
156178
}

netfilter.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,27 +27,29 @@
2727
#include <linux/netfilter.h>
2828
#include <libnetfilter_queue/libnetfilter_queue.h>
2929

30-
extern uint go_callback(int id, unsigned char* data, int len, void** cb_func);
30+
extern uint go_callback(int id, unsigned char* data, int len, u_int32_t idx);
3131

3232
static int nf_callback(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *cb_func){
3333
uint32_t id = -1;
3434
struct nfqnl_msg_packet_hdr *ph = NULL;
3535
unsigned char *buffer = NULL;
3636
int ret = 0;
3737
int verdict = 0;
38+
u_int32_t idx;
3839

3940
ph = nfq_get_msg_packet_hdr(nfa);
4041
id = ntohl(ph->packet_id);
4142

4243
ret = nfq_get_payload(nfa, &buffer);
43-
verdict = go_callback(id, buffer, ret, cb_func);
44+
idx = (uint32_t)((uintptr_t)cb_func);
45+
verdict = go_callback(id, buffer, ret, idx);
4446

4547
return nfq_set_verdict(qh, id, verdict, 0, NULL);
4648
}
4749

48-
static inline struct nfq_q_handle* CreateQueue(struct nfq_handle *h, u_int16_t queue, void* cb_func)
50+
static inline struct nfq_q_handle* CreateQueue(struct nfq_handle *h, u_int16_t queue, u_int32_t idx)
4951
{
50-
return nfq_create_queue(h, queue, &nf_callback, cb_func);
52+
return nfq_create_queue(h, queue, &nf_callback, (void*)((uintptr_t)idx));
5153
}
5254

5355
static inline void Run(struct nfq_handle *h, int fd)

netfilter_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package netfilter
2+
3+
import (
4+
"testing"
5+
"time"
6+
)
7+
8+
var stopCh = make(chan struct{})
9+
10+
func serve(t *testing.T, queueNum uint16) {
11+
nfq, err := NewNFQueue(queueNum, 100, NF_DEFAULT_PACKET_SIZE)
12+
if err != nil {
13+
t.Skipf("Skipping the test due to %s", err)
14+
}
15+
defer nfq.Close()
16+
packets := nfq.GetPackets()
17+
18+
t.Logf("Starting (NFQ %d)..", queueNum)
19+
for true {
20+
select {
21+
case p := <-packets:
22+
t.Logf("Accepting %s", p.Packet)
23+
p.SetVerdict(NF_ACCEPT)
24+
case <-stopCh:
25+
t.Logf("Exiting..")
26+
return
27+
}
28+
}
29+
}
30+
31+
// very dumb test, but enough for testing golang/go#14210
32+
func TestNetfilter(t *testing.T) {
33+
queueNum := 42
34+
go serve(t, uint16(queueNum))
35+
wait := 3 * time.Second
36+
t.Logf("Sleeping for %s", wait)
37+
time.Sleep(wait)
38+
close(stopCh)
39+
}

0 commit comments

Comments
 (0)