Proxying implementation finished

This commit is contained in:
Kioubit 2021-12-21 06:00:28 -05:00
parent 711400607d
commit b7ca59b1bd
5 changed files with 111 additions and 43 deletions

View File

@ -8,9 +8,11 @@ const (
)
type NDRequest struct {
requestType NDPType
//TODO use global unicast for router advertisements
srcIP []byte
answeringForIP []byte
mac []byte
requestType NDPType
srcIP []byte
answeringForIP []byte
dstIP []byte
mac []byte
receivedIfaceMac []byte
sourceIface string
}

22
main.go
View File

@ -3,6 +3,8 @@ package main
import (
"fmt"
"os"
"os/signal"
"syscall"
)
func main() {
@ -27,8 +29,14 @@ func simpleRespond(iface string) {
defer close(requests)
go respond(iface, requests, NDP_ADV)
go listen(iface, requests, NDP_SOL)
select {}
//TODO os.signal
sigCh := make(chan os.Signal)
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)
select {
case <-sigCh:
fmt.Println("Exit")
os.Exit(0)
}
}
func proxy(iface1, iface2 string) {
@ -52,7 +60,11 @@ func proxy(iface1, iface2 string) {
go listen(iface2, req_iface2_adv_iface1, NDP_ADV)
go respond(iface1, req_iface2_adv_iface1, NDP_ADV)
select {}
// TODO os.signal
sigCh := make(chan os.Signal)
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)
select {
case <-sigCh:
fmt.Println("Exit")
os.Exit(0)
}
}

View File

@ -2,6 +2,9 @@ package main
import (
"encoding/binary"
"errors"
"net"
"strings"
)
var emptyIpv6 = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
@ -18,8 +21,11 @@ type IPv6Header struct {
payload []byte
}
func newIpv6Header(srcIp []byte, dstIp []byte) *IPv6Header {
return &IPv6Header{dstIP: dstIp, srcIP: srcIp, protocol: 0x3a}
func newIpv6Header(srcIp []byte, dstIp []byte) (*IPv6Header, error) {
if len(dstIp) != 16 || len(srcIp) != 16 {
return nil, errors.New("malformed IP")
}
return &IPv6Header{dstIP: dstIp, srcIP: srcIp, protocol: 0x3a}, nil
}
func (h *IPv6Header) addPayload(payload Payload) {
@ -61,23 +67,29 @@ type NdpPayload struct {
mac []byte
}
func newNdpPacket(answeringForIP []byte, mac []byte, packetType NDPType) *NdpPayload {
func newNdpPacket(answeringForIP []byte, mac []byte, packetType NDPType) (*NdpPayload, error) {
if len(answeringForIP) != 16 || len(mac) != 6 {
return nil, errors.New("malformed IP")
}
return &NdpPayload{
packetType: packetType,
answeringForIP: answeringForIP,
mac: mac,
}
}, nil
}
func (p *NdpPayload) constructPacket() ([]byte, int) {
var protocol byte
var flags byte
var linkType byte
if p.packetType == NDP_SOL {
protocol = 0x87
flags = 0x0
linkType = 0x01
} else {
protocol = 0x88
flags = 0x60
linkType = 0x02
}
header := []byte{
protocol, // Type: NDPType
@ -89,14 +101,11 @@ func (p *NdpPayload) constructPacket() ([]byte, int) {
0x0, // Reserved
0x0, // Reserved
}
if len(p.answeringForIP) != 16 {
panic("malformed IP")
} //TODO check IP lengths on assign everywhere
final := append(header, p.answeringForIP...)
secondHeader := []byte{
0x02, // Type: Target link-layer address (2)
0x01, // Length: 1 (8 bytes)
linkType, // Type
0x01, // Length: 1 (8 bytes)
}
final = append(final, secondHeader...)
@ -123,3 +132,8 @@ func checksumAddition(b []byte) uint32 {
}
return sum
}
func IsIPv6(ip string) bool {
rip := net.ParseIP(ip)
return rip != nil && strings.Contains(ip, ":")
}

View File

@ -50,7 +50,6 @@ func listen(iface string, responder chan *NDRequest, requestType NDPType) {
Protocol: htons16(syscall.ETH_P_IPV6),
Ifindex: niface.Index,
}
fmt.Println(niface.HardwareAddr)
fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, htons(syscall.ETH_P_IPV6))
if err != nil {
@ -65,7 +64,7 @@ func listen(iface string, responder chan *NDRequest, requestType NDPType) {
err = syscall.Bind(fd, tiface)
if err != nil {
fmt.Println(err.Error())
panic(err.Error())
}
var f Filter
@ -83,7 +82,7 @@ func listen(iface string, responder chan *NDRequest, requestType NDPType) {
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."
// Verdict is "send up to 4k of the packet to userspace."buf
bpf.RetConstant{Val: 4096},
// Verdict is "ignore packet."
bpf.RetConstant{Val: 0},
@ -122,16 +121,21 @@ func listen(iface string, responder chan *NDRequest, requestType NDPType) {
}
fmt.Println("Source IP:")
fmt.Printf("% X\n", buf[:numRead][22:38])
fmt.Println("Destination IP:")
fmt.Printf("% X\n", buf[:numRead][38:54])
fmt.Println("Requested IP:")
fmt.Printf("% X\n", buf[:numRead][62:78])
fmt.Println("Source MAC")
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: buf[:numRead][80:86],
requestType: requestType,
srcIP: buf[:numRead][22:38],
dstIP: buf[:numRead][38:54],
answeringForIP: buf[:numRead][62:78],
mac: buf[:numRead][80:86],
receivedIfaceMac: niface.HardwareAddr,
sourceIface: iface,
}
}
}

View File

@ -1,48 +1,84 @@
package main
import (
"bytes"
"fmt"
"net"
"syscall"
)
var fd int
var globalFd int
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)
fd, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_RAW, syscall.IPPROTO_RAW)
if err != nil {
panic(err)
}
defer syscall.Close(globalFd)
globalFd = fd
err = syscall.BindToDevice(fd, iface)
if err != nil {
panic(err)
}
niface, err := net.InterfaceByName(iface)
if err != nil {
panic(err.Error())
}
var result = emptyIpv6
ifaceaddrs, err := niface.Addrs()
for _, n := range ifaceaddrs {
tip, _, err := net.ParseCIDR(n.String())
if err != nil {
break
}
if IsIPv6(tip.String()) {
if tip.IsGlobalUnicast() {
result = tip
_, tnet, _ := net.ParseCIDR("fc00::/7")
if !tnet.Contains(tip) {
break
}
}
}
}
for {
n := <-requests
pkt(n.srcIP, n.answeringForIP, niface.HardwareAddr, respondType)
if n.sourceIface == iface {
pkt(result, n.srcIP, n.answeringForIP, niface.HardwareAddr, respondType)
} else {
if !bytes.Equal(n.mac, n.receivedIfaceMac) {
pkt(n.srcIP, n.dstIP, n.answeringForIP, niface.HardwareAddr, respondType)
}
}
}
}
func pkt(srcip []byte, tgtip []byte, mac []byte, respondType NDPType) {
v6 := newIpv6Header(emptyIpv6, srcip)
NDPa := newNdpPacket(tgtip, mac, respondType)
func pkt(ownIP []byte, dstIP []byte, tgtip []byte, mac []byte, respondType NDPType) {
v6, err := newIpv6Header(ownIP, dstIP)
if err != nil {
return
}
NDPa, err := newNdpPacket(tgtip, mac, respondType)
if err != nil {
return
}
v6.addPayload(NDPa)
response := v6.constructPacket()
var t [16]byte
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)
}
copy(t[:], dstIP)
d := syscall.SockaddrInet6{
Port: 0,
Addr: t,
}
err := syscall.Sendto(fd, response, 0, &d)
if err != nil {
panic(err)
}
fmt.Println("Sending packet of type", respondType, "to")
fmt.Printf("% X\n", t)
syscall.Close(fd)
err = syscall.Sendto(globalFd, response, 0, &d)
fmt.Println(err.Error())
}