免费arp进行ip检测

235 阅读7分钟

一、 免费arp检测ip冲突的原理

ARP(Address Resolution Protocol)是一种网络协议,用于将IP地址映射到MAC地址。它可以让设备在局域网中找到其他设备的物理地址。

当一台设备想要发送数据包到另一台设备时,它首先需要确定目标设备的MAC地址。它会在本地ARP缓存中查找该地址,如果找不到,则会广播一个ARP请求,询问该IP地址所对应的MAC地址是什么。其他设备收到这个请求后,会回复一个ARP响应,告诉源设备该IP地址所对应的MAC地址是什么。

当局域网中有两台或多台设备使用相同的IP地址时,就会发生IP地址冲突。这时候,当其中一台设备发送ARP请求询问该IP地址所对应的MAC地址时,会同时收到多个设备的ARP响应,因为这些设备都使用该IP地址。这样就会导致通信故障和网络拥堵等问题。

免费ARP检测IP冲突的原理就是利用ARP协议来检测局域网中是否存在IP地址冲突。它会向局域网中广播ARP请求,询问每个设备所对应的IP地址和MAC地址,检测是否有多个设备使用相同的IP地址。如果有,就会报告IP地址冲突,并显示与之冲突的设备的MAC地址,以便管理员进行排查和解决。

二、 使用arp-scan进行免费arp探测

使用linux命令进行免费ARP探测可以使用以下步骤:

  1. 打开终端窗口,输入以下命令来查看系统的网络接口信息:
ifconfig

2. 查看输出结果,找到需要探测的网络接口名称(通常为eth0)。

  1. 输入以下命令来执行ARP请求并获取响应:
sudo arp-scan --interface=eth0 --localnet

其中,“--interface=eth0”表示使用指定的网络接口,“--localnet”表示扫描本地网络中的所有IP地址。

  1. 查看输出结果,可以看到扫描到的所有设备的MAC地址和IP地址。

注意:在某些Linux发行版中,可能需要先安装arp-scan软件包才能使用以上命令。可使用以下命令进行安装:

sudo apt-get install arp-scan

image.png

image.png

1.1 arp.isgratuitous gratuitous arp 的作用

Gratuitous ARP(GARP)是一种 ARP 请求/应答消息,由发送者发送给本地网络上的所有设备,而不仅仅是目标设备。GARP 携带了发送者的 MAC 地址和 IP 地址,并且对于网络中的其他设备来说,它表明了该地址已经在网络上被分配和使用。

Gratuitous ARP的作用主要有以下几个方面:

  • 改变IP-MAC映射关系: 当一个设备改变了它的IP地址或MAC地址时,它可以通过发送GARP消息来通知网络中的其他设备更新自己的ARP缓存表。这样可以防止其他设备继续使用旧的IP-MAC映射关系,从而避免出现通信异常或冲突。

  • 防止IP地址冲突: 当一个新设备加入网络时,它会发送GARP消息来检查它想要使用的IP地址是否已经被其他设备使用。如果收到回复,则说明该IP地址已经被占用,新设备需要重新选择一个空闲的IP地址。

  • 提高网络性能: 当一个设备需要向多个设备发送数据时,它可以发送GARP消息来更新其他设备的ARP缓存表,这样可以减少后续通信时的ARP请求和应答次数,从而提高网络性能和响应速度。

总之,Gratuitous ARP在ARP协议中起着重要的作用,它能够帮助网络设备维护IP-MAC映射关系,提高网络性能和可靠性。

1.1 arp.isannouncement announcement arp 的作用

ARP协议中的announcement指的是主机向网络中广播自己的IP地址和MAC地址的消息。其作用主要有以下两个方面:

  • 建立ARP缓存:当一个主机需要向另一个主机发送数据时,它需要知道目标主机的MAC地址。如果该主机没有目标主机的MAC地址缓存,则会向网络中广播announcement消息,请求其他主机响应并告知目标主机的MAC地址。在这个过程中,其他主机会收到announcement消息,并检查其中是否包含了自己所需要的IP地址。如果包含,则会返回响应消息,告知目标主机自己的MAC地址。目标主机收到响应消息后,将其缓存起来,以便下次发送数据时直接使用。

  • 更新ARP缓存:由于网络中主机之间的IP地址和MAC地址可能会发生变化,因此ARP缓存也需要随之更新。当某个主机更改了自己的MAC地址或者与网络断开连接后再次连接时,它需要向网络中广播announcement消息,通知其他主机自己的新MAC地址。其他主机收到消息后会更新自己的ARP缓存,确保能够正确地向该主机发送数据。

注意annuncement 也受arp table大小的影响,因为这个只更新一次,不能重刷,arp table太小,可能导致arp广播依旧存在,arp table也一直在更新。

三、 基于go编码实现免费arp检测ip冲突

使用go语言实现免费ARP检测IP冲突,可以使用以下步骤:

  1. 导入必要的包:
import (
	"fmt"
	"net"
	"time"
)
  1. 编写函数checkIPConflict以进行ARP请求并获取响应:
func checkIPConflict(ip string, iface *net.Interface) (bool, error) {
	targetIP := net.ParseIP(ip)
	if targetIP == nil {
		return false, fmt.Errorf("invalid IP address")
	}

	reqPacket := &arpPacket{
		hardwareType:   0x0001,
		protocolType:   0x0800,
		hardwareSize:   6,
		protocolSize:   4,
		opcode:         0x0001,
		senderMac:      iface.HardwareAddr,
		senderIP:       iface.IP,
		targetMac:      zeroMAC,
		targetIP:       targetIP,
	}

	dstMAC := broadcastMAC
	conn, err := net.DialARP("arp", iface, nil)
	if err != nil {
		return false, err
	}
	defer conn.Close()

	conn.SetDeadline(time.Now().Add(timeout))
	if err := conn.WriteTo(reqPacket.Marshal(), &net.IPAddr{IP: dstIP}); err != nil {
		return false, err
	}

	respPacket := &arpPacket{}
	buf := make([]byte, 28)
	for {
		n, _, err := conn.ReadFrom(buf)
		if err != nil {
			if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
				return false, nil
			} else {
				return false, err
			}
		}
		respPacket.Unmarshal(buf[:n])
		if respPacket.opcode == 0x0002 && respPacket.senderIP.Equal(targetIP) {
			return true, nil
		}
	}
}
  1. 编写主函数调用checkIPConflict函数,检查指定IP地址是否存在冲突:
func main() {
	ifaceName := "eth0"
	ip := "192.168.1.100"

	iface, err := net.InterfaceByName(ifaceName)
	if err != nil {
		panic(err)
	}

	conflict, err := checkIPConflict(ip, iface)
	if err != nil {
		panic(err)
	}

	if conflict {
		fmt.Printf("IP address %s conflicts with another device\n", ip)
	} else {
		fmt.Printf("IP address %s is free to use\n", ip)
	}
}

注意:在以上示例代码中,需要自定义类型arpPacket和变量zeroMACbroadcastMACdstIP,并设置超时时间timeout。完整的示例代码可以参考下面的代码:

package main

import (
	"encoding/binary"
	"fmt"
	"net"
	"time"
)

const (
	timeout = 5 * time.Second
)

var (
	zeroMAC      = net.HardwareAddr{0, 0, 0, 0, 0, 0}
	broadcastMAC = net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
	dstIP        = net.IPv4(255, 255, 255, 255)
)

type arpPacket struct {
	hardwareType   uint16
	protocolType   uint16
	hardwareSize   uint8
	protocolSize   uint8
	opcode         uint16
	senderMac      net.HardwareAddr
	senderIP       net.IP
	targetMac      net.HardwareAddr
	targetIP       net.IP
}

func (p *arpPacket) Marshal() []byte {
	buf := make([]byte, 28)
	binary.BigEndian.PutUint16(buf[0:2], p.hardwareType)
	binary.BigEndian.PutUint16(buf[2:4], p.protocolType)
	buf[4] = p.hardwareSize
	buf[5] = p.protocolSize
	binary.BigEndian.PutUint16(buf[6:8], p.opcode)
	copy(buf[8:14], p.senderMac)
	copy(buf[14:18], p.senderIP.To4())
	copy(buf[18:24], p.targetMac)
	copy(buf[24:28], p.targetIP.To4())
	return buf
}

func (p *arpPacket) Unmarshal(buf []byte) {
	p.hardwareType = binary.BigEndian.Uint16(buf[0:2])
	p.protocolType = binary.BigEndian.Uint16(buf[2:4])
	p.hardwareSize = buf[4]
	p.protocolSize = buf[5]
	p.opcode = binary.BigEndian.Uint16(buf[6:8])
	copy(p.senderMac, buf[8:14])
	p.senderIP = net.IPv4(buf[14], buf[15], buf[16], buf[17])
	copy(p.targetMac, buf[18:24])
	p.targetIP = net.IPv4(buf[24], buf[25], buf[26], buf[27])
}

func checkIPConflict(ip string, iface *net.Interface) (bool, error) {
	targetIP := net.ParseIP(ip)
	if targetIP == nil {
		return false, fmt.Errorf("invalid IP address")
	}

	reqPacket := &arpPacket{
		hardwareType:   0x0001,
		protocolType:   0x0800,
		hardwareSize:   6,
		protocolSize:   4,
		opcode:         0x0001,
		senderMac:      iface.HardwareAddr,
		senderIP:       iface.IP,
		targetMac:      zeroMAC,
		targetIP:       targetIP,
	}

	dstMAC := broadcastMAC
	conn, err := net.DialARP("arp", iface, nil)
	if err != nil {
		return false, err
	}
	defer conn.Close()

	conn.SetDeadline(time.Now().Add(timeout))
	if err := conn.WriteTo(reqPacket.Marshal(), &net.IPAddr{IP: dstIP}); err != nil {
		return false, err
	}

	respPacket := &arpPacket{}
	buf := make([]byte, 28)
	for {
		n, _, err := conn.ReadFrom(buf)
		if err != nil {
			if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
				return false, nil
			} else {
				return false, err
			}
		}
		respPacket.Unmarshal(buf[:n])
		if respPacket.opcode == 0x0002 && respPacket.senderIP.Equal(targetIP) {
			return true, nil
		}
	}
}

func main() {
	ifaceName := "eth0"
	ip := "192.168.1.100"

	iface, err := net.InterfaceByName(ifaceName)
	if err != nil {
		panic(err)
	}

	conflict, err := checkIPConflict(ip, iface)
	if err != nil {
		panic(err)
	}

	if conflict {
		fmt.Printf("IP address %s conflicts with another device\n", ip)
	} else {
		fmt.Printf("IP address %s is free to use\n", ip)
	}
}