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}