1 /*
2 * Copyright (c) 2014, Peter Haag
3 * Copyright (c) 2009, Peter Haag
4 * Copyright (c) 2004-2008, SWITCH - Teleinformatikdienste fuer Lehre und Forschung
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * * Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 * * Neither the name of the author nor the names of its contributors may be
16 * used to endorse or promote products derived from this software without
17 * specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 * $Author: peter $
32 *
33 * $Id: nftrack_rrd.c 224 2014-02-16 12:59:29Z peter $
34 *
35 * $LastChangedRevision: 224 $
36 *
37 *
38 */
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <limits.h>
43 #include <time.h>
44 #include <unistd.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <fcntl.h>
48 #include <string.h>
49 #include <errno.h>
50
51 #include "config.h"
52
53 #ifdef HAVE_STDINT_H
54 #include <stdint.h>
55 #endif
56
57 #include "rrd.h"
58 #include "nftrack_stat.h"
59 #include "nftrack_rrd.h"
60
61 #define BUFF_CHECK(num,buffsize) if ( (num) >= (buffsize) ) { \
62 fprintf(stderr, "No enough space to create RRD arg\n"); \
63 exit(0); \
64 }
65
66 // temporary RRD file
67 #define TMPRRD "ports.rrd"
68
69 #define MAXBUFF 15 * 1024;
70
71 /* global const */
72 static const char *proto[] = { "tcp", "udp" };
73 static const char *type[] = { "flows", "packets", "bytes" };
74
75 /* Local prototypes */
76
77 static void CreateRRDB (char *filename, time_t when);
78
79 /* Functions */
80
CreateRRDB(char * filename,time_t when)81 static void CreateRRDB (char *filename, time_t when) {
82 char *buff, *s, *rrd_arg[1100];
83 long i, num, buffsize, argc;
84
85 optind = 0; opterr = 0;
86 argc = 0;
87 /*
88 Create bufferspace for create args:
89 1024 DS records: each ~ 23 bytes in average +
90 3 RRA records + filename + start time => 512 bytes should be more than enough
91 */
92 buffsize = 23 * 1024 + 512;
93 buff = (char *)malloc(buffsize);
94 if ( !buff ) {
95 perror("Memory error!");
96 exit(0);
97 }
98
99 s = buff;
100
101 unlink(filename);
102
103 rrd_arg[argc++] = "create";
104
105 // add DB name
106 rrd_arg[argc++] = filename;
107
108 // Add start time
109 num = snprintf(s, buffsize, "--start=%lld", (long long)when);
110 num++; // include '\0'
111 BUFF_CHECK(num,buffsize);
112 rrd_arg[argc++] = s;
113
114 buffsize -= num;
115 s += num;
116
117 /* Add the DS strings */
118 for ( i=0; i<1024; i++) {
119 num = snprintf(s, buffsize, "DS:p%ld:GAUGE:600:0:U", i);
120 num++; // include '\0'
121 // printf("I: %ld ", i);
122 BUFF_CHECK(num,buffsize);
123 rrd_arg[argc++] = s;
124
125 buffsize -= num;
126 s += num;
127 }
128
129 /*
130 RRD DB layout:
131 1 x 5min = 5 min samples 7 * 288 ( per day ) = 2016 => 7 days
132 24 x 5min = 2 hour samples 60 * 12 ( per day ) = 720 => 60 days
133 288 x 5min = 1 day samples 180 * 1 ( per day ) = 180 => 180 days
134 */
135
136 num = snprintf(s, buffsize, "RRA:AVERAGE:0.5:1:2016");
137 num++; // include '\0'
138 BUFF_CHECK(num,buffsize);
139 rrd_arg[argc++] = s;
140
141 buffsize -= num;
142 s += num;
143
144 num = snprintf(s, buffsize, "RRA:AVERAGE:0.5:24:720");
145 num++; // include '\0'
146 BUFF_CHECK(num,buffsize);
147 rrd_arg[argc++] = s;
148
149 buffsize -= num;
150 s += num;
151
152 num = snprintf(s, buffsize, "RRA:AVERAGE:0.5:288:180");
153 num++; // include '\0'
154 BUFF_CHECK(num,buffsize);
155 rrd_arg[argc] = s;
156
157 /*
158 for ( i=0; i<=argc; i++ ) {
159 printf("I:%ld %s\n", i, rrd_arg[i]);
160 }
161 */
162
163 rrd_clear_error();
164 if ( ( i=rrd_create(argc, rrd_arg))) {
165 fprintf(stderr, "Create DB Error: %ld %s\n", i, rrd_get_error());
166 }
167
168 } // End of CreateRRDB
169
CreateRRDBs(char * path,time_t when)170 int CreateRRDBs (char *path, time_t when) {
171 const char progress[] = { '|', '/', '-', '|', '\\', '-' };
172 char rrd_filename[1024];
173 int fd, i, p, t, len, total;
174 struct stat statbuf;
175 void *buff;
176
177 // Check if path exists
178 if ( (stat(path, &statbuf) < 0 ) || !(statbuf.st_mode & S_IFDIR) ) {
179 fprintf(stderr, "No such directory: '%s'\n", path);
180 return 0;
181 }
182
183 // make stdout unbuffered for progress pointer
184 setvbuf(stdout, (char *)NULL, _IONBF, 0);
185
186 printf("Create DBs ... ");
187
188 /*
189 * we create an RRD DB file and will copy this file
190 * that many time as required - so every RRD file looks the
191 * same. They only distinguish by their name
192 */
193 len = snprintf(rrd_filename, 1024, "%s/%s", path, TMPRRD);
194 if ( len >= 1024 ) {
195 fprintf(stderr, "Failed to concat RRD filename: string overflow");
196 return 0;
197 }
198
199 CreateRRDB(rrd_filename, when);
200 if ( (i = stat(rrd_filename, &statbuf) < 0 )) {
201 fprintf(stderr, "Can't create RRD file '%s': %s\n", rrd_filename, strerror(errno));
202 return 0;
203 }
204 buff = malloc(statbuf.st_size);
205 if ( !buff ) {
206 perror("Buffer allocation failed");
207 unlink(rrd_filename);
208 return 0;
209 }
210 fd = open(rrd_filename, O_RDONLY, 0);
211 if ( fd < 0 ) {
212 perror("Failed to open RRD file");
213 unlink(rrd_filename);
214 return 0;
215 }
216 if ( read(fd, buff, statbuf.st_size) != statbuf.st_size ) {
217 perror("Failed to read data from RRD file");
218 close(fd);
219 unlink(rrd_filename);
220 return 0;
221 }
222 close(fd);
223 unlink(rrd_filename);
224 printf("\n");
225
226 // we are now ready to multiplicate the DB files
227 total = 384; // 2 * 3 * 64 files total
228 for (p=tcp; p<=udp; p++) { // for TCP and UDP
229 for (t=flows; t<=bytes; t++) { // for flows, packets and bytes
230 for (i=0; i<64; i++) { // Create 64 times an RRD DB - each for 1024 ports
231 printf("Creating %s:%s %c Left: %d files \r", proto[p], type[t], progress[i % 6], total );
232 len = snprintf(rrd_filename, 1024, "%s/%s-%s-%d.rrd", path, proto[p], type[t], i);
233 if ( len >= 1024 ) {
234 fprintf(stderr, "Failed to concat RRD filename: string overflow");
235 free(buff);
236 return 0;
237 }
238 fd = open(rrd_filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH );
239 if ( fd < 0 ) {
240 fprintf(stderr, "Failed to create RRD file '%s': %s\n", rrd_filename, strerror(errno));
241 free(buff);
242 return 0;
243 }
244 if ( write(fd, buff, statbuf.st_size) != statbuf.st_size ) {
245 fprintf(stderr, "Failed to write RRD file '%s': %s\n", rrd_filename, strerror(errno));
246 free(buff);
247 return 0;
248 }
249 close(fd);
250 total--;
251 }
252 }
253 }
254
255 printf("\n");
256 return 1;
257
258 } // End of CreateRRDBs
259
RRD_StoreDataRow(char * path,char * iso_time,data_row * row)260 int RRD_StoreDataRow(char *path, char *iso_time, data_row *row) {
261 char rrd_filename[1024], *buff, *s;
262 char *rrd_arg[10];
263 time_t when, frag;
264 int i, j, len, p, t, buffsize, argc;
265 uint32_t pnum;
266 struct stat statbuf;
267
268 buffsize = MAXBUFF;
269 buff = (char *)malloc(buffsize);
270 if ( !buff ) {
271 perror("Memory error!");
272 return 0;
273 }
274
275 when = ISO2UNIX(iso_time);
276 if ( !when )
277 return 0;
278
279 // make sure, we are at a 5min boundary
280 frag = when % 300;
281 if ( frag ) {
282 fprintf(stderr, "Round to next timeslot: offset %lld\n", (long long)frag);
283 when -= frag;
284 }
285
286 for ( p=tcp; p<=udp; p++ ) {
287 // for every protocol TCP - UDP
288 for ( t=flows; t<=bytes; t++ ) {
289 // for every type flows - packets - bytes
290 for (j=0; j<64; j++) {
291 // for all 64 RRD files in proto - type
292 len = snprintf(rrd_filename, 1024, "%s/%s-%s-%d.rrd", path, proto[p], type[t], j);
293 if ( len >= 1024 ) {
294 fprintf(stderr, "Failed to concat RRD filename: string overflow");
295 return 0;
296 }
297
298 // Check if RRD file exists
299 if ( (stat(rrd_filename, &statbuf) < 0 ) || !(statbuf.st_mode & S_IFREG) ) {
300 fprintf(stderr, "No such RRD file: '%s'\n", rrd_filename);
301 return 0;
302 }
303
304 buffsize = MAXBUFF;
305 s = buff;
306
307 /* add time to RRD arg string */
308 len = snprintf(s, buffsize, "%lld:", (long long)when);
309 buffsize -= len;
310 s += len;
311
312 /* add port data to RRD arg string */
313 for ( i=0; i<1024; i++) {
314 pnum = ( j << 10 ) + i;
315 /*
316 if ( row[pnum].proto[p].type[t] ) {
317 fprintf(stderr, "%d %d %d\n", pnum, p, t);
318 }
319 */
320 len = snprintf(s, buffsize, "%llu:", (long long unsigned)row[pnum].proto[p].type[t]);
321 if ( len >= buffsize ) {
322 fprintf(stderr, "No enough space to create RRD arg\n");
323 return 0;
324 }
325 buffsize -= len;
326 s += len;
327 }
328 s--;
329 *s = '\0';
330
331 // Create arg vector
332 argc = 0;
333 rrd_arg[argc++] = "update";
334 rrd_arg[argc++] = rrd_filename;
335 rrd_arg[argc++] = buff;
336 rrd_arg[argc] = NULL;
337
338 optind = 0; opterr = 0;
339 rrd_clear_error();
340 if ( ( i=rrd_update(argc, rrd_arg))) {
341 fprintf(stderr, "RRD: %s Insert Error: %d %s\n", rrd_filename, i, rrd_get_error());
342 }
343 } // for all 64 rrd files
344 } // for every type flows - packets - bytes
345 } // for every protocol TCP - UDP
346
347 return 1;
348 } // End of RRD_StoreDataRow
349
RRD_GetDataRow(char * path,time_t when)350 data_row *RRD_GetDataRow(char *path, time_t when) {
351 time_t last, frag;
352 struct tm * t1, *t2;
353 struct stat statbuf;
354 char datestr1[64] , datestr2[64], rrd_filename[1024];
355 char *rrd_arg[10];
356 char **ds_namv;
357 int ret, i, j, p, t, len, argc;
358 unsigned long step, ds_cnt, pnum;
359 data_row *row;
360 rrd_value_t *data;
361 uint64_t dummy;
362
363 data = NULL;
364 frag = when % 300;
365 if ( frag ) {
366 fprintf(stderr, "Round to next timeslot: offset %lld\n", (long long)frag);
367 when -= frag;
368 }
369
370 last = RRD_LastUpdate(path);
371 if ( when > last ) {
372 t1 = localtime(&when);
373 strftime(datestr1, 63, "%b %d %Y %T", t1);
374
375 t2 = localtime(&last);
376 strftime(datestr2, 63, "%b %d %Y %T", t2);
377
378 fprintf(stderr, "Error get data: Requested time slot '%s' later then last available time slot '%s'\n",
379 datestr1, datestr2);
380
381 return NULL;
382 }
383
384 row = (data_row *)calloc(65536, sizeof(data_row));
385 if ( !row ) {
386 perror("Memory allocation error");
387 return NULL;
388 }
389
390 len = snprintf(datestr1, 64, "--start=%lld", (long long)when);
391 if ( len >= 64 ) {
392 fprintf(stderr, "String overflow --start\n");
393 free(row);
394 return NULL;
395 }
396 len = snprintf(datestr2, 64, "--end=%lld", (long long)when);
397 if ( len >= 64 ) {
398 fprintf(stderr, "String overflow --end\n");
399 free(row);
400 return NULL;
401 }
402
403 for ( p=tcp; p<=udp; p++ ) {
404 // for every protocol TCP - UDP
405 for ( t=flows; t<=bytes; t++ ) {
406 // for every type flows - packets - bytes
407 for (j=0; j<64; j++) {
408 // for all 64 RRD files in proto - type
409 len = snprintf(rrd_filename, 1024, "%s/%s-%s-%d.rrd", path, proto[p], type[t], j);
410 if ( len >= 1024 ) {
411 fprintf(stderr, "Failed to concat RRD filename: string overflow");
412 free(row);
413 return NULL;
414 }
415
416 // Check if RRD file exists
417 if ( (stat(rrd_filename, &statbuf) < 0 ) || !(statbuf.st_mode & S_IFREG) ) {
418 fprintf(stderr, "No such RRD file: '%s'\n", rrd_filename);
419 free(row);
420 return NULL;
421 }
422
423
424 // Create arg vector
425 argc = 0;
426 rrd_arg[argc++] = "fetch";
427 rrd_arg[argc++] = rrd_filename;
428 rrd_arg[argc++] = "AVERAGE";
429 rrd_arg[argc++] = datestr1;
430 rrd_arg[argc++] = datestr2;
431 rrd_arg[argc] = NULL;
432
433 optind = 0; opterr = 0;
434 rrd_clear_error();
435 if ( ( ret=rrd_fetch(argc, rrd_arg, &when, &when, &step, &ds_cnt, &ds_namv, &data))) {
436 fprintf(stderr, "RRD: %s Fetch Error: %d %s\n", rrd_filename, ret, rrd_get_error());
437 }
438 if ( ds_cnt != 1024 ) {
439 fprintf(stderr, "RRD: %s Fetch Error: Short read: Expected 1024 records got %lu\n",
440 rrd_filename, ds_cnt);
441 free(row);
442 return NULL;
443 }
444
445 for ( i=0; i<1024; i++) {
446 pnum = ( j << 10 ) + i;
447 dummy = data[0];
448 row[pnum].proto[p].type[t] = dummy;
449 }
450
451 free(ds_namv);
452 free(data);
453
454 } // for all 64 rrd files
455 } // for every type flows - packets - bytes
456 } // for every protocol TCP - UDP
457
458 return row;
459
460 } // End of RRD_GetDataRow
461
RRD_LastUpdate(char * path)462 time_t RRD_LastUpdate(char *path) {
463 struct stat statbuf;
464 char rrd_filename[1024];
465 char *rrd_arg[10];
466 time_t when;
467 int len, argc;
468
469 // Get timestamp from the first file
470 len = snprintf(rrd_filename, 1024, "%s/%s-%s-%d.rrd", path, "tcp", "flows", 0);
471 if ( len >= 1024 ) {
472 fprintf(stderr, "Failed to concat RRD filename: string overflow");
473 return 0;
474 }
475
476 // Check if RRD file exists
477 if ( (stat(rrd_filename, &statbuf) < 0 ) || !(statbuf.st_mode & S_IFREG) ) {
478 fprintf(stderr, "RRD files not found in '%s'\n", path);
479 return 0;
480 }
481
482 argc = 0;
483 rrd_arg[argc++] = "last";
484 rrd_arg[argc++] = rrd_filename;
485 rrd_arg[argc] = NULL;
486
487 when = rrd_last(argc, rrd_arg);
488
489 return when;
490
491 } // End of RRD_LastUpdate
492
493 /*
494 int main () {
495 char *buff, *s, *rrd_arg[10];
496 long i, num, buffsize, argc;
497 time_t now;
498
499 CreateRRDBs("/data/rrd-db");
500 exit(0);
501
502 buffsize = 15 * 1024;
503 buff = (char *)malloc(buffsize);
504 if ( !buff ) {
505 perror("Memory error!");
506 exit(0);
507 }
508
509 s = buff;
510 now = time(NULL);
511 now -= now % 300;
512
513 num = snprintf(s, buffsize, "%ld:", now);
514 // num = snprintf(s, buffsize, "N:");
515 buffsize -= num;
516 s += num;
517
518 for ( i=0; i<1024; i++) {
519 num = snprintf(s, buffsize, "%ld:", i);
520 if ( num >= buffsize ) {
521 fprintf(stderr, "No enough space to create RRD arg\n");
522 exit(0);
523 }
524 buffsize -= num;
525 s += num;
526 }
527 s--;
528 *s = '\0';
529 printf("String: %s\n", buff);
530
531 argc = 0;
532 rrd_arg[argc++] = "update";
533 rrd_arg[argc++] = "ports.rrd";
534 rrd_arg[argc++] = buff;
535 rrd_arg[argc] = NULL;
536
537 rrd_clear_error();
538 if ( ( i=rrd_update(argc, rrd_arg))) {
539 fprintf(stderr, "Insert Error: %ld %s\n", i, rrd_get_error());
540 }
541
542 return 0;
543 }
544
545 */
546