仿真平台内核初版 -tlib库 包含<sparc arm riscv powerPC>
This commit is contained in:
99
lib/Packet.Net/ArchitectureNotes
Normal file
99
lib/Packet.Net/ArchitectureNotes
Normal file
@@ -0,0 +1,99 @@
|
||||
Packet.Net
|
||||
|
||||
The high level design goals of Packet.Net are:
|
||||
- High performance. As little memory allocation/access and cpu usage as possible
|
||||
- Simpler, modular design
|
||||
- Clear and well documented api
|
||||
|
||||
|
||||
Packet.Net is a rewrite of SharpPcap's packet parsers.
|
||||
|
||||
SharpPcap's packet parsers have a few shortcomings in their design. To understand
|
||||
what these are it is important to know more about SharpPcap's design.
|
||||
|
||||
|
||||
SharpPcap's packet parsing
|
||||
--------------------------
|
||||
SharpPcap considers all packets to be of type 'Packet'. Packet types are subclassed, so
|
||||
a UdpPacket is an IpPacket which is an EthernetPacket which is a Packet. This is useful
|
||||
because a user can have a 'UdpPacket udp' and can access the source mac address via
|
||||
'udp.SourceHwAddress', without having to jump through any hoops.
|
||||
|
||||
As packets are received they are classified into their most specific type, first by
|
||||
looking at whether they are an EthernetPacket, checking if the EthernetPacket is an IpPacket
|
||||
and then if the IpPacket is a Udp or Tcp packet. At this time the most specific packet is created
|
||||
by passing in the byte stream, like TcpPacket tcp = new TcpPacket(packetBytes);
|
||||
|
||||
SharpPcap performs lazy field parsing. Each packet field is extracted from the byte stream
|
||||
when requested. Some caching is performed in the TcpPacket class but in most cases the
|
||||
values are re-parsed by reading a number of bytes from a specific offset each time
|
||||
the class property is read.
|
||||
|
||||
|
||||
Shortcomings of SharpPcap's design
|
||||
----------------------------------
|
||||
|
||||
Creating packets from values is cumbersome. Because a TcpPacket is an IpPacket which is an
|
||||
EthernetPacket, creating a TcpPacket means we need to somehow simultaniously create the
|
||||
TcpPacket, IpPacket and EthernetPacket. The most natural way to do this was to have the
|
||||
TcpPacket constructor take in the parameters that make up a TcpPacket in addition to an IpPacket.
|
||||
The IpPacket constructor follows the same rule, it takes in its unique parameters plus an EthernetPacket.
|
||||
One user reported that this hierarchy seems confusing because the encapsulation is usually
|
||||
thought of in the reverse order where an ethernet packet contains an ip packet which contains a
|
||||
tcp packet. Another issue of a more technical aspect is the internal method
|
||||
used to actually build the TcpPacket. Because packet bytes are contiguous the TcpPacket
|
||||
constructor has to allocate enough bytes for the IpPacket plus enough bytes for itself.
|
||||
The constructor then has to set its internal buffer to point to this newly allocated buffer, copy in
|
||||
the IpPacket's bytes and then set its own values. This code is complex and can be error prone.
|
||||
The same complexity exists in almost all of the value constructors.
|
||||
|
||||
|
||||
Shortcomings in SharpPcap's implementation
|
||||
------------------------------------------
|
||||
|
||||
SharpPcap allows direct access to the header and content bytes of a packet. The Packet class,
|
||||
defined in Packet.cs, defines public accessors for retrieving the byte[] of the header and
|
||||
packet bytes, byte[] Bytes and byte[] Header. These routines do not take care to make the
|
||||
packets valid, ie. if the size of a TcpPacket changes the IpPacket length field is not
|
||||
updated to reflect the change.
|
||||
|
||||
|
||||
Design improvements over SharpPcap
|
||||
----------------------------------
|
||||
|
||||
The inheritance order should be reversed. A user refers to the payload of an EthernetPacket
|
||||
to get an IpPacket and the IpPacket payload to get the TcpPacket. Reversing the encapsulation
|
||||
better matches how packets are parsed. Reversing the encapsulation also allows for modifying
|
||||
the type of packet without conflicting with the original class type for example when
|
||||
an IpPacket's payload is changed from a TcpPacket to a UdpPacket. With SharpPcap this is
|
||||
an impossible change because the packet instance is of type TcpPacket. There is no way to
|
||||
convert it to a UdpPacket without creating a new packet that is a copy of the original but with
|
||||
a its type altered.
|
||||
|
||||
Eliminating the inheritance model of SharpPcap also makes packets easier to construct by value.
|
||||
In SharpPcap a TcpPacket required building an EthernetPacket and an IpPacket and passing
|
||||
both to a TcpPacket constructor that would discard their payloads and copy their headers
|
||||
into a byte[] that was allocated to fit the headers plus the tcp payload. In Packet.Net because
|
||||
each packet is separate a user is less confused by having to build both an IpPacket, that
|
||||
contains an EthernetPacket, and an EthernetPacket separately.
|
||||
|
||||
|
||||
Implementation improvements over SharpPcap
|
||||
------------------------------------------
|
||||
|
||||
Packet.Net's Packet has a 'byte[] Bytes' get accessor retrieves packet bytes from
|
||||
the current packet as well as all sub packets. It does so using a fast mechanism in the case
|
||||
where memory is contiguous. Packet data will be contiguous unless the packet was constructed
|
||||
by values or had its payload altered. The contiguous fast case is the typical usage case as it
|
||||
reflects the condition when a packet is captured over the line by a capture library like
|
||||
SharpPcap and parsed using Packet.Net. In the case of non-contiguous memory a fallback
|
||||
mechanism is used that is slightly slower.
|
||||
|
||||
|
||||
Things kept the same as SharpPcap
|
||||
---------------------------------
|
||||
|
||||
Packet.Net does as much lazy field evaluation as possible. Values are read from memory only when
|
||||
requested by a call to the appropriate accessor. This reduces the number of memory
|
||||
reads in most cases and performs well even in the case when a user wants to retrieve
|
||||
each of the fields of a packet.
|
||||
Reference in New Issue
Block a user