Initial commit
This commit is contained in:
commit
7682e19876
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal 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
8
.idea/modules.xml
generated
Normal 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
9
.idea/pndpd.iml
generated
Normal 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
20
.idea/remote-targets.xml
generated
Normal 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 /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
6
.idea/vcs.xml
generated
Normal 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
7
NDPRequest.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type NDRequest struct {
|
||||||
|
srcIP []byte
|
||||||
|
answeringForIP []byte
|
||||||
|
mac []byte
|
||||||
|
}
|
8
go.mod
Normal file
8
go.mod
Normal 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
8
go.sum
Normal 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
19
main.go
Normal 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
114
packet.go
Normal 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
114
rawsocket.go
Normal 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
37
responder.go
Normal 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)
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user