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