cfengine  3.15.4
About: CFEngine is a configuration management system for configuring and maintaining Unix-like computers (using an own high level policy language). Community version.
  Fossies Dox: cfengine-3.15.4.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

unix_iface.c
Go to the documentation of this file.
1 /*
2  Copyright 2019 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 
93 
94 /*********************************************************************/
95 
96 
97 /******************************************************************/
98 
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 
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());
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 
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
310  "mac_unknown", CF_DATA_TYPE_STRING,
311  "source=agent");
312  EvalContextClassPutHard(ctx, "mac_unknown", "source=agent");
313 # endif
314 }
315 
316 /******************************************************************/
317 
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;
348  RlistAppend(flags, fp, RVAL_TYPE_SCALAR);
349  }
350 }
351 
352 /******************************************************************/
353 
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? */
369 
370  memset(ifbuf, 0, sizeof(ifbuf));
371 
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  {
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  {
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
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));
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 
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 
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:
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:
602  "source=agent");
603  }
604  if (ips)
605  {
606  // Define sys.ip_addresses:
608  "source=agent");
609  }
610 
611  RlistDestroy(interfaces);
612  RlistDestroy(hardware);
613  RlistDestroy(flags);
614  RlistDestroy(ips);
615 }
616 
617 /*******************************************************************/
618 
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  {
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  {
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 
741  ctx,
743  variable_name,
744  hw_mac,
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  {
779  ctx, ip->name, current_interface);
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);
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 
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  {
837  }
838  }
839 
840  fclose(fin);
841 }
842 
843 /*******************************************************************/
844 
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
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?
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 
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 
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 
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 
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  return NULL;
1059 }
1060 
1062 {
1063  JsonElement *entry = json;
1064 
1065  JsonRewriteParsedIPAddress(entry, "raw_address", "address", false);
1066 
1067  JsonExtractParsedNumber(entry, "raw_device_number", "device_number", true, false);
1068  JsonExtractParsedNumber(entry, "raw_prefix_length", "prefix_length", true, false);
1069  JsonExtractParsedNumber(entry, "raw_scope", "scope", true, false);
1070  return NULL;
1071 }
1072 
1073 /*******************************************************************/
1074 
1075 static const char* GetPortStateString(ARG_LINUX_ONLY int state)
1076 {
1077 # if defined (__linux__)
1078  switch (state)
1079  {
1080  case TCP_ESTABLISHED: return "ESTABLISHED";
1081  case TCP_SYN_SENT: return "SYN_SENT";
1082  case TCP_SYN_RECV: return "SYN_RECV";
1083  case TCP_FIN_WAIT1: return "FIN_WAIT1";
1084  case TCP_FIN_WAIT2: return "FIN_WAIT2";
1085  case TCP_TIME_WAIT: return "TIME_WAIT";
1086  case TCP_CLOSE: return "CLOSE";
1087  case TCP_CLOSE_WAIT: return "CLOSE_WAIT";
1088  case TCP_LAST_ACK: return "LAST_ACK";
1089  case TCP_LISTEN: return "LISTEN";
1090  case TCP_CLOSING: return "CLOSING";
1091  }
1092 
1093 # endif
1094  return "UNKNOWN";
1095 }
1096 
1097 // used in evalfunction.c but defined here so
1098 // JsonRewriteParsedIPAddress() etc. can stay local
1100 {
1101  JsonElement *conn = json;
1102 
1103  if (conn != NULL)
1104  {
1105  JsonRewriteParsedIPAddress(conn, "raw_local", "local", true);
1106  JsonRewriteParsedIPAddress(conn, "raw_remote", "remote", true);
1107 
1108  long num_state = JsonExtractParsedNumber(conn, "raw_state", "temp_state", false, false);
1109 
1110  if (JsonObjectGetAsString(conn, "temp_state") != NULL)
1111  {
1112  JsonObjectRemoveKey(conn, "temp_state");
1113  JsonObjectAppendString(conn, "state", GetPortStateString(num_state));
1114  }
1115  }
1116 
1117  return NULL;
1118 }
1119 
1120 /*******************************************************************/
1121 
1122 static JsonElement* GetNetworkingStatsInfo(const char *filename)
1123 {
1124  JsonElement *stats = NULL;
1125  assert(filename);
1126 
1127  FILE *fin = safe_fopen(filename, "rt");
1128  if (fin)
1129  {
1130  Log(LOG_LEVEL_VERBOSE, "Reading netstat info from %s", filename);
1131  size_t header_line_size = CF_BUFSIZE;
1132  char *header_line = xmalloc(header_line_size);
1133  stats = JsonObjectCreate(2);
1134 
1135  while (CfReadLine(&header_line, &header_line_size, fin) != -1)
1136  {
1137  char* colon_ptr = strchr(header_line, ':');
1138  if (colon_ptr != NULL &&
1139  colon_ptr+2 < header_line + strlen(header_line))
1140  {
1141  JsonElement *stat = JsonObjectCreate(3);
1142  Buffer *type = BufferNewFrom(header_line, colon_ptr - header_line);
1143  size_t type_length = BufferSize(type);
1144  Rlist *info = RlistFromSplitString(colon_ptr+2, ' ');
1145  size_t line_size = CF_BUFSIZE;
1146  char *line = xmalloc(line_size);
1147  if (CfReadLine(&line, &line_size, fin) != -1)
1148  {
1149  if (strlen(line) > type_length+2)
1150  {
1151  Rlist *data = RlistFromSplitString(line+type_length+2, ' ');
1152  for (const Rlist *rp = info, *rdp = data;
1153  rp != NULL && rdp != NULL;
1154  rp = rp->next, rdp = rdp->next)
1155  {
1157  }
1158  RlistDestroy(data);
1159  }
1160  }
1161 
1162  JsonObjectAppendElement(stats, BufferData(type), stat);
1163 
1164  free(line);
1165  RlistDestroy(info);
1166  BufferDestroy(type);
1167  }
1168 
1169  }
1170 
1171  free(header_line);
1172 
1173  fclose(fin);
1174  }
1175 
1176  return stats;
1177 }
1178 
1179 /*******************************************************************/
1180 
1181 // always returns the parsed data. If the key is not NULL, also
1182 // creates a sys.KEY variable.
1183 
1184 JsonElement* GetProcFileInfo(EvalContext *ctx, const char* filename, const char* key, const char* extracted_key, ProcPostProcessFn post, const char* regex)
1185 {
1186  JsonElement *info = NULL;
1187  bool extract_key_mode = (extracted_key != NULL);
1188 
1189  FILE *fin = safe_fopen(filename, "rt");
1190  if (fin)
1191  {
1192  Log(LOG_LEVEL_VERBOSE, "Reading %s info from %s", key, filename);
1193 
1194  pcre *pattern = NULL;
1195  {
1196  const char *errorstr;
1197  int erroffset;
1198  pattern = pcre_compile(regex, PCRE_MULTILINE | PCRE_DOTALL,
1199  &errorstr, &erroffset, NULL);
1200  }
1201 
1202  if (pattern != NULL)
1203  {
1204  size_t line_size = CF_BUFSIZE;
1205  char *line = xmalloc(line_size);
1206 
1207  info = extract_key_mode ? JsonObjectCreate(10) : JsonArrayCreate(10);
1208 
1209  while (CfReadLine(&line, &line_size, fin) != -1)
1210  {
1211  JsonElement *item = StringCaptureData(pattern, regex, line);
1212 
1213  if (item != NULL)
1214  {
1215  if (post != NULL)
1216  {
1217  (*post)(ctx, item);
1218  }
1219 
1220  if (extract_key_mode)
1221  {
1222  if (JsonObjectGetAsString(item, extracted_key) == NULL)
1223  {
1224  Log(LOG_LEVEL_ERR, "While parsing %s, looked to extract key %s but couldn't find it in line %s", filename, extracted_key, line);
1225  }
1226  else
1227  {
1228  Log(LOG_LEVEL_DEBUG, "While parsing %s, got key %s from line %s", filename, JsonObjectGetAsString(item, extracted_key), line);
1229  JsonObjectAppendElement(info, JsonObjectGetAsString(item, extracted_key), item);
1230  }
1231  }
1232  else
1233  {
1234  JsonArrayAppendElement(info, item);
1235  }
1236  }
1237  }
1238 
1239  free(line);
1240 
1241  if (key != NULL)
1242  {
1243  Buffer *varname = BufferNew();
1244  BufferPrintf(varname, "%s", key);
1246  "networking,/proc,source=agent,procfs");
1247  BufferDestroy(varname);
1248  }
1249 
1250  pcre_free(pattern);
1251  }
1252 
1253  fclose(fin);
1254  }
1255 
1256  return info;
1257 }
1258 
1259 /*******************************************************************/
1260 
1262 {
1263  const char *procdir_root = GetRelocatedProcdirRoot();
1264 
1265  Buffer *pbuf = BufferNew();
1266 
1267  JsonElement *inet = JsonObjectCreate(2);
1268 
1269  BufferPrintf(pbuf, "%s/proc/net/netstat", procdir_root);
1270  JsonElement *inet_stats = GetNetworkingStatsInfo(BufferData(pbuf));
1271 
1272  if (inet_stats != NULL)
1273  {
1274  JsonObjectAppendElement(inet, "stats", inet_stats);
1275  }
1276 
1277  BufferPrintf(pbuf, "%s/proc/net/route", procdir_root);
1279  // format: Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
1280  // eth0 00000000 0102A8C0 0003 0 0 1024 00000000 0 0 0
1281  "^(?<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:]]+)");
1282 
1283  if (routes != NULL &&
1285  {
1286  JsonObjectAppendElement(inet, "routes", routes);
1287 
1288  JsonIterator iter = JsonIteratorInit(routes);
1289  const JsonElement *default_route = NULL;
1290  long lowest_metric = 0;
1291  const JsonElement *route = NULL;
1292  while ((route = JsonIteratorNextValue(&iter)))
1293  {
1294  JsonElement *active = JsonObjectGet(route, "active_default_gateway");
1295  if (active != NULL &&
1298  JsonPrimitiveGetAsBool(active))
1299  {
1300  JsonElement *metric = JsonObjectGet(route, "metric");
1301  if (metric != NULL &&
1304  (default_route == NULL ||
1305  JsonPrimitiveGetAsInteger(metric) < lowest_metric))
1306  {
1307  default_route = route;
1308  }
1309  }
1310  }
1311 
1312  if (default_route != NULL)
1313  {
1314  JsonObjectAppendString(inet, "default_gateway", JsonObjectGetAsString(default_route, "gateway"));
1315  JsonObjectAppendElement(inet, "default_route", JsonCopy(default_route));
1316  }
1317  }
1318 
1320  "networking,/proc,source=agent,procfs");
1321  JsonDestroy(inet);
1322 
1323  JsonElement *inet6 = JsonObjectCreate(3);
1324 
1325  BufferPrintf(pbuf, "%s/proc/net/snmp6", procdir_root);
1326  JsonElement *inet6_stats = GetProcFileInfo(ctx, BufferData(pbuf), NULL, NULL, NULL,
1327  "^\\s*(?<key>\\S+)\\s+(?<value>\\d+)");
1328 
1329  if (inet6_stats != NULL)
1330  {
1331  // map the key to the value (as a number) in the "stats" map
1332  JsonElement *rewrite = JsonObjectCreate(JsonLength(inet6_stats));
1333  JsonIterator iter = JsonIteratorInit(inet6_stats);
1334  const JsonElement *stat = NULL;
1335  while ((stat = JsonIteratorNextValue(&iter)))
1336  {
1337  long num = 0;
1338  const char* key = JsonObjectGetAsString(stat, "key");
1339  const char* value = JsonObjectGetAsString(stat, "value");
1340  if (key && value &&
1341  sscanf(value, "%ld", &num) == 1)
1342  {
1343  JsonObjectAppendInteger(rewrite, key, num);
1344  }
1345  }
1346 
1347  JsonObjectAppendElement(inet6, "stats", rewrite);
1348  JsonDestroy(inet6_stats);
1349  }
1350 
1351  BufferPrintf(pbuf, "%s/proc/net/ipv6_route", procdir_root);
1353  // format: dest dest_prefix source source_prefix next_hop metric refcnt use flags interface
1354  // fe800000000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000000 00000000 00000001 eth0
1355  "^(?<raw_dest>[[:xdigit:]]+)\\s+(?<dest_prefix>[[:xdigit:]]+)\\s+"
1356  "(?<raw_source>[[:xdigit:]]+)\\s+(?<source_prefix>[[:xdigit:]]+)\\s+"
1357  "(?<raw_next_hop>[[:xdigit:]]+)\\s+(?<raw_metric>[[:xdigit:]]+)\\s+"
1358  "(?<refcnt>\\d+)\\s+(?<use>\\d+)\\s+"
1359  "(?<raw_flags>[[:xdigit:]]+)\\s+(?<interface>\\S+)");
1360 
1361  if (inet6_routes != NULL)
1362  {
1363  JsonObjectAppendElement(inet6, "routes", inet6_routes);
1364  }
1365 
1366  BufferPrintf(pbuf, "%s/proc/net/if_inet6", procdir_root);
1368  // format: address device_number prefix_length scope flags interface_name
1369  // 00000000000000000000000000000001 01 80 10 80 lo
1370  // fe80000000000000004249fffebdd7b4 04 40 20 80 docker0
1371  // fe80000000000000c27cd1fffe3eada6 02 40 20 80 enp4s0
1372  "^(?<raw_address>[[:xdigit:]]+)\\s+(?<raw_device_number>[[:xdigit:]]+)\\s+"
1373  "(?<raw_prefix_length>[[:xdigit:]]+)\\s+(?<raw_scope>[[:xdigit:]]+)\\s+"
1374  "(?<raw_flags>[[:xdigit:]]+)\\s+(?<interface>\\S+)");
1375 
1376  if (inet6_addresses != NULL)
1377  {
1378  JsonObjectAppendElement(inet6, "addresses", inet6_addresses);
1379  }
1380 
1382  "networking,/proc,source=agent,procfs");
1383  JsonDestroy(inet6);
1384 
1385  // Inter-| Receive | Transmit
1386  // face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
1387  // eth0: 74850544807 75236137 0 0 0 0 0 1108775 63111535625 74696758 0 0 0 0 0 0
1388 
1389  BufferPrintf(pbuf, "%s/proc/net/dev", procdir_root);
1390  JsonElement *interfaces_data =
1391  GetProcFileInfo(ctx, BufferData(pbuf), "interfaces_data", "device", NULL,
1392  "^\\s*(?<device>[^:]+)\\s*:\\s*"
1393  // All of the below are just decimal digits separated by spaces
1394  "(?<receive_bytes>\\d+)\\s+"
1395  "(?<receive_packets>\\d+)\\s+"
1396  "(?<receive_errors>\\d+)\\s+"
1397  "(?<receive_drop>\\d+)\\s+"
1398  "(?<receive_fifo>\\d+)\\s+"
1399  "(?<receive_frame>\\d+)\\s+"
1400  "(?<receive_compressed>\\d+)\\s+"
1401  "(?<receive_multicast>\\d+)\\s+"
1402  "(?<transmit_bytes>\\d+)\\s+"
1403  "(?<transmit_packets>\\d+)\\s+"
1404  "(?<transmit_errors>\\d+)\\s+"
1405  "(?<transmit_drop>\\d+)\\s+"
1406  "(?<transmit_fifo>\\d+)\\s+"
1407  "(?<transmit_frame>\\d+)\\s+"
1408  "(?<transmit_compressed>\\d+)\\s+"
1409  "(?<transmit_multicast>\\d+)");
1410  JsonDestroy(interfaces_data);
1411  BufferDestroy(pbuf);
1412 }
1413 
1415 {
1416  const char *procdir_root = GetRelocatedProcdirRoot();
1417  JsonElement *json = JsonObjectCreate(5);
1418  const char* ports_regex = "^\\s*\\d+:\\s+(?<raw_local>[0-9A-F:]+)\\s+(?<raw_remote>[0-9A-F:]+)\\s+(?<raw_state>[0-9]+)";
1419 
1420  JsonElement *data = NULL;
1421  Buffer *pbuf = BufferNew();
1422 
1423  BufferPrintf(pbuf, "%s/proc/net/tcp", procdir_root);
1425  if (data != NULL)
1426  {
1427  JsonObjectAppendElement(json, "tcp", data);
1428  }
1429 
1430  BufferPrintf(pbuf, "%s/proc/net/tcp6", procdir_root);
1432  if (data != NULL)
1433  {
1434  JsonObjectAppendElement(json, "tcp6", data);
1435  }
1436 
1437  BufferPrintf(pbuf, "%s/proc/net/udp", procdir_root);
1439  if (data != NULL)
1440  {
1441  JsonObjectAppendElement(json, "udp", data);
1442  }
1443 
1444  BufferPrintf(pbuf, "%s/proc/net/udp6", procdir_root);
1446  if (data != NULL)
1447  {
1448  JsonObjectAppendElement(json, "udp6", data);
1449  }
1450 
1451  if (JsonLength(json) < 1)
1452  {
1453  // nothing was collected, this is a failure
1454  JsonDestroy(json);
1455  return NULL;
1456  }
1457 
1458  return json;
1459 }
1460 
1461 #endif /* !__MINGW32__ */
void * xmalloc(size_t size)
Definition: alloc-mini.c:46
void BufferDestroy(Buffer *buffer)
Destroys a buffer and frees the memory associated with it.
Definition: buffer.c:72
Buffer * BufferNew(void)
Buffer initialization routine.
Definition: buffer.c:48
Buffer * BufferNewFrom(const char *data, unsigned int length)
Initializes a buffer based on a const char pointer.
Definition: buffer.c:64
const char * BufferData(const Buffer *buffer)
Provides a pointer to the internal data.
Definition: buffer.c:470
int BufferPrintf(Buffer *buffer, const char *format,...)
Stores complex data on the buffer.
Definition: buffer.c:348
unsigned int BufferSize(const Buffer *buffer)
Returns the size of the buffer.
Definition: buffer.c:464
#define ARG_UNUSED
Definition: cf-net.c:47
@ RVAL_TYPE_SCALAR
Definition: cf3.defs.h:606
@ CF_DATA_TYPE_STRING_LIST
Definition: cf3.defs.h:372
@ CF_DATA_TYPE_STRING
Definition: cf3.defs.h:369
@ CF_DATA_TYPE_CONTAINER
Definition: cf3.defs.h:384
void free(void *)
#define CF_MAX_IP_LEN
Definition: cfnet.h:39
void DoCleanupAndExit(int ret)
Definition: cleanup.c:57
char VIPADDRESS[64]
Definition: cf3globals.c:81
bool IsIPV6Address(char *name)
Definition: communication.c:70
#define CF_BUFSIZE
Definition: definitions.h:50
#define CF_MAXVARSIZE
Definition: definitions.h:36
void EvalContextDeleteIpAddresses(EvalContext *ctx)
bool EvalContextClassPutHard(EvalContext *ctx, const char *name, const char *tags)
void EvalContextAddIpAddress(EvalContext *ctx, const char *ip_address, const char *iface)
bool EvalContextVariablePutSpecial(EvalContext *ctx, SpecialScope scope, const char *lval, const void *value, DataType type, const char *tags)
bool FileCanOpen(const char *path, const char *modes)
Definition: file_lib.c:46
const char * GetRelocatedProcdirRoot()
For testing things against /proc, uses env var CFENGINE_TEST_OVERRIDE_PROCDIR.
Definition: file_lib.c:1576
ssize_t CfReadLine(char **buff, size_t *size, FILE *fp)
Works exactly like posix 'getline', EXCEPT it does not include carriage return at the end.
Definition: file_lib.c:1476
FILE * safe_fopen(const char *const path, const char *const mode)
Definition: file_lib.c:812
#define FILE_SEPARATOR
Definition: file_lib.h:102
char * CanonifyName(const char *str)
Definition: files_names.c:483
int getnameinfo(const struct sockaddr *sa, socklen_t salen, char *node, socklen_t nodelen, char *service, socklen_t servicelen, int flags)
Definition: getaddrinfo.c:476
int errno
#define NULL
Definition: getopt1.c:56
IPAddress * IPAddressNewHex(Buffer *source)
Creates a new IPAddress object from a hex string (as in procfs).
Definition: ip_address.c:992
int IPAddressGetPort(IPAddress *address)
Recovers the appropriate port from the given address.
Definition: ip_address.c:1105
Buffer * IPAddressGetAddress(IPAddress *address)
Produces a fully usable IPV6 or IPV4 address string representation.
Definition: ip_address.c:1055
int IPAddressDestroy(IPAddress **address)
Destroys an IPAddress object.
Definition: ip_address.c:1031
Item * SplitStringAsItemList(const char *string, char sep)
Definition: item_lib.c:613
void DeleteItemList(Item *item)
Definition: item_lib.c:808
JsonElement * StringCaptureData(pcre *pattern, const char *regex, const char *data)
bool JsonPrimitiveGetAsBool(const JsonElement *const primitive)
Definition: json.c:741
JsonElement * JsonObjectCreate(const size_t initialCapacity)
Create a new JSON object.
Definition: json.c:880
void JsonDestroy(JsonElement *const element)
Destroy a JSON element.
Definition: json.c:386
JsonElementType JsonGetElementType(const JsonElement *const element)
Definition: json.c:667
void JsonObjectAppendInteger(JsonElement *const object, const char *const key, const int value)
Append an integer field to an object.
Definition: json.c:1062
JsonIterator JsonIteratorInit(const JsonElement *const container)
Definition: json.c:549
JsonElement * JsonCopy(const JsonElement *const element)
Definition: json.c:235
JsonElement * JsonObjectGet(const JsonElement *const object, const char *const key)
Definition: json.c:1266
void JsonObjectAppendBool(JsonElement *const object, const char *const key, const _Bool value)
Definition: json.c:1069
void JsonObjectAppendElement(JsonElement *const object, const char *const key, JsonElement *const element)
Append any JSON element to an object.
Definition: json.c:1111
JsonElement * JsonArrayCreate(const size_t initialCapacity)
Create a new JSON array.
Definition: json.c:1281
const char * JsonObjectGetAsString(const JsonElement *const object, const char *const key)
Get the value of a field in an object, as a string.
Definition: json.c:1204
void JsonArrayAppendString(JsonElement *const array, const char *const value)
Append a string to an array.
Definition: json.c:1287
JsonElement * JsonIteratorNextValue(JsonIterator *const iter)
Definition: json.c:568
JsonPrimitiveType JsonGetPrimitiveType(const JsonElement *const primitive)
Definition: json.c:693
void JsonObjectAppendString(JsonElement *const object, const char *const key, const char *const value)
Append a string field to an object.
Definition: json.c:1055
bool JsonObjectRemoveKey(JsonElement *const object, const char *const key)
Remove key from the object.
Definition: json.c:1167
long JsonPrimitiveGetAsInteger(const JsonElement *const primitive)
Definition: json.c:750
size_t JsonLength(const JsonElement *const element)
Get the length of a JsonElement. This is the number of elements or fields in an array or object respe...
Definition: json.c:531
void JsonArrayAppendElement(JsonElement *const array, JsonElement *const element)
Append any JSON element to an array.
Definition: json.c:1336
@ JSON_PRIMITIVE_TYPE_INTEGER
Definition: json.h:65
@ JSON_PRIMITIVE_TYPE_BOOL
Definition: json.h:67
@ JSON_ELEMENT_TYPE_PRIMITIVE
Definition: json.h:53
@ JSON_ELEMENT_TYPE_CONTAINER
Definition: json.h:52
const char * GetInputDir(void)
Definition: known_dirs.c:182
LogLevel
Definition: log.h:30
const char * GetErrorStr(void)
Definition: logging.c:275
void Log(LogLevel level, const char *fmt,...)
Definition: logging.c:409
@ LOG_LEVEL_ERR
Definition: logging.h:42
@ LOG_LEVEL_DEBUG
Definition: logging.h:47
@ LOG_LEVEL_VERBOSE
Definition: logging.h:46
void xsnprintf(char *str, size_t str_size, const char *format,...)
Definition: misc_lib.c:114
#define UnexpectedError(...)
Definition: misc_lib.h:38
int cf_pclose(FILE *pp)
Definition: pipes_unix.c:812
FILE * cf_popen(const char *command, const char *type, bool capture_stderr)
Definition: pipes_unix.c:332
#define ARG_LINUX_ONLY
Definition: platform.h:407
bool StringMatchFull(const char *regex, const char *str)
Definition: regex.c:106
char * RlistScalarValue(const Rlist *rlist)
Definition: rlist.c:83
Rlist * RlistAppend(Rlist **start, const void *item, RvalType type)
Definition: rlist.c:560
Rlist * RlistFromSplitString(const char *string, char sep)
Definition: rlist.c:1067
void RlistDestroy(Rlist *rl)
Definition: rlist.c:501
Rlist * RlistAppendScalar(Rlist **start, const char *scalar)
Definition: rlist.c:545
Rlist * RlistAppendString(Rlist **start, const char *string)
Definition: rlist.c:550
bool RlistContainsString(const Rlist *list, const char *string)
Definition: rlist.c:290
Rlist * RlistPrependScalarIdemp(Rlist **start, const char *scalar)
Definition: rlist.c:535
state
Definition: rlist.c:708
@ SPECIAL_SCOPE_SYS
Definition: scope.h:38
size_t SeqLength(const Seq *seq)
Length of the sequence.
Definition: sequence.c:354
void SeqDestroy(Seq *seq)
Destroy an existing Sequence.
Definition: sequence.c:60
static void * SeqAt(const Seq *seq, int i)
Definition: sequence.h:57
char * strcasestr(const char *haystack, const char *needle)
Definition: strcasestr.c:36
bool StringStartsWith(const char *str, const char *prefix)
Check if a string starts with the given prefix.
Definition: string_lib.c:1335
size_t StringCopy(const char *const from, char *const to, const size_t buf_size)
Copy a string from from to to (a buffer of at least buf_size)
Definition: string_lib.c:60
char * TrimWhitespace(char *s)
Definition: string_lib.c:1196
Seq * SeqStringFromString(const char *str, char delimiter)
Create a new Sequence from splitting a string on a fixed delimiter.
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:34
Definition: buffer.h:50
Definition: item_lib.h:33
Item * next
Definition: item_lib.h:38
char * name
Definition: item_lib.h:34
Definition: rlist.h:35
Rlist * next
Definition: rlist.h:37
Sequence data-structure.
Definition: sequence.h:50
#define CF_IFREQ
Definition: unix_iface.c:58
ProcPostProcessFn NetworkingPortsPostProcessInfo(void *passed_ctx, void *json)
Definition: unix_iface.c:1099
void(* ProcPostProcessFn)(void *ctx, void *json)
Definition: unix_iface.c:91
static IPAddress * ParsedIPAddressHex(const char *data)
Definition: unix_iface.c:911
static const char * GetPortStateString(int state)
Definition: unix_iface.c:1075
JsonElement * GetNetworkingConnections(EvalContext *ctx)
Definition: unix_iface.c:1414
static bool IgnoreJailInterface(int ifaceidx, struct sockaddr_in *inaddr)
Definition: unix_iface.c:99
static long JsonExtractParsedNumber(JsonElement *element, const char *raw_key, const char *new_key, const bool hex_mode, const bool keep_number)
Definition: unix_iface.c:953
static bool IgnoreInterface(char *name)
Definition: unix_iface.c:845
static void FindV6InterfacesInfo(EvalContext *ctx, Rlist **interfaces, Rlist **hardware, Rlist **ips)
Definition: unix_iface.c:619
#define SIZEOF_IFREQ(x)
Definition: unix_iface.c:75
void GetInterfacesInfo(EvalContext *ctx)
Definition: unix_iface.c:354
void GetNetworkingInfo(EvalContext *ctx)
Definition: unix_iface.c:1261
static void JsonRewriteParsedIPAddress(JsonElement *element, const char *raw_key, const char *new_key, const bool as_map)
Definition: unix_iface.c:924
JsonElement * GetProcFileInfo(EvalContext *ctx, const char *filename, const char *key, const char *extracted_key, ProcPostProcessFn post, const char *regex)
Definition: unix_iface.c:1184
static JsonElement * GetNetworkingStatsInfo(const char *filename)
Definition: unix_iface.c:1122
#define IPV6_PREFIX
Definition: unix_iface.c:61
#define CF_IGNORE_INTERFACES
Definition: unix_iface.c:59
static Rlist * IGNORE_INTERFACES
Definition: unix_iface.c:89
static void GetInterfaceFlags(EvalContext *ctx, struct ifreq *ifr, Rlist **flags)
Definition: unix_iface.c:318
static ProcPostProcessFn NetworkingIPv6RoutesPostProcessInfo(void *passed_ctx, void *json)
Definition: unix_iface.c:1025
static void NetworkingRoutesPostProcessInfo(void *passed_ctx, void *json)
Definition: unix_iface.c:978
static void InitIgnoreInterfaces(void)
Definition: unix_iface.c:816
static ProcPostProcessFn NetworkingIPv6AddressesPostProcessInfo(void *passed_ctx, void *json)
Definition: unix_iface.c:1061
static void GetMacAddress(EvalContext *ctx, int fd, struct ifreq *ifr, struct ifreq *ifp, Rlist **interfaces, Rlist **hardware)
Definition: unix_iface.c:138