1 /*
2 Copyright 2020 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 #include <sysinfo.h>
25
26 #include <files_names.h>
27 #include <eval_context.h>
28 #include <item_lib.h>
29 #include <pipes.h>
30 #include <misc_lib.h>
31 #include <communication.h>
32 #include <string_lib.h>
33 #include <string_sequence.h> /* SeqStringFromString */
34 #include <regex.h> /* StringMatchFull */
35 #include <files_interfaces.h>
36 #include <files_names.h>
37 #include <known_dirs.h>
38 #include <ip_address.h>
39 #include <file_lib.h>
40 #include <cleanup.h>
41
42 #ifdef HAVE_SYS_JAIL_H
43 # include <sys/jail.h>
44 #endif
45
46 #ifdef HAVE_GETIFADDRS
47 # include <ifaddrs.h>
48 # ifdef HAVE_NET_IF_DL_H
49 # include <net/if_dl.h>
50 # endif
51 #endif
52
53 #ifdef HAVE_NET_IF_ARP_H
54 # include <net/if_arp.h>
55 #endif
56
57 #define CF_IFREQ 2048 /* Reportedly the largest size that does not segfault 32/64 bit */
58 #define CF_IGNORE_INTERFACES "ignore_interfaces.rx"
59
60 #define IPV6_PREFIX "ipv6_"
61
62 #ifndef __MINGW32__
63
64 # if defined(HAVE_STRUCT_SOCKADDR_SA_LEN) && !defined(__NetBSD__)
65 # ifdef _SIZEOF_ADDR_IFREQ
66 # define SIZEOF_IFREQ(x) _SIZEOF_ADDR_IFREQ(x)
67 # else
68 # define SIZEOF_IFREQ(x) \
69 ((x).ifr_addr.sa_len > sizeof(struct sockaddr) ? \
70 (sizeof(struct ifreq) - sizeof(struct sockaddr) + \
71 (x).ifr_addr.sa_len) : sizeof(struct ifreq))
72 # endif
73 # else
74 # define SIZEOF_IFREQ(x) sizeof(struct ifreq)
75 # endif
76
77 #ifdef _AIX
78 #include <sys/ndd_var.h>
79 #include <sys/kinfo.h>
80 static int aix_get_mac_addr(const char *device_name, uint8_t mac[6]);
81 #endif
82
83 static void FindV6InterfacesInfo(EvalContext *ctx, Rlist **interfaces, Rlist **hardware, Rlist **ips);
84 static bool IgnoreJailInterface(int ifaceidx, struct sockaddr_in *inaddr);
85 static bool IgnoreInterface(char *name);
86 static void InitIgnoreInterfaces(void);
87
88 static Rlist *IGNORE_INTERFACES = NULL; /* GLOBAL_E */
89
90 typedef void (*ProcPostProcessFn)(void *ctx, void *json);
91 typedef JsonElement * (*ProcTiebreakerFn)(JsonElement *prev_item, JsonElement *this_item);
92
93
94 /*********************************************************************/
95
96
97 /******************************************************************/
98
IgnoreJailInterface(ARG_UNUSED int ifaceidx,ARG_UNUSED struct sockaddr_in * inaddr)99 static bool IgnoreJailInterface(
100 #if !defined(HAVE_JAIL_GET)
101 ARG_UNUSED int ifaceidx, ARG_UNUSED struct sockaddr_in *inaddr
102 #else
103 int ifaceidx, struct sockaddr_in *inaddr
104 #endif
105 )
106 {
107 /* FreeBSD jails */
108 # ifdef HAVE_JAIL_GET
109 struct iovec fbsd_jparams[4];
110 struct in_addr fbsd_jia;
111 int fbsd_lastjid = 0;
112
113 *(const void **) &fbsd_jparams[0].iov_base = "lastjid";
114 fbsd_jparams[0].iov_len = sizeof("lastjid");
115 fbsd_jparams[1].iov_base = &fbsd_lastjid;
116 fbsd_jparams[1].iov_len = sizeof(fbsd_lastjid);
117
118 *(const void **) &fbsd_jparams[2].iov_base = "ip4.addr";
119 fbsd_jparams[2].iov_len = sizeof("ip4.addr");
120 fbsd_jparams[3].iov_len = sizeof(struct in_addr);
121 fbsd_jparams[3].iov_base = &fbsd_jia;
122
123 while ((fbsd_lastjid = jail_get(fbsd_jparams, 4, 0)) > 0)
124 {
125 if (fbsd_jia.s_addr == inaddr->sin_addr.s_addr)
126 {
127 Log(LOG_LEVEL_VERBOSE, "Interface %d belongs to a FreeBSD jail %s", ifaceidx, inet_ntoa(fbsd_jia));
128 return true;
129 }
130 }
131 # endif
132
133 return false;
134 }
135
136 /******************************************************************/
137
GetMacAddress(EvalContext * ctx,ARG_UNUSED int fd,struct ifreq * ifr,struct ifreq * ifp,Rlist ** interfaces,Rlist ** hardware)138 static void GetMacAddress(EvalContext *ctx, ARG_UNUSED int fd, struct ifreq *ifr, struct ifreq *ifp, Rlist **interfaces,
139 Rlist **hardware)
140 {
141 char name[CF_MAXVARSIZE];
142
143 snprintf(name, sizeof(name), "hardware_mac[%s]", CanonifyName(ifp->ifr_name));
144
145 // mac address on a loopback interface doesn't make sense
146 if (ifr->ifr_flags & IFF_LOOPBACK)
147 {
148 return;
149 }
150
151 # if defined(SIOCGIFHWADDR) && defined(HAVE_STRUCT_IFREQ_IFR_HWADDR)
152 char hw_mac[CF_MAXVARSIZE];
153
154 if ((ioctl(fd, SIOCGIFHWADDR, ifr) == -1))
155 {
156 Log(LOG_LEVEL_ERR, "Couldn't get mac address for '%s' interface. (ioctl: %s)", ifr->ifr_name, GetErrorStr());
157 return;
158 }
159
160 snprintf(hw_mac, sizeof(hw_mac), "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
161 (unsigned char) ifr->ifr_hwaddr.sa_data[0],
162 (unsigned char) ifr->ifr_hwaddr.sa_data[1],
163 (unsigned char) ifr->ifr_hwaddr.sa_data[2],
164 (unsigned char) ifr->ifr_hwaddr.sa_data[3],
165 (unsigned char) ifr->ifr_hwaddr.sa_data[4],
166 (unsigned char) ifr->ifr_hwaddr.sa_data[5]);
167
168 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, name, hw_mac, CF_DATA_TYPE_STRING, "source=agent");
169 if (!RlistContainsString(*hardware, hw_mac))
170 {
171 RlistAppend(hardware, hw_mac, RVAL_TYPE_SCALAR);
172 }
173 if (!RlistContainsString(*interfaces, ifp->ifr_name))
174 {
175 RlistAppend(interfaces, ifp->ifr_name, RVAL_TYPE_SCALAR);
176 }
177
178 snprintf(name, sizeof(name), "mac_%s", CanonifyName(hw_mac));
179 EvalContextClassPutHard(ctx, name, "inventory,attribute_name=none,source=agent");
180
181 # elif defined(HAVE_GETIFADDRS) && !defined(__sun)
182 char hw_mac[CF_MAXVARSIZE];
183 char *m;
184 struct ifaddrs *ifaddr, *ifa;
185 struct sockaddr_dl *sdl;
186
187 if (getifaddrs(&ifaddr) == -1)
188 {
189 Log(LOG_LEVEL_ERR, "!! Could not get interface %s addresses",
190 ifp->ifr_name);
191
192 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, name, "mac_unknown", CF_DATA_TYPE_STRING, "source=agent");
193 EvalContextClassPutHard(ctx, "mac_unknown", "source=agent");
194 return;
195 }
196 for (ifa = ifaddr; ifa != NULL; ifa=ifa->ifa_next)
197 {
198 if ( strcmp(ifa->ifa_name, ifp->ifr_name) == 0)
199 {
200 if (ifa->ifa_addr->sa_family == AF_LINK)
201 {
202 sdl = (struct sockaddr_dl *)ifa->ifa_addr;
203 m = (char *) LLADDR(sdl);
204
205 snprintf(hw_mac, sizeof(hw_mac), "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
206 (unsigned char) m[0],
207 (unsigned char) m[1],
208 (unsigned char) m[2],
209 (unsigned char) m[3],
210 (unsigned char) m[4],
211 (unsigned char) m[5]);
212
213 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, name, hw_mac, CF_DATA_TYPE_STRING, "source=agent");
214 if (!RlistContainsString(*hardware, hw_mac))
215 {
216 RlistAppend(hardware, hw_mac, RVAL_TYPE_SCALAR);
217 }
218 RlistAppend(interfaces, ifa->ifa_name, RVAL_TYPE_SCALAR);
219
220 snprintf(name, sizeof(name), "mac_%s", CanonifyName(hw_mac));
221 EvalContextClassPutHard(ctx, name, "source=agent");
222 }
223 }
224
225 }
226 freeifaddrs(ifaddr);
227
228 # elif defined(_AIX) && !defined(HAVE_GETIFADDRS)
229 char hw_mac[CF_MAXVARSIZE];
230 char mac[CF_MAXVARSIZE];
231
232 if (aix_get_mac_addr(ifp->ifr_name, mac) == 0)
233 {
234 sprintf(hw_mac, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
235 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
236
237 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, name, hw_mac, CF_DATA_TYPE_STRING, "source=agent");
238
239 if (!RlistContainsString(*hardware, hw_mac))
240 {
241 RlistAppend(hardware, hw_mac, RVAL_TYPE_SCALAR);
242 }
243 if (!RlistContainsString(*interfaces, ifp->ifr_name))
244 {
245 RlistAppend(interfaces, ifp->ifr_name, RVAL_TYPE_SCALAR);
246 }
247
248 snprintf(name, CF_MAXVARSIZE, "mac_%s", CanonifyName(hw_mac));
249 EvalContextClassPutHard(ctx, name, "inventory,attribute_name=none,source=agent");
250 }
251 else
252 {
253 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, name, "mac_unknown", CF_DATA_TYPE_STRING, "source=agent");
254 EvalContextClassPutHard(ctx, "mac_unknown", "source=agent");
255 }
256
257 # elif defined(SIOCGARP)
258
259 struct arpreq arpreq;
260
261 ((struct sockaddr_in *) &arpreq.arp_pa)->sin_addr.s_addr =
262 ((struct sockaddr_in *) &ifp->ifr_addr)->sin_addr.s_addr;
263
264 if (ioctl(fd, SIOCGARP, &arpreq) == -1)
265 {
266 // ENXIO happens if there is no MAC address assigned, which is not that
267 // uncommon.
268 LogLevel log_level =
269 (errno == ENXIO) ? LOG_LEVEL_VERBOSE : LOG_LEVEL_ERR;
270 Log(log_level,
271 "Could not get interface '%s' addresses (ioctl(SIOCGARP): %s)",
272 ifp->ifr_name, GetErrorStr());
273 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, name,
274 "mac_unknown", CF_DATA_TYPE_STRING,
275 "source=agent");
276 EvalContextClassPutHard(ctx, "mac_unknown", "source=agent");
277 return;
278 }
279
280 char hw_mac[CF_MAXVARSIZE];
281
282 snprintf(hw_mac, sizeof(hw_mac), "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
283 (unsigned char) arpreq.arp_ha.sa_data[0],
284 (unsigned char) arpreq.arp_ha.sa_data[1],
285 (unsigned char) arpreq.arp_ha.sa_data[2],
286 (unsigned char) arpreq.arp_ha.sa_data[3],
287 (unsigned char) arpreq.arp_ha.sa_data[4],
288 (unsigned char) arpreq.arp_ha.sa_data[5]);
289
290 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, name,
291 hw_mac, CF_DATA_TYPE_STRING,
292 "source=agent");
293
294 if (!RlistContainsString(*hardware, hw_mac))
295 {
296 RlistAppend(hardware, hw_mac, RVAL_TYPE_SCALAR);
297 }
298
299 if (!RlistContainsString(*interfaces, ifp->ifr_name))
300 {
301 RlistAppend(interfaces, ifp->ifr_name, RVAL_TYPE_SCALAR);
302 }
303
304 snprintf(name, sizeof(name), "mac_%s", CanonifyName(hw_mac));
305 EvalContextClassPutHard(ctx, name,
306 "inventory,attribute_name=none,source=agent");
307
308 # else
309 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, name,
310 "mac_unknown", CF_DATA_TYPE_STRING,
311 "source=agent");
312 EvalContextClassPutHard(ctx, "mac_unknown", "source=agent");
313 # endif
314 }
315
316 /******************************************************************/
317
GetInterfaceFlags(EvalContext * ctx,struct ifreq * ifr,Rlist ** flags)318 static void GetInterfaceFlags(EvalContext *ctx, struct ifreq *ifr, Rlist **flags)
319 {
320 char name[CF_MAXVARSIZE];
321 char buffer[CF_BUFSIZE] = "";
322 char *fp = NULL;
323
324 snprintf(name, sizeof(name), "interface_flags[%s]", ifr->ifr_name);
325
326 if (ifr->ifr_flags & IFF_UP) strcat(buffer, " up");
327 if (ifr->ifr_flags & IFF_BROADCAST) strcat(buffer, " broadcast");
328 if (ifr->ifr_flags & IFF_DEBUG) strcat(buffer, " debug");
329 if (ifr->ifr_flags & IFF_LOOPBACK) strcat(buffer, " loopback");
330 if (ifr->ifr_flags & IFF_POINTOPOINT) strcat(buffer, " pointopoint");
331
332 #ifdef IFF_NOTRAILERS
333 if (ifr->ifr_flags & IFF_NOTRAILERS) strcat(buffer, " notrailers");
334 #endif
335
336 if (ifr->ifr_flags & IFF_RUNNING) strcat(buffer, " running");
337 if (ifr->ifr_flags & IFF_NOARP) strcat(buffer, " noarp");
338 if (ifr->ifr_flags & IFF_PROMISC) strcat(buffer, " promisc");
339 if (ifr->ifr_flags & IFF_ALLMULTI) strcat(buffer, " allmulti");
340 if (ifr->ifr_flags & IFF_MULTICAST) strcat(buffer, " multicast");
341
342 // If a least 1 flag is found
343 if (strlen(buffer) > 1)
344 {
345 // Skip leading space
346 fp = buffer + 1;
347 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, name, fp, CF_DATA_TYPE_STRING, "source=agent");
348 RlistAppend(flags, fp, RVAL_TYPE_SCALAR);
349 }
350 }
351
352 /******************************************************************/
353
GetInterfacesInfo(EvalContext * ctx)354 void GetInterfacesInfo(EvalContext *ctx)
355 {
356 bool address_set = false;
357 int fd, len, i, j;
358 struct ifreq ifbuf[CF_IFREQ], ifr, *ifp;
359 struct ifconf list;
360 struct sockaddr_in *sin;
361 char *sp, workbuf[CF_BUFSIZE];
362 char ip[CF_MAXVARSIZE];
363 char name[CF_MAXVARSIZE];
364 Rlist *interfaces = NULL, *hardware = NULL, *flags = NULL, *ips = NULL;
365
366 /* This function may be called many times, while interfaces come and go */
367 /* TODO cache results for non-daemon processes? */
368 EvalContextDeleteIpAddresses(ctx);
369
370 memset(ifbuf, 0, sizeof(ifbuf));
371
372 InitIgnoreInterfaces();
373
374 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
375 {
376 Log(LOG_LEVEL_ERR, "Couldn't open socket. (socket: %s)", GetErrorStr());
377 DoCleanupAndExit(EXIT_FAILURE);
378 }
379
380 list.ifc_len = sizeof(ifbuf);
381 list.ifc_req = ifbuf;
382
383 /* WARNING: *BSD use unsigned long as second argument to ioctl() while
384 * POSIX specifies *signed* int. Using the largest possible signed type is
385 * the best strategy.*/
386 #ifdef SIOCGIFCONF
387 intmax_t request = SIOCGIFCONF;
388 #else
389 intmax_t request = OSIOCGIFCONF;
390 #endif
391 int ret = ioctl(fd, request, &list);
392 if (ret == -1)
393 {
394 Log(LOG_LEVEL_ERR,
395 "Couldn't get interfaces (ioctl(SIOCGIFCONF): %s)",
396 GetErrorStr());
397 DoCleanupAndExit(EXIT_FAILURE);
398 }
399
400 if (list.ifc_len < (int) sizeof(struct ifreq))
401 {
402 Log(LOG_LEVEL_VERBOSE,
403 "Interface list returned is too small (%d bytes), "
404 "assuming no interfaces present", list.ifc_len);
405 list.ifc_len = 0;
406 }
407
408 char last_name[sizeof(ifp->ifr_name)] = "";
409
410 for (j = 0, len = 0, ifp = list.ifc_req; len < list.ifc_len;
411 len += SIZEOF_IFREQ(*ifp), j++, ifp = (struct ifreq *) ((char *) ifp + SIZEOF_IFREQ(*ifp)))
412 {
413
414 if (ifp->ifr_addr.sa_family == 0)
415 {
416 continue;
417 }
418
419 if (strlen(ifp->ifr_name) == 0)
420 {
421 continue;
422 }
423
424 /* Skip network interfaces listed in ignore_interfaces.rx */
425
426 if (IgnoreInterface(ifp->ifr_name))
427 {
428 continue;
429 }
430 else
431 {
432 Log(LOG_LEVEL_VERBOSE, "Interface %d: %s", j + 1, ifp->ifr_name);
433 }
434
435 /* If interface name appears a second time in a row then it has more
436 than one IP addresses (linux: ip addr add $IP dev $IF).
437 But the variable is already added so don't set it again. */
438 if (strcmp(last_name, ifp->ifr_name) != 0)
439 {
440 strcpy(last_name, ifp->ifr_name);
441 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "interface", last_name, CF_DATA_TYPE_STRING, "source=agent");
442 }
443
444 snprintf(workbuf, sizeof(workbuf), "net_iface_%s", CanonifyName(ifp->ifr_name));
445 EvalContextClassPutHard(ctx, workbuf, "source=agent");
446
447 /* TODO IPv6 should be handled transparently */
448 if (ifp->ifr_addr.sa_family == AF_INET)
449 {
450 strlcpy(ifr.ifr_name, ifp->ifr_name, sizeof(ifp->ifr_name));
451
452 if (ioctl(fd, SIOCGIFFLAGS, &ifr) == -1)
453 {
454 Log(LOG_LEVEL_ERR, "No such network device. (ioctl: %s)", GetErrorStr());
455 continue;
456 }
457 else
458 {
459 GetInterfaceFlags(ctx, &ifr, &flags);
460 }
461
462 if (ifr.ifr_flags & IFF_UP)
463 {
464 sin = (struct sockaddr_in *) &ifp->ifr_addr;
465
466 if (IgnoreJailInterface(j + 1, sin))
467 {
468 Log(LOG_LEVEL_VERBOSE, "Ignoring interface %d", j + 1);
469 continue;
470 }
471
472 /* No DNS lookup, just convert IP address to string. */
473 char txtaddr[CF_MAX_IP_LEN] = "";
474 assert(sizeof(VIPADDRESS) >= sizeof(txtaddr));
475
476 getnameinfo((struct sockaddr *) sin, sizeof(*sin),
477 txtaddr, sizeof(txtaddr),
478 NULL, 0, NI_NUMERICHOST);
479
480 Log(LOG_LEVEL_DEBUG, "Adding hostip '%s'", txtaddr);
481 EvalContextClassPutHard(ctx, txtaddr, "inventory,attribute_name=none,source=agent");
482
483 if (strcmp(txtaddr, "0.0.0.0") == 0)
484 {
485 /* TODO remove, interface address can't be 0.0.0.0 and
486 * even then DNS is not a safe way to set a variable... */
487 Log(LOG_LEVEL_VERBOSE, "Cannot discover hardware IP, using DNS value");
488 assert(sizeof(ip) >= sizeof(VIPADDRESS) + sizeof("ipv4_"));
489 strcpy(ip, "ipv4_");
490 strcat(ip, VIPADDRESS);
491 EvalContextAddIpAddress(ctx, VIPADDRESS, NULL); // we don't know the interface
492 RlistAppendScalar(&ips, VIPADDRESS);
493
494 for (sp = ip + strlen(ip) - 1; (sp > ip); sp--)
495 {
496 if (*sp == '.')
497 {
498 *sp = '\0';
499 EvalContextClassPutHard(ctx, ip, "inventory,attribute_name=none,source=agent");
500 }
501 }
502
503 strcpy(ip, VIPADDRESS);
504 i = 3;
505
506 for (sp = ip + strlen(ip) - 1; (sp > ip); sp--)
507 {
508 if (*sp == '.')
509 {
510 *sp = '\0';
511 snprintf(name, sizeof(name), "ipv4_%d[%s]", i--, CanonifyName(VIPADDRESS));
512 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, name, ip, CF_DATA_TYPE_STRING, "source=agent");
513 }
514 }
515 continue;
516 }
517
518 assert(sizeof(ip) >= sizeof(txtaddr) + sizeof("ipv4_"));
519 strcpy(ip, "ipv4_");
520 strcat(ip, txtaddr);
521 EvalContextClassPutHard(ctx, ip, "inventory,attribute_name=none,source=agent");
522
523 /* VIPADDRESS has already been set to the DNS address of
524 * VFQNAME by GetNameInfo3() during initialisation. Here we
525 * reset VIPADDRESS to the address of the first non-loopback
526 * interface. */
527 if (!address_set && !(ifr.ifr_flags & IFF_LOOPBACK))
528 {
529 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "ipv4", txtaddr, CF_DATA_TYPE_STRING, "inventory,source=agent,attribute_name=none");
530
531 strcpy(VIPADDRESS, txtaddr);
532 Log(LOG_LEVEL_VERBOSE, "IP address of host set to %s",
533 VIPADDRESS);
534 address_set = true;
535 }
536
537 EvalContextAddIpAddress(ctx, txtaddr, CanonifyName(ifp->ifr_name));
538 RlistAppendScalar(&ips, txtaddr);
539
540 for (sp = ip + strlen(ip) - 1; (sp > ip); sp--)
541 {
542 if (*sp == '.')
543 {
544 *sp = '\0';
545 EvalContextClassPutHard(ctx, ip, "inventory,attribute_name=none,source=agent");
546 }
547 }
548
549 // Set the IPv4 on interface array
550
551 strcpy(ip, txtaddr);
552
553 snprintf(name, sizeof(name), "ipv4[%s]", CanonifyName(ifp->ifr_name));
554
555 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, name, ip, CF_DATA_TYPE_STRING, "source=agent");
556
557 // generate the reverse mapping
558 snprintf(name, sizeof(name), "ip2iface[%s]", txtaddr);
559
560 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, name, CanonifyName(ifp->ifr_name), CF_DATA_TYPE_STRING, "source=agent");
561
562 i = 3;
563
564 for (sp = ip + strlen(ip) - 1; (sp > ip); sp--)
565 {
566 if (*sp == '.')
567 {
568 *sp = '\0';
569
570 snprintf(name, sizeof(name), "ipv4_%d[%s]", i--, CanonifyName(ifp->ifr_name));
571
572 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, name, ip, CF_DATA_TYPE_STRING, "source=agent");
573 }
574 }
575 }
576
577 // Set the hardware/mac address array
578 GetMacAddress(ctx, fd, &ifr, ifp, &interfaces, &hardware);
579 }
580 }
581
582 close(fd);
583
584 FindV6InterfacesInfo(ctx, &interfaces, &hardware, &ips);
585
586 if (interfaces)
587 {
588 // Define sys.interfaces:
589 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "interfaces", interfaces, CF_DATA_TYPE_STRING_LIST,
590 "inventory,source=agent,attribute_name=Interfaces");
591 }
592 if (hardware)
593 {
594 // Define sys.hardware_addresses:
595 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "hardware_addresses", hardware, CF_DATA_TYPE_STRING_LIST,
596 "inventory,source=agent,attribute_name=MAC addresses");
597 }
598 if (flags)
599 {
600 // Define sys.hardware_flags:
601 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "hardware_flags", flags, CF_DATA_TYPE_STRING_LIST,
602 "source=agent");
603 }
604 if (ips)
605 {
606 // Define sys.ip_addresses:
607 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "ip_addresses", ips, CF_DATA_TYPE_STRING_LIST,
608 "source=agent");
609 }
610
611 RlistDestroy(interfaces);
612 RlistDestroy(hardware);
613 RlistDestroy(flags);
614 RlistDestroy(ips);
615 }
616
617 /*******************************************************************/
618
FindV6InterfacesInfo(EvalContext * ctx,Rlist ** interfaces,Rlist ** hardware,Rlist ** ips)619 static void FindV6InterfacesInfo(EvalContext *ctx, Rlist **interfaces, Rlist **hardware, Rlist **ips)
620 {
621 assert(interfaces != NULL);
622 assert(hardware != NULL);
623
624 FILE *pp = NULL;
625
626 /* Whatever the manuals might say, you cannot get IPV6
627 interface configuration from the ioctls. This seems
628 to be implemented in a non standard way across OSes
629 BSDi has done getifaddrs(), solaris 8 has a new ioctl, Stevens
630 book shows the suggestion which has not been implemented...
631 */
632
633 Log(LOG_LEVEL_VERBOSE, "Trying to locate my IPv6 address");
634
635 #if defined(__CYGWIN__)
636 /* NT cannot do this */
637 return;
638 #elif defined(__hpux)
639 if ((pp = cf_popen("/usr/sbin/ifconfig -a", "r", true)) == NULL)
640 {
641 Log(LOG_LEVEL_VERBOSE, "Could not find interface info");
642 return;
643 }
644 #elif defined(_AIX)
645 if ((pp = cf_popen("/etc/ifconfig -a", "r", true)) == NULL)
646 {
647 Log(LOG_LEVEL_VERBOSE, "Could not find interface info");
648 return;
649 }
650 #else
651 if ((!FileCanOpen("/sbin/ifconfig", "r") || ((pp = cf_popen("/sbin/ifconfig -a", "r", true)) == NULL)) &&
652 (!FileCanOpen("/bin/ifconfig", "r") || ((pp = cf_popen("/bin/ifconfig -a", "r", true)) == NULL)))
653 {
654 Log(LOG_LEVEL_VERBOSE, "Could not find interface info");
655 return;
656 }
657 #endif
658
659 /* Don't know the output format of ifconfig on all these .. hope for the best*/
660
661 char ifconfig_line[CF_BUFSIZE];
662 char current_interface[CF_BUFSIZE];
663 current_interface[0] = '\0';
664 for(;;)
665 {
666 if (fgets(ifconfig_line, sizeof(ifconfig_line), pp) == NULL)
667 {
668 if (ferror(pp))
669 {
670 UnexpectedError("Failed to read line from stream");
671 break;
672 }
673 else /* feof */
674 {
675 break;
676 }
677 }
678
679 if (!isspace(ifconfig_line[0])) // Name of interface followed by colon
680 {
681 // Find colon:
682 const char *colon = strchr(ifconfig_line, ':');
683 if (colon != NULL)
684 {
685 // Bytes to "copy" includes colon(src)/NUL-byte(dst):
686 const size_t bytes_to_copy = colon - ifconfig_line + 1;
687 assert(bytes_to_copy <= sizeof(current_interface));
688
689 const size_t src_length = StringCopy(
690 ifconfig_line, current_interface, bytes_to_copy);
691 // StringCopy effectively returns the length of the src string,
692 // up to (inclusive) a maximum of passed in buffer size
693
694 // We know there was more data, at least a colon:
695 assert(src_length == bytes_to_copy);
696 const size_t dst_length = src_length - 1;
697
698 // We copied everything up to, but not including, the colon:
699 assert(ifconfig_line[dst_length] == ':');
700 assert(current_interface[dst_length] == '\0');
701 }
702 }
703
704
705 const char *const stripped_ifconfig_line =
706 TrimWhitespace(ifconfig_line);
707 if (StringStartsWith(stripped_ifconfig_line, "ether "))
708 {
709 Seq *const ether_line =
710 SeqStringFromString(stripped_ifconfig_line, ' ');
711 const size_t length = SeqLength(ether_line);
712
713 if (length < 2)
714 {
715 Log(LOG_LEVEL_ERR,
716 "Failed to parse hw_mac address for: %s",
717 current_interface);
718 }
719 else
720 {
721 const char *const hw_mac = SeqAt(ether_line, 1);
722
723 if (!RlistContainsString(*hardware, hw_mac))
724 {
725 Log(LOG_LEVEL_VERBOSE,
726 "Adding MAC address: %s for %s",
727 hw_mac,
728 current_interface);
729
730 RlistAppendString(hardware, hw_mac);
731
732 char variable_name[CF_MAXVARSIZE];
733
734 snprintf(
735 variable_name,
736 sizeof(variable_name),
737 "hardware_mac[%s]",
738 CanonifyName(current_interface));
739
740 EvalContextVariablePutSpecial(
741 ctx,
742 SPECIAL_SCOPE_SYS,
743 variable_name,
744 hw_mac,
745 CF_DATA_TYPE_STRING,
746 "source=agent,derived-from=ifconfig");
747 }
748 }
749
750 SeqDestroy(ether_line);
751 }
752
753 if (strcasestr(ifconfig_line, "inet6"))
754 {
755 Item *ip, *list = NULL;
756 char *sp;
757
758 list = SplitStringAsItemList(ifconfig_line, ' ');
759
760 for (ip = list; ip != NULL; ip = ip->next)
761 {
762 for (sp = ip->name; *sp != '\0'; sp++)
763 {
764 if (*sp == '/') /* Remove CIDR mask */
765 {
766 *sp = '\0';
767 }
768 }
769
770 if ((IsIPV6Address(ip->name)) && ((strcmp(ip->name, "::1") != 0)))
771 {
772 char prefixed_ip[CF_MAX_IP_LEN + sizeof(IPV6_PREFIX)] = {0};
773 Log(LOG_LEVEL_VERBOSE, "Found IPv6 address %s", ip->name);
774
775 if (current_interface[0] != '\0'
776 && !IgnoreInterface(current_interface))
777 {
778 EvalContextAddIpAddress(
779 ctx, ip->name, current_interface);
780 EvalContextClassPutHard(
781 ctx,
782 ip->name,
783 "inventory,attribute_name=none,source=agent");
784
785 xsnprintf(
786 prefixed_ip,
787 sizeof(prefixed_ip),
788 IPV6_PREFIX "%s",
789 ip->name);
790 EvalContextClassPutHard(
791 ctx,
792 prefixed_ip,
793 "inventory,attribute_name=none,source=agent");
794
795 // Add IPv6 address to sys.ip_addresses
796 RlistAppendString(ips, ip->name);
797
798 if (!RlistContainsString(
799 *interfaces, current_interface))
800 {
801 RlistAppendString(interfaces, current_interface);
802 }
803 }
804 }
805 }
806
807 DeleteItemList(list);
808 }
809 }
810
811 cf_pclose(pp);
812 }
813
814 /*******************************************************************/
815
InitIgnoreInterfaces()816 static void InitIgnoreInterfaces()
817 {
818 FILE *fin;
819 char filename[CF_BUFSIZE],regex[256];
820
821 snprintf(filename, sizeof(filename), "%s%c%s", GetInputDir(), FILE_SEPARATOR, CF_IGNORE_INTERFACES);
822
823 if ((fin = fopen(filename,"r")) == NULL)
824 {
825 Log(LOG_LEVEL_VERBOSE, "No interface exception file %s",filename);
826 return;
827 }
828
829 while (!feof(fin))
830 {
831 regex[0] = '\0';
832 int scanCount = fscanf(fin,"%255s",regex);
833
834 if (scanCount != 0 && *regex != '\0')
835 {
836 RlistPrependScalarIdemp(&IGNORE_INTERFACES, regex);
837 }
838 }
839
840 fclose(fin);
841 }
842
843 /*******************************************************************/
844
IgnoreInterface(char * name)845 static bool IgnoreInterface(char *name)
846 {
847 Rlist *rp;
848
849 for (rp = IGNORE_INTERFACES; rp != NULL; rp=rp->next)
850 {
851 /* FIXME: review this strcmp. Moved out from StringMatch */
852 if (!strcmp(RlistScalarValue(rp), name)
853 || StringMatchFull(RlistScalarValue(rp), name))
854 {
855 Log(LOG_LEVEL_VERBOSE, "Ignoring interface '%s' because it matches '%s'",name,CF_IGNORE_INTERFACES);
856 return true;
857 }
858 }
859
860 return false;
861 }
862
863 #ifdef _AIX
aix_get_mac_addr(const char * device_name,uint8_t mac[6])864 static int aix_get_mac_addr(const char *device_name, uint8_t mac[6])
865 {
866 size_t ksize;
867 struct kinfo_ndd *ndd;
868 int count, i;
869
870 ksize = getkerninfo(KINFO_NDD, 0, 0, 0);
871 if (ksize == 0)
872 {
873 errno = ENOSYS;
874 return -1;
875 }
876
877 ndd = (struct kinfo_ndd *)xmalloc(ksize);
878 if (ndd == NULL)
879 {
880 errno = ENOMEM;
881 return -1;
882 }
883
884 if (getkerninfo(KINFO_NDD, ndd, &ksize, 0) == -1)
885 {
886 errno = ENOSYS;
887 return -1;
888 }
889
890 count= ksize/sizeof(struct kinfo_ndd);
891 for (i=0;i<count;i++)
892 {
893 if ((ndd[i].ndd_type == NDD_ETHER ||
894 ndd[i].ndd_type == NDD_ISO88023) &&
895 ndd[i].ndd_addrlen == 6 &&
896 (strcmp(ndd[i].ndd_alias, device_name) == 0 ||
897 strcmp(ndd[i].ndd_name, device_name == 0)))
898 {
899 memcpy(mac, ndd[i].ndd_addr, 6);
900 free(ndd);
901 return 0;
902 }
903 }
904 free(ndd);
905 errno = ENOENT;
906 return -1;
907 }
908 #endif /* _AIX */
909
910 // TODO: perhaps rename and move these to json.c and ip_address.c? Or even let JsonElements store IPAddress structs?
ParsedIPAddressHex(const char * data)911 static IPAddress* ParsedIPAddressHex(const char *data)
912 {
913 IPAddress *ip = NULL;
914 Buffer *buffer = BufferNewFrom(data, strlen(data));
915 if (buffer != NULL)
916 {
917 ip = IPAddressNewHex(buffer);
918 BufferDestroy(buffer);
919 }
920
921 return ip;
922 }
923
JsonRewriteParsedIPAddress(JsonElement * element,const char * raw_key,const char * new_key,const bool as_map)924 static void JsonRewriteParsedIPAddress(JsonElement* element, const char* raw_key, const char *new_key, const bool as_map)
925 {
926 IPAddress *addr = ParsedIPAddressHex(JsonObjectGetAsString(element, raw_key));
927 if (addr != NULL)
928 {
929 Buffer *buf = IPAddressGetAddress(addr);
930 if (buf != NULL)
931 {
932 JsonObjectRemoveKey(element, raw_key);
933 if (as_map)
934 {
935 JsonElement *ip = JsonObjectCreate(2);
936 JsonObjectAppendString(ip, "address", BufferData(buf));
937 BufferPrintf(buf, "%d", IPAddressGetPort(addr));
938 JsonObjectAppendString(ip, "port", BufferData(buf));
939 JsonObjectAppendElement(element, new_key, ip);
940 }
941 else
942 {
943 JsonObjectAppendString(element, new_key, BufferData(buf));
944 }
945
946 BufferDestroy(buf);
947 }
948
949 IPAddressDestroy(&addr);
950 }
951 }
952
JsonExtractParsedNumber(JsonElement * element,const char * raw_key,const char * new_key,const bool hex_mode,const bool keep_number)953 static long JsonExtractParsedNumber(JsonElement* element, const char* raw_key, const char *new_key, const bool hex_mode, const bool keep_number)
954 {
955 long num = 0;
956
957 if (sscanf(JsonObjectGetAsString(element, raw_key),
958 hex_mode ? "%lx" : "%ld",
959 &num)
960 == 1)
961 {
962 if (!keep_number)
963 {
964 JsonObjectRemoveKey(element, raw_key);
965 }
966
967 if (new_key != NULL)
968 {
969 JsonObjectAppendInteger(element, new_key, num);
970 }
971 }
972
973 return num;
974 }
975
976 /*******************************************************************/
977
NetworkingRoutesPostProcessInfo(ARG_LINUX_ONLY void * passed_ctx,ARG_LINUX_ONLY void * json)978 static void NetworkingRoutesPostProcessInfo(
979 ARG_LINUX_ONLY void *passed_ctx, ARG_LINUX_ONLY void *json)
980 {
981 # if defined (__linux__)
982 EvalContext *ctx = passed_ctx;
983 JsonElement *route = json;
984
985 JsonRewriteParsedIPAddress(route, "raw_dest", "dest", false);
986 JsonRewriteParsedIPAddress(route, "raw_gw", "gateway", false);
987 JsonRewriteParsedIPAddress(route, "raw_mask", "mask", false);
988
989 // TODO: check that the metric and the others are decimal (ipv6_route uses hex for metric and others maybe)
990 JsonExtractParsedNumber(route, "metric", "metric", false, false);
991 JsonExtractParsedNumber(route, "mtu", "mtu", false, false);
992 JsonExtractParsedNumber(route, "refcnt", "refcnt", false, false);
993 JsonExtractParsedNumber(route, "use", "use", false, false);
994 JsonExtractParsedNumber(route, "window", "window", false, false);
995 JsonExtractParsedNumber(route, "irtt", "irtt", false, false);
996
997 JsonElement *decoded_flags = JsonArrayCreate(3);
998 long num_flags = JsonExtractParsedNumber(route, "raw_flags", NULL, true, false);
999
1000 bool is_up = (num_flags & RTF_UP);
1001 bool is_gw = (num_flags & RTF_GATEWAY);
1002 bool is_host = (num_flags & RTF_HOST);
1003 bool is_default_route = (strcmp(JsonObjectGetAsString(route, "dest"), "0.0.0.0") == 0);
1004
1005 const char* gw_type = is_gw ? "gateway":"local";
1006
1007 // These flags are always included on Linux in platform.h
1008 JsonArrayAppendString(decoded_flags, is_up ? "up":"down");
1009 JsonArrayAppendString(decoded_flags, is_host ? "host":"net");
1010 JsonArrayAppendString(decoded_flags, is_default_route ? "default" : "not_default");
1011 JsonArrayAppendString(decoded_flags, gw_type);
1012 JsonObjectAppendElement(route, "flags", decoded_flags);
1013 JsonObjectAppendBool(route, "active_default_gateway", is_default_route && is_up && is_gw);
1014
1015 if (is_up && is_gw)
1016 {
1017 Buffer *formatter = BufferNew();
1018 BufferPrintf(formatter, "ipv4_gw_%s", JsonObjectGetAsString(route, "gateway"));
1019 EvalContextClassPutHard(ctx, BufferData(formatter), "inventory,networking,/proc,source=agent,attribute_name=none,procfs");
1020 BufferDestroy(formatter);
1021 }
1022 # endif
1023 }
1024
NetworkingIPv6RoutesPostProcessInfo(ARG_UNUSED void * passed_ctx,ARG_LINUX_ONLY void * json)1025 static void NetworkingIPv6RoutesPostProcessInfo(
1026 ARG_UNUSED void *passed_ctx, ARG_LINUX_ONLY void *json)
1027 {
1028 # if defined (__linux__)
1029 JsonElement *route = json;
1030
1031 JsonRewriteParsedIPAddress(route, "raw_dest", "dest", false);
1032 JsonRewriteParsedIPAddress(route, "raw_next_hop", "next_hop", false);
1033 JsonRewriteParsedIPAddress(route, "raw_source", "dest", false);
1034
1035 JsonExtractParsedNumber(route, "raw_metric", "metric", true, false);
1036 JsonExtractParsedNumber(route, "refcnt", "refcnt", false, false);
1037 JsonExtractParsedNumber(route, "use", "use", false, false);
1038
1039 JsonElement *decoded_flags = JsonArrayCreate(3);
1040 long num_flags = JsonExtractParsedNumber(route, "raw_flags", NULL, true, false);
1041
1042 bool is_up = (num_flags & RTF_UP);
1043 bool is_gw = (num_flags & RTF_GATEWAY);
1044 bool is_host = (num_flags & RTF_HOST);
1045
1046 const char* gw_type = is_gw ? "gateway":"local";
1047
1048 // These flags are always included on Linux in platform.h
1049 JsonArrayAppendString(decoded_flags, is_up ? "up":"down");
1050 JsonArrayAppendString(decoded_flags, is_host ? "host":"net");
1051 JsonArrayAppendString(decoded_flags, gw_type);
1052 JsonObjectAppendElement(route, "flags", decoded_flags);
1053
1054 // TODO: figure out if we can grab any default gateway info here
1055 // like we do with IPv4 routes
1056
1057 # endif
1058 }
1059
NetworkingIPv6AddressesPostProcessInfo(ARG_UNUSED void * passed_ctx,void * json)1060 static void NetworkingIPv6AddressesPostProcessInfo(ARG_UNUSED void *passed_ctx, void *json)
1061 {
1062 JsonElement *entry = json;
1063
1064 JsonRewriteParsedIPAddress(entry, "raw_address", "address", false);
1065
1066 JsonExtractParsedNumber(entry, "raw_device_number", "device_number", true, false);
1067 JsonExtractParsedNumber(entry, "raw_prefix_length", "prefix_length", true, false);
1068 JsonExtractParsedNumber(entry, "raw_scope", "scope", true, false);
1069 }
1070
RankIPv6Address(const char * address)1071 static unsigned RankIPv6Address(const char *address)
1072 {
1073 unsigned long first_word = 0;
1074 char *end;
1075
1076 if (address == NULL)
1077 {
1078 return 0;
1079 }
1080
1081 first_word = strtoul(address, &end, 16);
1082
1083 if (*end != ':')
1084 {
1085 return 0; // invalid IPv6 address?
1086 }
1087
1088 if ((first_word & 0xffc0) == 0xfe80)
1089 {
1090 // link-local (fe80:://10)
1091
1092 return 1;
1093 }
1094 else
1095 {
1096 return 2;
1097 }
1098 }
1099
NetworkingIPv6AddressesTiebreaker(JsonElement * prev_item,JsonElement * this_item)1100 static JsonElement *NetworkingIPv6AddressesTiebreaker(JsonElement *prev_item, JsonElement *this_item)
1101 {
1102 const char *prev_addr = JsonObjectGetAsString(prev_item, "address");
1103 const char *this_addr = JsonObjectGetAsString(this_item, "address");
1104
1105 if (RankIPv6Address(this_addr) >= RankIPv6Address(prev_addr))
1106 {
1107 return this_item;
1108 }
1109 else
1110 {
1111 return prev_item;
1112 }
1113 }
1114
1115 /*******************************************************************/
1116
GetPortStateString(ARG_LINUX_ONLY int state)1117 static const char* GetPortStateString(ARG_LINUX_ONLY int state)
1118 {
1119 # if defined (__linux__)
1120 switch (state)
1121 {
1122 case TCP_ESTABLISHED: return "ESTABLISHED";
1123 case TCP_SYN_SENT: return "SYN_SENT";
1124 case TCP_SYN_RECV: return "SYN_RECV";
1125 case TCP_FIN_WAIT1: return "FIN_WAIT1";
1126 case TCP_FIN_WAIT2: return "FIN_WAIT2";
1127 case TCP_TIME_WAIT: return "TIME_WAIT";
1128 case TCP_CLOSE: return "CLOSE";
1129 case TCP_CLOSE_WAIT: return "CLOSE_WAIT";
1130 case TCP_LAST_ACK: return "LAST_ACK";
1131 case TCP_LISTEN: return "LISTEN";
1132 case TCP_CLOSING: return "CLOSING";
1133 }
1134
1135 # endif
1136 return "UNKNOWN";
1137 }
1138
1139 // used in evalfunction.c but defined here so
1140 // JsonRewriteParsedIPAddress() etc. can stay local
NetworkingPortsPostProcessInfo(ARG_UNUSED void * passed_ctx,void * json)1141 void NetworkingPortsPostProcessInfo(ARG_UNUSED void *passed_ctx, void *json)
1142 {
1143 JsonElement *conn = json;
1144
1145 if (conn != NULL)
1146 {
1147 JsonRewriteParsedIPAddress(conn, "raw_local", "local", true);
1148 JsonRewriteParsedIPAddress(conn, "raw_remote", "remote", true);
1149
1150 long num_state = JsonExtractParsedNumber(conn, "raw_state", "temp_state", false, false);
1151
1152 if (JsonObjectGetAsString(conn, "temp_state") != NULL)
1153 {
1154 JsonObjectRemoveKey(conn, "temp_state");
1155 JsonObjectAppendString(conn, "state", GetPortStateString(num_state));
1156 }
1157 }
1158 }
1159
1160 /*******************************************************************/
1161
GetNetworkingStatsInfo(const char * filename)1162 static JsonElement* GetNetworkingStatsInfo(const char *filename)
1163 {
1164 JsonElement *stats = NULL;
1165 assert(filename);
1166
1167 FILE *fin = safe_fopen(filename, "rt");
1168 if (fin)
1169 {
1170 Log(LOG_LEVEL_VERBOSE, "Reading netstat info from %s", filename);
1171 size_t header_line_size = CF_BUFSIZE;
1172 char *header_line = xmalloc(header_line_size);
1173 stats = JsonObjectCreate(2);
1174
1175 while (CfReadLine(&header_line, &header_line_size, fin) != -1)
1176 {
1177 char* colon_ptr = strchr(header_line, ':');
1178 if (colon_ptr != NULL &&
1179 colon_ptr+2 < header_line + strlen(header_line))
1180 {
1181 JsonElement *stat = JsonObjectCreate(3);
1182 Buffer *type = BufferNewFrom(header_line, colon_ptr - header_line);
1183 size_t type_length = BufferSize(type);
1184 Rlist *info = RlistFromSplitString(colon_ptr+2, ' ');
1185 size_t line_size = CF_BUFSIZE;
1186 char *line = xmalloc(line_size);
1187 if (CfReadLine(&line, &line_size, fin) != -1)
1188 {
1189 if (strlen(line) > type_length+2)
1190 {
1191 Rlist *data = RlistFromSplitString(line+type_length+2, ' ');
1192 for (const Rlist *rp = info, *rdp = data;
1193 rp != NULL && rdp != NULL;
1194 rp = rp->next, rdp = rdp->next)
1195 {
1196 JsonObjectAppendString(stat, RlistScalarValue(rp), RlistScalarValue(rdp));
1197 }
1198 RlistDestroy(data);
1199 }
1200 }
1201
1202 JsonObjectAppendElement(stats, BufferData(type), stat);
1203
1204 free(line);
1205 RlistDestroy(info);
1206 BufferDestroy(type);
1207 }
1208
1209 }
1210
1211 free(header_line);
1212
1213 fclose(fin);
1214 }
1215
1216 return stats;
1217 }
1218
1219 /*******************************************************************/
1220
1221 // always returns the parsed data. If the key is not NULL, also
1222 // creates a sys.KEY variable.
1223
GetProcFileInfo(EvalContext * ctx,const char * filename,const char * key,const char * extracted_key,ProcPostProcessFn post,ProcTiebreakerFn tiebreak,const char * regex)1224 JsonElement* GetProcFileInfo(EvalContext *ctx, const char* filename, const char* key, const char* extracted_key, ProcPostProcessFn post, ProcTiebreakerFn tiebreak, const char* regex)
1225 {
1226 JsonElement *info = NULL;
1227 bool extract_key_mode = (extracted_key != NULL);
1228
1229 FILE *fin = safe_fopen(filename, "rt");
1230 if (fin)
1231 {
1232 Log(LOG_LEVEL_VERBOSE, "Reading %s info from %s", key, filename);
1233
1234 pcre *pattern = NULL;
1235 {
1236 const char *errorstr;
1237 int erroffset;
1238 pattern = pcre_compile(regex, PCRE_MULTILINE | PCRE_DOTALL,
1239 &errorstr, &erroffset, NULL);
1240 }
1241
1242 if (pattern != NULL)
1243 {
1244 size_t line_size = CF_BUFSIZE;
1245 char *line = xmalloc(line_size);
1246
1247 info = extract_key_mode ? JsonObjectCreate(10) : JsonArrayCreate(10);
1248
1249 while (CfReadLine(&line, &line_size, fin) != -1)
1250 {
1251 JsonElement *item = StringCaptureData(pattern, regex, line);
1252
1253 if (item != NULL)
1254 {
1255 if (post != NULL)
1256 {
1257 (*post)(ctx, item);
1258 }
1259
1260 if (extract_key_mode)
1261 {
1262 const char *extracted_key_value = JsonObjectGetAsString(item, extracted_key);
1263
1264 if (extracted_key_value == NULL)
1265 {
1266 Log(LOG_LEVEL_ERR, "While parsing %s, looked to extract key %s but couldn't find it in line %s", filename, extracted_key, line);
1267 }
1268 else
1269 {
1270 JsonElement *prev_item = JsonObjectGet(info, extracted_key_value);
1271
1272 Log(LOG_LEVEL_DEBUG, "While parsing %s, got key %s from line %s", filename, extracted_key_value, line);
1273
1274 if (prev_item != NULL && tiebreak != NULL)
1275 {
1276 JsonElement *winner = (*tiebreak)(prev_item, item);
1277
1278 if (winner == prev_item)
1279 {
1280 Log(LOG_LEVEL_DEBUG, "Multiple entries for key %s, preferring previous value", extracted_key_value);
1281
1282 JsonDestroy(item);
1283 item = NULL;
1284 }
1285 else
1286 {
1287 Log(LOG_LEVEL_DEBUG, "Multiple entries for key %s, preferring new value", extracted_key_value);
1288 }
1289 }
1290
1291 if (item != NULL)
1292 {
1293 JsonObjectAppendElement(info, extracted_key_value, item);
1294 }
1295 }
1296 }
1297 else
1298 {
1299 JsonArrayAppendElement(info, item);
1300 }
1301 }
1302 }
1303
1304 free(line);
1305
1306 if (key != NULL)
1307 {
1308 Buffer *varname = BufferNew();
1309 BufferPrintf(varname, "%s", key);
1310 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, BufferData(varname), info, CF_DATA_TYPE_CONTAINER,
1311 "networking,/proc,source=agent,procfs");
1312 BufferDestroy(varname);
1313 }
1314
1315 pcre_free(pattern);
1316 }
1317
1318 fclose(fin);
1319 }
1320
1321 return info;
1322 }
1323
1324 /*******************************************************************/
1325
GetNetworkingInfo(EvalContext * ctx)1326 void GetNetworkingInfo(EvalContext *ctx)
1327 {
1328 const char *procdir_root = GetRelocatedProcdirRoot();
1329
1330 Buffer *pbuf = BufferNew();
1331
1332 JsonElement *inet = JsonObjectCreate(2);
1333
1334 BufferPrintf(pbuf, "%s/proc/net/netstat", procdir_root);
1335 JsonElement *inet_stats = GetNetworkingStatsInfo(BufferData(pbuf));
1336
1337 if (inet_stats != NULL)
1338 {
1339 JsonObjectAppendElement(inet, "stats", inet_stats);
1340 }
1341
1342 BufferPrintf(pbuf, "%s/proc/net/route", procdir_root);
1343 JsonElement *routes = GetProcFileInfo(ctx, BufferData(pbuf), NULL, NULL, &NetworkingRoutesPostProcessInfo, NULL,
1344 // format: Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
1345 // eth0 00000000 0102A8C0 0003 0 0 1024 00000000 0 0 0
1346 "^(?<interface>\\S+)\\t(?<raw_dest>[[:xdigit:]]+)\\t(?<raw_gw>[[:xdigit:]]+)\\t(?<raw_flags>[[:xdigit:]]+)\\t(?<refcnt>\\d+)\\t(?<use>\\d+)\\t(?<metric>[[:xdigit:]]+)\\t(?<raw_mask>[[:xdigit:]]+)\\t(?<mtu>\\d+)\\t(?<window>\\d+)\\t(?<irtt>[[:xdigit:]]+)");
1347
1348 if (routes != NULL &&
1349 JsonGetElementType(routes) == JSON_ELEMENT_TYPE_CONTAINER)
1350 {
1351 JsonObjectAppendElement(inet, "routes", routes);
1352
1353 JsonIterator iter = JsonIteratorInit(routes);
1354 const JsonElement *default_route = NULL;
1355 long lowest_metric = 0;
1356 const JsonElement *route = NULL;
1357 while ((route = JsonIteratorNextValue(&iter)))
1358 {
1359 JsonElement *active = JsonObjectGet(route, "active_default_gateway");
1360 if (active != NULL &&
1361 JsonGetElementType(active) == JSON_ELEMENT_TYPE_PRIMITIVE &&
1362 JsonGetPrimitiveType(active) == JSON_PRIMITIVE_TYPE_BOOL &&
1363 JsonPrimitiveGetAsBool(active))
1364 {
1365 JsonElement *metric = JsonObjectGet(route, "metric");
1366 if (metric != NULL &&
1367 JsonGetElementType(metric) == JSON_ELEMENT_TYPE_PRIMITIVE &&
1368 JsonGetPrimitiveType(metric) == JSON_PRIMITIVE_TYPE_INTEGER &&
1369 (default_route == NULL ||
1370 JsonPrimitiveGetAsInteger(metric) < lowest_metric))
1371 {
1372 default_route = route;
1373 }
1374 }
1375 }
1376
1377 if (default_route != NULL)
1378 {
1379 JsonObjectAppendString(inet, "default_gateway", JsonObjectGetAsString(default_route, "gateway"));
1380 JsonObjectAppendElement(inet, "default_route", JsonCopy(default_route));
1381 }
1382 }
1383
1384 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "inet", inet, CF_DATA_TYPE_CONTAINER,
1385 "networking,/proc,source=agent,procfs");
1386 JsonDestroy(inet);
1387
1388 JsonElement *inet6 = JsonObjectCreate(3);
1389
1390 BufferPrintf(pbuf, "%s/proc/net/snmp6", procdir_root);
1391 JsonElement *inet6_stats = GetProcFileInfo(ctx, BufferData(pbuf), NULL, NULL, NULL, NULL,
1392 "^\\s*(?<key>\\S+)\\s+(?<value>\\d+)");
1393
1394 if (inet6_stats != NULL)
1395 {
1396 // map the key to the value (as a number) in the "stats" map
1397 JsonElement *rewrite = JsonObjectCreate(JsonLength(inet6_stats));
1398 JsonIterator iter = JsonIteratorInit(inet6_stats);
1399 const JsonElement *stat = NULL;
1400 while ((stat = JsonIteratorNextValue(&iter)))
1401 {
1402 long num = 0;
1403 const char* key = JsonObjectGetAsString(stat, "key");
1404 const char* value = JsonObjectGetAsString(stat, "value");
1405 if (key && value &&
1406 sscanf(value, "%ld", &num) == 1)
1407 {
1408 JsonObjectAppendInteger(rewrite, key, num);
1409 }
1410 }
1411
1412 JsonObjectAppendElement(inet6, "stats", rewrite);
1413 JsonDestroy(inet6_stats);
1414 }
1415
1416 BufferPrintf(pbuf, "%s/proc/net/ipv6_route", procdir_root);
1417 JsonElement *inet6_routes = GetProcFileInfo(ctx, BufferData(pbuf), NULL, NULL, &NetworkingIPv6RoutesPostProcessInfo, NULL,
1418 // format: dest dest_prefix source source_prefix next_hop metric refcnt use flags interface
1419 // fe800000000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000000 00000000 00000001 eth0
1420 "^(?<raw_dest>[[:xdigit:]]+)\\s+(?<dest_prefix>[[:xdigit:]]+)\\s+"
1421 "(?<raw_source>[[:xdigit:]]+)\\s+(?<source_prefix>[[:xdigit:]]+)\\s+"
1422 "(?<raw_next_hop>[[:xdigit:]]+)\\s+(?<raw_metric>[[:xdigit:]]+)\\s+"
1423 "(?<refcnt>\\d+)\\s+(?<use>\\d+)\\s+"
1424 "(?<raw_flags>[[:xdigit:]]+)\\s+(?<interface>\\S+)");
1425
1426 if (inet6_routes != NULL)
1427 {
1428 JsonObjectAppendElement(inet6, "routes", inet6_routes);
1429 }
1430
1431 BufferPrintf(pbuf, "%s/proc/net/if_inet6", procdir_root);
1432 JsonElement *inet6_addresses = GetProcFileInfo(ctx, BufferData(pbuf), NULL, "interface", &NetworkingIPv6AddressesPostProcessInfo, &NetworkingIPv6AddressesTiebreaker,
1433 // format: address device_number prefix_length scope flags interface_name
1434 // 00000000000000000000000000000001 01 80 10 80 lo
1435 // fe80000000000000004249fffebdd7b4 04 40 20 80 docker0
1436 // fe80000000000000c27cd1fffe3eada6 02 40 20 80 enp4s0
1437 "^(?<raw_address>[[:xdigit:]]+)\\s+(?<raw_device_number>[[:xdigit:]]+)\\s+"
1438 "(?<raw_prefix_length>[[:xdigit:]]+)\\s+(?<raw_scope>[[:xdigit:]]+)\\s+"
1439 "(?<raw_flags>[[:xdigit:]]+)\\s+(?<interface>\\S+)");
1440
1441 if (inet6_addresses != NULL)
1442 {
1443 JsonObjectAppendElement(inet6, "addresses", inet6_addresses);
1444 }
1445
1446 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "inet6", inet6, CF_DATA_TYPE_CONTAINER,
1447 "networking,/proc,source=agent,procfs");
1448 JsonDestroy(inet6);
1449
1450 // Inter-| Receive | Transmit
1451 // face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
1452 // eth0: 74850544807 75236137 0 0 0 0 0 1108775 63111535625 74696758 0 0 0 0 0 0
1453
1454 BufferPrintf(pbuf, "%s/proc/net/dev", procdir_root);
1455 JsonElement *interfaces_data =
1456 GetProcFileInfo(ctx, BufferData(pbuf), "interfaces_data", "device", NULL, NULL,
1457 "^\\s*(?<device>[^:]+)\\s*:\\s*"
1458 // All of the below are just decimal digits separated by spaces
1459 "(?<receive_bytes>\\d+)\\s+"
1460 "(?<receive_packets>\\d+)\\s+"
1461 "(?<receive_errors>\\d+)\\s+"
1462 "(?<receive_drop>\\d+)\\s+"
1463 "(?<receive_fifo>\\d+)\\s+"
1464 "(?<receive_frame>\\d+)\\s+"
1465 "(?<receive_compressed>\\d+)\\s+"
1466 "(?<receive_multicast>\\d+)\\s+"
1467 "(?<transmit_bytes>\\d+)\\s+"
1468 "(?<transmit_packets>\\d+)\\s+"
1469 "(?<transmit_errors>\\d+)\\s+"
1470 "(?<transmit_drop>\\d+)\\s+"
1471 "(?<transmit_fifo>\\d+)\\s+"
1472 "(?<transmit_frame>\\d+)\\s+"
1473 "(?<transmit_compressed>\\d+)\\s+"
1474 "(?<transmit_multicast>\\d+)");
1475 JsonDestroy(interfaces_data);
1476 BufferDestroy(pbuf);
1477 }
1478
GetNetworkingConnections(EvalContext * ctx)1479 JsonElement* GetNetworkingConnections(EvalContext *ctx)
1480 {
1481 const char *procdir_root = GetRelocatedProcdirRoot();
1482 JsonElement *json = JsonObjectCreate(5);
1483 const char* ports_regex = "^\\s*\\d+:\\s+(?<raw_local>[0-9A-F:]+)\\s+(?<raw_remote>[0-9A-F:]+)\\s+(?<raw_state>[0-9]+)";
1484
1485 JsonElement *data = NULL;
1486 Buffer *pbuf = BufferNew();
1487
1488 BufferPrintf(pbuf, "%s/proc/net/tcp", procdir_root);
1489 data = GetProcFileInfo(ctx, BufferData(pbuf), NULL, NULL, &NetworkingPortsPostProcessInfo, NULL, ports_regex);
1490 if (data != NULL)
1491 {
1492 JsonObjectAppendElement(json, "tcp", data);
1493 }
1494
1495 BufferPrintf(pbuf, "%s/proc/net/tcp6", procdir_root);
1496 data = GetProcFileInfo(ctx, BufferData(pbuf), NULL, NULL, &NetworkingPortsPostProcessInfo, NULL, ports_regex);
1497 if (data != NULL)
1498 {
1499 JsonObjectAppendElement(json, "tcp6", data);
1500 }
1501
1502 BufferPrintf(pbuf, "%s/proc/net/udp", procdir_root);
1503 data = GetProcFileInfo(ctx, BufferData(pbuf), NULL, NULL, &NetworkingPortsPostProcessInfo, NULL, ports_regex);
1504 if (data != NULL)
1505 {
1506 JsonObjectAppendElement(json, "udp", data);
1507 }
1508
1509 BufferPrintf(pbuf, "%s/proc/net/udp6", procdir_root);
1510 data = GetProcFileInfo(ctx, BufferData(pbuf), NULL, NULL, &NetworkingPortsPostProcessInfo, NULL, ports_regex);
1511 if (data != NULL)
1512 {
1513 JsonObjectAppendElement(json, "udp6", data);
1514 }
1515 BufferDestroy(pbuf);
1516
1517 if (JsonLength(json) < 1)
1518 {
1519 // nothing was collected, this is a failure
1520 JsonDestroy(json);
1521 return NULL;
1522 }
1523
1524 return json;
1525 }
1526
1527 #endif /* !__MINGW32__ */
1528