forked from davidediger/softflowd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
netflow9.c
502 lines (470 loc) · 16.5 KB
/
netflow9.c
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
/*
* Copyright 2002 Damien Miller <[email protected]> All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "common.h"
#include "log.h"
#include "treetype.h"
#include "softflowd.h"
/* Netflow v.9 */
struct NF9_HEADER {
u_int16_t version, flows;
u_int32_t uptime_ms, time_sec;
u_int32_t package_sequence, source_id;
} __packed;
struct NF9_FLOWSET_HEADER_COMMON {
u_int16_t flowset_id, length;
} __packed;
struct NF9_TEMPLATE_FLOWSET_HEADER {
struct NF9_FLOWSET_HEADER_COMMON c;
u_int16_t template_id, count;
} __packed;
struct NF9_OPTION_TEMPLATE_FLOWSET_HEADER {
struct NF9_FLOWSET_HEADER_COMMON c;
u_int16_t template_id, scope_length, option_length;
} __packed;
struct NF9_TEMPLATE_FLOWSET_RECORD {
u_int16_t type, length;
} __packed;
struct NF9_DATA_FLOWSET_HEADER {
struct NF9_FLOWSET_HEADER_COMMON c;
} __packed;
#define NF9_TEMPLATE_FLOWSET_ID 0
#define NF9_OPTIONS_FLOWSET_ID 1
#define NF9_MIN_RECORD_FLOWSET_ID 256
/* Flowset record types the we care about */
#define NF9_IN_BYTES 1
#define NF9_IN_PACKETS 2
/* ... */
#define NF9_PROTOCOL 4
#define NF9_TOS 5
/* ... */
#define NF9_TCP_FLAGS 6
#define NF9_L4_SRC_PORT 7
#define NF9_IPV4_SRC_ADDR 8
/* ... */
#define NF9_IF_INDEX_IN 10
#define NF9_L4_DST_PORT 11
#define NF9_IPV4_DST_ADDR 12
/* ... */
#define NF9_IF_INDEX_OUT 14
/* ... */
#define NF9_LAST_SWITCHED 21
#define NF9_FIRST_SWITCHED 22
/* ... */
#define NF9_IPV6_SRC_ADDR 27
#define NF9_IPV6_DST_ADDR 28
/* ... */
#define NF9_ICMP_TYPE 32
/* ... */
#define NF9_SAMPLING_INTERVAL 34
#define NF9_SAMPLING_ALGORITHM 35
/* ... */
#define NF9_SRC_VLAN 58
/* ... */
#define NF9_IP_PROTOCOL_VERSION 60
/* Stuff pertaining to the templates that softflowd uses */
#ifdef ENABLE_NF9_VLAN
#define NF9_SOFTFLOWD_TEMPLATE_NRECORDS 16
#else /* ENABLE_NF9_VLAN */
#define NF9_SOFTFLOWD_TEMPLATE_NRECORDS 14
#endif /* ENABLE_NF9_VLAN */
struct NF9_SOFTFLOWD_TEMPLATE {
struct NF9_TEMPLATE_FLOWSET_HEADER h;
struct NF9_TEMPLATE_FLOWSET_RECORD r[NF9_SOFTFLOWD_TEMPLATE_NRECORDS];
} __packed;
#define NF9_SOFTFLOWD_OPTION_TEMPLATE_SCOPE_RECORDS 1
#define NF9_SOFTFLOWD_OPTION_TEMPLATE_NRECORDS 2
struct NF9_SOFTFLOWD_OPTION_TEMPLATE {
struct NF9_OPTION_TEMPLATE_FLOWSET_HEADER h;
struct NF9_TEMPLATE_FLOWSET_RECORD s[NF9_SOFTFLOWD_OPTION_TEMPLATE_SCOPE_RECORDS];
struct NF9_TEMPLATE_FLOWSET_RECORD r[NF9_SOFTFLOWD_OPTION_TEMPLATE_NRECORDS];
} __packed;
/* softflowd data flowset types */
struct NF9_SOFTFLOWD_DATA_COMMON {
u_int32_t last_switched, first_switched;
u_int32_t bytes, packets;
u_int32_t if_index_in, if_index_out;
u_int16_t src_port, dst_port;
u_int8_t protocol, tcp_flags, ipproto, tos;
#ifdef ENABLE_NF9_VLAN
u_int16_t icmp_type, vlanid;
#endif /* ENABLE_NF9_VLAN */
} __packed;
struct NF9_SOFTFLOWD_DATA_V4 {
u_int32_t src_addr, dst_addr;
struct NF9_SOFTFLOWD_DATA_COMMON c;
} __packed;
struct NF9_SOFTFLOWD_DATA_V6 {
u_int8_t src_addr[16], dst_addr[16];
struct NF9_SOFTFLOWD_DATA_COMMON c;
} __packed;
struct NF9_SOFTFLOWD_OPTION_DATA {
struct NF9_FLOWSET_HEADER_COMMON c;
u_int32_t scope_ifidx;
u_int32_t sampling_interval;
u_int8_t sampling_algorithm;
u_int8_t padding[3];
} __packed;
/* Local data: templates and counters */
#define NF9_SOFTFLOWD_MAX_PACKET_SIZE 512
#define NF9_SOFTFLOWD_V4_TEMPLATE_ID 1024
#define NF9_SOFTFLOWD_V6_TEMPLATE_ID 2048
#define NF9_SOFTFLOWD_OPTION_TEMPLATE_ID 256
#define NF9_DEFAULT_TEMPLATE_INTERVAL 16
/* ... */
#define NF9_OPTION_SCOPE_SYSTEM 1
#define NF9_OPTION_SCOPE_INTERFACE 2
#define NF9_OPTION_SCOPE_LINECARD 3
#define NF9_OPTION_SCOPE_CACHE 4
#define NF9_OPTION_SCOPE_TEMPLATE 5
/* ... */
#define NF9_SAMPLING_ALGORITHM_DETERMINISTIC 1
#define NF9_SAMPLING_ALGORITHM_RANDOM 2
/* ... */
static struct NF9_SOFTFLOWD_TEMPLATE v4_template;
static struct NF9_SOFTFLOWD_TEMPLATE v6_template;
static struct NF9_SOFTFLOWD_OPTION_TEMPLATE option_template;
static struct NF9_SOFTFLOWD_OPTION_DATA option_data;
static int nf9_pkts_until_template = -1;
static void
nf9_init_template(void)
{
bzero(&v4_template, sizeof(v4_template));
v4_template.h.c.flowset_id = htons(NF9_TEMPLATE_FLOWSET_ID);
v4_template.h.c.length = htons(sizeof(v4_template));
v4_template.h.template_id = htons(NF9_SOFTFLOWD_V4_TEMPLATE_ID);
v4_template.h.count = htons(NF9_SOFTFLOWD_TEMPLATE_NRECORDS);
v4_template.r[0].type = htons(NF9_IPV4_SRC_ADDR);
v4_template.r[0].length = htons(4);
v4_template.r[1].type = htons(NF9_IPV4_DST_ADDR);
v4_template.r[1].length = htons(4);
v4_template.r[2].type = htons(NF9_LAST_SWITCHED);
v4_template.r[2].length = htons(4);
v4_template.r[3].type = htons(NF9_FIRST_SWITCHED);
v4_template.r[3].length = htons(4);
v4_template.r[4].type = htons(NF9_IN_BYTES);
v4_template.r[4].length = htons(4);
v4_template.r[5].type = htons(NF9_IN_PACKETS);
v4_template.r[5].length = htons(4);
v4_template.r[6].type = htons(NF9_IF_INDEX_IN);
v4_template.r[6].length = htons(4);
v4_template.r[7].type = htons(NF9_IF_INDEX_OUT);
v4_template.r[7].length = htons(4);
v4_template.r[8].type = htons(NF9_L4_SRC_PORT);
v4_template.r[8].length = htons(2);
v4_template.r[9].type = htons(NF9_L4_DST_PORT);
v4_template.r[9].length = htons(2);
v4_template.r[10].type = htons(NF9_PROTOCOL);
v4_template.r[10].length = htons(1);
v4_template.r[11].type = htons(NF9_TCP_FLAGS);
v4_template.r[11].length = htons(1);
v4_template.r[12].type = htons(NF9_IP_PROTOCOL_VERSION);
v4_template.r[12].length = htons(1);
v4_template.r[13].type = htons(NF9_TOS);
v4_template.r[13].length = htons(1);
#ifdef ENABLE_NF9_VLAN
v4_template.r[14].type = htons(NF9_ICMP_TYPE);
v4_template.r[14].length = htons(2);
v4_template.r[15].type = htons(NF9_SRC_VLAN);
v4_template.r[15].length = htons(2);
#endif /* ENABLE_NF9_VLAN */
bzero(&v6_template, sizeof(v6_template));
v6_template.h.c.flowset_id = htons(NF9_TEMPLATE_FLOWSET_ID);
v6_template.h.c.length = htons(sizeof(v6_template));
v6_template.h.template_id = htons(NF9_SOFTFLOWD_V6_TEMPLATE_ID);
v6_template.h.count = htons(NF9_SOFTFLOWD_TEMPLATE_NRECORDS);
v6_template.r[0].type = htons(NF9_IPV6_SRC_ADDR);
v6_template.r[0].length = htons(16);
v6_template.r[1].type = htons(NF9_IPV6_DST_ADDR);
v6_template.r[1].length = htons(16);
v6_template.r[2].type = htons(NF9_LAST_SWITCHED);
v6_template.r[2].length = htons(4);
v6_template.r[3].type = htons(NF9_FIRST_SWITCHED);
v6_template.r[3].length = htons(4);
v6_template.r[4].type = htons(NF9_IN_BYTES);
v6_template.r[4].length = htons(4);
v6_template.r[5].type = htons(NF9_IN_PACKETS);
v6_template.r[5].length = htons(4);
v6_template.r[6].type = htons(NF9_IF_INDEX_IN);
v6_template.r[6].length = htons(4);
v6_template.r[7].type = htons(NF9_IF_INDEX_OUT);
v6_template.r[7].length = htons(4);
v6_template.r[8].type = htons(NF9_L4_SRC_PORT);
v6_template.r[8].length = htons(2);
v6_template.r[9].type = htons(NF9_L4_DST_PORT);
v6_template.r[9].length = htons(2);
v6_template.r[10].type = htons(NF9_PROTOCOL);
v6_template.r[10].length = htons(1);
v6_template.r[11].type = htons(NF9_TCP_FLAGS);
v6_template.r[11].length = htons(1);
v6_template.r[12].type = htons(NF9_IP_PROTOCOL_VERSION);
v6_template.r[12].length = htons(1);
v6_template.r[13].type = htons(NF9_TOS);
v6_template.r[13].length = htons(1);
#ifdef ENABLE_NF9_VLAN
v6_template.r[14].type = htons(NF9_ICMP_TYPE);
v6_template.r[14].length = htons(2);
v6_template.r[15].type = htons(NF9_SRC_VLAN);
v6_template.r[15].length = htons(2);
#endif /* ENABLE_NF9_VLAN */
}
static void
nf9_init_option(u_int16_t ifidx, struct OPTION *option) {
bzero(&option_template, sizeof(option_template));
option_template.h.c.flowset_id = htons(NF9_OPTIONS_FLOWSET_ID);
option_template.h.c.length = htons(sizeof(option_template));
option_template.h.template_id = htons(NF9_SOFTFLOWD_OPTION_TEMPLATE_ID);
option_template.h.scope_length = htons(sizeof(option_template.s));
option_template.h.option_length = htons(sizeof(option_template.r));
option_template.s[0].type = htons(NF9_OPTION_SCOPE_INTERFACE);
option_template.s[0].length = htons(sizeof(option_data.scope_ifidx));
option_template.r[0].type = htons(NF9_SAMPLING_INTERVAL);
option_template.r[0].length = htons(sizeof(option_data.sampling_interval));
option_template.r[1].type = htons(NF9_SAMPLING_ALGORITHM);
option_template.r[1].length = htons(sizeof(option_data.sampling_algorithm));
bzero(&option_data, sizeof(option_data));
option_data.c.flowset_id = htons(NF9_SOFTFLOWD_OPTION_TEMPLATE_ID);
option_data.c.length = htons(sizeof(option_data));
option_data.scope_ifidx = htonl(ifidx);
option_data.sampling_interval = htonl(option->sample);
option_data.sampling_algorithm = NF9_SAMPLING_ALGORITHM_DETERMINISTIC;
}
static int
nf_flow_to_flowset(const struct FLOW *flow, u_char *packet, u_int len,
u_int16_t ifidx, const struct timeval *system_boot_time, u_int *len_used)
{
union {
struct NF9_SOFTFLOWD_DATA_V4 d4;
struct NF9_SOFTFLOWD_DATA_V6 d6;
} d[2];
struct NF9_SOFTFLOWD_DATA_COMMON *dc[2];
u_int freclen, ret_len, nflows;
bzero(d, sizeof(d));
*len_used = nflows = ret_len = 0;
switch (flow->af) {
case AF_INET:
freclen = sizeof(struct NF9_SOFTFLOWD_DATA_V4);
memcpy(&d[0].d4.src_addr, &flow->addr[0].v4, 4);
memcpy(&d[0].d4.dst_addr, &flow->addr[1].v4, 4);
memcpy(&d[1].d4.src_addr, &flow->addr[1].v4, 4);
memcpy(&d[1].d4.dst_addr, &flow->addr[0].v4, 4);
dc[0] = &d[0].d4.c;
dc[1] = &d[1].d4.c;
dc[0]->ipproto = dc[1]->ipproto = 4;
break;
case AF_INET6:
freclen = sizeof(struct NF9_SOFTFLOWD_DATA_V6);
memcpy(&d[0].d6.src_addr, &flow->addr[0].v6, 16);
memcpy(&d[0].d6.dst_addr, &flow->addr[1].v6, 16);
memcpy(&d[1].d6.src_addr, &flow->addr[1].v6, 16);
memcpy(&d[1].d6.dst_addr, &flow->addr[0].v6, 16);
dc[0] = &d[0].d6.c;
dc[1] = &d[1].d6.c;
dc[0]->ipproto = dc[1]->ipproto = 6;
break;
default:
return (-1);
}
dc[0]->first_switched = dc[1]->first_switched =
htonl(timeval_sub_ms(&flow->flow_start, system_boot_time));
dc[0]->last_switched = dc[1]->last_switched =
htonl(timeval_sub_ms(&flow->flow_last, system_boot_time));
dc[0]->bytes = htonl(flow->octets[0]);
dc[1]->bytes = htonl(flow->octets[1]);
dc[0]->packets = htonl(flow->packets[0]);
dc[1]->packets = htonl(flow->packets[1]);
dc[0]->if_index_in = dc[0]->if_index_out = htonl(ifidx);
dc[1]->if_index_in = dc[1]->if_index_out = htonl(ifidx);
dc[0]->src_port = dc[1]->dst_port = flow->port[0];
dc[1]->src_port = dc[0]->dst_port = flow->port[1];
dc[0]->protocol = dc[1]->protocol = flow->protocol;
dc[0]->tcp_flags = flow->tcp_flags[0];
dc[1]->tcp_flags = flow->tcp_flags[1];
dc[0]->tos = flow->tos[0];
dc[1]->tos = flow->tos[1];
#ifdef ENABLE_NF9_VLAN
if (flow->protocol == IPPROTO_ICMP || flow->protocol == IPPROTO_ICMPV6) {
dc[0]->icmp_type = dc[0]->dst_port;
dc[1]->icmp_type = dc[1]->dst_port;
}
dc[0]->vlanid = dc[1]->vlanid = htons(flow->vlanid);
#endif /* ENABLE_NF9_VLAN */
if (flow->octets[0] > 0) {
if (ret_len + freclen > len)
return (-1);
memcpy(packet + ret_len, &d[0], freclen);
ret_len += freclen;
nflows++;
}
if (flow->octets[1] > 0) {
if (ret_len + freclen > len)
return (-1);
memcpy(packet + ret_len, &d[1], freclen);
ret_len += freclen;
nflows++;
}
*len_used = ret_len;
return (nflows);
}
/*
* Given an array of expired flows, send netflow v9 report packets
* Returns number of packets sent or -1 on error
*/
int
send_netflow_v9(struct FLOW **flows, int num_flows, int nfsock,
u_int16_t ifidx, struct FLOWTRACKPARAMETERS *param,
int verbose_flag)
{
struct NF9_HEADER *nf9;
struct NF9_DATA_FLOWSET_HEADER *dh;
struct timeval now;
u_int offset, last_af, i, j, num_packets, inc, last_valid;
socklen_t errsz;
int err, r;
u_char packet[NF9_SOFTFLOWD_MAX_PACKET_SIZE];
struct timeval *system_boot_time = ¶m->system_boot_time;
u_int64_t *flows_exported = ¶m->flows_exported;
u_int64_t *packets_sent = ¶m->packets_sent;
struct OPTION *option = ¶m->option;
gettimeofday(&now, NULL);
if (nf9_pkts_until_template == -1) {
nf9_init_template();
nf9_pkts_until_template = 0;
if (option != NULL && option->sample > 1){
nf9_init_option(ifidx, option);
}
}
last_valid = num_packets = 0;
for (j = 0; j < num_flows;) {
bzero(packet, sizeof(packet));
nf9 = (struct NF9_HEADER *)packet;
nf9->version = htons(9);
nf9->flows = 0; /* Filled as we go, htons at end */
nf9->uptime_ms = htonl(timeval_sub_ms(&now, system_boot_time));
nf9->time_sec = htonl(time(NULL));
nf9->source_id = 0;
offset = sizeof(*nf9);
/* Refresh template headers if we need to */
if (nf9_pkts_until_template <= 0) {
memcpy(packet + offset, &v4_template,
sizeof(v4_template));
offset += sizeof(v4_template);
nf9->flows++;
memcpy(packet + offset, &v6_template,
sizeof(v6_template));
offset += sizeof(v6_template);
nf9->flows++;
if (option != NULL && option->sample > 1){
memcpy(packet + offset, &option_template,
sizeof(option_template));
offset += sizeof(option_template);
nf9->flows++;
memcpy(packet + offset, &option_data,
sizeof(option_data));
offset += sizeof(option_data);
nf9->flows++;
}
nf9_pkts_until_template = NF9_DEFAULT_TEMPLATE_INTERVAL;
}
dh = NULL;
last_af = 0;
for (i = 0; i + j < num_flows; i++) {
if (dh == NULL || flows[i + j]->af != last_af) {
if (dh != NULL) {
if (offset % 4 != 0) {
/* Pad to multiple of 4 */
dh->c.length += 4 - (offset % 4);
offset += 4 - (offset % 4);
}
/* Finalise last header */
dh->c.length = htons(dh->c.length);
}
if (offset + sizeof(*dh) > sizeof(packet)) {
/* Mark header is finished */
dh = NULL;
break;
}
dh = (struct NF9_DATA_FLOWSET_HEADER *)
(packet + offset);
dh->c.flowset_id =
(flows[i + j]->af == AF_INET) ?
v4_template.h.template_id :
v6_template.h.template_id;
last_af = flows[i + j]->af;
last_valid = offset;
dh->c.length = sizeof(*dh); /* Filled as we go */
offset += sizeof(*dh);
}
r = nf_flow_to_flowset(flows[i + j], packet + offset,
sizeof(packet) - offset, ifidx, system_boot_time, &inc);
if (r <= 0) {
/* yank off data header, if we had to go back */
if (last_valid)
offset = last_valid;
break;
}
offset += inc;
dh->c.length += inc;
nf9->flows += r;
last_valid = 0; /* Don't clobber this header now */
if (verbose_flag) {
logit(LOG_DEBUG, "Flow %d/%d: "
"r %d offset %d type %04x len %d(0x%04x) "
"flows %d", r, i, j, offset,
dh->c.flowset_id, dh->c.length,
dh->c.length, nf9->flows);
}
}
/* Don't finish header if it has already been done */
if (dh != NULL) {
if (offset % 4 != 0) {
/* Pad to multiple of 4 */
dh->c.length += 4 - (offset % 4);
offset += 4 - (offset % 4);
}
/* Finalise last header */
dh->c.length = htons(dh->c.length);
}
param->records_sent += nf9->flows;
nf9->flows = htons(nf9->flows);
nf9->package_sequence = htonl((u_int32_t)((*packets_sent + num_packets + 1) & 0x00000000ffffffff));
if (verbose_flag)
logit(LOG_DEBUG, "Sending flow packet len = %d", offset);
errsz = sizeof(err);
/* Clear ICMP errors */
getsockopt(nfsock, SOL_SOCKET, SO_ERROR, &err, &errsz);
if (send(nfsock, packet, (size_t)offset, 0) == -1)
return (-1);
num_packets++;
nf9_pkts_until_template--;
j += i;
}
*flows_exported += j;
return (num_packets);
}
void
netflow9_resend_template(void)
{
if (nf9_pkts_until_template > 0)
nf9_pkts_until_template = 0;
}