Initial proxying implementation

This commit is contained in:
Kioubit 2021-12-20 16:46:26 -05:00
parent 18f14ba47d
commit 711400607d
5 changed files with 135 additions and 42 deletions

View File

@ -1,6 +1,15 @@
package main
type NDPType int
const (
NDP_ADV NDPType = 0
NDP_SOL NDPType = 1
)
type NDRequest struct {
requestType NDPType
//TODO use global unicast for router advertisements
srcIP []byte
answeringForIP []byte
mac []byte

51
main.go
View File

@ -6,14 +6,53 @@ import (
)
func main() {
fmt.Println("Usage: pndpd respond <interface>")
fmt.Println("Usage: pndpd proxy <interface1> <interface2>")
if len(os.Args) <= 1 {
fmt.Println("Specify interface")
fmt.Println("Specify command")
os.Exit(1)
}
iface := os.Args[1]
requests := make(chan *NDRequest, 100)
defer close(requests)
go respond(iface, requests)
listen(iface, requests)
if os.Args[1] == "respond" {
simpleRespond(os.Args[2])
}
if os.Args[1] == "proxy" {
proxy(os.Args[2], os.Args[3])
}
}
func simpleRespond(iface string) {
requests := make(chan *NDRequest, 100)
defer close(requests)
go respond(iface, requests, NDP_ADV)
go listen(iface, requests, NDP_SOL)
select {}
//TODO os.signal
}
func proxy(iface1, iface2 string) {
req_iface1_sol_iface2 := make(chan *NDRequest, 100)
defer close(req_iface1_sol_iface2)
go listen(iface1, req_iface1_sol_iface2, NDP_SOL)
go respond(iface2, req_iface1_sol_iface2, NDP_SOL)
req_iface2_sol_iface1 := make(chan *NDRequest, 100)
defer close(req_iface2_sol_iface1)
go listen(iface2, req_iface2_sol_iface1, NDP_SOL)
go respond(iface1, req_iface2_sol_iface1, NDP_SOL)
req_iface1_adv_iface2 := make(chan *NDRequest, 100)
defer close(req_iface1_adv_iface2)
go listen(iface1, req_iface1_adv_iface2, NDP_ADV)
go respond(iface2, req_iface1_adv_iface2, NDP_ADV)
req_iface2_adv_iface1 := make(chan *NDRequest, 100)
defer close(req_iface2_adv_iface1)
go listen(iface2, req_iface2_adv_iface1, NDP_ADV)
go respond(iface1, req_iface2_adv_iface1, NDP_ADV)
select {}
// TODO os.signal
}

View File

@ -55,28 +55,39 @@ func (h *IPv6Header) constructPacket() []byte {
return final
}
type NDPAdvPayload struct {
type NdpPayload struct {
packetType NDPType
answeringForIP []byte
mac []byte
}
func newNdpPacket(answeringForIP []byte, mac []byte) *NDPAdvPayload {
return &NDPAdvPayload{
func newNdpPacket(answeringForIP []byte, mac []byte, packetType NDPType) *NdpPayload {
return &NdpPayload{
packetType: packetType,
answeringForIP: answeringForIP,
mac: mac,
}
}
func (p *NDPAdvPayload) constructPacket() ([]byte, int) {
func (p *NdpPayload) constructPacket() ([]byte, int) {
var protocol byte
var flags byte
if p.packetType == NDP_SOL {
protocol = 0x87
flags = 0x0
} else {
protocol = 0x88
flags = 0x60
}
header := []byte{
0x88, // Type: Neighbor Advertisement
0x0, // Code
0x0, // Checksum filled in later
0x0, // Checksum filled in later
0x60, // Flags (Solicited,Override)
0x0, // Reserved
0x0, // Reserved
0x0, // Reserved
protocol, // Type: NDPType
0x0, // Code
0x0, // Checksum filled in later
0x0, // Checksum filled in later
flags, // Flags (Solicited,Override)
0x0, // Reserved
0x0, // Reserved
0x0, // Reserved
}
if len(p.answeringForIP) != 16 {
panic("malformed IP")

View File

@ -40,7 +40,7 @@ func htons(v uint16) int {
}
func htons16(v uint16) uint16 { return v<<8 | v>>8 }
func listen(iface string, responder chan *NDRequest) {
func listen(iface string, responder chan *NDRequest, requestType NDPType) {
niface, err := net.InterfaceByName(iface)
if err != nil {
@ -68,23 +68,45 @@ func listen(iface string, responder chan *NDRequest) {
fmt.Println(err.Error())
}
var f Filter = []bpf.Instruction{
// Load "EtherType" field from the ethernet header.
bpf.LoadAbsolute{Off: 12, Size: 2},
// Jump to the drop packet instruction if EtherType is not IPv6.
bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 0x86dd, SkipTrue: 4},
// Load "Next Header" field from IPV6 header.
bpf.LoadAbsolute{Off: 20, Size: 1},
// Jump to the drop packet instruction if Next Header is not ICMPv6.
bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 0x3a, SkipTrue: 2},
// Load "Type" field from ICMPv6 header.
bpf.LoadAbsolute{Off: 54, Size: 1},
// Jump to the drop packet instruction if Type is not Neighbor Solicitation.
bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 0x87, SkipTrue: 1},
// Verdict is "send up to 4k of the packet to userspace."
bpf.RetConstant{Val: 4096},
// Verdict is "ignore packet."
bpf.RetConstant{Val: 0},
var f Filter
if requestType == NDP_SOL {
f = []bpf.Instruction{
// Load "EtherType" field from the ethernet header.
bpf.LoadAbsolute{Off: 12, Size: 2},
// Jump to the drop packet instruction if EtherType is not IPv6.
bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 0x86dd, SkipTrue: 4},
// Load "Next Header" field from IPV6 header.
bpf.LoadAbsolute{Off: 20, Size: 1},
// Jump to the drop packet instruction if Next Header is not ICMPv6.
bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 0x3a, SkipTrue: 2},
// Load "Type" field from ICMPv6 header.
bpf.LoadAbsolute{Off: 54, Size: 1},
// Jump to the drop packet instruction if Type is not Neighbor Solicitation.
bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 0x87, SkipTrue: 1},
// Verdict is "send up to 4k of the packet to userspace."
bpf.RetConstant{Val: 4096},
// Verdict is "ignore packet."
bpf.RetConstant{Val: 0},
}
} else {
f = []bpf.Instruction{
// Load "EtherType" field from the ethernet header.
bpf.LoadAbsolute{Off: 12, Size: 2},
// Jump to the drop packet instruction if EtherType is not IPv6.
bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 0x86dd, SkipTrue: 4},
// Load "Next Header" field from IPV6 header.
bpf.LoadAbsolute{Off: 20, Size: 1},
// Jump to the drop packet instruction if Next Header is not ICMPv6.
bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 0x3a, SkipTrue: 2},
// Load "Type" field from ICMPv6 header.
bpf.LoadAbsolute{Off: 54, Size: 1},
// Jump to the drop packet instruction if Type is not Neighbor Advertisement.
bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 0x88, SkipTrue: 1},
// Verdict is "send up to 4k of the packet to userspace."
bpf.RetConstant{Val: 4096},
// Verdict is "ignore packet."
bpf.RetConstant{Val: 0},
}
}
err = f.ApplyTo(fd)
@ -106,9 +128,10 @@ func listen(iface string, responder chan *NDRequest) {
fmt.Printf("% X\n", buf[:numRead][80:86])
fmt.Println()
responder <- &NDRequest{
requestType: requestType,
srcIP: buf[:numRead][22:38],
answeringForIP: buf[:numRead][62:78],
mac: niface.HardwareAddr,
mac: buf[:numRead][80:86],
}
}
}

View File

@ -1,29 +1,40 @@
package main
import (
"net"
"syscall"
)
var fd int
func respond(iface string, requests chan *NDRequest) {
func respond(iface string, requests chan *NDRequest, respondType NDPType) {
fd, _ = syscall.Socket(syscall.AF_INET6, syscall.SOCK_RAW, syscall.IPPROTO_RAW)
syscall.BindToDevice(fd, iface)
niface, err := net.InterfaceByName(iface)
if err != nil {
panic(err.Error())
}
for {
n := <-requests
pkt(n.srcIP, n.answeringForIP, n.mac)
pkt(n.srcIP, n.answeringForIP, niface.HardwareAddr, respondType)
}
}
func pkt(srcip []byte, tgtip []byte, mac []byte) {
func pkt(srcip []byte, tgtip []byte, mac []byte, respondType NDPType) {
v6 := newIpv6Header(emptyIpv6, srcip)
NDPa := newNdpPacket(tgtip, mac)
NDPa := newNdpPacket(tgtip, mac, respondType)
v6.addPayload(NDPa)
response := v6.constructPacket()
var t [16]byte
copy(t[:], srcip)
if respondType == NDP_SOL {
copy(t[:], []byte{0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02})
} else {
copy(t[:], srcip)
}
d := syscall.SockaddrInet6{
Port: 0,
Addr: t,