-
Notifications
You must be signed in to change notification settings - Fork 4
/
nk.go
148 lines (125 loc) · 3.57 KB
/
nk.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
package main
import (
"context"
"flag"
"fmt"
"log"
"os"
"os/signal"
"golang.org/x/sys/unix"
"kernel.org/pub/linux/libs/security/libcap/cap"
)
//go:generate bpf2go filter bpf/filter.c -- -I/usr/include -I./bpf -nostdinc -O3
const (
nicID = 1
bufSize = 4 << 20 // 4MB.
)
var (
flagListen bool
flagUDP bool
flagDebug bool
flagSrcPort int
flagInterface string
flagWait int
)
func init() {
flag.BoolVar(&flagListen, "listen", false, "Bind and listen for incoming connections")
flag.BoolVar(&flagListen, "l", false, "Bind and listen for incoming connections")
flag.BoolVar(&flagUDP, "udp", false, "Use UDP instead of default TCP")
flag.BoolVar(&flagUDP, "u", false, "Use UDP instead of default TCP")
flag.BoolVar(&flagDebug, "debug", false, "Debug")
flag.BoolVar(&flagDebug, "d", false, "Debug")
flag.IntVar(&flagSrcPort, "source-port", 0, "Specify source port to use on connections")
flag.IntVar(&flagSrcPort, "p", 0, "Specify source port to use on connections")
flag.StringVar(&flagInterface, "interface", "", "Specify interface to use. Default interface with default route")
flag.StringVar(&flagInterface, "i", "", "Specify interface to use. Default interface with default route")
flag.IntVar(&flagWait, "wait", 5, "Connect timeout")
flag.IntVar(&flagWait, "w", 5, "Connect timeout")
flag.Usage = func() {
fmt.Fprint(os.Stderr, "Usage: nk [options] [hostname] [port]\n\n"+
"In connect mode, the hostname and port arguments tell what to connect.\n"+
"In listen mode, hostname and port control the address the server will bind to.\n\n")
flag.PrintDefaults()
}
}
func main() {
// Check system requirements
// Kernel must be > 5.2
k, m, err := getKernelVersion()
if err != nil {
log.Fatal(err)
}
if k < 5 || k == 5 && m < 2 {
log.Fatalf("Host Kernel (%d.%d) does not meet minimum required version: (%d.%d)",
k, m, 5, 2)
}
c := cap.GetProc()
if on, _ := c.GetFlag(cap.Permitted, cap.NET_RAW); !on {
log.Fatalf("insufficient privilege to open RAW sockets - want %q, have %q", cap.NET_RAW, c)
}
if on, _ := c.GetFlag(cap.Permitted, cap.SYS_RESOURCE); !on {
log.Fatalf("insufficient privilege to set rlimit - want %q, have %q", cap.SYS_RESOURCE, c)
}
// Parse command line flags and arguments
flag.Parse()
args := flag.Args()
// trap Ctrl+C and call cancel on the context
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
// Enable signal handler
signalCh := make(chan os.Signal, 2)
defer func() {
close(signalCh)
cancel()
}()
signal.Notify(signalCh, os.Interrupt, unix.SIGINT)
go func() {
select {
case <-signalCh:
log.Printf("Exiting: received signal")
cancel()
case <-ctx.Done():
}
}()
if len(args) != 2 {
flag.Usage()
os.Exit(1)
}
// Increase rlimit so the eBPF map and program can be loaded.
if err := unix.Setrlimit(unix.RLIMIT_MEMLOCK, &unix.Rlimit{
Cur: unix.RLIM_INFINITY,
Max: unix.RLIM_INFINITY,
}); err != nil {
log.Fatalf("setting temporary rlimit: %s", err)
}
if !flagListen {
err = connect(ctx, args)
if err != nil {
log.Fatalln(err)
}
} else {
err = listen(ctx, args)
if err != nil {
log.Fatalln(err)
}
}
os.Exit(0)
}
func getKernelVersion() (kernel, major int, err error) {
uts := unix.Utsname{}
if err = unix.Uname(&uts); err != nil {
return
}
ba := make([]byte, 0, len(uts.Release))
for _, b := range uts.Release {
if b == 0 {
break
}
ba = append(ba, byte(b))
}
var rest string
if n, _ := fmt.Sscanf(string(ba), "%d.%d%s", &kernel, &major, &rest); n < 2 {
err = fmt.Errorf("can't parse kernel version in %q", string(ba))
}
return
}