1 /*
2 Copyright 2021 Northern.tech AS
3
4 This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; version 3.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18
19 To the extent this program is licensed as part of the Enterprise
20 versions of CFEngine, the applicable Commercial Open Source License
21 (COSL) may apply to this file if you as a licensee so wish it. See
22 included file COSL.txt.
23 */
24
25 #include <cf3.defs.h>
26
27 #include <mon.h>
28 #include <systype.h>
29 #include <item_lib.h>
30 #include <files_names.h>
31 #include <files_interfaces.h>
32 #include <files_lib.h>
33 #include <file_lib.h> // SetUmask()
34 #include <pipes.h>
35 #include <known_dirs.h>
36
37 #ifdef __linux__
38 #include <proc_net_parsing.h>
39 #endif
40
41 /* Globals */
42
43 Item *ALL_INCOMING = NULL;
44 Item *MON_UDP4 = NULL, *MON_UDP6 = NULL, *MON_TCP4 = NULL, *MON_TCP6 = NULL;
45
46 typedef enum {
47 cfn_udp4 = 0,
48 cfn_udp6,
49 cfn_tcp4,
50 cfn_tcp6,
51 cfn_unknown,
52 } SocketType;
53
54 /*******************************************************************/
55 /* Anomaly */
56 /*******************************************************************/
57
58 typedef struct
59 {
60 uint32_t port;
61 char *portnr;
62 char *name;
63 enum observables in;
64 enum observables out;
65 } Sock;
66
67 static const Sock ECGSOCKS[] = /* extended to map old to new using enum */
68 {
69 {137, "137", "netbiosns", ob_netbiosns_in, ob_netbiosns_out},
70 {138, "138", "netbiosdgm", ob_netbiosdgm_in, ob_netbiosdgm_out},
71 {139, "139", "netbiosssn", ob_netbiosssn_in, ob_netbiosssn_out},
72 {445, "445", "microsoft_ds", ob_microsoft_ds_in, ob_microsoft_ds_out},
73 {5308, "5308", "cfengine", ob_cfengine_in, ob_cfengine_out},
74 {2049, "2049", "nfsd", ob_nfsd_in, ob_nfsd_out},
75 {25, "25", "smtp", ob_smtp_in, ob_smtp_out},
76 {80, "80", "www", ob_www_in, ob_www_out},
77 {8080, "8080", "www-alt", ob_www_alt_in, ob_www_alt_out},
78 {21, "21", "ftp", ob_ftp_in, ob_ftp_out},
79 {22, "22", "ssh", ob_ssh_in, ob_ssh_out},
80 {433, "443", "wwws", ob_wwws_in, ob_wwws_out},
81 {143, "143", "imap", ob_imap_in, ob_imap_out},
82 {993, "993", "imaps", ob_imaps_in, ob_imaps_out},
83 {389, "389", "ldap", ob_ldap_in, ob_ldap_out},
84 {636, "636", "ldaps", ob_ldaps_in, ob_ldaps_out},
85 {27017, "27017", "mongo", ob_mongo_in, ob_mongo_out},
86 {3306, "3306", "mysql", ob_mysql_in, ob_mysql_out},
87 {5432, "5432", "postgresql", ob_postgresql_in, ob_postgresql_out},
88 {631, "631", "ipp", ob_ipp_in, ob_ipp_out},
89 };
90 #define ATTR (sizeof(ECGSOCKS) / sizeof(ECGSOCKS[0]))
91
92 static const char *const VNETSTAT[] =
93 {
94 [PLATFORM_CONTEXT_UNKNOWN] = "-",
95 [PLATFORM_CONTEXT_OPENVZ] = "/bin/netstat", /* virt_host_vz_vzps */
96 [PLATFORM_CONTEXT_HP] = "/usr/bin/netstat", /* hpux */
97 [PLATFORM_CONTEXT_AIX] = "/usr/bin/netstat", /* aix */
98 [PLATFORM_CONTEXT_LINUX] = "/bin/netstat", /* linux */
99 [PLATFORM_CONTEXT_BUSYBOX] = "/bin/netstat", /* linux */
100 [PLATFORM_CONTEXT_SOLARIS] = "/usr/bin/netstat", /* solaris */
101 [PLATFORM_CONTEXT_SUN_SOLARIS] = "/usr/bin/netstat", /* solaris */
102 [PLATFORM_CONTEXT_FREEBSD] = "/usr/bin/netstat", /* freebsd */
103 [PLATFORM_CONTEXT_NETBSD] = "/usr/bin/netstat", /* netbsd */
104 [PLATFORM_CONTEXT_CRAYOS] = "/usr/ucb/netstat", /* cray */
105 [PLATFORM_CONTEXT_WINDOWS_NT] = "/cygdrive/c/WINNT/System32/netstat", /* CygWin */
106 [PLATFORM_CONTEXT_SYSTEMV] = "/usr/bin/netstat", /* Unixware */
107 [PLATFORM_CONTEXT_OPENBSD] = "/usr/bin/netstat", /* openbsd */
108 [PLATFORM_CONTEXT_CFSCO] = "/usr/bin/netstat", /* sco */
109 [PLATFORM_CONTEXT_DARWIN] = "/usr/sbin/netstat", /* darwin */
110 [PLATFORM_CONTEXT_QNX] = "/usr/bin/netstat", /* qnx */
111 [PLATFORM_CONTEXT_DRAGONFLY] = "/usr/bin/netstat", /* dragonfly */
112 [PLATFORM_CONTEXT_MINGW] = "mingw-invalid", /* mingw */
113 [PLATFORM_CONTEXT_VMWARE] = "/usr/bin/netstat", /* vmware */
114 [PLATFORM_CONTEXT_ANDROID] = "/system/xbin/netstat", /* android */
115 };
116
117 /* Implementation */
118
MonNetworkInit(void)119 void MonNetworkInit(void)
120 {
121
122 DeleteItemList(MON_TCP4);
123 DeleteItemList(MON_TCP6);
124 DeleteItemList(MON_UDP4);
125 DeleteItemList(MON_UDP6);
126
127 MON_UDP4 = MON_UDP6 = MON_TCP4 = MON_TCP6 = NULL;
128
129 char vbuff[CF_BUFSIZE];
130 const char* const statedir = GetStateDir();
131
132 const char* const file_stems[] = { "cf_incoming", "cf_outgoing" };
133 const size_t num_files = sizeof(file_stems) / sizeof(char*);
134
135 for (size_t i = 0; i < ATTR; i++)
136 {
137 for (size_t j = 0; j < num_files; j++)
138 {
139 snprintf(vbuff, CF_BUFSIZE, "%s/%s.%s",
140 statedir, file_stems[j], ECGSOCKS[i].name);
141
142 MapName(vbuff);
143 CreateEmptyFile(vbuff);
144 }
145 }
146 }
147
148 /******************************************************************************/
149
SetNetworkEntropyClasses(const char * service,const char * direction,const Item * list)150 static void SetNetworkEntropyClasses(const char *service, const char *direction, const Item *list)
151 {
152 const Item *ip;
153 Item *addresses = NULL;
154 double entropy;
155
156 for (ip = list; ip != NULL; ip = ip->next)
157 {
158 if (strlen(ip->name) > 0)
159 {
160 char local[CF_BUFSIZE];
161 char remote[CF_BUFSIZE];
162 char vbuff[CF_BUFSIZE];
163 char *sp;
164
165 if (strncmp(ip->name, "tcp", 3) == 0)
166 {
167 sscanf(ip->name, "%*s %*s %*s %s %s", local, remote); /* linux-like */
168 }
169 else
170 {
171 sscanf(ip->name, "%s %s", local, remote); /* solaris-like */
172 }
173
174 strncpy(vbuff, remote, CF_BUFSIZE - 1);
175 vbuff[CF_BUFSIZE-1] = '\0';
176
177 for (sp = vbuff + strlen(vbuff) - 1; isdigit((int) *sp) && (sp > vbuff); sp--)
178 {
179 }
180
181 *sp = '\0';
182
183 if (!IsItemIn(addresses, vbuff))
184 {
185 AppendItem(&addresses, vbuff, "");
186 }
187
188 IncrementItemListCounter(addresses, vbuff);
189 }
190 }
191
192 entropy = MonEntropyCalculate(addresses);
193 MonEntropyClassesSet(service, direction, entropy);
194 DeleteItemList(addresses);
195 }
196
197 /******************************************************************************/
198
199 static void SaveNetworkData(Item * const *in, Item * const *out);
200 static void GetNetworkDataFromNetstat(FILE *fp, double *cf_this, Item **in, Item **out);
201 #ifdef __linux__
202 static bool GetNetworkDataFromProcNet(double *cf_this, Item **in, Item **out);
203 #endif
204
ResetNetworkData()205 static inline void ResetNetworkData()
206 {
207 DeleteItemList(ALL_INCOMING);
208 ALL_INCOMING = NULL;
209
210 DeleteItemList(MON_TCP4);
211 DeleteItemList(MON_TCP6);
212 DeleteItemList(MON_UDP4);
213 DeleteItemList(MON_UDP6);
214 MON_UDP4 = MON_UDP6 = MON_TCP4 = MON_TCP6 = NULL;
215 }
216
MonNetworkGatherData(double * cf_this)217 void MonNetworkGatherData(double *cf_this)
218 {
219 ResetNetworkData();
220
221 Item *in[ATTR] = {0};
222 Item *out[ATTR] = {0};
223
224 #ifdef __linux__
225 /* On Linux, prefer parsing data from /proc/net with our custom code (more
226 * efficient), but fall back to netstat if proc parsing fails. */
227 if ((access("/proc/net/tcp", R_OK) != 0) || !GetNetworkDataFromProcNet(cf_this, in, out))
228 #endif
229 {
230 char comm[PATH_MAX + 4] = {0}; /* path to the binary + " -an" */
231 strncpy(comm, VNETSTAT[VSYSTEMHARDCLASS], (sizeof(comm) - 1));
232
233 if (!FileCanOpen(comm, "r"))
234 {
235 Log(LOG_LEVEL_VERBOSE,
236 "Cannot open '%s', aborting gathering of network data (monitoring)",
237 comm);
238 return;
239 }
240
241 strncat(comm, " -an", sizeof(comm) - 1);
242
243 FILE *pp;
244 if ((pp = cf_popen(comm, "r", true)) == NULL)
245 {
246 Log(LOG_LEVEL_VERBOSE,
247 "Opening '%s' failed, aborting gathering of network data (monitoring)",
248 comm);
249 return;
250 }
251
252 GetNetworkDataFromNetstat(pp, cf_this, in, out);
253 cf_pclose(pp);
254 }
255
256 /* Now save the state for ShowState()
257 the state is not smaller than the last or at least 40 minutes
258 older. This mirrors the persistence of the maxima classes */
259 SaveNetworkData(in, out);
260 }
261
262 #ifdef __linux__
SaveSocketInfo(const char * local_addr,uint32_t local_port,uint32_t remote_port,SocketState state,SocketType type,const char * socket_info,double * cf_this,Item ** in,Item ** out)263 static inline void SaveSocketInfo(const char *local_addr,
264 uint32_t local_port,
265 uint32_t remote_port,
266 SocketState state,
267 SocketType type,
268 const char *socket_info,
269 double *cf_this,
270 Item **in, Item **out)
271 {
272 Log(LOG_LEVEL_DEBUG, "Saving socket info '%s:%d:%d [%d, %d]",
273 local_addr, local_port, remote_port, state, type);
274
275 if (state == SOCK_STATE_LISTEN)
276 {
277 char port_str[CF_MAX_PORT_LEN];
278 snprintf(port_str, sizeof(port_str), "%d", local_port);
279
280 IdempPrependItem(&ALL_INCOMING, port_str, NULL);
281
282 switch (type)
283 {
284 case cfn_tcp4:
285 IdempPrependItem(&MON_TCP4, port_str, local_addr);
286 break;
287 case cfn_tcp6:
288 IdempPrependItem(&MON_TCP6, port_str, local_addr);
289 break;
290 case cfn_udp4:
291 IdempPrependItem(&MON_UDP4, port_str, local_addr);
292 break;
293 case cfn_udp6:
294 IdempPrependItem(&MON_UDP6, port_str, local_addr);
295 break;
296 default:
297 debug_abort_if_reached();
298 break;
299 }
300 }
301 for (size_t i = 0; i < ATTR; i++)
302 {
303 if (local_port == ECGSOCKS[i].port)
304 {
305 cf_this[ECGSOCKS[i].in]++;
306 AppendItem(&in[i], socket_info, "");
307
308 }
309
310 if (remote_port == ECGSOCKS[i].port)
311 {
312 cf_this[ECGSOCKS[i].out]++;
313 AppendItem(&out[i], socket_info, "");
314
315 }
316 }
317 }
318
GetNetworkDataFromProcNetTCP(char local_addr[INET_ADDRSTRLEN],char remote_addr[INET_ADDRSTRLEN],char ** buff,size_t * buff_size,Seq * lines,double * cf_this,Item ** in,Item ** out)319 static inline bool GetNetworkDataFromProcNetTCP(char local_addr[INET_ADDRSTRLEN],
320 char remote_addr[INET_ADDRSTRLEN],
321 char **buff, size_t *buff_size, Seq *lines,
322 double *cf_this, Item **in, Item **out)
323 {
324 FILE *fp = fopen("/proc/net/tcp", "r");
325 if (fp == NULL)
326 {
327 Log(LOG_LEVEL_ERR, "Failed to open /proc/net/tcp for reading");
328 return false;
329 }
330 /* Read the header */
331 ssize_t ret = CfReadLine(buff, buff_size, fp);
332 if (ret == -1)
333 {
334 Log(LOG_LEVEL_ERR, "Failed to read data from /proc/net/tcp");
335 fclose(fp);
336 return false;
337 }
338
339 /* Read real data */
340 ret = CfReadLines(buff, buff_size, fp, lines);
341 if (ret < 0)
342 {
343 Log(LOG_LEVEL_ERR, "Failed to read data from /proc/net/tcp");
344 fclose(fp);
345 return false;
346 }
347 Log(LOG_LEVEL_VERBOSE, "Read %zu lines from /proc/net/tcp", ret);
348
349 uint32_t l_port, r_port;
350 SocketState state;
351 for (size_t i = 0; i < (size_t) ret; i++)
352 {
353 char *line = SeqAt(lines,i);
354 if (ParseIPv4SocketInfo(line, local_addr, &l_port, remote_addr, &r_port, &state))
355 {
356 SaveSocketInfo(local_addr, l_port, r_port,
357 state, cfn_tcp4,
358 line, cf_this, in, out);
359 }
360 }
361 fclose(fp);
362 SeqClear(lines);
363 return true;
364 }
365
GetNetworkDataFromProcNetTCP6(char local_addr6[INET6_ADDRSTRLEN],char remote_addr6[INET6_ADDRSTRLEN],char ** buff,size_t * buff_size,Seq * lines,double * cf_this,Item ** in,Item ** out)366 static inline bool GetNetworkDataFromProcNetTCP6(char local_addr6[INET6_ADDRSTRLEN],
367 char remote_addr6[INET6_ADDRSTRLEN],
368 char **buff, size_t *buff_size, Seq *lines,
369 double *cf_this, Item **in, Item **out)
370 {
371 FILE *fp = fopen("/proc/net/tcp6", "r");
372 if (fp == NULL)
373 {
374 /* IPv6 may be completely disabled in kernel so this should be handled gracefully. */
375 Log(LOG_LEVEL_VERBOSE, "Failed to read data from /proc/net/tcp6");
376 return true;
377 }
378 ssize_t ret = CfReadLine(buff, buff_size, fp);
379 if (ret == -1)
380 {
381 Log(LOG_LEVEL_ERR, "Failed to read data from /proc/net/tcp6");
382 fclose(fp);
383 return false;
384 }
385
386 /* Read real data */
387 ret = CfReadLines(buff, buff_size, fp, lines);
388 if (ret < 0)
389 {
390 Log(LOG_LEVEL_ERR, "Failed to read data from /proc/net/tcp6");
391 fclose(fp);
392 return false;
393 }
394 Log(LOG_LEVEL_VERBOSE, "Read %zu lines from /proc/net/tcp6", ret);
395
396 uint32_t l_port, r_port;
397 SocketState state;
398 for (size_t i = 0; i < (size_t) ret; i++)
399 {
400 char *line = SeqAt(lines,i);
401 if (ParseIPv6SocketInfo(line, local_addr6, &l_port, remote_addr6, &r_port, &state))
402 {
403 SaveSocketInfo(local_addr6, l_port, r_port,
404 state, cfn_tcp6,
405 line, cf_this, in, out);
406 }
407 }
408 fclose(fp);
409 SeqClear(lines);
410 return true;
411 }
412
GetNetworkDataFromProcNetUDP(char local_addr[INET_ADDRSTRLEN],char remote_addr[INET_ADDRSTRLEN],char ** buff,size_t * buff_size,Seq * lines,double * cf_this,Item ** in,Item ** out)413 static inline bool GetNetworkDataFromProcNetUDP(char local_addr[INET_ADDRSTRLEN],
414 char remote_addr[INET_ADDRSTRLEN],
415 char **buff, size_t *buff_size, Seq *lines,
416 double *cf_this, Item **in, Item **out)
417 {
418 FILE *fp = fopen("/proc/net/udp", "r");
419 if (fp == NULL)
420 {
421 Log(LOG_LEVEL_ERR, "Failed to open /proc/net/udp for reading");
422 return false;
423 }
424 /* Read the header */
425 ssize_t ret = CfReadLine(buff, buff_size, fp);
426 if (ret == -1)
427 {
428 Log(LOG_LEVEL_ERR, "Failed to read data from /proc/net/udp");
429 fclose(fp);
430 return false;
431 }
432
433 /* Read real data */
434 ret = CfReadLines(buff, buff_size, fp, lines);
435 if (ret < 0)
436 {
437 Log(LOG_LEVEL_ERR, "Failed to read data from /proc/net/udp");
438 fclose(fp);
439 return false;
440 }
441 Log(LOG_LEVEL_VERBOSE, "Read %zu lines from /proc/net/udp", ret);
442
443 uint32_t l_port, r_port;
444 SocketState state;
445 for (size_t i = 0; i < (size_t) ret; i++)
446 {
447 char *line = SeqAt(lines,i);
448 if (ParseIPv4SocketInfo(line, local_addr, &l_port, remote_addr, &r_port, &state))
449 {
450 SaveSocketInfo(local_addr, l_port, r_port,
451 state, cfn_udp4,
452 line, cf_this, in, out);
453 }
454 }
455 fclose(fp);
456 SeqClear(lines);
457 return true;
458 }
459
GetNetworkDataFromProcNetUDP6(char local_addr6[INET6_ADDRSTRLEN],char remote_addr6[INET6_ADDRSTRLEN],char ** buff,size_t * buff_size,Seq * lines,double * cf_this,Item ** in,Item ** out)460 static inline bool GetNetworkDataFromProcNetUDP6(char local_addr6[INET6_ADDRSTRLEN],
461 char remote_addr6[INET6_ADDRSTRLEN],
462 char **buff, size_t *buff_size, Seq *lines,
463 double *cf_this, Item **in, Item **out)
464 {
465 FILE *fp = fopen("/proc/net/udp6", "r");
466 if (fp == NULL)
467 {
468 /* IPv6 may be completely disabled in kernel so this should be handled gracefully. */
469 Log(LOG_LEVEL_VERBOSE, "Failed to read data from /proc/net/udp6");
470 return true;
471 }
472 ssize_t ret = CfReadLine(buff, buff_size, fp);
473 if (ret == -1)
474 {
475 Log(LOG_LEVEL_ERR, "Failed to read data from /proc/net/udp6");
476 fclose(fp);
477 return false;
478 }
479
480 /* Read real data */
481 ret = CfReadLines(buff, buff_size, fp, lines);
482 if (ret < 0)
483 {
484 Log(LOG_LEVEL_ERR, "Failed to read data from /proc/net/udp6");
485 fclose(fp);
486 return false;
487 }
488 Log(LOG_LEVEL_VERBOSE, "Read %zu lines from /proc/net/udp6", ret);
489
490 uint32_t l_port, r_port;
491 SocketState state;
492 for (size_t i = 0; i < (size_t) ret; i++)
493 {
494 char *line = SeqAt(lines,i);
495 if (ParseIPv6SocketInfo(line, local_addr6, &l_port, remote_addr6, &r_port, &state))
496 {
497 SaveSocketInfo(local_addr6, l_port, r_port,
498 state, cfn_udp6,
499 line, cf_this, in, out);
500 }
501 }
502 fclose(fp);
503 SeqClear(lines);
504 return true;
505 }
506
GetNetworkDataFromProcNet(double * cf_this,Item ** in,Item ** out)507 static bool GetNetworkDataFromProcNet(double *cf_this, Item **in, Item **out)
508 {
509 bool result = true;
510 Seq *lines = SeqNew(64, free);
511
512 size_t buff_size = 256;
513 char *buff = xmalloc(buff_size);
514
515 {
516 char local_addr[INET_ADDRSTRLEN];
517 char remote_addr[INET_ADDRSTRLEN];
518 if (!GetNetworkDataFromProcNetTCP(local_addr, remote_addr,
519 &buff, &buff_size, lines,
520 cf_this, in, out))
521 {
522 SeqDestroy(lines);
523 free(buff);
524 return false;
525 }
526 SeqClear(lines);
527 if (!GetNetworkDataFromProcNetUDP(local_addr, remote_addr,
528 &buff, &buff_size, lines,
529 cf_this, in, out))
530 {
531 SeqDestroy(lines);
532 free(buff);
533 return false;
534 }
535 SeqClear(lines);
536 }
537
538 {
539 char local_addr6[INET6_ADDRSTRLEN];
540 char remote_addr6[INET6_ADDRSTRLEN];
541 if (!GetNetworkDataFromProcNetTCP6(local_addr6, remote_addr6,
542 &buff, &buff_size, lines,
543 cf_this, in, out))
544 {
545 Log(LOG_LEVEL_VERBOSE, "Failed to get IPv6 TCP sockets information");
546 }
547 SeqClear(lines);
548 if (!GetNetworkDataFromProcNetUDP6(local_addr6, remote_addr6,
549 &buff, &buff_size, lines,
550 cf_this, in, out))
551 {
552 Log(LOG_LEVEL_VERBOSE, "Failed to get IPv6 UDP sockets information");
553 }
554 }
555
556 SeqDestroy(lines);
557 free(buff);
558 return result;
559 }
560 #endif /* __linux__ */
561
GetNetworkDataFromNetstat(FILE * fp,double * cf_this,Item ** in,Item ** out)562 static void GetNetworkDataFromNetstat(FILE *fp, double *cf_this, Item **in, Item **out)
563 {
564 enum cf_netstat_type { cfn_new, cfn_old } type = cfn_new;
565 SocketType packet = cfn_tcp4;
566
567 size_t vbuff_size = CF_BUFSIZE;
568 char *vbuff = xmalloc(vbuff_size);
569 for (;;)
570 {
571 char local[CF_MAX_IP_LEN + CF_MAX_PORT_LEN] = {0};
572 char remote[CF_MAX_IP_LEN + CF_MAX_PORT_LEN] = {0};
573
574 ssize_t res = CfReadLine(&vbuff, &vbuff_size, fp);
575 if (res == -1)
576 {
577 if (!feof(fp))
578 {
579 Log(LOG_LEVEL_DEBUG,
580 "Error occured while reading data from 'netstat' "
581 "(CfReadLine/getline returned -1 but no EOF found)");
582 free(vbuff);
583 return;
584 }
585 else
586 {
587 break;
588 }
589 }
590
591 if (strstr(vbuff, "UNIX"))
592 {
593 break;
594 }
595
596 if (!((strstr(vbuff, ":")) || (strstr(vbuff, "."))))
597 {
598 continue;
599 }
600
601 /* Different formats here ... ugh.. pick a model */
602
603 // If this is old style, we look for chapter headings, e.g. "TCP: IPv4"
604
605 if ((strncmp(vbuff,"UDP:",4) == 0) && (strstr(vbuff+4,"6")))
606 {
607 packet = cfn_udp6;
608 type = cfn_old;
609 continue;
610 }
611 else if ((strncmp(vbuff,"TCP:",4) == 0) && (strstr(vbuff+4,"6")))
612 {
613 packet = cfn_tcp6;
614 type = cfn_old;
615 continue;
616 }
617 else if ((strncmp(vbuff,"UDP:",4) == 0) && (strstr(vbuff+4,"4")))
618 {
619 packet = cfn_udp4;
620 type = cfn_old;
621 continue;
622 }
623 else if ((strncmp(vbuff,"TCP:",4) == 0) && (strstr(vbuff+4,"4")))
624 {
625 packet = cfn_tcp4;
626 type = cfn_old;
627 continue;
628 }
629
630 // Line by line state in modern/linux output
631
632 if (strncmp(vbuff,"udp6",4) == 0)
633 {
634 packet = cfn_udp6;
635 type = cfn_new;
636 }
637 else if (strncmp(vbuff,"tcp6",4) == 0)
638 {
639 packet = cfn_tcp6;
640 type = cfn_new;
641 }
642 else if (strncmp(vbuff,"udp",3) == 0)
643 {
644 packet = cfn_udp4;
645 type = cfn_new;
646 }
647 else if (strncmp(vbuff,"tcp",3) == 0)
648 {
649 packet = cfn_tcp4;
650 type = cfn_new;
651 }
652
653 // End extract type
654
655 switch (type)
656 {
657 case cfn_new: sscanf(vbuff, "%*s %*s %*s %s %s", local, remote); /* linux-like */
658 break;
659
660 case cfn_old:
661 sscanf(vbuff, "%s %s", local, remote);
662 break;
663 }
664
665 if (strlen(local) == 0)
666 {
667 continue;
668 }
669
670 // Extract the port number from the end of the string
671
672 char *sp;
673 for (sp = local + strlen(local); (*sp != '.') && (*sp != ':') && (sp > local); sp--)
674 {
675 }
676
677 *sp = '\0'; // Separate address from port number
678 sp++;
679
680 char *localport = sp;
681
682 if (strstr(vbuff, "LISTEN"))
683 {
684 // General bucket
685
686 IdempPrependItem(&ALL_INCOMING, sp, NULL);
687
688 // Categories the incoming ports by packet types
689
690 switch (packet)
691 {
692 case cfn_udp4:
693 IdempPrependItem(&MON_UDP4, sp, local);
694 break;
695 case cfn_udp6:
696 IdempPrependItem(&MON_UDP6, sp, local);
697 break;
698 case cfn_tcp4:
699 IdempPrependItem(&MON_TCP4, localport, local);
700 break;
701 case cfn_tcp6:
702 IdempPrependItem(&MON_TCP6, localport, local);
703 break;
704 default:
705 break;
706 }
707 }
708
709
710 // Now look at outgoing
711
712 for (sp = remote + strlen(remote) - 1; (sp >= remote) && (isdigit((int) *sp)); sp--)
713 {
714 }
715
716 sp++;
717 char *remoteport = sp;
718
719 // Now look for the specific vital signs to count frequencies
720
721 for (size_t i = 0; i < ATTR; i++)
722 {
723 if (strcmp(localport, ECGSOCKS[i].portnr) == 0)
724 {
725 cf_this[ECGSOCKS[i].in]++;
726 AppendItem(&in[i], vbuff, "");
727
728 }
729
730 if (strcmp(remoteport, ECGSOCKS[i].portnr) == 0)
731 {
732 cf_this[ECGSOCKS[i].out]++;
733 AppendItem(&out[i], vbuff, "");
734
735 }
736 }
737 }
738 free(vbuff);
739 }
740
SaveNetworkData(Item * const * in,Item * const * out)741 static void SaveNetworkData(Item * const *in, Item * const *out)
742 {
743 const char* const statedir = GetStateDir();
744
745 char vbuff[CF_BUFSIZE];
746 for (size_t i = 0; i < ATTR; i++)
747 {
748 struct stat statbuf;
749 time_t now = time(NULL);
750
751 Log(LOG_LEVEL_DEBUG, "save incoming '%s'", ECGSOCKS[i].name);
752
753 snprintf(vbuff, CF_MAXVARSIZE, "%s%ccf_incoming.%s", statedir, FILE_SEPARATOR, ECGSOCKS[i].name);
754
755 if (stat(vbuff, &statbuf) != -1)
756 {
757 if (ItemListSize(in[i]) < (size_t) statbuf.st_size &&
758 now < statbuf.st_mtime + 40 * 60)
759 {
760 Log(LOG_LEVEL_VERBOSE, "New state '%s' is smaller, retaining old for 40 mins longer", ECGSOCKS[i].name);
761 DeleteItemList(in[i]);
762 continue;
763 }
764 }
765
766 SetNetworkEntropyClasses(CanonifyName(ECGSOCKS[i].name), "in", in[i]);
767 const mode_t old_umask = SetUmask(0077);
768 RawSaveItemList(in[i], vbuff, NewLineMode_Unix);
769 RestoreUmask(old_umask);
770 DeleteItemList(in[i]);
771 Log(LOG_LEVEL_DEBUG, "Saved in netstat data in '%s'", vbuff);
772 }
773
774 for (size_t i = 0; i < ATTR; i++)
775 {
776 struct stat statbuf;
777 time_t now = time(NULL);
778
779 Log(LOG_LEVEL_DEBUG, "save outgoing '%s'", ECGSOCKS[i].name);
780 snprintf(vbuff, CF_MAXVARSIZE, "%s%ccf_outgoing.%s", statedir, FILE_SEPARATOR, ECGSOCKS[i].name);
781
782 if (stat(vbuff, &statbuf) != -1)
783 {
784 if (ItemListSize(out[i]) < (size_t) statbuf.st_size &&
785 now < statbuf.st_mtime + 40 * 60)
786 {
787 Log(LOG_LEVEL_VERBOSE, "New state '%s' is smaller, retaining old for 40 mins longer", ECGSOCKS[i].name);
788 DeleteItemList(out[i]);
789 continue;
790 }
791 }
792
793 SetNetworkEntropyClasses(CanonifyName(ECGSOCKS[i].name), "out", out[i]);
794 const mode_t old_umask = SetUmask(0077);
795 RawSaveItemList(out[i], vbuff, NewLineMode_Unix);
796 RestoreUmask(old_umask);
797 Log(LOG_LEVEL_DEBUG, "Saved out netstat data in '%s'", vbuff);
798 DeleteItemList(out[i]);
799 }
800 }
801