-
Notifications
You must be signed in to change notification settings - Fork 12
/
ArchitectureNotes
99 lines (75 loc) · 5.33 KB
/
ArchitectureNotes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
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.