|
|
|
package nfq
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"github.com/florianl/go-nfqueue/v2"
|
|
|
|
"github.com/google/gopacket"
|
|
|
|
"github.com/google/gopacket/layers"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type NfQueue struct {
|
|
|
|
queid uint16
|
|
|
|
maxqueue uint32
|
|
|
|
ctx context.Context
|
|
|
|
stopFn context.CancelFunc
|
|
|
|
recallFn func(id uint32, q *nfqueue.Nfqueue, p Packet)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *NfQueue) RecallFn() func(id uint32, q *nfqueue.Nfqueue, p Packet) {
|
|
|
|
return n.recallFn
|
|
|
|
}
|
|
|
|
|
|
|
|
type Packet struct {
|
|
|
|
Packet gopacket.Packet
|
|
|
|
Attr nfqueue.Attribute
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewNfQueue(ctx context.Context, queid uint16, maxqueue uint32) *NfQueue {
|
|
|
|
var q = new(NfQueue)
|
|
|
|
if ctx == nil {
|
|
|
|
q.ctx, q.stopFn = context.WithCancel(context.Background())
|
|
|
|
} else {
|
|
|
|
q.ctx, q.stopFn = context.WithCancel(ctx)
|
|
|
|
}
|
|
|
|
q.queid = queid
|
|
|
|
q.maxqueue = maxqueue
|
|
|
|
return q
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *NfQueue) SetRecall(fn func(id uint32, q *nfqueue.Nfqueue, p Packet)) {
|
|
|
|
n.recallFn = fn
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *NfQueue) Stop() {
|
|
|
|
if n.stopFn != nil {
|
|
|
|
n.stopFn()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *NfQueue) Run() error {
|
|
|
|
cfg := nfqueue.Config{
|
|
|
|
NfQueue: n.queid,
|
|
|
|
MaxQueueLen: n.maxqueue,
|
|
|
|
Copymode: nfqueue.NfQnlCopyPacket,
|
|
|
|
WriteTimeout: time.Second * 10,
|
|
|
|
}
|
|
|
|
nfq, err := nfqueue.Open(&cfg)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to open nfqueue, err:", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := nfq.RegisterWithErrorFunc(n.ctx, func(a nfqueue.Attribute) int {
|
|
|
|
return n.handlePacket(nfq, a)
|
|
|
|
}, func(e error) int {
|
|
|
|
return 0
|
|
|
|
}); err != nil {
|
|
|
|
return fmt.Errorf("failed to register handlers, err:", err)
|
|
|
|
}
|
|
|
|
<-n.ctx.Done()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *NfQueue) handlePacket(q *nfqueue.Nfqueue, a nfqueue.Attribute) int {
|
|
|
|
if a.Payload != nil && len(*a.Payload) != 0 {
|
|
|
|
var packet gopacket.Packet
|
|
|
|
data := *a.Payload
|
|
|
|
if data[0]&0xf0 == 0x40 {
|
|
|
|
packet = gopacket.NewPacket(data, layers.LayerTypeIPv4, gopacket.DecodeOptions{Lazy: true, NoCopy: true})
|
|
|
|
} else {
|
|
|
|
packet = gopacket.NewPacket(data, layers.LayerTypeIPv6, gopacket.DecodeOptions{Lazy: true, NoCopy: true})
|
|
|
|
}
|
|
|
|
if n.recallFn != nil {
|
|
|
|
n.recallFn(*a.PacketID, q, Packet{
|
|
|
|
Packet: packet,
|
|
|
|
Attr: a,
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
q.SetVerdict(*a.PacketID, nfqueue.NfAccept)
|
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
q.SetVerdict(*a.PacketID, nfqueue.NfAccept)
|
|
|
|
return 0
|
|
|
|
}
|