1 /* $Id$ */
2 /****************************************************************************
3 *
4 * Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
5 * Copyright (C) 2005-2013 Sourcefire, Inc.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License Version 2 as
9 * published by the Free Software Foundation. You may not use, modify or
10 * distribute this program under any other version of the GNU General
11 * Public License.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 *
22 ****************************************************************************/
23
24 #ifdef NORMALIZER
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include <string.h>
31 #ifdef HAVE_DUMBNET_H
32 #include <dumbnet.h>
33 #else
34 #include <dnet.h>
35 #endif
36
37 #include "normalize.h"
38 #include "perf.h"
39 #include "sfdaq.h"
40
41 typedef enum {
42 PC_IP4_TRIM,
43 PC_IP4_TOS,
44 PC_IP4_DF,
45 PC_IP4_RF,
46 PC_IP4_TTL,
47 PC_IP4_OPTS,
48 PC_ICMP4_ECHO,
49 PC_IP6_TTL,
50 PC_IP6_OPTS,
51 PC_ICMP6_ECHO,
52 PC_TCP_SYN_OPT,
53 PC_TCP_OPT,
54 PC_TCP_PAD,
55 PC_TCP_RSV,
56 PC_TCP_NS,
57 PC_TCP_URP,
58 PC_TCP_ECN_PKT,
59 PC_TCP_TS_ECR,
60 PC_TCP_REQ_URG,
61 PC_TCP_REQ_PAY,
62 PC_TCP_REQ_URP,
63 PC_MAX
64 } PegCounts;
65
66 static const char* pegName[PC_MAX] = {
67 "ip4::trim",
68 "ip4::tos",
69 "ip4::df",
70 "ip4::rf",
71 "ip4::ttl",
72 "ip4::opts",
73 "icmp4::echo",
74 "ip6::ttl",
75 "ip6::opts",
76 "icmp6::echo",
77 "tcp::syn_opt",
78 "tcp::opt",
79 "tcp::pad",
80 "tcp::rsv",
81 "tcp::ns",
82 "tcp::urp",
83 "tcp::ecn_pkt",
84 "tcp::ts_ecr",
85 "tcp::req_urg",
86 "tcp::req_pay",
87 "tcp::req_urp",
88 };
89
90 static PegCount normStats[PC_MAX][NORM_MODE_MAX];
91
92 //static int Norm_Eth(Packet*, uint8_t layer, int changes);
93 static int Norm_IP4(NormalizerContext*, Packet*, uint8_t layer, int changes);
94 static int Norm_ICMP4(NormalizerContext*, Packet*, uint8_t layer, int changes);
95 static int Norm_IP6(NormalizerContext*, Packet*, uint8_t layer, int changes);
96 static int Norm_ICMP6(NormalizerContext*, Packet*, uint8_t layer, int changes);
97 static int Norm_IP6_Opts(NormalizerContext*, Packet*, uint8_t layer, int changes);
98 //static int Norm_UDP(NormalizerContext*, Packet*, uint8_t layer, int changes);
99 static int Norm_TCP(NormalizerContext*, Packet*, uint8_t layer, int changes);
100
101 static const uint8_t MAX_EOL_PAD[TCP_OPTLENMAX] = {
102 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
103 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
104 };
105
106 // go from inner to outer
Norm_Packet(NormalizerContext * c,Packet * p)107 int Norm_Packet (NormalizerContext* c, Packet* p)
108 {
109 uint8_t lyr = p->next_layer;
110 int changes = 0;
111
112 while ( lyr > 0 )
113 {
114 PROTO_ID proto = p->layers[--lyr].proto;
115 Normalizer n = c->normalizers[proto];
116 if ( n ) changes = n(c, p, lyr, changes);
117 }
118
119 if ( changes > 0 )
120 {
121 p->packet_flags |= PKT_MODIFIED;
122 return 1;
123 }
124 if ( p->packet_flags & PKT_RESIZED )
125 {
126 return 1;
127 }
128 return 0;
129 }
130
131 //-----------------------------------------------------------------------
132 // in the following code, we mostly use the actual packet data and the
133 // packet layers[]. use of other decoded packet members is largely
134 // avoided to ensure that we don't get tripped up by nested protocols.
135 // TCP options count and length are a notable exception.
136 //
137 // also note that checksums are not calculated here. they are only
138 // calculated once after all normalizations are done (here, stream5)
139 // and any replacements are made.
140 //-----------------------------------------------------------------------
141
142 #if 0
143 static int Norm_Eth (Packet * p, uint8_t layer, int changes)
144 {
145 return 0;
146 }
147 #endif
148
149 //-----------------------------------------------------------------------
150
151 #define IP4_FLAG_RF 0x8000
152 #define IP4_FLAG_DF 0x4000
153 #define IP4_FLAG_MF 0x2000
154
155 // TBD support configurable minimum length / obtain from DAQ
156 // ether header + min payload (excludes FCS, which makes it 64 total)
157 #define ETH_MIN_LEN 60
158
getNormMode(NormalizerContext * c)159 static inline int getNormMode (NormalizerContext* c)
160 {
161 return c->normMode;
162 }
163
Norm_IP4(NormalizerContext * c,Packet * p,uint8_t layer,int changes)164 static int Norm_IP4 (NormalizerContext* c, Packet * p, uint8_t layer, int changes)
165 {
166 IPHdr* h = (IPHdr*)(p->layers[layer].start);
167 uint16_t fragbits = ntohs(h->ip_off);
168 uint16_t origbits = fragbits;
169 NormMode mode = getNormMode(c);
170
171 if ( Norm_IsEnabled(c, NORM_IP4_TRIM) && (layer == 1) )
172 {
173 uint32_t len = p->layers[0].length + ntohs(h->ip_len);
174
175 if ( (len < p->pkth->pktlen) &&
176 ( (len >= ETH_MIN_LEN) || (p->pkth->pktlen > ETH_MIN_LEN) )
177 ) {
178 if ( mode == NORM_MODE_ON )
179 {
180 ((DAQ_PktHdr_t*)p->pkth)->pktlen = (len < ETH_MIN_LEN) ? ETH_MIN_LEN : len;
181 p->packet_flags |= PKT_RESIZED;
182 changes++;
183 }
184 normStats[PC_IP4_TRIM][mode]++;
185 sfBase.iPegs[PERF_COUNT_IP4_TRIM][mode]++;
186 }
187 }
188 if ( Norm_IsEnabled(c, NORM_IP4_TOS) )
189 {
190 if ( h->ip_tos )
191 {
192 if ( mode == NORM_MODE_ON )
193 {
194 h->ip_tos = 0;
195 changes++;
196 }
197 normStats[PC_IP4_TOS][mode]++;
198 sfBase.iPegs[PERF_COUNT_IP4_TOS][mode]++;
199 }
200 }
201 #if 0
202 if ( Norm_IsEnabled(c, NORM_IP4_ID) )
203 {
204 // TBD implement IP ID normalization / randomization
205 }
206 #endif
207 if ( Norm_IsEnabled(c, NORM_IP4_DF) )
208 {
209 if ( fragbits & IP4_FLAG_DF )
210 {
211 if ( mode == NORM_MODE_ON )
212 {
213 fragbits &= ~IP4_FLAG_DF;
214 changes++;
215 }
216 normStats[PC_IP4_DF][mode]++;
217 sfBase.iPegs[PERF_COUNT_IP4_DF][mode]++;
218 }
219 }
220 if ( Norm_IsEnabled(c, NORM_IP4_RF) )
221 {
222 if ( fragbits & IP4_FLAG_RF )
223 {
224 if ( mode == NORM_MODE_ON )
225 {
226 fragbits &= ~IP4_FLAG_RF;
227 changes++;
228 }
229 normStats[PC_IP4_RF][mode]++;
230 sfBase.iPegs[PERF_COUNT_IP4_RF][mode]++;
231 }
232 }
233 if ( fragbits != origbits )
234 {
235 h->ip_off = htons(fragbits);
236 }
237 if ( Norm_IsEnabled(c, NORM_IP4_TTL) )
238 {
239 if ( h->ip_ttl < ScMinTTL() )
240 {
241 if ( mode == NORM_MODE_ON )
242 {
243 h->ip_ttl = ScNewTTL();
244 p->error_flags &= ~PKT_ERR_BAD_TTL;
245 changes++;
246 }
247 normStats[PC_IP4_TTL][mode]++;
248 sfBase.iPegs[PERF_COUNT_IP4_TTL][mode]++;
249 }
250 }
251 if ( p->layers[layer].length > IP_HEADER_LEN )
252 {
253 if ( mode == NORM_MODE_ON )
254 {
255 uint8_t* opts = p->layers[layer].start + IP_HEADER_LEN;
256 uint8_t len = p->layers[layer].length - IP_HEADER_LEN;
257 // expect len > 0 because IHL yields a multiple of 4
258 memset(opts, IPOPT_NOP, len);
259 changes++;
260 }
261 normStats[PC_IP4_OPTS][mode]++;
262 sfBase.iPegs[PERF_COUNT_IP4_OPTS][mode]++;
263 }
264 return changes;
265 }
266
267 //-----------------------------------------------------------------------
268
Norm_ICMP4(NormalizerContext * c,Packet * p,uint8_t layer,int changes)269 static int Norm_ICMP4 (NormalizerContext* c, Packet * p, uint8_t layer, int changes)
270 {
271 ICMPHdr* h = (ICMPHdr*)(p->layers[layer].start);
272 NormMode mode = getNormMode(c);
273
274 if ( (h->type == ICMP_ECHO || h->type == ICMP_ECHOREPLY) &&
275 (h->code != 0) )
276 {
277 if ( mode == NORM_MODE_ON )
278 {
279 h->code = 0;
280 changes++;
281 }
282 normStats[PC_ICMP4_ECHO][mode]++;
283 sfBase.iPegs[PERF_COUNT_ICMP4_ECHO][mode]++;
284 }
285 return changes;
286 }
287
288 //-----------------------------------------------------------------------
289
Norm_IP6(NormalizerContext * c,Packet * p,uint8_t layer,int changes)290 static int Norm_IP6 (NormalizerContext* c, Packet * p, uint8_t layer, int changes)
291 {
292 IP6RawHdr* h = (IP6RawHdr*)(p->layers[layer].start);
293 NormMode mode = getNormMode(c);
294
295 if ( Norm_IsEnabled(c, NORM_IP6_TTL) )
296 {
297 if ( h->ip6hops < ScMinTTL() )
298 {
299 if ( mode == NORM_MODE_ON )
300 {
301 h->ip6hops = ScNewTTL();
302 p->error_flags &= ~PKT_ERR_BAD_TTL;
303 changes++;
304 }
305 normStats[PC_IP6_TTL][mode]++;
306 sfBase.iPegs[PERF_COUNT_IP6_TTL][mode]++;
307 }
308 }
309 return changes;
310 }
311
312 //-----------------------------------------------------------------------
313
Norm_ICMP6(NormalizerContext * c,Packet * p,uint8_t layer,int changes)314 static int Norm_ICMP6 (NormalizerContext* c, Packet * p, uint8_t layer, int changes)
315 {
316 ICMPHdr* h = (ICMPHdr*)(p->layers[layer].start);
317 NormMode mode = getNormMode(c);
318
319 if ( (h->type == ICMP6_ECHO || h->type == ICMP6_REPLY) &&
320 (h->code != 0) )
321 {
322 if ( mode == NORM_MODE_ON )
323 {
324 h->code = 0;
325 changes++;
326 }
327 normStats[PC_ICMP6_ECHO][mode]++;
328 sfBase.iPegs[PERF_COUNT_ICMP6_ECHO][mode]++;
329 }
330 return changes;
331 }
332
333 //-----------------------------------------------------------------------
334 // we assume here that the decoder has not pushed ip6 option extension
335 // headers unless the basic sizing is correct (size = N*8 octetes, N>0).
336
337 typedef struct
338 {
339 uint8_t next;
340 uint8_t xlen;
341 uint8_t type;
342 uint8_t olen;
343 } ExtOpt;
344
345 #define IP6_OPT_PAD_N 1
346
Norm_IP6_Opts(NormalizerContext * c,Packet * p,uint8_t layer,int changes)347 static int Norm_IP6_Opts (NormalizerContext* c, Packet * p, uint8_t layer, int changes)
348 {
349 NormMode mode = getNormMode(c);
350
351 if ( mode == NORM_MODE_ON )
352 {
353 uint8_t* b = p->layers[layer].start;
354 ExtOpt* x = (ExtOpt*)b;
355
356 // whatever was here, turn it into one PADN option
357 x->type = IP6_OPT_PAD_N;
358 x->olen = (x->xlen * 8) + 8 - sizeof(*x);
359 memset(b+sizeof(*x), 0, x->olen);
360
361 changes++;
362 }
363 normStats[PC_IP6_OPTS][mode]++;
364 sfBase.iPegs[PERF_COUNT_IP6_OPTS][mode]++;
365
366 return changes;
367 }
368
369 //-----------------------------------------------------------------------
370
371 #if 0
372 static int Norm_UDP (Packet * p, uint8_t layer, int changes)
373 {
374 return 0;
375 }
376 #endif
377
378 //-----------------------------------------------------------------------
379
NopDaOpt(uint8_t * opt,uint8_t len)380 static inline void NopDaOpt (uint8_t* opt, uint8_t len)
381 {
382 memset(opt, TCPOPT_NOP, len);
383 }
384
385 #define TS_ECR_OFFSET 6
386 #define TS_ECR_LENGTH 4
387
Norm_TCPOptions(NormalizerContext * context,uint8_t * opts,size_t len,const TCPHdr * h,uint8_t numOpts,int changes)388 static inline int Norm_TCPOptions (NormalizerContext* context, uint8_t* opts, size_t len, const TCPHdr* h, uint8_t numOpts, int changes)
389 {
390 size_t i = 0;
391 uint8_t c = 0;
392 NormMode mode = getNormMode(context);
393
394 while ( (i < len) && (opts[i] != TCPOPT_EOL) &&
395 (c++ < numOpts) )
396 {
397 uint8_t olen = ( opts[i] <= 1 ) ? 1 : opts[i+1];
398
399 // we know that the first numOpts options have valid lengths
400 // so we should not need to check individual or total option lengths.
401 // however, we keep this as a sanity check.
402 if ( i + olen > len)
403 break;
404
405 switch ( opts[i] )
406 {
407 case TCPOPT_NOP:
408 break;
409
410 case TCPOPT_MAXSEG:
411 case TCPOPT_WSCALE:
412 if ( !(h->th_flags & TH_SYN) )
413 {
414 if ( mode == NORM_MODE_ON )
415 {
416 NopDaOpt(opts+i, olen);
417 changes++;
418 }
419 normStats[PC_TCP_SYN_OPT][mode]++;
420 sfBase.iPegs[PERF_COUNT_TCP_SYN_OPT][mode]++;
421 }
422 break;
423
424 case TCPOPT_TIMESTAMP:
425 if ( !(h->th_flags & TH_ACK) &&
426 // use memcmp because opts have arbitrary alignment
427 memcmp(opts+i+TS_ECR_OFFSET, MAX_EOL_PAD, TS_ECR_LENGTH) )
428 {
429 if ( mode == NORM_MODE_ON )
430 {
431 // TSecr should be zero unless ACK is set
432 memset(opts+i+TS_ECR_OFFSET, 0, TS_ECR_LENGTH);
433 changes++;
434 }
435 normStats[PC_TCP_TS_ECR][mode]++;
436 sfBase.iPegs[PERF_COUNT_TCP_TS_ECR][mode]++;
437 }
438 break;
439
440 default:
441 if ( !Norm_TcpIsOptional(context, opts[i]) )
442 {
443 if ( mode == NORM_MODE_ON )
444 {
445 NopDaOpt(opts+i, olen);
446 changes++;
447 }
448 normStats[PC_TCP_OPT][mode]++;
449 sfBase.iPegs[PERF_COUNT_TCP_OPT][mode]++;
450 }
451 }
452 i += olen;
453 }
454 if ( ++i < len && memcmp(opts+i, MAX_EOL_PAD, len-i) )
455 {
456 if ( mode == NORM_MODE_ON )
457 {
458 memset(opts+i, 0, len-i);
459 changes++;
460 }
461 normStats[PC_TCP_PAD][mode]++;
462 sfBase.iPegs[PERF_COUNT_TCP_PAD][mode]++;
463 }
464 return changes;
465 }
466
Norm_TCPPadding(NormalizerContext * context,uint8_t * opts,size_t len,uint8_t numOpts,int changes)467 static inline int Norm_TCPPadding (NormalizerContext* context, uint8_t* opts, size_t len, uint8_t numOpts, int changes)
468 {
469 size_t i = 0;
470 uint8_t c = 0;
471 NormMode mode = getNormMode(context);
472
473 while ( (i < len) && (opts[i] != TCPOPT_EOL) && (c++ < numOpts) )
474 {
475 i += ( opts[i] <= 1 ) ? 1 : opts[i+1];
476 }
477 if ( ++i < len && memcmp(opts+i, MAX_EOL_PAD, len-i) )
478 {
479 if ( mode == NORM_MODE_ON )
480 {
481 memset(opts+i, 0, len-i);
482 changes++;
483 }
484 normStats[PC_TCP_PAD][mode]++;
485 sfBase.iPegs[PERF_COUNT_TCP_PAD][mode]++;
486 }
487 return changes;
488 }
489
Norm_TCP(NormalizerContext * c,Packet * p,uint8_t layer,int changes)490 static int Norm_TCP (NormalizerContext* c, Packet * p, uint8_t layer, int changes)
491 {
492 NormMode mode = getNormMode(c);
493 TCPHdr* h = (TCPHdr*)(p->layers[layer].start);
494
495 if ( Norm_IsEnabled(c, NORM_TCP_RSV) )
496 {
497 if ( h->th_offx2 & TH_RSV )
498 {
499 if ( mode == NORM_MODE_ON )
500 {
501 h->th_offx2 &= ~TH_RSV;
502 changes++;
503 }
504 normStats[PC_TCP_RSV][mode]++;
505 sfBase.iPegs[PERF_COUNT_TCP_RSV][mode]++;
506 }
507 }
508 if ( Norm_IsEnabled(c, NORM_TCP_ECN_PKT) )
509 {
510 if ( h->th_flags & (TH_CWR|TH_ECE) )
511 {
512 if ( mode == NORM_MODE_ON )
513 {
514 h->th_flags &= ~(TH_CWR|TH_ECE);
515 changes++;
516 }
517 normStats[PC_TCP_ECN_PKT][mode]++;
518 sfBase.iPegs[PERF_COUNT_TCP_ECN_PKT][mode]++;
519 }
520 if ( h->th_offx2 & TH_NS )
521 {
522 if ( mode == NORM_MODE_ON )
523 {
524 h->th_offx2 &= ~TH_NS;
525 changes++;
526 }
527 normStats[PC_TCP_NS][mode]++;
528 sfBase.iPegs[PERF_COUNT_TCP_NS][mode]++;
529 }
530 }
531 if ( h->th_urp )
532 {
533 if ( !(h->th_flags & TH_URG) )
534 {
535 if ( Norm_IsEnabled(c, NORM_TCP_REQ_URG) )
536 {
537 if ( mode == NORM_MODE_ON )
538 {
539 h->th_urp = 0;
540 changes++;
541 }
542 normStats[PC_TCP_REQ_URG][mode]++;
543 sfBase.iPegs[PERF_COUNT_TCP_REQ_URG][mode]++;
544 }
545 }
546 else if ( !p->dsize )
547 {
548 if ( Norm_IsEnabled(c, NORM_TCP_REQ_PAY) )
549 {
550 if ( mode == NORM_MODE_ON )
551 {
552 h->th_flags &= ~TH_URG;
553 h->th_urp = 0;
554 changes++;
555 }
556 normStats[PC_TCP_REQ_PAY][mode]++;
557 sfBase.iPegs[PERF_COUNT_TCP_REQ_PAY][mode]++;
558 }
559 }
560 else if ( (ntohs(h->th_urp) > p->dsize) )
561 {
562 if ( Norm_IsEnabled(c, NORM_TCP_URP) )
563 {
564 if ( mode == NORM_MODE_ON )
565 {
566 h->th_urp = ntohs(p->dsize);
567 changes++;
568 }
569 normStats[PC_TCP_URP][mode]++;
570 sfBase.iPegs[PERF_COUNT_TCP_URP][mode]++;
571 }
572 }
573 }
574 else if ( Norm_IsEnabled(c, NORM_TCP_REQ_URP) &&
575 h->th_flags & TH_URG )
576 {
577 if ( mode == NORM_MODE_ON )
578 {
579 h->th_flags &= ~TH_URG;
580 changes++;
581 }
582 normStats[PC_TCP_REQ_URP][mode]++;
583 sfBase.iPegs[PERF_COUNT_TCP_REQ_URP][mode]++;
584 }
585 if ( p->tcp_options_len > 0 )
586 {
587 uint8_t* opts = p->layers[layer].start + TCP_HEADER_LEN;
588
589 if ( Norm_IsEnabled(c, NORM_TCP_OPT) )
590 {
591 changes = Norm_TCPOptions(c, opts, p->tcp_options_len,
592 h, p->tcp_option_count, changes);
593 }
594 else if ( Norm_IsEnabled(c, NORM_TCP_PAD) )
595 {
596 changes = Norm_TCPPadding(c, opts, p->tcp_options_len,
597 p->tcp_option_count, changes);
598 }
599 }
600 return changes;
601 }
602
603 //-----------------------------------------------------------------------
604
Norm_PrintStats(void)605 void Norm_PrintStats (void)
606 {
607 int i;
608 LogMessage("Normalizer statistics:\n");
609
610 for ( i = 0; i < PC_MAX; i++ )
611 {
612 // for now, 23 aligns with frag3
613 LogMessage("%23s: " STDu64 "\n", pegName[i], normStats[i][NORM_MODE_ON]);
614 LogMessage("Would %17s: " STDu64 "\n", pegName[i], normStats[i][NORM_MODE_WOULDA]);
615 }
616 }
617
Norm_ResetStats(void)618 void Norm_ResetStats (void)
619 {
620 memset(normStats, 0, sizeof(normStats));
621 }
622
623 //-----------------------------------------------------------------------
624
Norm_SetConfig(NormalizerContext * nc)625 int Norm_SetConfig (NormalizerContext* nc)
626 {
627 if ( !DAQ_CanReplace() )
628 {
629 LogMessage("WARNING: normalizations disabled because DAQ"
630 " can't replace packets.\n");
631 nc->normalizer_flags = 0x0;
632 return -1;
633 }
634 if ( !nc->normalizer_flags )
635 {
636 return 0;
637 }
638 if ( Norm_IsEnabled(nc, NORM_IP4) )
639 {
640 nc->normalizers[PROTO_IP4] = Norm_IP4;
641 }
642 if ( Norm_IsEnabled(nc, NORM_IP4_TRIM) )
643 {
644 if ( !DAQ_CanInject() )
645 {
646 LogMessage("WARNING: normalize_ip4: trim disabled since DAQ "
647 "can't inject packets.\n");
648 Norm_Disable(nc, NORM_IP4_TRIM);
649 }
650 }
651 if ( Norm_IsEnabled(nc, NORM_ICMP4) )
652 {
653 nc->normalizers[PROTO_ICMP4] = Norm_ICMP4;
654 }
655 if ( Norm_IsEnabled(nc, NORM_IP6) )
656 {
657 nc->normalizers[PROTO_IP6] = Norm_IP6;
658 nc->normalizers[PROTO_IP6_HOP_OPTS] = Norm_IP6_Opts;
659 nc->normalizers[PROTO_IP6_DST_OPTS] = Norm_IP6_Opts;
660 }
661 if ( Norm_IsEnabled(nc, NORM_ICMP6) )
662 {
663 nc->normalizers[PROTO_ICMP6] = Norm_ICMP6;
664 }
665 if ( Norm_IsEnabled(nc, NORM_TCP) )
666 {
667 nc->normalizers[PROTO_TCP] = Norm_TCP;
668 }
669 return 0;
670 }
671 #endif // NORMALIZER
672
673