Skip to content

Commit bb49a6e

Browse files
committed
Fixed transmit not having any headers. Oops.
1 parent e9e683d commit bb49a6e

File tree

4 files changed

+167
-48
lines changed

4 files changed

+167
-48
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "py_pnet"
3-
version = "0.1.0"
3+
version = "0.2.0"
44
edition = "2021"
55

66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

src/lib.rs

Lines changed: 122 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
use pyo3::prelude::*;
22
use pyo3::types::{PyBytes, PyDict, PyList};
3-
use pnet::datalink::{self, Channel::Ethernet};
4-
use pnet::packet::ethernet::{EtherTypes, EthernetPacket};
5-
use pnet::packet::ipv4::Ipv4Packet;
3+
use pnet::datalink::{self, Channel::Ethernet, MacAddr};
4+
use pnet::packet::ethernet::{MutableEthernetPacket, EtherTypes, EthernetPacket};
5+
use pnet::packet::ip::IpNextHeaderProtocols;
6+
use pnet::packet::ipv4::{MutableIpv4Packet, Ipv4Packet};
7+
use pnet::packet::udp::{MutableUdpPacket, UdpPacket};
68
use pnet::packet::tcp::TcpPacket;
7-
use pnet::packet::udp::UdpPacket;
89
use pnet::packet::Packet;
9-
use pnet::packet::ip::IpNextHeaderProtocols;
10+
use std::net::Ipv4Addr;
11+
use std::str::FromStr;
12+
13+
14+
15+
16+
1017

1118
#[pyclass]
1219
struct DataLinkInterface {
@@ -20,6 +27,7 @@ impl DataLinkInterface {
2027
DataLinkInterface { interface_name }
2128
}
2229

30+
2331
#[pyo3(signature = (
2432
num_packets,
2533
*,
@@ -177,12 +185,45 @@ impl DataLinkInterface {
177185
Ok(py_packets.into())
178186
}
179187

180-
#[pyo3(signature = (packet, num_packets=None))]
188+
#[pyo3(signature = (payload, src_mac, src_ip, src_port, dst_mac, dst_ip, dst_port))]
181189
fn transmit_packet(
182190
&self,
183-
packet: &[u8],
184-
num_packets: Option<usize>,
191+
payload: &[u8],
192+
src_mac: &str,
193+
src_ip: &str,
194+
src_port: u16,
195+
dst_mac: &str,
196+
dst_ip: &str,
197+
dst_port: u16,
185198
) -> PyResult<()> {
199+
// Parse IP addresses
200+
let src_ip: Ipv4Addr = src_ip.parse().map_err(|e| {
201+
PyErr::new::<pyo3::exceptions::PyValueError, _>(format!(
202+
"Invalid source IP address: {}",
203+
e
204+
))
205+
})?;
206+
let dst_ip: Ipv4Addr = dst_ip.parse().map_err(|e| {
207+
PyErr::new::<pyo3::exceptions::PyValueError, _>(format!(
208+
"Invalid destination IP address: {}",
209+
e
210+
))
211+
})?;
212+
213+
// Parse MAC addresses
214+
let src_mac = MacAddr::from_str(src_mac).map_err(|e| {
215+
PyErr::new::<pyo3::exceptions::PyValueError, _>(format!(
216+
"Invalid source MAC address: {}",
217+
e
218+
))
219+
})?;
220+
let dst_mac = MacAddr::from_str(dst_mac).map_err(|e| {
221+
PyErr::new::<pyo3::exceptions::PyValueError, _>(format!(
222+
"Invalid destination MAC address: {}",
223+
e
224+
))
225+
})?;
226+
186227
// Find the network interface
187228
let interface = {
188229
if cfg!(target_os = "windows") {
@@ -202,9 +243,69 @@ impl DataLinkInterface {
202243
))
203244
})?;
204245

246+
// Create a new UDP packet
247+
let mut udp_buffer = vec![0u8; MutableUdpPacket::minimum_packet_size() + payload.len()];
248+
let mut udp_packet = MutableUdpPacket::new(&mut udp_buffer).ok_or_else(|| {
249+
PyErr::new::<pyo3::exceptions::PyRuntimeError, _>("Failed to create UDP packet")
250+
})?;
251+
252+
udp_packet.set_source(src_port);
253+
udp_packet.set_destination(dst_port);
254+
udp_packet.set_length((MutableUdpPacket::minimum_packet_size() + payload.len()) as u16);
255+
udp_packet.set_payload(payload);
256+
257+
// Calculate UDP checksum
258+
let checksum = pnet::packet::udp::ipv4_checksum(
259+
&udp_packet.to_immutable(),
260+
&src_ip,
261+
&dst_ip,
262+
);
263+
udp_packet.set_checksum(checksum);
264+
265+
// Create a new IPv4 packet
266+
let mut ip_buffer = vec![
267+
0u8;
268+
MutableIpv4Packet::minimum_packet_size() + udp_packet.packet().len()
269+
];
270+
let mut ip_packet = MutableIpv4Packet::new(&mut ip_buffer).ok_or_else(|| {
271+
PyErr::new::<pyo3::exceptions::PyRuntimeError, _>("Failed to create IPv4 packet")
272+
})?;
273+
274+
ip_packet.set_version(4);
275+
ip_packet.set_header_length(5);
276+
ip_packet.set_total_length(
277+
(MutableIpv4Packet::minimum_packet_size() + udp_packet.packet().len()) as u16,
278+
);
279+
ip_packet.set_ttl(64);
280+
ip_packet.set_next_level_protocol(IpNextHeaderProtocols::Udp);
281+
ip_packet.set_source(src_ip);
282+
ip_packet.set_destination(dst_ip);
283+
ip_packet.set_payload(udp_packet.packet());
284+
285+
// Calculate IPv4 checksum
286+
let checksum = pnet::packet::ipv4::checksum(&ip_packet.to_immutable());
287+
ip_packet.set_checksum(checksum);
288+
289+
// Create a new Ethernet packet
290+
let mut ethernet_buffer = vec![
291+
0u8;
292+
MutableEthernetPacket::minimum_packet_size() + ip_packet.packet().len()
293+
];
294+
let mut ethernet_packet =
295+
MutableEthernetPacket::new(&mut ethernet_buffer).ok_or_else(|| {
296+
PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(
297+
"Failed to create Ethernet packet",
298+
)
299+
})?;
300+
301+
ethernet_packet.set_destination(dst_mac);
302+
ethernet_packet.set_source(src_mac);
303+
ethernet_packet.set_ethertype(pnet::packet::ethernet::EtherTypes::Ipv4);
304+
ethernet_packet.set_payload(ip_packet.packet());
305+
205306
// Create a channel to send on
206-
let (mut tx, _) = match datalink::channel(&interface, Default::default()) {
207-
Ok(Ethernet(tx, _rx)) => (tx, _rx),
307+
let mut tx = match datalink::channel(&interface, Default::default()) {
308+
Ok(Ethernet(tx, _rx)) => tx,
208309
Ok(_) => {
209310
return Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(
210311
"Unhandled channel type",
@@ -218,35 +319,17 @@ impl DataLinkInterface {
218319
}
219320
};
220321

221-
let packet_bytes = packet;
222-
223-
let packet_size = packet_bytes.len();
224-
225-
let num_packets = num_packets.unwrap_or(1);
226-
let num_packets = if num_packets < 1 { 1 } else { num_packets };
227-
228-
for _ in 0..num_packets {
229-
let send_result = tx.build_and_send(1, packet_size, &mut |new_packet: &mut [u8]| {
230-
new_packet.copy_from_slice(packet_bytes);
231-
});
232-
233-
match send_result {
234-
Some(Ok(())) => {
235-
// Packet sent successfully
236-
}
237-
Some(Err(e)) => {
238-
return Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(format!(
239-
"Failed to send packet: {}",
240-
e
241-
)));
242-
}
243-
None => {
244-
return Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(
245-
"DataLinkSender has been closed",
246-
));
247-
}
248-
}
249-
}
322+
// Send the packet
323+
tx.send_to(ethernet_packet.packet(), None)
324+
.ok_or_else(|| {
325+
PyErr::new::<pyo3::exceptions::PyRuntimeError, _>("Failed to send packet")
326+
})?
327+
.map_err(|e| {
328+
PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(format!(
329+
"Failed to send packet: {}",
330+
e
331+
))
332+
})?;
250333

251334
Ok(())
252335
}

test.py

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,28 +13,64 @@
1313

1414
from py_pnet import DataLinkInterface, list_interfaces
1515

16-
17-
def main():
16+
def return_valid_interfaces():
1817
# List available interfaces
1918
interfaces = list_interfaces()
2019
print("Available interfaces:")
2120
for iface in interfaces:
2221
#print("test")
2322
print(f"- {iface}")
2423

25-
# Replace with your interface name
24+
def udp_transmit():
25+
26+
interface_name = "en0" # Update this to match your interface
27+
interface = DataLinkInterface(interface_name)
28+
29+
payload = b'Hello, World?'
30+
31+
# Specify source and destination MAC addresses
32+
src_mac = '00:11:22:33:44:55'
33+
dst_mac = '66:77:88:99:AA:BB'
34+
35+
# Specify source and destination IPs and ports
36+
src_ip = '192.168.1.2'
37+
src_port = 12345
38+
dst_ip = '192.168.1.3'
39+
dst_port = 54321
40+
41+
# Transmit the packet
42+
interface.transmit_packet(
43+
payload,
44+
src_mac,
45+
src_ip,
46+
src_port,
47+
dst_mac,
48+
dst_ip,
49+
dst_port
50+
)
51+
52+
def udp_receive():
53+
# Replace with your interface name
2654
interface_name = "en0" # Update this to match your interface
2755

2856
sniffer = DataLinkInterface(interface_name)
2957

30-
# Capture 5 packets without any filters
58+
# Capture 100 packets without any filters
3159
print("\nCapturing 100 packets without filters:")
3260
packets = sniffer.capture_packets(100)
33-
for pkt in packets:
34-
print(pkt['payload'])
61+
for pkt in packets: #Only prints first 8 bytes of payload
62+
print(f"src_mac: {pkt['src_mac']} | dst_mac {pkt['dst_mac']} | ethertype {pkt['ethertype']} | src_ip {pkt['src_ip']} | dst_ip {pkt['dst_ip']} | payload {pkt['payload'][0:8]}")
63+
64+
def main():
65+
66+
#To List Network Interfaces:
67+
#return_valid_interfaces()
68+
69+
#To Receive:
70+
udp_receive()
3571

3672
#To Transmit:
37-
#sniffer.transmit_packet(packet_data, num_packets=5)
73+
#udp_transmit()
3874

3975
if __name__ == "__main__":
4076
main()

0 commit comments

Comments
 (0)