mirror of
https://github.com/bjdgyc/anylink.git
synced 2025-08-08 11:44:11 +08:00
更改目录结构
This commit is contained in:
99
server/pkg/arpdis/addr.go
Normal file
99
server/pkg/arpdis/addr.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package arpdis
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
StaleTimeNormal = time.Minute * 5
|
||||
StaleTimeUnreachable = time.Minute * 10
|
||||
|
||||
TypeNormal = 0
|
||||
TypeUnreachable = 1
|
||||
TypeStatic = 2
|
||||
)
|
||||
|
||||
var (
|
||||
table = make(map[string]*Addr)
|
||||
tableMu sync.RWMutex
|
||||
)
|
||||
|
||||
type Addr struct {
|
||||
IP net.IP
|
||||
HardwareAddr net.HardwareAddr
|
||||
disTime time.Time
|
||||
Type int8
|
||||
}
|
||||
|
||||
func Lookup(ip net.IP, onlyTable bool) *Addr {
|
||||
addr := tableLookup(ip.To4())
|
||||
if addr != nil || onlyTable {
|
||||
return addr
|
||||
}
|
||||
|
||||
addr = doLookup(ip.To4())
|
||||
Add(addr)
|
||||
return addr
|
||||
}
|
||||
|
||||
// Add adds a IP-MAC map to a runtime ARP table.
|
||||
func tableLookup(ip net.IP) *Addr {
|
||||
tableMu.Lock()
|
||||
addr := table[ip.To4().String()]
|
||||
tableMu.Unlock()
|
||||
if addr == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 判断老化过期时间
|
||||
tsub := time.Since(addr.disTime)
|
||||
switch addr.Type {
|
||||
case TypeNormal:
|
||||
if tsub > StaleTimeNormal {
|
||||
return nil
|
||||
}
|
||||
case TypeUnreachable:
|
||||
if tsub > StaleTimeUnreachable {
|
||||
return nil
|
||||
}
|
||||
case TypeStatic:
|
||||
}
|
||||
|
||||
return addr
|
||||
}
|
||||
|
||||
// Add adds a IP-MAC map to a runtime ARP table.
|
||||
func Add(addr *Addr) {
|
||||
if addr == nil {
|
||||
return
|
||||
}
|
||||
if addr.disTime.IsZero() {
|
||||
addr.disTime = time.Now()
|
||||
}
|
||||
ip := addr.IP.To4().String()
|
||||
tableMu.Lock()
|
||||
defer tableMu.Unlock()
|
||||
if a, ok := table[ip]; ok {
|
||||
// 静态地址只能设置一次
|
||||
if a.Type == TypeStatic {
|
||||
return
|
||||
}
|
||||
}
|
||||
table[ip] = addr
|
||||
}
|
||||
|
||||
// Delete removes an IP from the runtime ARP table.
|
||||
func Delete(ip net.IP) {
|
||||
tableMu.Lock()
|
||||
defer tableMu.Unlock()
|
||||
delete(table, ip.To4().String())
|
||||
}
|
||||
|
||||
// List returns the current runtime ARP table.
|
||||
func List() map[string]*Addr {
|
||||
tableMu.RLock()
|
||||
defer tableMu.RUnlock()
|
||||
return table
|
||||
}
|
35
server/pkg/arpdis/addr_test.go
Normal file
35
server/pkg/arpdis/addr_test.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package arpdis
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestLookup(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
ip := net.IPv4(192, 168, 10, 2)
|
||||
hw, _ := net.ParseMAC("08:00:27:a0:17:42")
|
||||
now := time.Now()
|
||||
addr1 := &Addr{
|
||||
IP: ip,
|
||||
HardwareAddr: hw,
|
||||
Type: TypeStatic,
|
||||
disTime: now,
|
||||
}
|
||||
Add(addr1)
|
||||
addr2 := Lookup(ip, true)
|
||||
assert.Equal(addr1, addr2)
|
||||
addr3 := &Addr{
|
||||
IP: ip,
|
||||
HardwareAddr: hw,
|
||||
Type: TypeNormal,
|
||||
disTime: now,
|
||||
}
|
||||
Add(addr3)
|
||||
addr4 := Lookup(ip, true)
|
||||
// 静态地址只能设置一次
|
||||
assert.NotEqual(addr3, addr4)
|
||||
}
|
56
server/pkg/arpdis/arp.go
Normal file
56
server/pkg/arpdis/arp.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package arpdis
|
||||
|
||||
// Reference: github.com/malfunkt/arpfox
|
||||
// TODO now only support IPv4
|
||||
|
||||
import (
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
)
|
||||
|
||||
var defaultSerializeOpts = gopacket.SerializeOptions{
|
||||
FixLengths: true,
|
||||
ComputeChecksums: true,
|
||||
}
|
||||
|
||||
// NewARPRequest creates a bew ARP packet of type "request.
|
||||
func NewARPRequest(src *Addr, dst *Addr) ([]byte, error) {
|
||||
return buildPacket(src, dst, layers.ARPRequest)
|
||||
}
|
||||
|
||||
// NewARPReply creates a new ARP packet of type "reply".
|
||||
func NewARPReply(src *Addr, dst *Addr) ([]byte, error) {
|
||||
return buildPacket(src, dst, layers.ARPReply)
|
||||
}
|
||||
|
||||
// buildPacket creates an template ARP packet with the given source and
|
||||
// destination.
|
||||
func buildPacket(src *Addr, dst *Addr, opt uint16) ([]byte, error) {
|
||||
ether := layers.Ethernet{
|
||||
EthernetType: layers.EthernetTypeARP,
|
||||
SrcMAC: src.HardwareAddr,
|
||||
DstMAC: dst.HardwareAddr,
|
||||
}
|
||||
arp := layers.ARP{
|
||||
AddrType: layers.LinkTypeEthernet,
|
||||
Protocol: layers.EthernetTypeIPv4,
|
||||
|
||||
HwAddressSize: 6,
|
||||
ProtAddressSize: 4,
|
||||
Operation: opt,
|
||||
|
||||
SourceHwAddress: src.HardwareAddr,
|
||||
SourceProtAddress: src.IP.To4(),
|
||||
|
||||
DstHwAddress: dst.HardwareAddr,
|
||||
DstProtAddress: dst.IP.To4(),
|
||||
}
|
||||
|
||||
buf := gopacket.NewSerializeBuffer()
|
||||
err := gopacket.SerializeLayers(buf, defaultSerializeOpts, ðer, &arp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
101
server/pkg/arpdis/icmp.go
Normal file
101
server/pkg/arpdis/icmp.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package arpdis
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/icmp"
|
||||
"golang.org/x/net/ipv4"
|
||||
)
|
||||
|
||||
const (
|
||||
ProtocolICMP = 1
|
||||
ProtocolIPv6ICMP = 58
|
||||
)
|
||||
|
||||
func doPing(ip string) error {
|
||||
raddr, _ := net.ResolveIPAddr("ip4:icmp", ip)
|
||||
conn, err := icmp.ListenPacket("ip4:icmp", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ipv4Conn := conn.IPv4PacketConn()
|
||||
// 限制跳跃数
|
||||
err = ipv4Conn.SetTTL(10)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := &icmp.Message{
|
||||
Type: ipv4.ICMPTypeEcho,
|
||||
Code: 0,
|
||||
Body: &icmp.Echo{
|
||||
ID: os.Getpid() & 0xffff,
|
||||
Seq: 1,
|
||||
Data: timeToBytes(time.Now()),
|
||||
},
|
||||
}
|
||||
|
||||
b, err := msg.Marshal(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = conn.WriteTo(b, raddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_ = conn.SetReadDeadline(time.Now().Add(time.Second * 2))
|
||||
|
||||
for {
|
||||
buf := make([]byte, 512)
|
||||
n, dst, err := conn.ReadFrom(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dst.String() != ip {
|
||||
continue
|
||||
}
|
||||
|
||||
var result *icmp.Message
|
||||
result, err = icmp.ParseMessage(ProtocolICMP, buf[:n])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch result.Type {
|
||||
case ipv4.ICMPTypeEchoReply:
|
||||
// success
|
||||
if rply, ok := result.Body.(*icmp.Echo); ok {
|
||||
_ = rply
|
||||
// log.Printf("%+v \n", rply)
|
||||
}
|
||||
return nil
|
||||
|
||||
// case ipv4.ICMPTypeTimeExceeded:
|
||||
// case ipv4.ICMPTypeDestinationUnreachable:
|
||||
default:
|
||||
return errors.New("DestinationUnreachable")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func timeToBytes(t time.Time) []byte {
|
||||
nsec := t.UnixNano()
|
||||
b := make([]byte, 8)
|
||||
for i := uint8(0); i < 8; i++ {
|
||||
b[i] = byte((nsec >> ((7 - i) * 8)) & 0xff)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func bytesToTime(b []byte) time.Time {
|
||||
var nsec int64
|
||||
for i := uint8(0); i < 8; i++ {
|
||||
nsec += int64(b[i]) << ((7 - i) * 8)
|
||||
}
|
||||
return time.Unix(nsec/1000000000, nsec%1000000000)
|
||||
}
|
61
server/pkg/arpdis/lookup.go
Normal file
61
server/pkg/arpdis/lookup.go
Normal file
@@ -0,0 +1,61 @@
|
||||
// Currently only Darwin and Linux support this.
|
||||
|
||||
package arpdis
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func doLookup(ip net.IP) *Addr {
|
||||
// ping := exec.Command("ping", "-c1", "-t1", ip.String())
|
||||
// if err := ping.Run(); err != nil {
|
||||
// addr := &Addr{IP: ip, Type: TypeUnreachable}
|
||||
// return addr
|
||||
// }
|
||||
|
||||
err := doPing(ip.String())
|
||||
if err != nil {
|
||||
// log.Println(err)
|
||||
addr := &Addr{IP: ip, Type: TypeUnreachable}
|
||||
return addr
|
||||
}
|
||||
|
||||
return doArpShow(ip)
|
||||
}
|
||||
|
||||
func doArpShow(ip net.IP) *Addr {
|
||||
cmd := exec.Command("ip", "n", "show", ip.String())
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
log.Println("lookup show", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// os.Open("/proc/net/arp")
|
||||
// 192.168.1.2 0x1 0x2 e0:94:67:e2:42:5d * eth0
|
||||
// 192.168.1.2 dev eth0 lladdr 08:00:27:94:a5:a4 STALE
|
||||
outS := strings.ReplaceAll(string(out), " ", " ")
|
||||
outS = strings.TrimSpace(outS)
|
||||
arpArr := strings.Split(outS, " ")
|
||||
if len(arpArr) != 6 {
|
||||
log.Println("lookup arpArr", outS, ip)
|
||||
return nil
|
||||
}
|
||||
mac, err := net.ParseMAC(arpArr[4])
|
||||
if err != nil {
|
||||
log.Println("lookup mac", outS, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return &Addr{IP: ip, HardwareAddr: mac}
|
||||
}
|
||||
|
||||
// IP address HW type Flags HW address Mask Device
|
||||
// 172.23.24.12 0x1 0x2 00:e0:4c:73:5c:48 * anylink0
|
||||
// 172.23.24.1 0x1 0x2 3c:8c:40:a0:7a:2d * anylink0
|
||||
// 172.23.24.13 0x1 0x2 00:1c:42:4d:33:46 * anylink0
|
||||
// 172.23.24.2 0x1 0x0 00:00:00:00:00:00 * anylink0
|
||||
// 172.23.24.14 0x1 0x0 00:00:00:00:00:00 * anylink0
|
Reference in New Issue
Block a user