libpacket_derive/lib.rs
1// Copyright (c) 2015 Robert Clipsham <robert@octarineparrot.com>
2// Copyright (c) 2021 Pierre Chifflier <chifflier@wzdftpd.net>
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10//! The pnet_macros crate provides the `#[packet]` macro and compiler plugin, which is used to
11//! specify the format of on-the-wire packets, and automatically generate zero-copy accessors and
12//! mutators for the fields. It is used as follows:
13//!
14//! ```
15//! /// Import the `Packet` custom derive attribute
16//! use libpacket_derive::Packet;
17//! /// This module contains a list of type aliases which may be used
18//! use libpacket_core::types::{u4, u12be};
19//!
20//! /// Packets are specified in the same way as normal Rust structs, but with a `#[packet]`
21//! /// attribute.
22//! #[derive(Packet)]
23//! pub struct Example {
24//! // This is a simple field which contains a 4-bit, unsigned integer.
25//! // Note that `u4` is simply an alias for `u8` - the name is a hint
26//! // to the compiler plugin, it is NOT a usable 4 bit type!
27//! simple_field1: u4,
28//!
29//! // This specifies that `simple_field2` should be a 12-bit field,
30//! // with bits stored in big endian
31//! simple_field2: u12be,
32//!
33//! // All packets must specify a `#[payload]`, which should be a
34//! // `Vec<u8>`. This represents the packet's payload, for example in
35//! // an IPv4 packet, the payload could be a UDP packet, or in a UDP
36//! // packet the payload could be the application data. All the
37//! // remaining space in the packet is considered to be the payload
38//! // (this doesn't have to be the case, see the documentation for
39//! // `#[payload]` below.
40//! #[payload]
41//! payload: Vec<u8>
42//! }
43//! ```
44//! A number of things will then be generated. You can see this in action in the documentation and
45//! source of each of the packet types in the `pnet::packet` module. Things generated include
46//! (assuming the `Example` struct from above):
47//!
48//! * An `ExamplePacket<'p>` structure, which is used for receiving packets on the network.
49//! This structure contains:
50//! - A method, `pub fn new<'p>(packet: &'p [u8]) -> ExamplePacket<'p>`, used for the
51//! construction of an `ExamplePacket`, given a buffer to store it. The buffer should be
52//! long enough to contain all the fields in the packet.
53//! - A method, `pub fn to_immutable<'p>(&'p self) -> ExamplePacket<'p>`, which is simply an
54//! identity function. It exists for consistency with `MutableExamplePacket`.
55//! - A number of accessor methods, of the form `pub get_{field_name}(&self) -> {field_type}`,
56//! which will retrieve the host representation of the on-the-wire value.
57//! * A `MutableExamplePacket<'p>` structure, which is used when sending packets on the network.
58//! This structure contains:
59//! - A method, `pub fn new<'p>(packet: &'p mut [u8]) -> MutableExamplePacket<'p>`, used for
60//! the construction of a `MutableExamplePacket`, given a buffer to store it. The buffer
61//! should be long enough to contain all the fields in the packet.
62//! - A method, `pub fn to_immutable<'p>(&'p self) -> ExamplePacket<'p>`, which converts from
63//! a `MutableExamplePacket` to an `ExamplePacket`
64//! - A method, `pub fn populate(&mut self, packet: Example)`, which, given an `Example`
65//! struct, will populate the `MutableExamplePacket` with the values from the `Example`
66//! struct.
67//! - A number of accessor methods, of the form `pub get_{field_name}(&self) -> {field_type}`,
68//! which will retrieve the host representation of the on-the-wire value.
69//! - A number of mutator methods, of the form `pub set_{field_name}(&mut self,
70//! val: {field_type})`, which will take a host value, convert it to the required
71//! on-the-wire format, and store it in the buffer which backs the `MutableExamplePacket`.
72//! * A number of trait implementations for each of the `MutableExamplePacket` and `ExamplePacket`
73//! structures. These include:
74//! - `pnet::packet::Packet` (`ExamplePacket` and `MutableExamplePacket`)
75//! - `pnet::packet::MutablePacket` (`MutableExamplePacket` only)
76//! - `std::fmt::Debug` (`ExamplePacket` and `MutableExamplePacket`)
77//! - `pnet::packet::FromPacket` (`ExamplePacket` and `MutableExamplePacket`)
78//! - `pnet::packet::PacketSize` (`ExamplePacket` and `MutableExamplePacket`)
79//! * An `ExampleIterator` structure, which implements `std::iter::Iterator`, to allow iterating
80//! over vectors of `ExamplePacket` contained within another packet. Used internally.
81//!
82//! ## Attributes
83//!
84//! There are a number of attributes which fields may have, these include:
85//!
86//! * \#[length_fn = "function_name"]
87//!
88//! This attribute is used to enable variable length fields. To specify a variable length field,
89//! it should have the type `Vec<T>`. It must have the `#[length_fn]` (or #[length]) attribute,
90//! which specifies a function name to calculate the length of the field. The signature for the
91//! length function should be
92//! `fn {function_name}<'a>(example_packet: &ExamplePacket<'a>) -> usize`, substituting
93//! `&ExamplePacket<'a>` for the appropriately named packet type for your structure. You may
94//! access whichever fields are required to calculate the length of the field. The returned
95//! value should be a number of bytes that the field uses.
96//!
97//! The type contained in the vector may either be one of the primitive types specified in
98//! `pnet_macros::types`, or another structure marked with #[derive(Packet)], for example
99//! `Vec<Example>`.
100//!
101//! * \#[length = "arithmetic expression"]
102//!
103//! This attribute is used to enable variable length fields. To specify a variable length field,
104//! it should have the type `Vec<T>`. It must have the `#[length]` (or #[length_fn]) attribute,
105//! which specifies an arithmetic expression to calculate the length of the field. Only field
106//! names, constants, integers, basic arithmetic expressions (+ - * / %) and parentheses are
107//! in the expression. An example would be `#[length = "field_name + CONSTANT - 4]`.
108//!
109//! The type contained in the vector may either be one of the primitive types specified in
110//! `pnet_macros::types`, or another structure marked with #[derive(Packet)], for example
111//! `Vec<Example>`.
112//!
113//! * \#[payload]
114//!
115//! This attribute specifies the payload associated with the packet. This should specify the
116//! data associated with the packet. It may be used in two places:
117//! - The last field in the packet, in which case it is assumed to use the remaining length of
118//! the buffer containing the packet
119//! - Another location in the packet, in which case the `#[length_fn]` attribute must also be
120//! specified to give the length of the payload.
121//! If the packet has no payload, you must still specify this attribute, but you can provide a
122//! `#[length_fn]` attribute returning zero.
123//!
124//! * \#[construct_with(<primitive type>, ...)]
125//!
126//! Unfortunately, compiler plugins do not currently have access to type information during the
127//! decoration stage (where all of the above is generated), so this attribute is required. This
128//! must be used for all fields which are neither primitive types, nor vectors of primitive
129//! types. Three things are required when using `#[construct_with]`:
130//! - The field type must have a method `new`, which takes one or more parameters of primitive
131//! types.
132//! - The field must be annotated with the `#[construct_with(...)]` attribute, specifying a
133//! list of types identical to those taken by the `new` method.
134//! - The `pnet::packet::ToPrimitiveValues` trait must be implemented for the field type,
135//! which must return a tuple of the primitive types specified in the parameters to the
136//! `#[construct_with(...)]` attribute, and in the `new` method.
137
138#![deny(missing_docs)]
139
140use proc_macro::TokenStream;
141use quote::quote;
142use syn::{parse_macro_input, DeriveInput, Error, Visibility};
143
144mod gen;
145mod parse;
146mod util;
147
148/// The entry point for the `derive(Packet)` custom derive
149#[proc_macro_derive(Packet, attributes(construct_with, length, payload))]
150pub fn derive_packet(input: TokenStream) -> TokenStream {
151 let ast = parse_macro_input!(input as DeriveInput);
152 // ensure struct is public
153 match ast.vis {
154 Visibility::Public(_) => (),
155 _ => {
156 let ts = syn::Error::new(ast.ident.span(), "#[packet] structs must be public")
157 .to_compile_error();
158 return ts.into();
159 }
160 }
161 let name = &ast.ident;
162 let s = match &ast.data {
163 syn::Data::Struct(ref s) => generate_packet(s, name.to_string()),
164 _ => panic!("Only structs are supported"),
165 };
166 match s {
167 Ok(ts) => ts.into(),
168 Err(e) => e.to_compile_error().into(),
169 }
170}
171
172fn generate_packet(s: &syn::DataStruct, name: String) -> Result<proc_macro2::TokenStream, Error> {
173 let packet = parse::packet(s, name)?;
174 let structs = gen::packet_struct(&packet);
175 let (ts_packet_impls, payload_bounds, packet_size) = gen::packet_impls(&packet)?;
176 let ts_trait_impls = gen::packet_trait_impls(&packet, &payload_bounds)?;
177 let ts_size_impls = gen::packet_size_impls(&packet, &packet_size)?;
178 let ts_iterables = gen::iterables(&packet)?;
179 let ts_converters = gen::converters(&packet)?;
180 let ts_debug_impls = gen::debug_impls(&packet)?;
181 let tts = quote! {
182 #structs
183 #ts_packet_impls
184 #ts_trait_impls
185 #ts_size_impls
186 #ts_iterables
187 #ts_converters
188 #ts_debug_impls
189 };
190 Ok(tts)
191}