-
Notifications
You must be signed in to change notification settings - Fork 1
/
nfqueue.go
164 lines (148 loc) · 4.5 KB
/
nfqueue.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
package main
import (
"context"
"fmt"
"log"
"os/exec"
"strconv"
"strings"
"time"
"github.com/florianl/go-nfqueue"
nfNetlink "github.com/mdlayher/netlink"
)
var (
proxyIPset = NewIPv4Set(1000)
nf *nfqueue.Nfqueue
nfCancel context.CancelFunc
)
func setupNfqueue() {
config := nfqueue.Config{
NfQueue: NFQUEUE,
MaxPacketLen: 0xFFFF,
MaxQueueLen: 0xFF,
Copymode: nfqueue.NfQnlCopyPacket,
WriteTimeout: 15 * time.Millisecond,
}
var err error
nf, err = nfqueue.Open(&config)
if err != nil {
log.Fatal("could not open nfqueue socket:", err)
return
}
// defer nf.Close()
// Avoid receiving ENOBUFS errors.
if err := nf.SetOption(nfNetlink.NoENOBUFS, true); err != nil {
fmt.Printf("failed to set netlink option %v: %v\n",
nfNetlink.NoENOBUFS, err)
return
}
var ctx context.Context
ctx, nfCancel = context.WithCancel(context.Background())
// defer cancel()
fn := func(a nfqueue.Attribute) int {
id := *a.PacketID
nf.SetVerdict(id, processPacket(*a.Payload))
return 0
}
err = nf.RegisterWithErrorFunc(ctx, fn, func(e error) int {
fmt.Println(err)
return -1
})
if err != nil {
if useNFT {
log.Println(red("Do you have `nft-queue` kernel module?"))
}
log.Fatal("Can't register NFQUEUE func: ", err)
}
if useNFT {
execCommand("nft add table ip dnsr-nf")
execCommand("nft add chain ip dnsr-nf input { type filter hook input priority 0 \\; }")
execCommand("nft add chain ip dnsr-nf forward { type filter hook forward priority 0 \\; }")
execCommand("nft add chain ip dnsr-nf output { type filter hook output priority 0 \\; }")
execCommand("nft add rule ip dnsr-nf input udp sport 53 queue num ", strconv.Itoa(NFQUEUE))
execCommand("nft add rule ip dnsr-nf forward udp sport 53 queue num ", strconv.Itoa(NFQUEUE))
execCommand("nft add rule ip dnsr-nf output udp sport 53 queue num ", strconv.Itoa(NFQUEUE))
} else {
execCommand("iptables -I INPUT -p udp --sport 53 -j NFQUEUE --queue-num", strconv.Itoa(NFQUEUE))
execCommand("iptables -I FORWARD -p udp --sport 53 -j NFQUEUE --queue-num", strconv.Itoa(NFQUEUE))
execCommand("iptables -I OUTPUT -p udp --sport 53 -j NFQUEUE --queue-num", strconv.Itoa(NFQUEUE))
}
log.Printf(green("NFQUEUE `%d` successfully configured"), NFQUEUE)
}
func removeNfqueue() {
if nf != nil {
nfCancel()
nf.Close()
}
if !useNFT {
output, err := exec.Command("sh", "-c", "iptables -L -n -v").Output()
if err == nil {
if strings.Contains(string(output), "NFQUEUE num "+strconv.Itoa(NFQUEUE)) {
execCommand("iptables -D INPUT -p udp --sport 53 -j NFQUEUE --queue-num", strconv.Itoa(NFQUEUE))
execCommand("iptables -D FORWARD -p udp --sport 53 -j NFQUEUE --queue-num", strconv.Itoa(NFQUEUE))
execCommand("iptables -D OUTPUT -p udp --sport 53 -j NFQUEUE --queue-num", strconv.Itoa(NFQUEUE))
log.Printf(green("NFQUEUE `%d` cleanup completed"), NFQUEUE)
return
}
log.Printf("NFQUEUE `%d` not found, nothing cleanup", NFQUEUE)
} else {
log.Fatal(err)
}
} else {
output, err := exec.Command("sh", "-c", "nft list tables").Output()
if err == nil {
if strings.Contains(string(output), "dnsr-nf") {
execCommand("nft delete table dnsr-nf")
log.Printf(green("NFQUEUE `%d` cleanup completed"), NFQUEUE)
return
}
log.Printf("NFQUEUE `%d` not found, nothing cleanup", NFQUEUE)
} else {
log.Fatal(err)
}
}
}
// processPacket обрабатывает перехваченный пакет
func processPacket(packet []byte) int {
dnsPayload, err := extractUdpPayload(packet)
if err != nil {
// Not a DNS-answer
if args.Verbose {
log.Printf("Received bad DNS-package")
}
return nfqueue.NfAccept // TODO or drop?
}
dnsResponse := parseDNSResponse(dnsPayload)
// Block?
for name, _ := range dnsResponse {
_, blocked := blockedDomains[name]
if blocked || checkPatterns(name, blockedPatterns) != "" {
if args.Verbose {
log.Printf("Blocking DNS-answer for %s", name)
}
return nfqueue.NfDrop
}
}
for name, ipList := range dnsResponse {
trimmedDomain := trimDomain(name)
_, proxied := proxiedDomains[trimmedDomain]
// Proxy?
if proxied || checkPatterns(name, proxiedPatterns) != "" {
for _, ip := range ipList {
if proxyIPset.Add(ip) {
go addRoute(ip)
if !args.Silent {
log.Printf("New proxy route %s :: %v", name, ip)
}
} else if args.Verbose {
log.Printf("Old proxy route %s :: %v", name, ip)
}
}
} else { // Direct
if args.Verbose {
log.Printf("Direct %s :: %v\n", name, ipList)
}
}
}
return nfqueue.NfAccept
}