1
/*********************************************************
2
* Copyright (C) 2005 VMware, Inc. All rights reserved.
4
* This program is free software; you can redistribute it and/or modify it
5
* under the terms of the GNU Lesser General Public License as published
6
* by the Free Software Foundation version 2.1 and no later version.
8
* This program is distributed in the hope that it will be useful, but
9
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10
* or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
11
* License for more details.
13
* You should have received a copy of the GNU Lesser General Public License
14
* along with this program; if not, write to the Free Software Foundation, Inc.,
15
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
17
*********************************************************/
20
* @file guestInfoPosix.c
22
* Contains POSIX-specific bits of GuestInfo collector library.
30
# include <sys/systeminfo.h>
33
#include <sys/types.h>
34
#include <sys/socket.h>
37
#if defined(__FreeBSD__) || defined(__APPLE__)
38
# include <sys/sysctl.h>
41
# ifdef DNET_IS_DUMBNET
48
#include <netinet/in.h>
49
#include <arpa/nameser.h>
58
* resolver(3) and IPv6:
60
* The ISC BIND resolver included various IPv6 implementations over time, but
61
* unfortunately the ISC hadn't bumped __RES accordingly. (__RES is -supposed-
62
* to behave as a version datestamp for the resolver interface.) Similarly
63
* the GNU C Library forked resolv.h and made modifications of their own, also
64
* without changing __RES.
66
* glibc 2.1.92 included a patch which provides IPv6 name server support by
67
* embedding in6_addr pointers in _res._u._ext. Since I only care about major
68
* and minor numbers, though, I'm going to condition this impl. on glibc 2.2.
70
* ISC, OTOH, provided accessing IPv6 servers via a res_getservers API.
71
* TTBOMK, this went public with BIND 8.3.0. Unfortunately __RES wasn't
72
* bumped for this release, so instead I'm going to assume that appearance with
73
* that release of a new macro, RES_F_DNS0ERR, implies this API is available.
74
* (For internal builds, we'll know instantly when a build breaks. The down-
75
* side is that this could cause some trouble for Open VM Tools users. ,_,)
77
* resolv.h version IPv6 API __RES
78
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
79
* glibc 2.2+ _ext 19991006
80
* BIND 8.3.0 getservers 19991006
81
* BIND 8.3.4+ getservers 20030124(+?)
83
* To distinguish between the variants where __RES == 19991006, I'll
84
* discriminate on the existence of new macros included with the appropriate
89
# if __GLIBC_PREREQ(2,2)
90
# define RESOLVER_IPV6_EXT
91
# endif // __GLIBC_PREREQ(2,2)
92
#elif (__RES > 19991006 || (__RES == 19991006 && defined RES_F_EDNS0ERR))
93
# define RESOLVER_IPV6_GETSERVERS
94
#endif // if defined __GLIBC__
98
#include "sys/utsname.h"
99
#include "sys/ioctl.h"
101
#include "hostinfo.h"
102
#include "getlibInt.h"
105
#include "guest_os.h"
106
#include "guestApp.h"
107
#include "guestInfo.h"
109
#ifdef USE_SLASH_PROC
110
# include "slashProc.h"
122
static void RecordNetworkAddress(GuestNicV3 *nic, const struct addr *addr);
123
static int ReadInterfaceDetails(const struct intf_entry *entry, void *arg);
124
static Bool RecordResolverInfo(NicInfoV3 *nicInfo);
125
static void RecordResolverNS(DnsConfigInfo *dnsConfigInfo);
126
static Bool RecordRoutingInfo(NicInfoV3 *nicInfo);
131
******************************************************************************
132
* GuestInfoGetFqdn -- */ /**
134
* @copydoc GuestInfo_GetFqdn
136
******************************************************************************
140
GuestInfoGetFqdn(int outBufLen, // IN: length of output buffer
141
char fqdn[]) // OUT: fully qualified domain name
144
if (gethostname(fqdn, outBufLen) < 0) {
145
g_debug("Error, gethostname failed\n");
154
******************************************************************************
155
* GuestInfoGetNicInfo -- */ /**
157
* @copydoc GuestInfo_GetNicInfo
159
******************************************************************************
163
GuestInfoGetNicInfo(NicInfoV3 *nicInfo) // OUT
168
/* Get a handle to read the network interface configuration details. */
169
if ((intf = intf_open()) == NULL) {
170
g_debug("Error, failed NULL result from intf_open()\n");
174
if (intf_loop(intf, ReadInterfaceDetails, nicInfo) < 0) {
176
g_debug("Error, negative result from intf_loop\n");
182
if (!RecordResolverInfo(nicInfo)) {
186
if (!RecordRoutingInfo(nicInfo)) {
198
******************************************************************************
199
* GuestInfo_GetDiskInfo -- */ /**
201
* Uses wiper library to enumerate fixed volumes and lookup utilization data.
203
* @return Pointer to a GuestDiskInfo structure on success or NULL on failure.
204
* Caller should free returned pointer with GuestInfoFreeDiskInfo.
206
******************************************************************************
210
GuestInfo_GetDiskInfo(void)
212
return GuestInfoGetDiskInfoWiper();
223
******************************************************************************
224
* RecordNetworkAddress -- */ /**
226
* @brief Massages a dnet(3)-style interface address (IPv4 or IPv6) and stores
227
* it as part of a GuestNicV3 structure.
229
* @param[in] nic Operand NIC.
230
* @param[in] addr dnet(3) address.
232
******************************************************************************
236
RecordNetworkAddress(GuestNicV3 *nic, // IN: operand NIC
237
const struct addr *addr) // IN: dnet(3) address to process
239
struct sockaddr_storage ss;
240
struct sockaddr *sa = (struct sockaddr *)&ss;
242
memset(&ss, 0, sizeof ss);
244
GuestInfoAddIpAddress(nic, sa, addr->addr_bits, NULL, NULL);
249
******************************************************************************
250
* ReadInterfaceDetails -- */ /**
252
* @brief Callback function called by libdnet when iterating over all the NICs
255
* @param[in] entry Current interface entry.
256
* @param[in] arg Pointer to NicInfoV3 container.
258
* @note New GuestNicV3 structures are added to the NicInfoV3 structure.
261
* @retval -1 Failure.
263
******************************************************************************
267
ReadInterfaceDetails(const struct intf_entry *entry, // IN: current interface entry
268
void *arg) // IN: Pointer to the GuestNicList
271
NicInfoV3 *nicInfo = arg;
276
if (entry->intf_type == INTF_TYPE_ETH &&
277
entry->intf_link_addr.addr_type == ADDR_TYPE_ETH) {
278
GuestNicV3 *nic = NULL;
279
char macAddress[NICINFO_MAC_LEN];
282
* There is a race where the guest info plugin might be iterating over the
283
* interfaces while the OS is modifying them (i.e. by bringing them up
284
* after a resume). If we see an ethernet interface with an invalid MAC,
285
* then ignore it for now. Subsequent iterations of the gather loop will
286
* pick up any changes.
288
if (entry->intf_link_addr.addr_type == ADDR_TYPE_ETH) {
289
Str_Sprintf(macAddress, sizeof macAddress, "%s",
290
addr_ntoa(&entry->intf_link_addr));
291
nic = GuestInfoAddNicEntry(nicInfo, macAddress, NULL, NULL);
292
ASSERT_MEM_ALLOC(nic);
294
/* Record the "primary" address. */
295
if (entry->intf_addr.addr_type == ADDR_TYPE_IP ||
296
entry->intf_addr.addr_type == ADDR_TYPE_IP6) {
297
RecordNetworkAddress(nic, &entry->intf_addr);
300
/* Walk the list of alias's and add those that are IPV4 or IPV6 */
301
for (i = 0; i < entry->intf_alias_num; i++) {
302
const struct addr *alias = &entry->intf_alias_addrs[i];
303
if (alias->addr_type == ADDR_TYPE_IP ||
304
alias->addr_type == ADDR_TYPE_IP6) {
305
RecordNetworkAddress(nic, alias);
316
******************************************************************************
317
* RecordResolverInfo -- */ /**
319
* @brief Query resolver(3), mapping settings to DnsConfigInfo.
321
* @param[out] nicInfo NicInfoV3 container.
323
* @retval TRUE Values collected, attached to @a nicInfo.
324
* @retval FALSE Something went wrong. @a nicInfo is unharmed.
326
******************************************************************************
330
RecordResolverInfo(NicInfoV3 *nicInfo) // OUT
332
DnsConfigInfo *dnsConfigInfo = NULL;
333
char namebuf[DNSINFO_MAX_ADDRLEN + 1];
336
if (res_init() == -1) {
340
dnsConfigInfo = Util_SafeCalloc(1, sizeof *dnsConfigInfo);
343
* Copy in the host name.
345
if (!GuestInfoGetFqdn(sizeof namebuf, namebuf)) {
348
dnsConfigInfo->hostName =
349
Util_SafeCalloc(1, sizeof *dnsConfigInfo->hostName);
350
*dnsConfigInfo->hostName = Util_SafeStrdup(namebuf);
353
* Repeat with the domain name.
355
dnsConfigInfo->domainName =
356
Util_SafeCalloc(1, sizeof *dnsConfigInfo->domainName);
357
*dnsConfigInfo->domainName = Util_SafeStrdup(_res.defdname);
362
RecordResolverNS(dnsConfigInfo);
367
for (s = _res.dnsrch; *s; s++) {
370
/* Check to see if we're going above our limit. See bug 605821. */
371
if (dnsConfigInfo->searchSuffixes.searchSuffixes_len == DNSINFO_MAX_SUFFIXES) {
372
g_message("%s: dns search suffix limit (%d) reached, skipping overflow.",
373
__FUNCTION__, DNSINFO_MAX_SUFFIXES);
377
suffix = XDRUTIL_ARRAYAPPEND(dnsConfigInfo, searchSuffixes, 1);
378
ASSERT_MEM_ALLOC(suffix);
379
*suffix = Util_SafeStrdup(*s);
383
* "Commit" dnsConfigInfo to nicInfo.
385
nicInfo->dnsConfigInfo = dnsConfigInfo;
390
VMX_XDR_FREE(xdr_DnsConfigInfo, dnsConfigInfo);
397
******************************************************************************
398
* RecordResolverNS -- */ /**
400
* @brief Copies name servers used by resolver(3) to @a dnsConfigInfo.
402
* @param[out] dnsConfigInfo Destination DnsConfigInfo container.
404
******************************************************************************
408
RecordResolverNS(DnsConfigInfo *dnsConfigInfo) // IN
412
#if defined RESOLVER_IPV6_GETSERVERS
414
union res_sockaddr_union *ns;
415
ns = Util_SafeCalloc(_res.nscount, sizeof *ns);
416
if (res_getservers(&_res, ns, _res.nscount) != _res.nscount) {
417
g_warning("%s: res_getservers failed.\n", __func__);
420
for (i = 0; i < _res.nscount; i++) {
421
struct sockaddr *sa = (struct sockaddr *)&ns[i];
422
if (sa->sa_family == AF_INET || sa->sa_family == AF_INET6) {
425
/* Check to see if we're going above our limit. See bug 605821. */
426
if (dnsConfigInfo->serverList.serverList_len == DNSINFO_MAX_SERVERS) {
427
g_message("%s: dns server limit (%d) reached, skipping overflow.",
428
__FUNCTION__, DNSINFO_MAX_SERVERS);
432
ip = XDRUTIL_ARRAYAPPEND(dnsConfigInfo, serverList, 1);
433
ASSERT_MEM_ALLOC(ip);
434
GuestInfoSockaddrToTypedIpAddress(sa, ip);
438
#else // if defined RESOLVER_IPV6_GETSERVERS
441
* Name servers (IPv4).
443
for (i = 0; i < MAXNS; i++) {
444
struct sockaddr_in *sin = &_res.nsaddr_list[i];
445
if (sin->sin_family == AF_INET) {
448
/* Check to see if we're going above our limit. See bug 605821. */
449
if (dnsConfigInfo->serverList.serverList_len == DNSINFO_MAX_SERVERS) {
450
g_message("%s: dns server limit (%d) reached, skipping overflow.",
451
__FUNCTION__, DNSINFO_MAX_SERVERS);
455
ip = XDRUTIL_ARRAYAPPEND(dnsConfigInfo, serverList, 1);
456
ASSERT_MEM_ALLOC(ip);
457
GuestInfoSockaddrToTypedIpAddress((struct sockaddr *)sin, ip);
460
# if defined RESOLVER_IPV6_EXT
462
* Name servers (IPv6).
464
for (i = 0; i < MAXNS; i++) {
465
struct sockaddr_in6 *sin6 = _res._u._ext.nsaddrs[i];
469
/* Check to see if we're going above our limit. See bug 605821. */
470
if (dnsConfigInfo->serverList.serverList_len == DNSINFO_MAX_SERVERS) {
471
g_message("%s: dns server limit (%d) reached, skipping overflow.",
472
__FUNCTION__, DNSINFO_MAX_SERVERS);
476
ip = XDRUTIL_ARRAYAPPEND(dnsConfigInfo, serverList, 1);
477
ASSERT_MEM_ALLOC(ip);
478
GuestInfoSockaddrToTypedIpAddress((struct sockaddr *)sin6, ip);
481
# endif // if defined RESOLVER_IPV6_EXT
483
#endif // if !defined RESOLVER_IPV6_GETSERVERS
487
#ifdef USE_SLASH_PROC
489
******************************************************************************
490
* RecordRoutingInfoIPv4 -- */ /**
492
* @brief Query the IPv4 routing subsystem and pack up contents
493
* (struct rtentry) into InetCidrRouteEntries.
495
* @param[out] nicInfo NicInfoV3 container.
497
* @note Do not call this routine without first populating @a nicInfo 's NIC
500
* @retval TRUE Values collected, attached to @a nicInfo.
501
* @retval FALSE Something went wrong. @a nicInfo is unharmed.
503
******************************************************************************
507
RecordRoutingInfoIPv4(NicInfoV3 *nicInfo)
509
GPtrArray *routes = NULL;
513
if ((routes = SlashProcNet_GetRoute()) == NULL) {
517
for (i = 0; i < routes->len; i++) {
518
struct rtentry *rtentry;
519
struct sockaddr_in *sin_dst;
520
struct sockaddr_in *sin_gateway;
521
struct sockaddr_in *sin_genmask;
522
InetCidrRouteEntry *icre;
525
/* Check to see if we're going above our limit. See bug 605821. */
526
if (nicInfo->routes.routes_len == NICINFO_MAX_ROUTES) {
527
g_message("%s: route limit (%d) reached, skipping overflow.",
528
__FUNCTION__, NICINFO_MAX_ROUTES);
532
rtentry = g_ptr_array_index(routes, i);
534
if ((rtentry->rt_flags & RTF_UP) == 0 ||
535
!GuestInfoGetNicInfoIfIndex(nicInfo,
536
if_nametoindex(rtentry->rt_dev),
541
icre = XDRUTIL_ARRAYAPPEND(nicInfo, routes, 1);
542
ASSERT_MEM_ALLOC(icre);
544
sin_dst = (struct sockaddr_in *)&rtentry->rt_dst;
545
sin_gateway = (struct sockaddr_in *)&rtentry->rt_gateway;
546
sin_genmask = (struct sockaddr_in *)&rtentry->rt_genmask;
548
GuestInfoSockaddrToTypedIpAddress((struct sockaddr *)sin_dst,
549
&icre->inetCidrRouteDest);
551
addr_stob((struct sockaddr *)sin_genmask,
552
(uint16_t *)&icre->inetCidrRoutePfxLen);
555
* Gateways are optional (ex: one can bind a route to an interface w/o
556
* specifying a next hop address).
558
if (rtentry->rt_flags & RTF_GATEWAY) {
559
TypedIpAddress *ip = Util_SafeCalloc(1, sizeof *ip);
560
GuestInfoSockaddrToTypedIpAddress((struct sockaddr *)sin_gateway, ip);
561
icre->inetCidrRouteNextHop = ip;
567
icre->inetCidrRouteIfIndex = ifIndex;
568
icre->inetCidrRouteMetric = rtentry->rt_metric;
573
SlashProcNet_FreeRoute(routes);
579
******************************************************************************
580
* RecordRoutingInfoIPv6 -- */ /**
582
* @brief Query the IPv6 routing subsystem and pack up contents
583
* (struct in6_rtmsg) into InetCidrRouteEntries.
585
* @param[out] nicInfo NicInfoV3 container.
587
* @note Do not call this routine without first populating @a nicInfo 's NIC
590
* @retval TRUE Values collected, attached to @a nicInfo.
591
* @retval FALSE Something went wrong. @a nicInfo is unharmed.
593
******************************************************************************
597
RecordRoutingInfoIPv6(NicInfoV3 *nicInfo)
599
GPtrArray *routes = NULL;
603
if ((routes = SlashProcNet_GetRoute6()) == NULL) {
607
for (i = 0; i < routes->len; i++) {
608
struct sockaddr_storage ss;
609
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss;
610
struct in6_rtmsg *in6_rtmsg;
611
InetCidrRouteEntry *icre;
612
uint32_t ifIndex = -1;
614
/* Check to see if we're going above our limit. See bug 605821. */
615
if (nicInfo->routes.routes_len == NICINFO_MAX_ROUTES) {
616
g_message("%s: route limit (%d) reached, skipping overflow.",
617
__FUNCTION__, NICINFO_MAX_ROUTES);
621
in6_rtmsg = g_ptr_array_index(routes, i);
623
if ((in6_rtmsg->rtmsg_flags & RTF_UP) == 0 ||
624
!GuestInfoGetNicInfoIfIndex(nicInfo, in6_rtmsg->rtmsg_ifindex,
629
icre = XDRUTIL_ARRAYAPPEND(nicInfo, routes, 1);
630
ASSERT_MEM_ALLOC(icre);
635
sin6->sin6_family = AF_INET6;
636
sin6->sin6_addr = in6_rtmsg->rtmsg_dst;
637
GuestInfoSockaddrToTypedIpAddress((struct sockaddr *)sin6,
638
&icre->inetCidrRouteDest);
640
icre->inetCidrRoutePfxLen = in6_rtmsg->rtmsg_dst_len;
645
if (in6_rtmsg->rtmsg_flags & RTF_GATEWAY) {
646
TypedIpAddress *ip = Util_SafeCalloc(1, sizeof *ip);
647
sin6->sin6_addr = in6_rtmsg->rtmsg_gateway;
648
GuestInfoSockaddrToTypedIpAddress((struct sockaddr *)sin6, ip);
649
icre->inetCidrRouteNextHop = ip;
655
icre->inetCidrRouteIfIndex = ifIndex;
656
icre->inetCidrRouteMetric = in6_rtmsg->rtmsg_metric;
661
SlashProcNet_FreeRoute6(routes);
667
******************************************************************************
668
* RecordRoutingInfo -- */ /**
670
* @brief Query the routing subsystem and pack up contents into
671
* InetCidrRouteEntries.
673
* @param[out] nicInfo NicInfoV3 container.
675
* @note Do not call this routine without first populating @a nicInfo 's NIC
678
* @retval TRUE Values collected, attached to @a nicInfo.
679
* @retval FALSE Something went wrong.
681
******************************************************************************
685
RecordRoutingInfo(NicInfoV3 *nicInfo)
689
if (File_Exists("/proc/net/route") && !RecordRoutingInfoIPv4(nicInfo)) {
690
g_warning("%s: Unable to collect IPv4 routing table.\n", __func__);
694
if (File_Exists("/proc/net/ipv6_route") && !RecordRoutingInfoIPv6(nicInfo)) {
695
g_warning("%s: Unable to collect IPv6 routing table.\n", __func__);
702
#else // ifdef USE_SLASH_PROC
704
RecordRoutingInfo(NicInfoV3 *nicInfo)
710
#endif // ifndef NO_DNET