Initial proxying implementation
This commit is contained in:
parent
18f14ba47d
commit
711400607d
@ -1,6 +1,15 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
type NDPType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
NDP_ADV NDPType = 0
|
||||||
|
NDP_SOL NDPType = 1
|
||||||
|
)
|
||||||
|
|
||||||
type NDRequest struct {
|
type NDRequest struct {
|
||||||
|
requestType NDPType
|
||||||
|
//TODO use global unicast for router advertisements
|
||||||
srcIP []byte
|
srcIP []byte
|
||||||
answeringForIP []byte
|
answeringForIP []byte
|
||||||
mac []byte
|
mac []byte
|
||||||
|
51
main.go
51
main.go
@ -6,14 +6,53 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
fmt.Println("Usage: pndpd respond <interface>")
|
||||||
|
fmt.Println("Usage: pndpd proxy <interface1> <interface2>")
|
||||||
|
|
||||||
if len(os.Args) <= 1 {
|
if len(os.Args) <= 1 {
|
||||||
fmt.Println("Specify interface")
|
fmt.Println("Specify command")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
iface := os.Args[1]
|
if os.Args[1] == "respond" {
|
||||||
requests := make(chan *NDRequest, 100)
|
simpleRespond(os.Args[2])
|
||||||
defer close(requests)
|
}
|
||||||
go respond(iface, requests)
|
if os.Args[1] == "proxy" {
|
||||||
listen(iface, requests)
|
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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
35
packet.go
35
packet.go
@ -55,28 +55,39 @@ func (h *IPv6Header) constructPacket() []byte {
|
|||||||
return final
|
return final
|
||||||
}
|
}
|
||||||
|
|
||||||
type NDPAdvPayload struct {
|
type NdpPayload struct {
|
||||||
|
packetType NDPType
|
||||||
answeringForIP []byte
|
answeringForIP []byte
|
||||||
mac []byte
|
mac []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func newNdpPacket(answeringForIP []byte, mac []byte) *NDPAdvPayload {
|
func newNdpPacket(answeringForIP []byte, mac []byte, packetType NDPType) *NdpPayload {
|
||||||
return &NDPAdvPayload{
|
return &NdpPayload{
|
||||||
|
packetType: packetType,
|
||||||
answeringForIP: answeringForIP,
|
answeringForIP: answeringForIP,
|
||||||
mac: mac,
|
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{
|
header := []byte{
|
||||||
0x88, // Type: Neighbor Advertisement
|
protocol, // Type: NDPType
|
||||||
0x0, // Code
|
0x0, // Code
|
||||||
0x0, // Checksum filled in later
|
0x0, // Checksum filled in later
|
||||||
0x0, // Checksum filled in later
|
0x0, // Checksum filled in later
|
||||||
0x60, // Flags (Solicited,Override)
|
flags, // Flags (Solicited,Override)
|
||||||
0x0, // Reserved
|
0x0, // Reserved
|
||||||
0x0, // Reserved
|
0x0, // Reserved
|
||||||
0x0, // Reserved
|
0x0, // Reserved
|
||||||
}
|
}
|
||||||
if len(p.answeringForIP) != 16 {
|
if len(p.answeringForIP) != 16 {
|
||||||
panic("malformed IP")
|
panic("malformed IP")
|
||||||
|
61
rawsocket.go
61
rawsocket.go
@ -40,7 +40,7 @@ func htons(v uint16) int {
|
|||||||
}
|
}
|
||||||
func htons16(v uint16) uint16 { return v<<8 | v>>8 }
|
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)
|
niface, err := net.InterfaceByName(iface)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -68,23 +68,45 @@ func listen(iface string, responder chan *NDRequest) {
|
|||||||
fmt.Println(err.Error())
|
fmt.Println(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
var f Filter = []bpf.Instruction{
|
var f Filter
|
||||||
// Load "EtherType" field from the ethernet header.
|
if requestType == NDP_SOL {
|
||||||
bpf.LoadAbsolute{Off: 12, Size: 2},
|
f = []bpf.Instruction{
|
||||||
// Jump to the drop packet instruction if EtherType is not IPv6.
|
// Load "EtherType" field from the ethernet header.
|
||||||
bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 0x86dd, SkipTrue: 4},
|
bpf.LoadAbsolute{Off: 12, Size: 2},
|
||||||
// Load "Next Header" field from IPV6 header.
|
// Jump to the drop packet instruction if EtherType is not IPv6.
|
||||||
bpf.LoadAbsolute{Off: 20, Size: 1},
|
bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 0x86dd, SkipTrue: 4},
|
||||||
// Jump to the drop packet instruction if Next Header is not ICMPv6.
|
// Load "Next Header" field from IPV6 header.
|
||||||
bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 0x3a, SkipTrue: 2},
|
bpf.LoadAbsolute{Off: 20, Size: 1},
|
||||||
// Load "Type" field from ICMPv6 header.
|
// Jump to the drop packet instruction if Next Header is not ICMPv6.
|
||||||
bpf.LoadAbsolute{Off: 54, Size: 1},
|
bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 0x3a, SkipTrue: 2},
|
||||||
// Jump to the drop packet instruction if Type is not Neighbor Solicitation.
|
// Load "Type" field from ICMPv6 header.
|
||||||
bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 0x87, SkipTrue: 1},
|
bpf.LoadAbsolute{Off: 54, Size: 1},
|
||||||
// Verdict is "send up to 4k of the packet to userspace."
|
// Jump to the drop packet instruction if Type is not Neighbor Solicitation.
|
||||||
bpf.RetConstant{Val: 4096},
|
bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 0x87, SkipTrue: 1},
|
||||||
// Verdict is "ignore packet."
|
// Verdict is "send up to 4k of the packet to userspace."
|
||||||
bpf.RetConstant{Val: 0},
|
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)
|
err = f.ApplyTo(fd)
|
||||||
@ -106,9 +128,10 @@ func listen(iface string, responder chan *NDRequest) {
|
|||||||
fmt.Printf("% X\n", buf[:numRead][80:86])
|
fmt.Printf("% X\n", buf[:numRead][80:86])
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
responder <- &NDRequest{
|
responder <- &NDRequest{
|
||||||
|
requestType: requestType,
|
||||||
srcIP: buf[:numRead][22:38],
|
srcIP: buf[:numRead][22:38],
|
||||||
answeringForIP: buf[:numRead][62:78],
|
answeringForIP: buf[:numRead][62:78],
|
||||||
mac: niface.HardwareAddr,
|
mac: buf[:numRead][80:86],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
21
responder.go
21
responder.go
@ -1,29 +1,40 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
var fd int
|
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)
|
fd, _ = syscall.Socket(syscall.AF_INET6, syscall.SOCK_RAW, syscall.IPPROTO_RAW)
|
||||||
syscall.BindToDevice(fd, iface)
|
syscall.BindToDevice(fd, iface)
|
||||||
|
|
||||||
|
niface, err := net.InterfaceByName(iface)
|
||||||
|
if err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
n := <-requests
|
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)
|
v6 := newIpv6Header(emptyIpv6, srcip)
|
||||||
NDPa := newNdpPacket(tgtip, mac)
|
NDPa := newNdpPacket(tgtip, mac, respondType)
|
||||||
v6.addPayload(NDPa)
|
v6.addPayload(NDPa)
|
||||||
response := v6.constructPacket()
|
response := v6.constructPacket()
|
||||||
|
|
||||||
var t [16]byte
|
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{
|
d := syscall.SockaddrInet6{
|
||||||
Port: 0,
|
Port: 0,
|
||||||
Addr: t,
|
Addr: t,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user