-
-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package netutils | ||
|
||
import ( | ||
"syscall" | ||
"unsafe" | ||
|
||
"golang.org/x/net/bpf" | ||
"golang.org/x/sys/unix" | ||
) | ||
|
||
// Convert a uint16 to host byte order (big endian) | ||
func Htons(v uint16) int { | ||
return int((v << 8) | (v >> 8)) | ||
} | ||
|
||
func GetBpfFilter(port int) []bpf.Instruction { | ||
// bpf filter: (ip or ip6 ) and (udp or tcp) and port 53 | ||
// fragmented packets are ignored | ||
var filter = []bpf.Instruction{ | ||
// Load eth.type (2 bytes at offset 12) and push-it in register A | ||
bpf.LoadAbsolute{Off: 12, Size: 2}, | ||
// if eth.type == IPv4 continue with the next instruction | ||
bpf.JumpIf{Cond: bpf.JumpEqual, Val: 0x0800, SkipTrue: 0, SkipFalse: 10}, | ||
// Load ip.proto (1 byte at offset 23) and push-it in register A | ||
bpf.LoadAbsolute{Off: 23, Size: 1}, | ||
// ip.proto == UDP ? | ||
bpf.JumpIf{Cond: bpf.JumpEqual, Val: 0x11, SkipTrue: 1, SkipFalse: 0}, | ||
// ip.proto == TCP ? | ||
bpf.JumpIf{Cond: bpf.JumpEqual, Val: 0x6, SkipTrue: 0, SkipFalse: 16}, | ||
// load flags and fragment offset (2 bytes at offset 20) to ignore fragmented packet | ||
bpf.LoadAbsolute{Off: 20, Size: 2}, | ||
// Only look at the last 13 bits of the data saved in regiter A | ||
// 0x1fff == 0001 1111 1111 1111 (fragment offset) | ||
// If any of the data in fragment offset is true, ignore the packet | ||
bpf.JumpIf{Cond: bpf.JumpBitsSet, Val: 0x1fff, SkipTrue: 14, SkipFalse: 0}, | ||
// Load ip.length | ||
// Register X = ip header len * 4 | ||
bpf.LoadMemShift{Off: 14}, | ||
// Load source port in tcp or udp (2 bytes at offset x+14) | ||
bpf.LoadIndirect{Off: 14, Size: 2}, | ||
// source port equal to 53 ? | ||
bpf.JumpIf{Cond: bpf.JumpEqual, Val: uint32(port), SkipTrue: 10, SkipFalse: 0}, | ||
// Load destination port in tcp or udp (2 bytes at offset x+16) | ||
bpf.LoadIndirect{Off: 16, Size: 2}, | ||
// destination port equal to 53 ? | ||
bpf.JumpIf{Cond: bpf.JumpEqual, Val: uint32(port), SkipTrue: 8, SkipFalse: 9}, | ||
// if eth.type == IPv6 continue with the next instruction | ||
bpf.JumpIf{Cond: bpf.JumpEqual, Val: 0x86dd, SkipTrue: 0, SkipFalse: 8}, | ||
// Load ipv6.nxt (2 bytes at offset 12) and push-it in register A | ||
bpf.LoadAbsolute{Off: 20, Size: 1}, | ||
// ip.proto == UDP ? | ||
bpf.JumpIf{Cond: bpf.JumpEqual, Val: 0x11, SkipTrue: 1, SkipFalse: 0}, | ||
// ip.proto == TCP ? | ||
bpf.JumpIf{Cond: bpf.JumpEqual, Val: 0x6, SkipTrue: 0, SkipFalse: 5}, | ||
// Load source port tcp or udp (2 bytes at offset 54) | ||
bpf.LoadAbsolute{Off: 54, Size: 2}, | ||
// source port equal to 53 ? | ||
bpf.JumpIf{Cond: bpf.JumpEqual, Val: uint32(port), SkipTrue: 2, SkipFalse: 0}, | ||
// Load destination port tcp or udp (2 bytes at offset 56) | ||
bpf.LoadAbsolute{Off: 56, Size: 2}, | ||
// destination port equal to 53 ? | ||
bpf.JumpIf{Cond: bpf.JumpEqual, Val: uint32(port), SkipTrue: 0, SkipFalse: 1}, | ||
// Keep the packet and send up to 65k of the packet to userspace | ||
bpf.RetConstant{Val: 0xFFFF}, | ||
// Ignore packet | ||
bpf.RetConstant{Val: 0}, | ||
} | ||
return filter | ||
} | ||
|
||
func ApplyBpfFilter(filter []bpf.Instruction, fd int) (err error) { | ||
var assembled []bpf.RawInstruction | ||
if assembled, err = bpf.Assemble(filter); err != nil { | ||
return err | ||
} | ||
|
||
prog := &unix.SockFprog{ | ||
Check failure on line 77 in netutils/bpf.go GitHub Actions / go-freebsd (1.21)
Check failure on line 77 in netutils/bpf.go GitHub Actions / go-freebsd (1.22)
Check failure on line 77 in netutils/bpf.go GitHub Actions / go-macos (1.22)
|
||
Len: uint16(len(assembled)), | ||
Filter: (*unix.SockFilter)(unsafe.Pointer(&assembled[0])), | ||
Check failure on line 79 in netutils/bpf.go GitHub Actions / go-freebsd (1.21)
Check failure on line 79 in netutils/bpf.go GitHub Actions / go-freebsd (1.22)
Check failure on line 79 in netutils/bpf.go GitHub Actions / go-macos (1.22)
|
||
} | ||
|
||
return unix.SetsockoptSockFprog(fd, syscall.SOL_SOCKET, syscall.SO_ATTACH_FILTER, prog) | ||
Check failure on line 82 in netutils/bpf.go GitHub Actions / go-freebsd (1.21)
Check failure on line 82 in netutils/bpf.go GitHub Actions / go-freebsd (1.21)
Check failure on line 82 in netutils/bpf.go GitHub Actions / go-freebsd (1.22)
Check failure on line 82 in netutils/bpf.go GitHub Actions / go-freebsd (1.22)
Check failure on line 82 in netutils/bpf.go GitHub Actions / go-macos (1.22)
Check failure on line 82 in netutils/bpf.go GitHub Actions / go-macos (1.22)
Check failure on line 82 in netutils/bpf.go GitHub Actions / go-win (1.22)
|
||
} | ||
|
||
func RemoveBpfFilter(fd int) (err error) { | ||
return syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_DETACH_FILTER, 0) | ||
Check failure on line 86 in netutils/bpf.go GitHub Actions / go-freebsd (1.21)
Check failure on line 86 in netutils/bpf.go GitHub Actions / go-freebsd (1.22)
Check failure on line 86 in netutils/bpf.go GitHub Actions / go-macos (1.22)
Check failure on line 86 in netutils/bpf.go GitHub Actions / go-win (1.22)
|
||
} |