环境
- Time 2022-11-20
- VirtualBox 7.0.2
- Rust 1.65.0
- pnet 0.31.0
- CentOS 7
前言
说明
参考:https://docs.rs/pnet_packet/latest/pnet_packet/index.html
目标
使用两台虚拟机,通过 IP 地址,获取到目标主机的 MAC 地址。
日常使用的时候,都是使用 IP 连接服务器,需要使用地址解析协议 ARP 获取 MAC 地址。
IP 地址属于三层协议的内容,所以已经从二层数据链路层进入到了三层网络层。
新增 IP 地址
增加 IP 地址的命令:ip addr add 192.168.44.88/24 dev enp0s8
主机一:
[root@centos7 ~]# hostname
centos7
[root@centos7 ~]# ip addr show enp0s8
3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 08:00:27:16:d2:3f brd ff:ff:ff:ff:ff:ff
inet 192.168.44.44/24 scope global enp0s8
valid_lft forever preferred_lft forever
主机二:
[root@centos7-1 ~]# ip addr show enp0s8
3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 08:00:27:36:b1:35 brd ff:ff:ff:ff:ff:ff
inet 192.168.44.88/24 scope global enp0s8
valid_lft forever preferred_lft forever
[root@centos7-1 ~]# hostname
centos7-1
可以看到主机一的 IP 是 44,主机二的 IP 是 88。
init_ethernet
fn init_ethernet(interface: &NetworkInterface) -> [u8; 42] {
// ARP 协议 28 字节,加上数据帧(目的MAC + 源MAC + 类型)14 字节,共 42 字节
let mut result = [0u8; 42];
let mut ethernet = MutableEthernetPacket::new(&mut result).unwrap();
// 地址解析协议还没有目的 MAC,只能发送广播
ethernet.set_destination(MacAddr::broadcast());
ethernet.set_source(interface.mac.unwrap());
// 类型为 ARP,对应数字:0x0806
ethernet.set_ethertype(EtherTypes::Arp);
result
}
init_arp
fn init_arp(interface: NetworkInterface) -> [u8; 28] {
let mut result = [0u8; 28];
let mut arp = MutableArpPacket::new(&mut result).unwrap();
let target_ip = "192.168.44.88".parse().unwrap();
let source_ip = match interface.ips.get(0).unwrap().ip() {
IpAddr::V4(ip) => ip,
_ => unreachable!(),
};
// 具体参考 RFC 826
arp.set_hardware_type(ArpHardwareTypes::Ethernet);
arp.set_protocol_type(EtherTypes::Ipv4);
arp.set_hw_addr_len(6);
arp.set_proto_addr_len(4);
arp.set_operation(ArpOperations::Request);
arp.set_sender_hw_addr(interface.mac.unwrap());
arp.set_sender_proto_addr(source_ip);
arp.set_target_hw_addr(MacAddr::zero());
arp.set_target_proto_addr(target_ip);
result
}
main
use std::net::IpAddr;
use pnet_datalink::{channel, Channel, MacAddr, NetworkInterface};
use pnet_packet::arp::{ArpHardwareTypes, ArpOperations, ArpPacket, MutableArpPacket};
use pnet_packet::ethernet::{EtherTypes, MutableEthernetPacket};
use pnet_packet::{MutablePacket, Packet};
fn main() {
let interface = pnet_datalink::interfaces()
.into_iter()
.find(|iface| iface.name == "enp0s8")
.unwrap();
let channel = channel(&interface, Default::default());
let (mut sender, mut reader) = match channel {
Ok(Channel::Ethernet(tx, rx)) => (tx, rx),
_ => panic!("Not a valid channel returned"),
};
let mut ethernet = init_ethernet(&interface);
let mut arp = init_arp(interface);
let mut ethernet = MutableEthernetPacket::new(&mut ethernet).unwrap();
let mut arp = MutableArpPacket::new(&mut arp).unwrap();
ethernet.set_payload(arp.packet_mut());
sender.send_to(ethernet.packet(), None).unwrap().unwrap();
println!("Sent ARP request");
let buf = reader.next().unwrap();
let arp = ArpPacket::new(&buf[MutableEthernetPacket::minimum_packet_size()..]).unwrap();
println!("Target MAC address: {}", arp.get_sender_hw_addr());
}
目的 MAC
Sent ARP request
Target MAC address: 08:00:27:36:b1:35
向目的 IP 地址发送的 ARP 协议,操作系统自动回应。
ARP 缓存
[root@centos7-1 ~]# ip neigh list
192.168.1.144 dev enp0s3 lladdr 08:00:27:93:92:f2 STALE
192.168.1.3 dev enp0s3 lladdr 00:e2:69:5c:cf:b5 DELAY
192.168.44.44 dev enp0s8 lladdr 08:00:27:16:d2:3f STALE
192.168.1.1 dev enp0s3 lladdr d0:60:8c:86:7f:9c STALE
fe80::1 dev enp0s3 lladdr d0:60:8c:86:7f:9c router STALE
可以通过命令 ip neigh list
查看 ARP 缓存。
总结
通过 ARP 协议获取到了目的主机的 MAC 地址。