The pnet_macros crate provides the #[packet]
macro and compiler plugin, which is used to
specify the format of on-the-wire packets, and automatically generate zero-copy accessors and
mutators for the fields. It is used as follows:
/// Import the `Packet` custom derive attribute
use Packet;
/// This module contains a list of type aliases which may be used
use ;
/// Packets are specified in the same way as normal Rust structs, but with a `#[packet]`
/// attribute.
A number of things will then be generated. You can see this in action in the documentation and
source of each of the packet types in the pnet::packet
module. Things generated include
(assuming the Example
struct from above):
- An
ExamplePacket<'p>
structure, which is used for receiving packets on the network. This structure contains:
- A method,
pub fn new<'p>(packet: &'p [u8]) -> ExamplePacket<'p>
, used for the construction of anExamplePacket
, given a buffer to store it. The buffer should be long enough to contain all the fields in the packet. - A method,
pub fn to_immutable<'p>(&'p self) -> ExamplePacket<'p>
, which is simply an identity function. It exists for consistency withMutableExamplePacket
. - A number of accessor methods, of the form
pub get_{field_name}(&self) -> {field_type}
, which will retrieve the host representation of the on-the-wire value.
- A
MutableExamplePacket<'p>
structure, which is used when sending packets on the network. This structure contains:
- A method,
pub fn new<'p>(packet: &'p mut [u8]) -> MutableExamplePacket<'p>
, used for the construction of aMutableExamplePacket
, given a buffer to store it. The buffer should be long enough to contain all the fields in the packet. - A method,
pub fn to_immutable<'p>(&'p self) -> ExamplePacket<'p>
, which converts from aMutableExamplePacket
to anExamplePacket
- A method,
pub fn populate(&mut self, packet: Example)
, which, given anExample
struct, will populate theMutableExamplePacket
with the values from theExample
struct. - A number of accessor methods, of the form
pub get_{field_name}(&self) -> {field_type}
, which will retrieve the host representation of the on-the-wire value. - A number of mutator methods, of the form
pub set_{field_name}(&mut self, val: {field_type})
, which will take a host value, convert it to the required on-the-wire format, and store it in the buffer which backs theMutableExamplePacket
.
- A number of trait implementations for each of the
MutableExamplePacket
andExamplePacket
structures. These include:
pnet::packet::Packet
(ExamplePacket
andMutableExamplePacket
)pnet::packet::MutablePacket
(MutableExamplePacket
only)core::fmt::Debug
(ExamplePacket
andMutableExamplePacket
)pnet::packet::FromPacket
(ExamplePacket
andMutableExamplePacket
)pnet::packet::PacketSize
(ExamplePacket
andMutableExamplePacket
)
- An
ExampleIterator
structure, which implementscore::iter::Iterator
, to allow iterating over vectors ofExamplePacket
contained within another packet. Used internally.
Attributes
There are a number of attributes which fields may have, these include:
- #[length_fn = "function_name"]
This attribute is used to enable variable length fields. To specify a variable length field,
it should have the type Vec<T>
. It must have the #[length_fn]
(or #[length]) attribute,
which specifies a function name to calculate the length of the field. The signature for the
length function should be
fn {function_name}<'a>(example_packet: &ExamplePacket<'a>) -> usize
, substituting
&ExamplePacket<'a>
for the appropriately named packet type for your structure. You may
access whichever fields are required to calculate the length of the field. The returned
value should be a number of bytes that the field uses.
The type contained in the vector may either be one of the primitive types specified in
pnet_macros::types
, or another structure marked with #[derive(Packet)], for example
Vec<Example>
.
- #[length = "arithmetic expression"]
This attribute is used to enable variable length fields. To specify a variable length field,
it should have the type Vec<T>
. It must have the #[length]
(or #[length_fn]) attribute,
which specifies an arithmetic expression to calculate the length of the field. Only field
names, constants, integers, basic arithmetic expressions (+ - * / %) and parentheses are
in the expression. An example would be #[length = "field_name + CONSTANT - 4]
.
The type contained in the vector may either be one of the primitive types specified in
pnet_macros::types
, or another structure marked with #[derive(Packet)], for example
Vec<Example>
.
- #[payload]
This attribute specifies the payload associated with the packet. This should specify the data associated with the packet. It may be used in two places:
- The last field in the packet, in which case it is assumed to use the remaining length of the buffer containing the packet
- Another location in the packet, in which case the
#[length_fn]
attribute must also be specified to give the length of the payload. If the packet has no payload, you must still specify this attribute, but you can provide a#[length_fn]
attribute returning zero.
- #[construct_with(, ...)]
Unfortunately, compiler plugins do not currently have access to type information during the
decoration stage (where all of the above is generated), so this attribute is required. This
must be used for all fields which are neither primitive types, nor vectors of primitive
types. Three things are required when using #[construct_with]
:
- The field type must have a method
new
, which takes one or more parameters of primitive types. - The field must be annotated with the
#[construct_with(...)]
attribute, specifying a list of types identical to those taken by thenew
method. - The
pnet::packet::ToPrimitiveValues
trait must be implemented for the field type, which must return a tuple of the primitive types specified in the parameters to the#[construct_with(...)]
attribute, and in thenew
method.