Different Packet handles may really internally share one Packet data structure if they both point to the same packet.
The packet library provides a consistent container interface for this representation.
Packet p = ...; // Change first byte of packet to 1 p.data()[0] = 1u; // Copy packet data into a vector std::vector<char> data (p.data().size()); std::copy(p.data().begin(), p.data().end(), data.begin());
This type of access is primarily needed when reading or writing packets (e.g. to/from the network).
Consider an Ethernet Packet with an IP payload holding a UDP packet. We may reference either the Ethernet packet as a whole or we may reference the IP or UDP interpreters (sub-packets or headers). All handles really refer to the same data structure but provide access to a different (sub-)range of the data in the packet.
We can navigate around this chained structure using appropriate members:
// eth, ip and udp all reference the same internal packet data albeit at different data ranges Packet eth = ...; Packet ip = eth.next(); Packet udp = ip.next(); eth.next() == ip // true eth.next().is<IPv4Packet>() // true eth.next().next() == udp // true eth.next().is<UDPPacket>() // false eth.find<UDPPacket>() == udp // true udp.find<EthernetPacket>() // throws InvalidPacketChainException udp.find<EthernetPacket>(senf::nothrow) // An in-valid() senf::Packet which tests as 'false' udp.find<UDPPacket> == udp // true udp.first<IPv4Packet>() // throws InvalidPacketChainException udp.prev() == ip // true udp.prev<EthernetPacket>() // throws InvalidPacketChainException
To access this information, we need to use a protocol specific handle, the senf::ConcretePacket which takes as a template argument the specific type of packet to be interpreted. This allows us to easily interpret or create packets. Here an example on how to create a new Ethernet / IP / UDP / Payload packet interpreter chain:
// EthernetPacket, IPv4Packet, UDPPacket and DataPacket are typedefs for corresponding // ConcretePacket instantiations senf::EthernetPacket eth (senf::EthernetPacket::create()); senf::IPv4Packet ip (senf::IPv4Packet ::createAfter(eth)); senf::UDPPacket udp (senf::UDPPacket ::createAfter(ip)); senf::DataPacket payload (senf::DataPacket ::createAfter(udp, std::string("Hello, world!"))); udp->source() = 2000u; udp->destination() = 2001u; ip->ttl() = 255u; ip->source() = senf::INet4Address::from_string("192.168.0.1"); ip->destination() = senf::INet4Address::from_string("192.168.0.2"); eth->source() = senf::MACAddress::from_string("00:11:22:33:44:55"); eth->destination() = senf::MACAddress::from_string("00:11:22:33:44:66"); eth.finalizeAll();
Again, realize, that eth, ip, udp and payload share the same internal packet data structure (the respective data()
members all provide access to the same underlying container however at different byte ranges): The complete packet can be accessed at eth.data()
whereas payload.data()
only holds UDP payload (in this case the string "Hello, world!").