Initial commit

This commit is contained in:
Kioubit 2021-12-20 14:53:42 -05:00
commit 7682e19876
12 changed files with 358 additions and 0 deletions

8
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

8
.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/pndpd.iml" filepath="$PROJECT_DIR$/.idea/pndpd.iml" />
</modules>
</component>
</project>

9
.idea/pndpd.iml generated Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

20
.idea/remote-targets.xml generated Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteTargetsManager">
<targets>
<target name="WSL - Debian" type="wsl" uuid="c04b0bc9-8ac6-47f9-8b32-9b096370586f">
<config>
<option name="distributionMsId" value="Debian" />
<option name="projectRootOnTarget" value="/mnt/c/Users/Main/AppData/Local/JetBrains/Toolbox/apps/Goland/ch-0/213.5744.269/jbr/bin&#10;/pndpd" />
</config>
<ContributedStateBase type="GoLanguageRuntime">
<config>
<option name="goPath" value="/home/main/go" />
<option name="goRoot" value="/usr/local/go/bin/go" />
<option name="goVersion" value="go1.17.5 linux/amd64" />
</config>
</ContributedStateBase>
</target>
</targets>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

7
NDPRequest.go Normal file
View File

@ -0,0 +1,7 @@
package main
type NDRequest struct {
srcIP []byte
answeringForIP []byte
mac []byte
}

8
go.mod Normal file
View File

@ -0,0 +1,8 @@
module pndpd
go 1.17
require (
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f
golang.org/x/sys v0.0.0-20210423082822-04245dca01da
)

8
go.sum Normal file
View File

@ -0,0 +1,8 @@
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM=
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

19
main.go Normal file
View File

@ -0,0 +1,19 @@
package main
import (
"fmt"
"os"
)
func main() {
if len(os.Args) <= 1 {
fmt.Println("Specify interface")
os.Exit(1)
}
iface := os.Args[1]
requests := make(chan *NDRequest, 100)
defer close(requests)
go respond(iface, requests)
listen(iface, requests)
}

114
packet.go Normal file
View File

@ -0,0 +1,114 @@
package main
import (
"encoding/binary"
)
var emptyIpv6 = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
type Payload interface {
constructPacket() ([]byte, int)
}
type IPv6Header struct {
protocol byte
srcIP []byte
dstIP []byte
payloadLen []byte
payload []byte
}
func newIpv6Header(srcIp []byte, dstIp []byte) *IPv6Header {
return &IPv6Header{dstIP: dstIp, srcIP: srcIp, protocol: 0x3a}
}
func (h *IPv6Header) addPayload(payload Payload) {
bPayload, checksumPos := payload.constructPacket()
bPayloadLen := make([]byte, 2)
binary.BigEndian.PutUint16(bPayloadLen, uint16(len(bPayload)))
h.payloadLen = bPayloadLen
if checksumPos > 0 {
bChecksum := make([]byte, 2)
binary.BigEndian.PutUint16(bChecksum, calculateChecksum(h, bPayload))
bPayload[checksumPos] = bChecksum[0]
bPayload[checksumPos+1] = bChecksum[1]
}
h.payload = bPayload
}
func (h *IPv6Header) constructPacket() []byte {
header := []byte{
0x60, // v6
0, // qos
0, // qos
0, // qos
h.payloadLen[0], // Payload Length
h.payloadLen[1], // Payload Length
h.protocol, // Protocol next header
0xff, // Hop limit
}
final := append(header, h.srcIP...)
final = append(final, h.dstIP...)
final = append(final, h.payload...)
return final
}
type NDPAdvPayload struct {
answeringForIP []byte
mac []byte
}
func newNdpPacket(answeringForIP []byte, mac []byte) *NDPAdvPayload {
return &NDPAdvPayload{
answeringForIP: answeringForIP,
mac: mac,
}
}
func (p *NDPAdvPayload) constructPacket() ([]byte, int) {
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
}
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)
}
final = append(final, secondHeader...)
final = append(final, p.mac...)
return final, 2
}
func calculateChecksum(h *IPv6Header, payload []byte) uint16 {
sumPseudoHeader := checksumAddition(h.srcIP) + checksumAddition(h.dstIP) + checksumAddition([]byte{0x00, h.protocol}) + checksumAddition(h.payloadLen)
sumPayload := checksumAddition(payload)
sumTotal := sumPayload + sumPseudoHeader
for sumTotal>>16 > 0x0 {
sumTotal = (sumTotal & 0xffff) + (sumTotal >> 16)
}
return uint16(sumTotal) ^ 0xFFFF
}
func checksumAddition(b []byte) uint32 {
var sum uint32 = 0
for i := 0; i < len(b); i++ {
if i%2 == 0 {
sum += uint32(uint16(b[i])<<8 | uint16(b[i+1]))
}
}
return sum
}

114
rawsocket.go Normal file
View File

@ -0,0 +1,114 @@
package main
import (
"fmt"
"golang.org/x/net/bpf"
"golang.org/x/sys/unix"
"net"
"syscall"
"unsafe"
)
// Filter represents a classic BPF filter program that can be applied to a socket
type Filter []bpf.Instruction
// ApplyTo applies the current filter onto the provided file descriptor
func (filter Filter) ApplyTo(fd int) (err error) {
var assembled []bpf.RawInstruction
if assembled, err = bpf.Assemble(filter); err != nil {
return err
}
var program = unix.SockFprog{
Len: uint16(len(assembled)),
Filter: (*unix.SockFilter)(unsafe.Pointer(&assembled[0])),
}
var b = (*[unix.SizeofSockFprog]byte)(unsafe.Pointer(&program))[:unix.SizeofSockFprog]
if _, _, errno := syscall.Syscall6(syscall.SYS_SETSOCKOPT,
uintptr(fd), uintptr(syscall.SOL_SOCKET), uintptr(syscall.SO_ATTACH_FILTER),
uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), 0); errno != 0 {
return errno
}
return nil
}
// Htons Convert a uint16 to host byte order (big endian)
func htons(v uint16) int {
return int((v << 8) | (v >> 8))
}
func htons16(v uint16) uint16 { return v<<8 | v>>8 }
func listen(iface string, responder chan *NDRequest) {
niface, err := net.InterfaceByName(iface)
if err != nil {
panic(err.Error())
}
tiface := &syscall.SockaddrLinklayer{
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 {
fmt.Println(err.Error())
}
defer syscall.Close(fd)
fmt.Println("Obtained fd ", fd)
if len([]byte(iface)) > syscall.IFNAMSIZ {
panic("Interface size larger then maximum allowed by the kernel")
}
err = syscall.Bind(fd, tiface)
if err != nil {
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},
}
err = f.ApplyTo(fd)
if err != nil {
panic(err.Error())
}
for {
buf := make([]byte, 4096)
numRead, err := syscall.Read(fd, buf)
if err != nil {
panic(err)
}
fmt.Println("Source IP:")
fmt.Printf("% X\n", buf[:numRead][22:38])
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{
srcIP: buf[:numRead][22:38],
answeringForIP: buf[:numRead][62:78],
mac: niface.HardwareAddr,
}
}
}

37
responder.go Normal file
View File

@ -0,0 +1,37 @@
package main
import (
"syscall"
)
var fd int
func respond(iface string, requests chan *NDRequest) {
fd, _ = syscall.Socket(syscall.AF_INET6, syscall.SOCK_RAW, syscall.IPPROTO_RAW)
syscall.BindToDevice(fd, iface)
for {
n := <-requests
pkt(n.srcIP, n.answeringForIP, n.mac)
}
}
func pkt(srcip []byte, tgtip []byte, mac []byte) {
v6 := newIpv6Header(emptyIpv6, srcip)
NDPa := newNdpPacket(tgtip, mac)
v6.addPayload(NDPa)
response := v6.constructPacket()
var t [16]byte
copy(t[:], srcip)
d := syscall.SockaddrInet6{
Port: 0,
Addr: t,
}
err := syscall.Sendto(fd, response, 0, &d)
if err != nil {
panic(err)
}
syscall.Close(fd)
}