Skip to content

Commit d12c168

Browse files
committed
Add a new VLAN sending option
1 parent fdadd64 commit d12c168

File tree

3 files changed

+58
-6
lines changed

3 files changed

+58
-6
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ Change or force the IPv4 address sent as source in the broadcasted ARP packets.
6969

7070
Change or force the MAC address sent as destination ARP request. By default, a broadcast destination (`00:00:00:00:00:00`) will be set.
7171

72+
#### Set VLAN ID `-Q 540`
73+
74+
Add a 802.1Q field in the Ethernet frame. This fields contains the given VLAN ID for outgoing ARP requests. By default, the Ethernet frame is sent without 802.1Q fields (no VLAN).
75+
7276
#### Show version `--version`
7377

7478
Display the ARP scan CLI version and exits the process.

src/main.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ fn main() {
3232
.arg(
3333
Arg::with_name("numeric").short("n").long("numeric").takes_value(false).help("Numeric mode, no hostname resolution")
3434
)
35+
.arg(
36+
Arg::with_name("vlan").short("Q").long("vlan").takes_value(true).value_name("VLAN_ID").help("Send using 802.1Q with VLAN ID")
37+
)
3538
.arg(
3639
Arg::with_name("list").short("l").long("list").takes_value(false).help("List network interfaces")
3740
)
@@ -111,6 +114,20 @@ fn main() {
111114
},
112115
None => None
113116
};
117+
118+
let vlan_id: Option<u16> = match matches.value_of("vlan") {
119+
Some(vlan) => {
120+
121+
match vlan.parse::<u16>() {
122+
Ok(vlan_number) => Some(vlan_number),
123+
Err(_) => {
124+
eprintln!("Expected valid VLAN identifier");
125+
process::exit(1);
126+
}
127+
}
128+
},
129+
None => None
130+
};
114131

115132
if !utils::is_root_user() {
116133
eprintln!("Should run this binary as root");
@@ -169,7 +186,7 @@ fn main() {
169186
for ip_address in ip_network.iter() {
170187

171188
if let IpAddr::V4(ipv4_address) = ip_address {
172-
network::send_arp_request(&mut tx, selected_interface, ipv4_address, source_ipv4, destination_mac);
189+
network::send_arp_request(&mut tx, selected_interface, ipv4_address, source_ipv4, destination_mac, vlan_id);
173190
}
174191
}
175192

src/network.rs

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ use pnet::datalink::{MacAddr, NetworkInterface, DataLinkSender, DataLinkReceiver
88
use pnet::packet::{MutablePacket, Packet};
99
use pnet::packet::ethernet::{EthernetPacket, MutableEthernetPacket, EtherTypes};
1010
use pnet::packet::arp::{MutableArpPacket, ArpOperations, ArpHardwareTypes, ArpPacket};
11+
use pnet::packet::vlan::{ClassOfService, MutableVlanPacket};
12+
13+
const VLAN_QOS_DEFAULT: u8 = 1;
14+
const ARP_PACKET_SIZE: usize = 28;
15+
const VLAN_PACKET_SIZE: usize = 32;
16+
17+
const ETHERNET_STD_PACKET_SIZE: usize = 42;
18+
const ETHERNET_VLAN_PACKET_SIZE: usize = 46;
1119

1220
/**
1321
* A target detail represents a single host on the local network with an IPv4
@@ -25,9 +33,12 @@ pub struct TargetDetails {
2533
* interface and a target IPv4 address. The ARP request will be broadcasted to
2634
* the whole local network with the first valid IPv4 address on the interface.
2735
*/
28-
pub fn send_arp_request(tx: &mut Box<dyn DataLinkSender>, interface: &NetworkInterface, target_ip: Ipv4Addr, forced_source_ipv4: Option<Ipv4Addr>, forced_destination_mac: Option<MacAddr>) {
36+
pub fn send_arp_request(tx: &mut Box<dyn DataLinkSender>, interface: &NetworkInterface, target_ip: Ipv4Addr, forced_source_ipv4: Option<Ipv4Addr>, forced_destination_mac: Option<MacAddr>, forced_vlan_id: Option<u16>) {
2937

30-
let mut ethernet_buffer = [0u8; 42];
38+
let mut ethernet_buffer = match forced_vlan_id {
39+
Some(_) => vec![0u8; ETHERNET_VLAN_PACKET_SIZE],
40+
None => vec![0u8; ETHERNET_STD_PACKET_SIZE]
41+
};
3142
let mut ethernet_packet = MutableEthernetPacket::new(&mut ethernet_buffer).unwrap();
3243

3344
let target_mac = match forced_destination_mac {
@@ -41,9 +52,14 @@ pub fn send_arp_request(tx: &mut Box<dyn DataLinkSender>, interface: &NetworkInt
4152

4253
ethernet_packet.set_destination(target_mac);
4354
ethernet_packet.set_source(source_mac);
44-
ethernet_packet.set_ethertype(EtherTypes::Arp);
4555

46-
let mut arp_buffer = [0u8; 28];
56+
let selected_ethertype = match forced_vlan_id {
57+
Some(_) => EtherTypes::Vlan,
58+
None => EtherTypes::Arp
59+
};
60+
ethernet_packet.set_ethertype(selected_ethertype);
61+
62+
let mut arp_buffer = [0u8; ARP_PACKET_SIZE];
4763
let mut arp_packet = MutableArpPacket::new(&mut arp_buffer).unwrap();
4864

4965
let source_ipv4 = find_source_ip(interface, forced_source_ipv4);
@@ -58,7 +74,22 @@ pub fn send_arp_request(tx: &mut Box<dyn DataLinkSender>, interface: &NetworkInt
5874
arp_packet.set_target_hw_addr(target_mac);
5975
arp_packet.set_target_proto_addr(target_ip);
6076

61-
ethernet_packet.set_payload(arp_packet.packet_mut());
77+
if let Some(vlan_id) = forced_vlan_id {
78+
79+
let mut vlan_buffer = [0u8; VLAN_PACKET_SIZE];
80+
let mut vlan_packet = MutableVlanPacket::new(&mut vlan_buffer).unwrap();
81+
vlan_packet.set_vlan_identifier(vlan_id);
82+
vlan_packet.set_priority_code_point(ClassOfService::new(VLAN_QOS_DEFAULT));
83+
vlan_packet.set_drop_eligible_indicator(0);
84+
vlan_packet.set_ethertype(EtherTypes::Arp);
85+
86+
vlan_packet.set_payload(arp_packet.packet_mut());
87+
88+
ethernet_packet.set_payload(vlan_packet.packet_mut());
89+
}
90+
else {
91+
ethernet_packet.set_payload(arp_packet.packet_mut());
92+
}
6293

6394
tx.send_to(&ethernet_packet.to_immutable().packet(), Some(interface.clone()));
6495
}

0 commit comments

Comments
 (0)