00001 //--------------------------------------------------------------------------
00002 // Copyright (C) 2014-2017 Cisco and/or its affiliates. All rights reserved.
00003 // Copyright (C) 2011-2013 Sourcefire, Inc.
00004 //
00005 // This program is free software; you can redistribute it and/or modify it
00006 // under the terms of the GNU General Public License Version 2 as published
00007 // by the Free Software Foundation. You may not use, modify or distribute
00008 // this program under any other version of the GNU General Public License.
00009 //
00010 // This program is distributed in the hope that it will be useful, but
00011 // WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00013 // General Public License for more details.
00014 //
00015 // You should have received a copy of the GNU General Public License along
00016 // with this program; if not, write to the Free Software Foundation, Inc.,
00017 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00018 //--------------------------------------------------------------------------
00019
00020 // paf.cc author Russ Combs <rcombs@sourcefire.com>
00021
00022 #ifdef HAVE_CONFIG_H
00023 #include "config.h"
00024 #endif
00025
00026 #include "paf.h"
00027
00028 #include "main/snort_debug.h"
00029 #include "protocols/packet.h"
00030
00031 //--------------------------------------------------------------------
00032 // private state
00033 //--------------------------------------------------------------------
00034
00035 enum FlushType
00036 {
00037 FT_NOP, // no flush
00038 FT_SFP, // abort paf
00039 FT_PAF, // flush to paf pt when len >= paf
00040 FT_LIMIT, // flush to paf pt, don't update flags
00041 FT_MAX // flush len when len >= max
00042 };
00043
00044 struct PafAux
00045 {
00046 FlushType ft;
00047 uint32_t len; // total bytes queued
00048 uint32_t idx; // offset from start of queued bytes
00049 };
00050
00051 #define PAF_LIMIT_FUZZ 1500
00052
00053 // 255 is max pseudo-random flush point; eth mtu ensures that maximum flushes
00054 // are not trimmed which throws off the tracking total in stream5_paf.c
00055 // max paf max = max datagram - eth mtu - 255 = 63780
00056 #define MAX_PAF_MAX (65535 - PAF_LIMIT_FUZZ - 255)
00057
00058 //--------------------------------------------------------------------
00059
00060 static uint32_t paf_flush (PAF_State* ps, PafAux& px, uint32_t* flags)
00061 {
00062 uint32_t at = 0;
00063 *flags &= ~(PKT_PDU_HEAD | PKT_PDU_TAIL);
00064
00065 DebugFormat(DEBUG_STREAM_PAF,
00066 "%s: type=%d, fpt=%u, len=%u, tot=%u\n",
00067 __func__, px.ft, ps->fpt, px.len, ps->tot);
00068
00069 switch ( px.ft )
00070 {
00071 case FT_NOP:
00072 return -1;
00073
00074 case FT_SFP:
00075 *flags = 0;
00076 return -1;
00077
00078 case FT_PAF:
00079 at = ps->fpt;
00080 *flags |= PKT_PDU_TAIL;
00081 break;
00082
00083 case FT_LIMIT:
00084 if (ps->fpt > px.len)
00085 {
00086 at = px.len;
00087 ps->fpt -= px.len;
00088 }
00089 else
00090 {
00091 at = ps->fpt;
00092 ps->fpt = px.len - ps->fpt; // number of characters scanned but not flushing
00093 }
00094 break;
00095
00096 // use of px.len is suboptimal here because the actual amount
00097 // flushed is determined later and can differ in certain cases
00098 // such as exceeding s5_pkt->max_dsize. the actual amount
00099 // flushed would ideally be applied to ps->fpt later. for
00100 // now we try to circumvent such cases so we track correctly.
00101 //
00102 // FIXIT-L max_dsize should no longer be exceeded since it excludes headers.
00103 case FT_MAX:
00104 at = px.len;
00105 if ( ps->fpt > px.len )
00106 ps->fpt -= px.len;
00107 else
00108 ps->fpt = 0;
00109 break;
00110 }
00111
00112 if ( !at || !px.len )
00113 return -1;
00114
00115 // safety - prevent seq + at < seq
00116 if ( at > 0x7FFFFFFF )
00117 at = 0x7FFFFFFF;
00118
00119 if ( !ps->tot )
00120 *flags |= PKT_PDU_HEAD;
00121
00122 if ( *flags & PKT_PDU_TAIL )
00123 ps->tot = 0;
00124 else
00125 ps->tot += at;
00126
00127 return at;
00128 }
00129
00130 //--------------------------------------------------------------------
00131
00132 static bool paf_callback (
00133 StreamSplitter* ss, PAF_State* ps, PafAux& px, Flow* ssn,
00134 const uint8_t* data, uint32_t len, uint32_t flags)
00135 {
00136 ps->fpt = 0;
00137 ps->paf = ss->scan(ssn, data, len, flags, &ps->fpt);
00138
00139 if ( ps->paf == StreamSplitter::ABORT )
00140 return false;
00141
00142 if ( ps->paf != StreamSplitter::SEARCH )
00143 {
00144 ps->fpt += px.idx;
00145
00146 if ( ps->fpt <= px.len )
00147 {
00148 px.idx = ps->fpt;
00149 return true;
00150 }
00151 }
00152 px.idx = px.len;
00153 return false;
00154 }
00155
00156 //--------------------------------------------------------------------
00157
00158 static inline bool paf_eval (
00159 StreamSplitter* ss, PAF_State* ps, PafAux& px, Flow* ssn,
00160 uint32_t flags, const uint8_t* data, uint32_t len)
00161 {
00162 DebugFormat(DEBUG_STREAM_PAF,
00163 "%s: paf=%d, idx=%u, len=%u, fpt=%u\n",
00164 __func__, ps->paf, px.idx, px.len, ps->fpt);
00165
00166 uint16_t fuzz = 0; // FIXIT-L PAF add a little zippedy-do-dah
00167
00168 switch ( ps->paf )
00169 {
00170 case StreamSplitter::SEARCH:
00171 if ( px.len > px.idx )
00172 {
00173 return paf_callback(ss, ps, px, ssn, data, len, flags);
00174 }
00175 return false;
00176
00177 case StreamSplitter::FLUSH:
00178 if ( px.len >= ps->fpt )
00179 {
00180 px.ft = FT_PAF;
00181 ps->paf = StreamSplitter::SEARCH;
00182 return true;
00183 }
00184 if ( px.len >= ss->max(ssn) + fuzz )
00185 {
00186 px.ft = FT_MAX;
00187 return false;
00188 }
00189 return false;
00190
00191 case StreamSplitter::LIMIT:
00192 // if we are within PAF_LIMIT_FUZZ character of paf_max ...
00193 if ( px.len + PAF_LIMIT_FUZZ >= ss->max(ssn) + fuzz)
00194 {
00195 px.ft = FT_LIMIT;
00196 ps->paf = StreamSplitter::LIMITED;
00197 return false;
00198 }
00199 ps->paf = StreamSplitter::SEARCH;
00200 return false;
00201
00202 case StreamSplitter::SKIP:
00203 if ( px.len > ps->fpt )
00204 {
00205 if ( ps->fpt > px.idx )
00206 {
00207 uint32_t delta = ps->fpt - px.idx;
00208 if ( delta > len )
00209 return false;
00210 data += delta;
00211 len -= delta;
00212 }
00213 px.idx = ps->fpt;
00214 return paf_callback(ss, ps, px, ssn, data, len, flags);
00215 }
00216 return false;
00217
00218 case StreamSplitter::LIMITED:
00219 // increment position by previously scanned bytes. set in paf_flush
00220 ps->paf = StreamSplitter::SEARCH;
00221 px.idx += ps->fpt;
00222 ps->fpt = 0;
00223 return true;
00224
00225 default:
00226 // StreamSplitter::ABORT || StreamSplitter::START
00227 break;
00228 }
00229
00230 px.ft = FT_SFP;
00231 return false;
00232 }
00233
00234 //--------------------------------------------------------------------
00235 // public stuff
00236 //--------------------------------------------------------------------
00237
00238 void paf_setup (PAF_State* ps)
00239 {
00240 // this is already cleared when instantiated
00241 //memset(ps, 0, sizeof(*ps));
00242 ps->paf = StreamSplitter::START;
00243 }
00244
00245 void paf_reset (PAF_State* ps)
00246 {
00247 memset(ps, 0, sizeof(*ps));
00248 ps->paf = StreamSplitter::START;
00249 }
00250
00251 void paf_clear (PAF_State* ps)
00252 {
00253 ps->paf = StreamSplitter::ABORT;
00254 }
00255
00256 //--------------------------------------------------------------------
00257
00258 int32_t paf_check (
00259 StreamSplitter* ss, PAF_State* ps, Flow* ssn,
00260 const uint8_t* data, uint32_t len, uint32_t total,
00261 uint32_t seq, uint32_t* flags)
00262 {
00263 DebugFormat(DEBUG_STREAM_PAF,
00264 "%s: len=%u, amt=%u, seq=%u, cur=%u, pos=%u, fpt=%u, tot=%u, paf=%d\n",
00265 __func__, len, total, seq, ps->seq, ps->pos, ps->fpt, ps->tot, ps->paf);
00266
00267 PafAux px;
00268
00269 if ( !paf_initialized(ps) )
00270 {
00271 ps->seq = ps->pos = seq;
00272 ps->paf = StreamSplitter::SEARCH;
00273 }
00274 else if ( SEQ_GT(seq, ps->seq) )
00275 {
00276 // if seq jumped we have a gap. Flush any queued data, then abort
00277 px.len = total - len;
00278
00279 if ( px.len )
00280 {
00281 ps->fpt = 0;
00282 px.ft = FT_MAX;
00283 return paf_flush(ps, px, flags);
00284 }
00285 *flags = 0;
00286 return -1;
00287 }
00288 else if ( SEQ_LEQ(seq + len, ps->seq) )
00289 {
00290 return -1;
00291 }
00292 else if ( SEQ_LT(seq, ps->seq) )
00293 {
00294 uint32_t shift = ps->seq - seq;
00295 data += shift;
00296 len -= shift;
00297 }
00298 ps->seq += len;
00299
00300 px.idx = total - len;
00301
00302 // if 'total' is greater than the maximum paf_max AND 'total' is greater
00303 // than paf_max bytes + fuzz (i.e. after we have finished analyzing the
00304 // current segment, total bytes analyzed will be greater than the
00305 // configured (fuzz + paf_max) == (ss->max() + fuzz), we must ensure a flush
00306 // occurs at the paf_max byte. So, we manually set the data's length and
00307 // total queued bytes (px.len) to guarantee that at most paf_max bytes will
00308 // be analyzed and flushed since the last flush point. It should also be
00309 // noted that we perform the check here rather in in paf_flush() to
00310 // avoid scanning the same data twice. The first scan would analyze the
00311 // entire segment and the second scan would analyze this segments
00312 // unflushed data.
00313 uint16_t fuzz = 0; // FIXIT-L PAF add a little zippedy-do-dah
00314
00315 if ( total >= MAX_PAF_MAX && total > ss->max(ssn) + fuzz )
00316 {
00317 px.len = MAX_PAF_MAX + fuzz;
00318 len = len + px.len - total;
00319 }
00320 else
00321 {
00322 px.len = total;
00323 }
00324
00325 do
00326 {
00327 px.ft = FT_NOP;
00328 uint32_t idx = px.idx;
00329 uint32_t shift;
00330 int32_t fp;
00331
00332 bool cont = paf_eval(ss, ps, px, ssn, *flags, data, len);
00333
00334 if ( px.ft != FT_NOP )
00335 {
00336 fp = paf_flush(ps, px, flags);
00337 paf_jump(ps, fp);
00338 return fp;
00339 }
00340 if ( !cont )
00341 break;
00342
00343 if ( px.idx > idx )
00344 {
00345 shift = px.idx - idx;
00346 if ( shift > len )
00347 shift = len;
00348 data += shift;
00349 len -= shift;
00350 }
00351 }
00352 while ( true );
00353
00354 if ( ps->paf == StreamSplitter::ABORT )
00355 *flags = 0;
00356
00357 else if ( (ps->paf != StreamSplitter::FLUSH) && (px.len > ss->max(ssn)+fuzz) )
00358 {
00359 px.ft = FT_MAX;
00360 uint32_t fp = paf_flush(ps, px, flags);
00361 paf_jump(ps, fp);
00362 return fp;
00363 }
00364 return -1;
00365 }
00366
END OF CODE