mirror of
https://github.com/impact-eintr/netstack.git
synced 2025-10-16 09:50:36 +08:00
arp报文定义与基本介绍
This commit is contained in:
99
tcpip/header/arp.go
Normal file
99
tcpip/header/arp.go
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
package header
|
||||||
|
|
||||||
|
import "netstack/tcpip"
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ARPProtocolNumber是ARP协议号,为0x0806
|
||||||
|
ARPProtocolNumber tcpip.NetworkProtocolNumber = 0x0806
|
||||||
|
|
||||||
|
// ARPSize是ARP报文在IPV4网络下的长度
|
||||||
|
ARPSize = 2 + 2 + 1 + 1 + 2 + 2*6 + 2*4 // 28 Bytes
|
||||||
|
)
|
||||||
|
|
||||||
|
// ARPOP 代表ARP的操作码
|
||||||
|
type ARPOp uint16
|
||||||
|
|
||||||
|
// RFC 826 定义的操作码
|
||||||
|
const (
|
||||||
|
// arp 请求
|
||||||
|
ARPRequest ARPOp = 1
|
||||||
|
// arp应答
|
||||||
|
ARPReply ARPOp = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
ARP报文的封装
|
||||||
|
1. 2B 硬件类型(hard type) 硬件类型用来指代需要什么样的物理地址,如果硬件类型为 1,表示以太网地址
|
||||||
|
2. 2B 协议类型 协议类型则是需要映射的协议地址类型,如果协议类型是 0x0800,表示 ipv4 协议。
|
||||||
|
3. 1B 硬件地址长度 表示硬件地址的长度,单位字节,一般都是以太网地址的长度为 6 字节。
|
||||||
|
4. 1B 协议地址长度: 表示协议地址的长度,单位字节,一般都是 ipv4 地址的长度为 4 字节。
|
||||||
|
5. 2B 操作码 这些值用于区分具体操作类型,因为字段都相同,所以必须指明操作码,不然连请求还是应答都分不清。
|
||||||
|
1=>ARP 请求, 2=>ARP 应答,3=>RARP 请求,4=>RARP 应答。
|
||||||
|
6. 6B 源硬件地址 源物理地址,如02:f2:02:f2:02:f2
|
||||||
|
7. 4B 源协议地址 源协议地址,如192.168.0.1
|
||||||
|
8. 6B 目标硬件地址 目标物理地址,如03:f2:03:f2:03:f2
|
||||||
|
9. 4B 目标协议地址 目标协议地址,如 192.168.0.2
|
||||||
|
*/
|
||||||
|
type ARP []byte
|
||||||
|
|
||||||
|
// 从报文中得到硬件类型
|
||||||
|
func (a ARP) hardwareAddressSpace() uint16 { return uint16(a[0])<<8 | uint16(a[1]) }
|
||||||
|
|
||||||
|
// 从报文中得到协议类型
|
||||||
|
func (a ARP) protocolAddressSpace() uint16 { return uint16(a[2])<<8 | uint16(a[3]) }
|
||||||
|
|
||||||
|
// 从报文中得到硬件地址的长度
|
||||||
|
func (a ARP) hardwareAddressSize() int { return int(a[4]) }
|
||||||
|
|
||||||
|
// 从报文中得到协议的地址长度
|
||||||
|
func (a ARP) protocolAddressSize() int { return int(a[5]) }
|
||||||
|
|
||||||
|
// Op从报文中得到arp操作码.
|
||||||
|
func (a ARP) Op() ARPOp { return ARPOp(a[6])<<8 | ARPOp(a[7]) }
|
||||||
|
|
||||||
|
// SetOp设置arp操作码.
|
||||||
|
func (a ARP) SetOp(op ARPOp) {
|
||||||
|
a[6] = uint8(op >> 8)
|
||||||
|
a[7] = uint8(op)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetIPv4OverEthernet设置IPV4网络在以太网中arp报文的硬件和协议信息.
|
||||||
|
func (a ARP) SetIPv4OverEthernet() {
|
||||||
|
a[0], a[1] = 0, 1 // htypeEthernet
|
||||||
|
a[2], a[3] = 0x08, 0x00 // IPv4ProtocolNumber
|
||||||
|
a[4] = 6 // macSize
|
||||||
|
a[5] = uint8(IPv4AddressSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProtocolAddressSender从报文中得到arp发送方的协议地址,为ipv4地址
|
||||||
|
func (a ARP) ProtocolAddressSender() []byte {
|
||||||
|
const s = 8 + 6 // 8 是arp的协议头部 6是本机MAC
|
||||||
|
return a[s : s+4] // 本机IP
|
||||||
|
}
|
||||||
|
|
||||||
|
// HardwareAddressTarget从报文中得到arp目的方的硬件地址
|
||||||
|
func (a ARP) HardwareAddressTarget() []byte {
|
||||||
|
const s = 8 + 6 + 4 // 8是arp协议头部 6 是本机MAC 4是本机ip
|
||||||
|
return a[s : s+6] // 目标MAC
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProtocolAddressTarget从报文中得到arp目的方的协议地址,为ipv4地址
|
||||||
|
func (a ARP) ProtocolAddressTarget() []byte {
|
||||||
|
const s = 8 + 6 + 4 + 6 // 8是arp协议头部 6 是本机MAC 4是本机ip 6是目标MAC
|
||||||
|
return a[s : s+4] // 目标IP
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid检查arp报文是否有效
|
||||||
|
func (a ARP) IsValid() bool {
|
||||||
|
// 比arp报文的长度小,返回无效
|
||||||
|
if len(a) < ARPSize {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const htypeEthernet = 1
|
||||||
|
const macSize = 6
|
||||||
|
// 是否以太网、ipv4、硬件和协议长度都对
|
||||||
|
return a.hardwareAddressSpace() == htypeEthernet &&
|
||||||
|
a.protocolAddressSpace() == uint16(IPv4ProtocolNumber) &&
|
||||||
|
a.hardwareAddressSize() == macSize &&
|
||||||
|
a.protocolAddressSize() == IPv4AddressSize
|
||||||
|
}
|
24
tcpip/network/arp/README.md
Normal file
24
tcpip/network/arp/README.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# arp协议介绍
|
||||||
|
|
||||||
|
在以太网协议中规定,同一局域网中的一台主机要和另一台主机进行直接通信,必须要知道目标主机的 MAC 地址。而在 TCP/IP 协议中,网络层和传输层只关心目标主机的 IP 地址。这就导致在以太网中使用 IP 协议时,数据链路层的以太网协议接到上层 IP 协议提供的数据中,只包含目的主机的 IP 地址。于是需要一种方法,根据目的主机的 IP 地址,获得其 MAC 地址。这就是 ARP 协议要做的事情。所谓地址解析(address resolution)就是主机在发送帧前将目标 IP 地址转换成目标 MAC 地址的过程。
|
||||||
|
|
||||||
|
当发送主机和目的主机不在同一个局域网中时,即便知道目的主机的 MAC 地址,两者也不能直接通信,必须经过路由转发才可以。所以此时,发送主机通过 ARP 协议获得的将不是目的主机的真实 MAC 地址,而是一台可以通往局域网外的路由器的 MAC 地址。于是此后发送主机发往目的主机的所有帧,都将发往该路由器,通过它向外发送。这种情况称为委托 ARP 或 ARP 代理(ARP Proxy)。
|
||||||
|
|
||||||
|
|
||||||
|
还有一种免费 ARP(gratuitous ARP),它是指主机发送 ARP 查询(广播)自己的 IP 地址,当 ARP 功能被开启或者是端口初始配置完成,主机向网络发送免费 ARP 来查询自己的 IP 地址确认地址唯一可用。用来确定网络中是否有其他主机使用了 IP 地址,如果有应答则产生错误消息。免费 ARP 也可以做更新 ARP 缓存用,网络中的其他主机收到该广播则在缓存中更新条目,收到该广播的主机无论是否存在与 IP 地址相关的条目都会强制更新,如果存在旧条目则会将 MAC 更新为广播包中的 MAC。
|
||||||
|
|
||||||
|
## arp报文组成
|
||||||
|
|
||||||
|
1. 硬件类型(hard type) 硬件类型用来指代需要什么样的物理地址,如果硬件类型为 1,表示以太网地址
|
||||||
|
2. 协议类型 协议类型则是需要映射的协议地址类型,如果协议类型是 0x0800,表示 ipv4 协议。
|
||||||
|
3. 硬件地址长度 表示硬件地址的长度,单位字节,一般都是以太网地址的长度为 6 字节。
|
||||||
|
4. 协议地址长度: 表示协议地址的长度,单位字节,一般都是 ipv4 地址的长度为 4 字节。
|
||||||
|
5. 操作码 这些值用于区分具体操作类型,因为字段都相同,所以必须指明操作码,不然连请求还是应答都分不清。 1=>ARP 请求, 2=>ARP 应答,3=>RARP 请求,4=>RARP 应答。
|
||||||
|
6. 源硬件地址 源物理地址,如02:f2:02:f2:02:f2
|
||||||
|
7. 源协议地址 源协议地址,如192.168.0.1
|
||||||
|
8. 目标硬件地址 目标物理地址,如03:f2:03:f2:03:f2
|
||||||
|
9. 目标协议地址。 目标协议地址,如 192.168.0.2
|
||||||
|
|
||||||
|
## ARP 高速缓存
|
||||||
|
|
||||||
|
知道了 ARP 发送的原理后,我们不禁疑惑,如果每次发之前都要发送 ARP 请求硬件地址会不会太慢,但是实际上 ARP 的运行是非常高效的。那是因为每一个主机上都有一个 ARP 高速缓存,我们可以在命令行键入 arp -a 获取本机 ARP 高速缓存的所有内容。
|
7
tcpip/network/arp/arp.go
Normal file
7
tcpip/network/arp/arp.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// 主机的链路层寻址是通过 arp 表来实现的
|
||||||
|
package arp
|
||||||
|
|
||||||
|
const (
|
||||||
|
ProtocolName = "arp"
|
||||||
|
ProtocolNumber = "arp"
|
||||||
|
)
|
1
tcpip/network/arp/arp_test.go
Normal file
1
tcpip/network/arp/arp_test.go
Normal file
@@ -0,0 +1 @@
|
|||||||
|
package arp_test
|
@@ -345,9 +345,12 @@ func (n *NIC) getRef(protocol tcpip.NetworkProtocolNumber, dst tcpip.Address) *r
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 当 NIC 从物理接口接收数据包时,将调用函数 DeliverNetworkPacket,用来分发网络层数据包。
|
||||||
|
// 比如 protocol 是 arp 协议号,那么会找到arp.HandlePacket来处理数据报。
|
||||||
|
// 简单来说就是根据网络层协议和目的地址来找到相应的网络层端,将网络层数据发给它,
|
||||||
|
// 当前实现的网络层协议有 arp、ipv4 和 ipv6。
|
||||||
func (n *NIC) DeliverNetworkPacket(linkEP LinkEndpoint, remoteLinkAddr, localLinkAddr tcpip.LinkAddress,
|
func (n *NIC) DeliverNetworkPacket(linkEP LinkEndpoint, remoteLinkAddr, localLinkAddr tcpip.LinkAddress,
|
||||||
protocol tcpip.NetworkProtocolNumber, vv buffer.VectorisedView) {
|
protocol tcpip.NetworkProtocolNumber, vv buffer.VectorisedView) {
|
||||||
// TODO 需要完成逻辑
|
|
||||||
netProto, ok := n.stack.networkProtocols[protocol]
|
netProto, ok := n.stack.networkProtocols[protocol]
|
||||||
if !ok {
|
if !ok {
|
||||||
n.stack.stats.UnknownProtocolRcvdPackets.Increment()
|
n.stack.stats.UnknownProtocolRcvdPackets.Increment()
|
||||||
|
Reference in New Issue
Block a user