Branch data Line data Source code
1 : : // $Id: Frag.cc 6219 2008-10-01 05:39:07Z vern $
2 : : //
3 : : // See the file "COPYING" in the main distribution directory for copyright.
4 : :
5 : : #include "config.h"
6 : :
7 : : #include "util.h"
8 : : #include "Hash.h"
9 : : #include "Frag.h"
10 : : #include "NetVar.h"
11 : : #include "Sessions.h"
12 : :
13 : : #define MIN_ACCEPTABLE_FRAG_SIZE 64
14 : : #define MAX_ACCEPTABLE_FRAG_SIZE 64000
15 : :
16 : 0 : FragTimer::~FragTimer()
17 : : {
18 [ # # ][ # # ]: 0 : if ( f )
[ # # ]
19 : 0 : f->ClearTimer();
20 [ # # ][ # # ]: 0 : }
[ # # ]
21 : :
22 : 0 : void FragTimer::Dispatch(double t, int /* is_expire */)
23 : : {
24 [ # # ]: 0 : if ( f )
25 : 0 : f->Expire(t);
26 : : else
27 : 0 : internal_error("fragment timer dispatched w/o reassembler");
28 : 0 : }
29 : :
30 : : FragReassembler::FragReassembler(NetSessions* arg_s,
31 : : const IP_Hdr* ip, const u_char* pkt,
32 : 0 : uint32 frag_field, HashKey* k, double t)
33 : 0 : : Reassembler(0, ip->DstAddr(), REASSEM_IP)
34 : : {
35 : 0 : s = arg_s;
36 : 0 : key = k;
37 : 0 : const struct ip* ip4 = ip->IP4_Hdr();
38 : 0 : proto_hdr_len = ip4->ip_hl * 4;
39 : 0 : proto_hdr = (struct ip*) new u_char[64]; // max IP header + slop
40 : : // Don't do a structure copy - need to pick up options, too.
41 : 0 : memcpy((void*) proto_hdr, (const void*) ip4, proto_hdr_len);
42 : :
43 : 0 : reassembled_pkt = 0;
44 : 0 : frag_size = 0; // flag meaning "not known"
45 : :
46 : 0 : AddFragment(t, ip, pkt, frag_field);
47 : :
48 [ # # # # ]: 0 : if ( frag_timeout != 0.0 )
49 : : {
50 : 0 : expire_timer = new FragTimer(this, t + frag_timeout);
51 : 0 : timer_mgr->Add(expire_timer);
52 : : }
53 : : else
54 : 0 : expire_timer = 0;
55 : 0 : }
56 : :
57 : 0 : FragReassembler::~FragReassembler()
58 : : {
59 : 0 : DeleteTimer();
60 [ # # # # ]: 0 : delete [] proto_hdr;
[ # # ]
61 [ # # ][ # # ]: 0 : delete reassembled_pkt;
[ # # ]
62 [ # # ][ # # ]: 0 : delete key;
[ # # ]
63 [ # # ][ # # ]: 0 : }
[ # # ]
64 : :
65 : : void FragReassembler::AddFragment(double t, const IP_Hdr* ip, const u_char* pkt,
66 : 0 : uint32 frag_field)
67 : : {
68 : 0 : const struct ip* ip4 = ip->IP4_Hdr();
69 : :
70 [ # # # # ]: 0 : if ( ip4->ip_p != proto_hdr->ip_p || ip4->ip_hl != proto_hdr->ip_hl )
71 : : // || ip4->ip_tos != proto_hdr->ip_tos
72 : : // don't check TOS, there's at least one stack that actually
73 : : // uses different values, and it's hard to see an associated
74 : : // attack.
75 : 0 : s->Weird("fragment_protocol_inconsistency", ip);
76 : :
77 [ # # ]: 0 : if ( frag_field & 0x4000 )
78 : : // Linux MTU discovery for UDP can do this, for example.
79 : 0 : s->Weird("fragment_with_DF", ip);
80 : :
81 : 0 : int offset = (ntohs(ip4->ip_off) & 0x1fff) * 8;
82 : 0 : int len = ntohs(ip4->ip_len);
83 : 0 : int hdr_len = proto_hdr->ip_hl * 4;
84 : 0 : int upper_seq = offset + len - hdr_len;
85 : :
86 [ # # ]: 0 : if ( (frag_field & 0x2000) == 0 )
87 : : {
88 : : // Last fragment.
89 [ # # ]: 0 : if ( frag_size == 0 )
90 : 0 : frag_size = upper_seq;
91 : :
92 [ # # ]: 0 : else if ( upper_seq != frag_size )
93 : : {
94 : 0 : s->Weird("fragment_size_inconsistency", ip);
95 : :
96 [ # # ]: 0 : if ( upper_seq > frag_size )
97 : 0 : frag_size = upper_seq;
98 : : }
99 : : }
100 : :
101 [ # # ]: 0 : else if ( len < MIN_ACCEPTABLE_FRAG_SIZE )
102 : 0 : s->Weird("excessively_small_fragment", ip);
103 : :
104 [ # # ]: 0 : if ( upper_seq > MAX_ACCEPTABLE_FRAG_SIZE )
105 : 0 : s->Weird("excessively_large_fragment", ip);
106 : :
107 [ # # ][ # # ]: 0 : if ( frag_size && upper_seq > frag_size )
108 : : {
109 : : // This can happen if we receive a fragment that's *not*
110 : : // the last fragment, but still imputes a size that's
111 : : // larger than the size we derived from a previously-seen
112 : : // "last fragment".
113 : :
114 : 0 : s->Weird("fragment_size_inconsistency", ip);
115 : 0 : frag_size = upper_seq;
116 : : }
117 : :
118 : : // Do we need to check for consistent options? That's tricky
119 : : // for things like LSRR that get modified in route.
120 : :
121 : : // Remove header.
122 : 0 : pkt += hdr_len;
123 : 0 : len -= hdr_len;
124 : :
125 : 0 : NewBlock(network_time, offset, len, pkt);
126 : 0 : }
127 : :
128 : 0 : void FragReassembler::Overlap(const u_char* b1, const u_char* b2, int n)
129 : : {
130 : 0 : IP_Hdr proto_h((const struct ip*) proto_hdr);
131 : :
132 [ # # ]: 0 : if ( memcmp((const void*) b1, (const void*) b2, n) )
133 : 0 : s->Weird("fragment_inconsistency", &proto_h);
134 : : else
135 : 0 : s->Weird("fragment_overlap", &proto_h);
136 : 0 : }
137 : :
138 : 0 : void FragReassembler::BlockInserted(DataBlock* /* start_block */)
139 : : {
140 [ # # ][ # # ]: 0 : if ( blocks->seq > 0 || ! frag_size )
141 : : // For sure don't have it all yet.
142 : 0 : return;
143 : :
144 : : // We might have it all - look for contiguous all the way.
145 : : DataBlock* b;
146 [ # # ]: 0 : for ( b = blocks; b->next; b = b->next )
147 [ # # ]: 0 : if ( b->upper != b->next->seq )
148 : 0 : break;
149 : :
150 [ # # ]: 0 : if ( b->next )
151 : : {
152 : : // We have a hole.
153 [ # # ]: 0 : if ( b->upper >= frag_size )
154 : : {
155 : : // We're stuck. The point where we stopped is
156 : : // contiguous up through the expected end of
157 : : // the fragment, but there's more stuff still
158 : : // beyond it, which is not contiguous. This
159 : : // can happen for benign reasons when we're
160 : : // intermingling parts of two fragmented packets.
161 : :
162 : 0 : IP_Hdr proto_h((const struct ip*) proto_hdr);
163 : 0 : s->Weird("fragment_size_inconsistency", &proto_h);
164 : :
165 : : // We decide to analyze the contiguous portion now.
166 : : // Extend the fragment up through the end of what
167 : : // we have.
168 : 0 : frag_size = b->upper;
169 : : }
170 : : else
171 : 0 : return;
172 : : }
173 : :
174 [ # # ]: 0 : else if ( last_block->upper > frag_size )
175 : : {
176 : 0 : IP_Hdr proto_h((const struct ip*) proto_hdr);
177 : 0 : s->Weird("fragment_size_inconsistency", &proto_h);
178 : 0 : frag_size = last_block->upper;
179 : : }
180 : :
181 [ # # ]: 0 : else if ( last_block->upper < frag_size )
182 : : // Missing the tail.
183 : 0 : return;
184 : :
185 : : // We have it all. Compute the expected size of the fragment.
186 : 0 : int n = proto_hdr_len + frag_size;
187 : :
188 : : // It's possible that we have blocks associated with this fragment
189 : : // that exceed this size, if we saw MF fragments (which don't lead
190 : : // to us setting frag_size) that went beyond the size indicated by
191 : : // the final, non-MF fragment. This can happen for benign reasons
192 : : // due to intermingling of fragments from an older datagram with those
193 : : // for a more recent one.
194 : :
195 : 0 : u_char* pkt = new u_char[n];
196 : 0 : memcpy((void*) pkt, (const void*) proto_hdr, proto_hdr_len);
197 : :
198 : 0 : struct ip* reassem4 = (struct ip*) pkt;
199 : 0 : reassem4->ip_len = htons(frag_size + proto_hdr_len);
200 : :
201 : 0 : pkt += proto_hdr_len;
202 : :
203 [ # # ]: 0 : for ( b = blocks; b; b = b->next )
204 : : {
205 : : // If we're above a hole, stop. This can happen because
206 : : // the logic above regarding a hole that's above the
207 : : // expected fragment size.
208 [ # # ][ # # ]: 0 : if ( b->prev && b->prev->upper < b->seq )
209 : 0 : break;
210 : :
211 [ # # ]: 0 : if ( b->upper > n )
212 : 0 : internal_error("bad fragment reassembly");
213 : :
214 : : memcpy((void*) &pkt[b->seq], (const void*) b->block,
215 : 0 : b->upper - b->seq);
216 : : }
217 : :
218 [ # # ]: 0 : delete reassembled_pkt;
219 : 0 : reassembled_pkt = new IP_Hdr(reassem4);
220 : :
221 : 0 : DeleteTimer();
222 : : }
223 : :
224 : 0 : void FragReassembler::Expire(double t)
225 : : {
226 [ # # ]: 0 : while ( blocks )
227 : : {
228 : 0 : DataBlock* b = blocks->next;
229 [ # # ]: 0 : delete blocks;
230 : 0 : blocks = b;
231 : : }
232 : :
233 : 0 : expire_timer->ClearReassembler();
234 : 0 : expire_timer = 0; // timer manager will delete it
235 : :
236 : 0 : sessions->Remove(this);
237 : 0 : }
238 : :
239 : 0 : void FragReassembler::DeleteTimer()
240 : : {
241 [ # # ]: 0 : if ( expire_timer )
242 : : {
243 : 0 : expire_timer->ClearReassembler();
244 : 0 : timer_mgr->Cancel(expire_timer);
245 : 0 : expire_timer = 0; // timer manager will delete it
246 : : }
247 [ + - ][ + - ]: 6 : }
|