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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
===============================================================================
$Id: DESIGN_NOTES,v 1.3 2004/01/17 07:51:19 mike Exp $
LIBNET 1.1 (c) 1998 - 2004 Mike D. Schiffman <mike@infonexus.com>
http://www.packetfactory.net/libnet
===============================================================================
DESIGN NOTES
In order to remove most of the decisions a user had to make (how much
memory to allocate for a packet, where to build the packet headers, where
to do the checksums, how to inject the packet, etc) I decided to move ALL
of that logic into the library, behind the scenes. To initialize
things and get an initial libnet context, the applications programmer
calls:
libnet_t *l;
l = libnet_init(INJECTION_TYPE, PROTOCOL, DEVICE, ERRBUFFER);
where:
INJECTION_TYPE = LIBNET_RAW4 (ipv4 raw socket)
LIBNET_RAW6 (ipv6 raw socket)
LIBNET_LINK (link-layer socket)
LIBNET_RAW4_ADV (advanced mode)
LIBNET_RAW6_ADV (advanced mode)
LIBNET_LINK_ADV (advanced mode)
PROTOCOL = IP protocol to be used for the raw socket. This is
ignored for the link-layer, and almost always
IPPROTO_RAW for ipv4.
DEVICE = The canoical name of the device, used only with the link
layer stuff. For ipv4 raw socket, you can leave this
NULL. If it's NULL with the link-layer, libnet will try
to find a suitable device.
ERRBUFFER = Until we have our libnet context l, this is where
errors will be.
Inside of this newly created context we have a ton of stuff including a
file descriptor for the packet device the injection type, the device name
(if applicable) a pointer to the libnet protocol block structure and some
other ancillary data.
Additionally, we will soon be supporting context manipulation functions
that will allow the user to set certain flags inside the context. This
interface will be akin to libnet_toggle_checksum() for those of you who
care.
When a packet is first constructed, the protocol block (pblock) stuff comes
into play. On the outside, to an applications programmer, a packet is
constructed more or less like normal (with a few notable exceptions):
libnet_ptag_t ip_tag;
ip_tag = libnet_build_ipv4(
LIBNET_UDP_H,
0,
242,
0,
64,
IPPROTO_UDP,
0, /* NEW: checksum */
src_ip,
dst_ip,
NULL,
0,
l, /* NEW: libnet context */
0 /* NEW: libnet ptag */
);
The checksum allows an applications programmer to decide if he wants to
specify his own random value (useful in NIDS fooling) or precompute the
sum elsewhere, or leave it zero and by default libnet will take care of it
(although this is over-ridable). The libnet context is the opague
pointer we allocated earlier and will show up in just about every libnet
function call from here on out. The libnet ptag is a way to reference an
ALREADY BUILT protocol block. This is necessary if you want to change
some values of a header inside of a packet injection loop.
So, when you call a build function, internally, it's a completely new
system. If the item you're constructing is NEW, a new pblock will be
allocated and linked onto the end of the list. It may be helpful to think
of this as a "protocol stack" because you MUST build your packets IN
ORDER, from the top of the protocol stack on down (i.e.: tcp -> ip ->
ethernet). Once you build a new protocol block, it's "pushed down on the
stack" and you move on to the next. However, this analogy breaks down
because you can modify any one of these items and when they're assembled
for the final packet, libnet starts at the head of the list. It may be
MORE helpful to think of the pblock chain as a doubly linked FIFO
queue, because that's what it is. :)
For example:
libnet_ptag_t 1;
libnet_ptag_t 2;
libnet_ptag_t 3;
1 = libnet_build_data(blah, l, 0);
2 = libnet_build_tcp(blah, l, 0);
3 = libnet_build_ipv4(blah, l, 0);
Will result in:
---------- ---------- ----------
l->protocol_blocks--->| data |----->| tcp |----->| ip |
| pblock |<-----| pblock |<-----| pblock |----|
--| ptag: 1| | ptag: 2| | ptag: 3| |
| ---------- ---------- ---------- v
| -----
|-------------------------------------------> ---
-
To access and change the ip header, an additional call to libnet_build_ipv4
with the ptag argument would be made:
libnet_build_ipv4(blah..., l, 3);
Note that the ptag DOES NOT CHANGE. Once a pblock is built, its tag is
set in stone.
When it comes time to write the packet to the wire,
libnet_pblock_coalesce() is called to assemble the packet fragments.
1) Gather up all of the pblock sizes in order to allocate one
contiguous block of memory.
2) Copy over the packet fragments.
3) Check each pblock to see which items need checksums, then perform
that checksum over each portion (the entire packet is needed for
some checksums).
So that's a quick description of what's going on under the hood. There's
more, but this should be enough to get you started.
EOF
|