forked from florianl/go-nfqueue
-
Notifications
You must be signed in to change notification settings - Fork 1
/
nfqueue_lt_1.12.go
176 lines (154 loc) · 4.25 KB
/
nfqueue_lt_1.12.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
//+build !go1.12
package nfqueue
import (
"context"
"encoding/binary"
"log"
"sync"
"github.com/twistlock/go-nfqueue/internal/unix"
"github.com/twistlock/netlink"
)
type verdict struct {
sync.Mutex
data []netlink.Message
}
// Nfqueue represents a netfilter queue handler
type Nfqueue struct {
// Con is the pure representation of a netlink socket
Con *netlink.Conn
logger *log.Logger
flags []byte // uint32
maxPacketLen []byte // uint32
family uint8
queue uint16
maxQueueLen []byte // uint32
copymode uint8
verdicts verdict
}
// Open a connection to the netfilter queue subsystem
func Open(config *Config) (*Nfqueue, error) {
var nfqueue Nfqueue
if config.Flags >= nfQaCfgFlagMax {
return nil, ErrInvFlag
}
// Disable netlink goroutine spawned in a different namespce, if no explicit network namespace is specified in the config
con, err := netlink.Dial(unix.NETLINK_NETFILTER, &netlink.Config{NetNS: config.NetNS, DisableNSGoroutine: config.NetNS == 0})
if err != nil {
return nil, err
}
nfqueue.Con = con
// default size of copied packages to userspace
nfqueue.maxPacketLen = []byte{0x00, 0x00, 0x00, 0x00}
binary.BigEndian.PutUint32(nfqueue.maxPacketLen, config.MaxPacketLen)
nfqueue.flags = []byte{0x00, 0x00, 0x00, 0x00}
binary.BigEndian.PutUint32(nfqueue.flags, config.Flags)
nfqueue.queue = config.NfQueue
nfqueue.family = config.AfFamily
nfqueue.maxQueueLen = []byte{0x00, 0x00, 0x00, 0x00}
binary.BigEndian.PutUint32(nfqueue.maxQueueLen, config.MaxQueueLen)
if config.Logger == nil {
nfqueue.logger = log.New(new(devNull), "", 0)
} else {
nfqueue.logger = config.Logger
}
nfqueue.copymode = config.Copymode
return &nfqueue, nil
}
func (nfqueue *Nfqueue) setVerdict(id uint32, verdict int, batch bool, attributes []byte) error {
/*
struct nfqnl_msg_verdict_hdr {
__be32 verdict;
__be32 id;
};
*/
if verdict != NfDrop && verdict != NfAccept && verdict != NfStolen && verdict != NfQeueue && verdict != NfRepeat {
return ErrInvalidVerdict
}
buf := make([]byte, 4)
binary.BigEndian.PutUint32(buf, uint32(id))
verdictData := append([]byte{0x0, 0x0, 0x0, byte(verdict)}, buf...)
cmd, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: nfQaVerdictHdr, Data: verdictData},
})
if err != nil {
return err
}
data := putExtraHeader(nfqueue.family, unix.NFNETLINK_V0, nfqueue.queue)
data = append(data, cmd...)
data = append(data, attributes...)
req := netlink.Message{
Header: netlink.Header{
Flags: netlink.Request,
Sequence: 0,
},
Data: data,
}
if batch {
req.Header.Type = netlink.HeaderType((nfnlSubSysQueue << 8) | nfQnlMsgVerdictBatch)
} else {
req.Header.Type = netlink.HeaderType((nfnlSubSysQueue << 8) | nfQnlMsgVerdict)
}
nfqueue.verdicts.Lock()
nfqueue.verdicts.data = append(nfqueue.verdicts.data, req)
nfqueue.verdicts.Unlock()
return nil
}
func (nfqueue *Nfqueue) sendVerdicts() error {
nfqueue.verdicts.Lock()
defer nfqueue.verdicts.Unlock()
if len(nfqueue.verdicts.data) == 0 {
return nil
}
_, err := nfqueue.Con.SendMessages(nfqueue.verdicts.data)
if err != nil {
nfqueue.logger.Printf("Could not send verdict: %v", err)
return err
}
nfqueue.verdicts.data = []netlink.Message{}
return nil
}
func (nfqueue *Nfqueue) socketCallback(ctx context.Context, fn HookFunc, seq uint32) {
defer func() {
// unbinding from queue
_, err := nfqueue.setConfig(uint8(unix.AF_UNSPEC), seq, nfqueue.queue, []netlink.Attribute{
{Type: nfQaCfgCmd, Data: []byte{nfUlnlCfgCmdUnbind, 0x0, 0x0, byte(nfqueue.family)}},
})
if err != nil {
nfqueue.logger.Printf("Could not unbind from queue: %v", err)
return
}
}()
for {
nfqueue.sendVerdicts()
replys, err := nfqueue.Con.Receive()
if err != nil {
nfqueue.logger.Printf("Could not receive message: %v", err)
select {
case <-ctx.Done():
return
default:
continue
}
}
for _, msg := range replys {
if msg.Header.Type == netlink.Done {
// this is the last message of a batch
// continue to receive messages
break
}
m, err := parseMsg(nfqueue.logger, msg)
if err != nil {
nfqueue.logger.Printf("Could not parse message: %v", err)
continue
}
if ret := fn(m); ret != 0 {
return
}
}
select {
case <-ctx.Done():
return
default:
}
}
}