libarp/
arp.rs

1use crate::interfaces::{Interface, MacAddr};
2use std::{
3    convert::TryFrom,
4    io::{Error, ErrorKind},
5    net::Ipv4Addr,
6    u16,
7};
8
9use num_derive::FromPrimitive;
10use num_traits::FromPrimitive;
11
12use pnet::packet::{
13    arp::{ArpHardwareTypes, ArpOperation, ArpPacket, MutableArpPacket},
14    ethernet::{
15        EtherType,
16        EtherTypes::{self},
17        MutableEthernetPacket,
18    },
19    MutablePacket, Packet,
20};
21
22pub struct ArpMessage {
23    pub source_hardware_address: MacAddr,
24    pub source_protocol_address: Ipv4Addr,
25
26    pub target_hardware_address: MacAddr,
27    pub target_protocol_address: Ipv4Addr,
28
29    pub ethertype: EtherType,
30    pub operation: Operation,
31}
32
33#[derive(Copy, Clone, FromPrimitive, PartialEq)]
34pub enum Operation {
35    ArpRequest = 0x1,
36    ArpResponse = 0x2,
37    RarpRequest = 0x3,
38    RarpResponse = 0x4,
39}
40
41impl ArpMessage {
42    /// Constructs a new ARP message with arbitrary field contents.
43    pub fn new(
44        ethertype: EtherType,
45        source_hardware_address: MacAddr,
46        source_protocol_address: Ipv4Addr,
47        target_hardware_address: MacAddr,
48        target_protocol_address: Ipv4Addr,
49        operation: Operation,
50    ) -> Self {
51        ArpMessage {
52            source_hardware_address: source_hardware_address,
53            source_protocol_address: source_protocol_address,
54            target_hardware_address: target_hardware_address,
55            target_protocol_address: target_protocol_address,
56            ethertype: ethertype,
57            operation: operation,
58        }
59    }
60
61    /// Constructs a new ARP request message.
62    pub fn new_arp_request(
63        source_hardware_address: MacAddr,
64        source_protocol_address: Ipv4Addr,
65        target_protocol_address: Ipv4Addr,
66    ) -> Self {
67        Self::new(
68            EtherTypes::Arp,
69            source_hardware_address,
70            source_protocol_address,
71            MacAddr(0, 0, 0, 0, 0, 0),
72            target_protocol_address,
73            Operation::ArpRequest,
74        )
75    }
76
77    /// Constructs a new ARP response message.
78    pub fn new_arp_response(
79        source_hardware_address: MacAddr,
80        source_protocol_address: Ipv4Addr,
81        target_hardware_address: MacAddr,
82        target_protocol_address: Ipv4Addr,
83    ) -> Self {
84        Self::new(
85            EtherTypes::Arp,
86            source_hardware_address,
87            source_protocol_address,
88            target_hardware_address,
89            target_protocol_address,
90            Operation::ArpResponse,
91        )
92    }
93
94    /// Constructs a new RARP request message.
95    pub fn new_rarp_request(
96        source_hardware_address: MacAddr,
97        target_hardware_address: MacAddr,
98    ) -> Self {
99        Self::new(
100            EtherTypes::Rarp,
101            source_hardware_address,
102            Ipv4Addr::new(0, 0, 0, 0),
103            target_hardware_address,
104            Ipv4Addr::new(0, 0, 0, 0),
105            Operation::RarpRequest,
106        )
107    }
108
109    /// Constructs a new RARP response message.
110    pub fn new_rarp_response(
111        source_hardware_address: MacAddr,
112        source_protocol_address: Ipv4Addr,
113        target_hardware_address: MacAddr,
114        target_protocol_address: Ipv4Addr,
115    ) -> Self {
116        Self::new(
117            EtherTypes::Rarp,
118            source_hardware_address,
119            source_protocol_address,
120            target_hardware_address,
121            target_protocol_address,
122            Operation::RarpResponse,
123        )
124    }
125
126    /// Sends the message on the given interface.
127    /// # Errors
128    /// Returns an error when sending fails.
129    pub fn send(&self, interface: &Interface) -> Result<(), Error> {
130        let mut tx = match interface.create_tx_rx_channels() {
131            Ok((tx, _)) => tx,
132            Err(err) => return Err(err),
133        };
134
135        let mut eth_buf = vec![0; 42];
136        let mut eth_packet = MutableEthernetPacket::new(&mut eth_buf).unwrap();
137
138        eth_packet.set_destination(MacAddr::new(0xff, 0xff, 0xff, 0xff, 0xff, 0xff).into());
139        eth_packet.set_source(interface.get_mac()?.into());
140        eth_packet.set_ethertype(self.ethertype);
141
142        let mut arp_buf = vec![0; 28];
143        let mut arp_packet = MutableArpPacket::new(&mut arp_buf).unwrap();
144
145        arp_packet.set_hardware_type(ArpHardwareTypes::Ethernet);
146        arp_packet.set_protocol_type(EtherTypes::Ipv4);
147        arp_packet.set_hw_addr_len(0x06);
148        arp_packet.set_proto_addr_len(0x04);
149        arp_packet.set_operation(ArpOperation::new(self.operation as u16));
150        arp_packet.set_sender_hw_addr(self.source_hardware_address.into());
151        arp_packet.set_sender_proto_addr(self.source_protocol_address);
152        arp_packet.set_target_hw_addr(self.target_hardware_address.into());
153        arp_packet.set_target_proto_addr(self.target_protocol_address);
154
155        eth_packet.set_payload(arp_packet.packet_mut());
156
157        tx.send_to(eth_packet.packet(), None).unwrap()
158    }
159}
160
161impl TryFrom<ArpPacket<'_>> for ArpMessage {
162    type Error = Error;
163
164    fn try_from(arp_packet: ArpPacket<'_>) -> Result<Self, Self::Error> {
165        let operation_raw = arp_packet.get_operation().0;
166        let operation = match FromPrimitive::from_u16(operation_raw) {
167            Some(op) => op,
168            None => {
169                return Err(Error::new(
170                    ErrorKind::InvalidData,
171                    format!(
172                        "Could not cast operation raw value {} to enum.",
173                        operation_raw
174                    ),
175                ))
176            }
177        };
178
179        Ok(ArpMessage::new(
180            arp_packet.get_protocol_type(),
181            arp_packet.get_sender_hw_addr().into(),
182            arp_packet.get_sender_proto_addr(),
183            arp_packet.get_target_hw_addr().into(),
184            arp_packet.get_target_proto_addr(),
185            operation,
186        ))
187    }
188}