Add sharedsock example (#1116)

This commit is contained in:
Misha Bragin 2023-08-31 17:01:32 +02:00 committed by GitHub
parent 00dddb9458
commit d51dc4fd33
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 97 additions and 6 deletions

View File

@ -0,0 +1,35 @@
### How to run
This will only work on Linux
1. Run netcat listening on the UDP port 51820. This is going to be our external process:
```bash
nc -kluvw 1 51820
```
2. Build and run the example Go code:
```bash
go build -o sharedsock && sudo ./sharedsock
```
3. Test the logic by sending a STUN binding request
```bash
STUN_PACKET="000100002112A4425454"
echo -n $STUN_PACKET | xxd -r -p | nc -u -w 1 localhost 51820
```
4. You should see a similar output of the Go program. Note that you'll see some binary output in the netcat server too. This is due to the fact that kernel copies packets to both processes.
```bash
read a STUN packet of size 18 from ...
```
5. Send a non-STUN packet
```bash
echo -n 'hello' | nc -u -w 1 localhost 51820
```
6. The Go program won't print anything.

View File

@ -0,0 +1,56 @@
package main
import (
"context"
"github.com/netbirdio/netbird/sharedsock"
log "github.com/sirupsen/logrus"
"os"
"os/signal"
)
func main() {
port := 51820
rawSock, err := sharedsock.Listen(port, sharedsock.NewIncomingSTUNFilter())
if err != nil {
panic(err)
}
log.Infof("attached to to the raw socket on port %d", port)
ctx, cancel := context.WithCancel(context.Background())
// read packets
go func() {
buf := make([]byte, 1500)
for {
select {
case <-ctx.Done():
log.Debugf("stopped reading from the shared socket")
return
default:
size, addr, err := rawSock.ReadFrom(buf)
if err != nil {
log.Errorf("error while reading packet from the shared socket: %s", err)
continue
}
log.Infof("read a STUN packet of size %d from %s", size, addr.String())
}
}
}()
// terminate the program on ^C
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
for range c {
log.Infof("received ^C signal, stopping the program")
cancel()
err = rawSock.Close()
if err != nil {
log.Errorf("failed closing raw socket")
}
}
}()
<-ctx.Done()
}

View File

@ -2,8 +2,6 @@ package sharedsock
import "golang.org/x/net/bpf"
const magicCookie uint32 = 0x2112A442
// BPFFilter is a generic filter that provides ipv4 and ipv6 BPF instructions
type BPFFilter interface {
// GetInstructions returns raw BPF instructions for ipv4 and ipv6

View File

@ -67,17 +67,17 @@ func Listen(port int, filter BPFFilter) (net.PacketConn, error) {
rawSock.router, err = netroute.New()
if err != nil {
return nil, fmt.Errorf("failed to create router: %rawSock", err)
return nil, fmt.Errorf("failed to create raw socket router: %v", err)
}
rawSock.conn4, err = socket.Socket(unix.AF_INET, unix.SOCK_RAW, unix.IPPROTO_UDP, "raw_udp4", nil)
if err != nil {
return nil, fmt.Errorf("socket.Socket for ipv4 failed with: %rawSock", err)
return nil, fmt.Errorf("failed to create ipv4 raw socket: %v", err)
}
rawSock.conn6, err = socket.Socket(unix.AF_INET6, unix.SOCK_RAW, unix.IPPROTO_UDP, "raw_udp6", nil)
if err != nil {
log.Errorf("socket.Socket for ipv6 failed with: %rawSock", err)
log.Errorf("failed to create ipv6 raw socket: %v", err)
}
ipv4Instructions, ipv6Instructions, err := filter.GetInstructions(uint32(rawSock.port))

View File

@ -8,7 +8,7 @@ import (
"runtime"
)
// Listen is not supported on other platforms
// Listen is not supported on other platforms then Linux
func Listen(port int, filter BPFFilter) (net.PacketConn, error) {
return nil, fmt.Errorf(fmt.Sprintf("Not supported OS %s. SharedSocket is only supported on Linux", runtime.GOOS))
}

View File

@ -2,6 +2,8 @@ package sharedsock
import "golang.org/x/net/bpf"
const magicCookie uint32 = 0x2112A442
// IncomingSTUNFilter implements BPFFilter and filters out anything but incoming STUN packets to a specified destination port.
// Other packets (non STUN) will be forwarded to the process that own the port (e.g., WireGuard).
type IncomingSTUNFilter struct {