Created
December 1, 2022 04:38
-
-
Save dylnuge/c41b76bacc8ce5338ebfad2b2fb48b39 to your computer and use it in GitHub Desktop.
Ethernet Frame Header Deserialization, as written by OpenAI's chat
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use byteorder::{BigEndian, ReadBytesExt}; | |
use std::io::{Cursor, Read}; | |
// This code was mostly authored by https://chat.openai.com/chat | |
// I asked the following questions to get the code: | |
// 1. "Do you know what an ethernet frame header is?" | |
// 2. "Excellent! Can you write me a data structure in Rust which represents an | |
// ethernet frame header?" | |
// 3. "This looks great! Can you write me a function in Rust that takes an | |
// arbitrary stream of bytes and deserializes it into your | |
// EthernetFrameHeader struct?" | |
// For the most part I copied the code provided as written, and made no | |
// adjustments. None of the code included imports, so I added those myself. | |
// The included coded also didn't include a println to test the struct results | |
// or a Debug derive on the struct, but the test cases themselves *are* from | |
// the AI. | |
// There are a couple issues with this code, but it's generally correct. The | |
// issues I see, from major to minor, are: | |
// 1. No CRC handling; the frame check is here included in the payload, which | |
// is incorrect. | |
// 2. No bounds check on the deserialize, so bad data will panic | |
// 3. No handling for VLAN tagging (802.1Q) | |
// 4. Technically it didn't ask if I wanted to deal with the data link or | |
// line data; the latter has a preamble and interpacket gap. But I didn't | |
// specify and data link layer would be the normal assumption here. | |
// 5. The compiler warnings on `let mut header` and the struct not having pub | |
// fields (or accessor methods) are, I think, not fair to dock points on. | |
// The former was example code and `let mut` was fine, and for the latter | |
// I asked about the data structure only. | |
// All in all I'm decently happy with this version of the code. I tried earlier | |
// on the same model and got different results: | |
// 1. The struct included FCS (good!) and padding (as a [u8; 12]...this is | |
// wrong on two levels—first, minimum transmission is 64 bytes, so there can | |
// be up to 46 bytes of padding, and second, the padding doesn't need to be | |
// stored). | |
// 2. The code had a bounds check, though the bounds check was len < 14, and | |
// the code read from bytes beyond that, because... | |
// 3. ...the code read bytes 15-18 as the FCS (wrong, these are after the | |
// payload) and everything after 18 as padding (very wrong, especially | |
// since it was stuffing it into a 12 byte array). | |
// 4. The code handled endianness differently; instead of using Cursor, it just | |
// directly pulled bytes, bitshifted them, and ORed them. | |
// 5. The deserialize returned a Result<EthernetFrameHeader, &'static str>, with | |
// the only error coming from the aforementioned bounds check. | |
#[derive(Debug)] | |
struct EthernetFrameHeader { | |
destination_mac: [u8; 6], | |
source_mac: [u8; 6], | |
ethertype: u16, | |
payload: Vec<u8>, | |
} | |
fn deserialize_ethernet_frame_header(data: &[u8]) -> EthernetFrameHeader { | |
let mut cursor = Cursor::new(data); | |
let mut destination_mac = [0; 6]; | |
cursor.read_exact(&mut destination_mac).unwrap(); | |
let mut source_mac = [0; 6]; | |
cursor.read_exact(&mut source_mac).unwrap(); | |
let ethertype = cursor.read_u16::<BigEndian>().unwrap(); | |
let mut payload = Vec::new(); | |
cursor.read_to_end(&mut payload).unwrap(); | |
EthernetFrameHeader { | |
destination_mac, | |
source_mac, | |
ethertype, | |
payload, | |
} | |
} | |
fn main() { | |
let mut header = EthernetFrameHeader { | |
destination_mac: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], | |
source_mac: [0x00, 0x11, 0x22, 0x33, 0x44, 0x55], | |
ethertype: 0x0800, | |
payload: Vec::new(), | |
}; | |
println!("header: {:?}", header); | |
let data = [ | |
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // destination MAC | |
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, // source MAC | |
0x08, 0x00, // ethertype | |
// payload | |
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, | |
]; | |
let header2 = deserialize_ethernet_frame_header(&data); | |
println!("header2: {:?}", header2); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment