Example #1
0
  int readTcpipCounters(HSP *sp, SFLHost_ip_counters *c_ip, SFLHost_icmp_counters *c_icmp, SFLHost_tcp_counters *c_tcp, SFLHost_udp_counters *c_udp) {
    int count = 0;
    FILE *procFile;
    char line[MAX_PROC_LINE_CHARS];
    
    procFile= fopen("/proc/net/snmp", "r");
    if(procFile) {
      while(fgets(line, MAX_PROC_LINE_CHARS, procFile)) {
	char *p = line;
	char buf[MAX_PROC_LINE_CHARS];
	char *var = parseNextTok(&p, " \t", NO, 0, NO, buf, MAX_PROC_LINE_CHARS);
	if(strcmp(var, "Ip:") == 0) {
	  count += parseCounterArray(p, (uint32_t *)c_ip, SFLHOST_NUM_IP_COUNTERS);
	}
	else if(strcmp(var, "Icmp:") == 0) {
	  count += parseCounterArray(p, (uint32_t *)c_icmp, SFLHOST_NUM_ICMP_COUNTERS);
	}
	else if(strcmp(var, "Tcp:") == 0) {
	  count += parseCounterArray(p, (uint32_t *)c_tcp, SFLHOST_NUM_TCP_COUNTERS);
	}
	else if(strcmp(var, "Udp:") == 0) {
	  count += parseCounterArray(p, (uint32_t *)c_udp, SFLHOST_NUM_UDP_COUNTERS);
	}
      }
      fclose(procFile);
    }
    return (count > 0);
  }
Example #2
0
  static int parseCounterArray(char *str, uint32_t *counters, int n) {
    char *p = str;
    int ff = 0;
    for(; ff < n; ff++) {
      char buf[MAX_PROC_LINE_CHARS];
      char *var = parseNextTok(&p, " \t", NO, 0, NO, buf, MAX_PROC_LINE_CHARS);
      // stop if we reach the end of the line - or if something was not a number
      if(var == NULL) {
	// no more tokens
	break;
      }
      char *end = NULL;
      long val = strtol(var, &end, 0);
      if(end == var) {
	// nothing was consumed - it wasn't a number
	break;
      }
      counters[ff] = (uint32_t)val;
    }
    return ff;
  }
  int readContainerInterfaces(HSP *sp, HSPVMState *vm)  {
    if(!vm->container) return 0;
    pid_t nspid = vm->container->pid;
    if(debug) myLog(LOG_INFO, "readContainerInterfaces: pid=%u", nspid);
    if(nspid == 0) return 0;

    // do the dirty work after a fork, so we can just exit afterwards,
    // same as they do in "ip netns exec"
    int pfd[2];
    if(pipe(pfd) == -1) {
      myLog(LOG_ERR, "pipe() failed : %s", strerror(errno));
      exit(EXIT_FAILURE);
    }
    pid_t cpid;
    if((cpid = fork()) == -1) {
      myLog(LOG_ERR, "fork() failed : %s", strerror(errno));
      exit(EXIT_FAILURE);
    }
    if(cpid == 0) {
      // in child
      close(pfd[0]);   // close read-end
      dup2(pfd[1], 1); // stdout -> write-end
      dup2(pfd[1], 2); // stderr -> write-end
      close(pfd[1]);
      
      // open /proc/<nspid>/ns/net
      char topath[HSF_DOCKER_MAX_FNAME_LEN+1];
      snprintf(topath, HSF_DOCKER_MAX_FNAME_LEN, "/proc/%u/ns/net", nspid);
      int nsfd = open(topath, O_RDONLY | O_CLOEXEC);
      if(nsfd < 0) {
	fprintf(stderr, "cannot open %s : %s", topath, strerror(errno));
	exit(EXIT_FAILURE);
      }
      
      /* set network namespace
	 CLONE_NEWNET means nsfd must refer to a network namespace
      */
      if(MY_SETNS(nsfd, CLONE_NEWNET) < 0) {
	fprintf(stderr, "seting network namespace failed: %s", strerror(errno));
	exit(EXIT_FAILURE);
      }
      
      /* From "man 2 unshare":  This flag has the same effect as the clone(2)
	 CLONE_NEWNS flag. Unshare the mount namespace, so that the calling
	 process has a private copy of its namespace which is not shared with
	 any other process. Specifying this flag automatically implies CLONE_FS
	 as well. Use of CLONE_NEWNS requires the CAP_SYS_ADMIN capability. */
      if(unshare(CLONE_NEWNS) < 0) {
	fprintf(stderr, "seting network namespace failed: %s", strerror(errno));
	exit(EXIT_FAILURE);
      }

      int fd = socket(PF_INET, SOCK_DGRAM, 0);
      if(fd < 0) {
	fprintf(stderr, "error opening socket: %d (%s)\n", errno, strerror(errno));
	return 0;
      }

      FILE *procFile = fopen("/proc/net/dev", "r");
      if(procFile) {
	struct ifreq ifr;
	memset(&ifr, 0, sizeof(ifr));
	char line[MAX_PROC_LINE_CHARS];
	int lineNo = 0;
	while(fgets(line, MAX_PROC_LINE_CHARS, procFile)) {
	  if(lineNo++ < 2) continue; // skip headers
	  char buf[MAX_PROC_LINE_CHARS];
	  char *p = line;
	  char *devName = parseNextTok(&p, " \t:", NO, '\0', NO, buf, MAX_PROC_LINE_CHARS);
	  if(devName && my_strlen(devName) < IFNAMSIZ) {
	    strncpy(ifr.ifr_name, devName, sizeof(ifr.ifr_name));
	    // Get the flags for this interface
	    if(ioctl(fd,SIOCGIFFLAGS, &ifr) < 0) {
	      fprintf(stderr, "container device %s Get SIOCGIFFLAGS failed : %s",
		      devName,
		      strerror(errno));
	    }
	    else {
	      int up = (ifr.ifr_flags & IFF_UP) ? YES : NO;
	      int loopback = (ifr.ifr_flags & IFF_LOOPBACK) ? YES : NO;

	      if(up && !loopback) {
		// try to get ifIndex next, because we only care about
		// ifIndex and MAC when looking at container interfaces
		if(ioctl(fd,SIOCGIFINDEX, &ifr) < 0) {
		  // only complain about this if we are debugging
		  if(debug) {
		    fprintf(stderr, "container device %s Get SIOCGIFINDEX failed : %s",
			  devName,
			  strerror(errno));
		  }
		}
		else {
		  int ifIndex = ifr.ifr_ifindex;
		  
		  // Get the MAC Address for this interface
		  if(ioctl(fd,SIOCGIFHWADDR, &ifr) < 0) {
		    if(debug) {
		      fprintf(stderr, "device %s Get SIOCGIFHWADDR failed : %s",
			      devName,
			      strerror(errno));
		    }
		  }
		  else {
		    u_char macStr[13];
		    printHex((u_char *)&ifr.ifr_hwaddr.sa_data, 6, macStr, 12, NO);
		    // send this info back up the pipe to my my parent
		    printf("VNIC: %u %s %s\n", ifIndex, devName, macStr);
		  }
		}
	      }
	    }
	  }
	}
      }

      // don't even bother to close file-descriptors,  just bail
      exit(0);
      
    }
    else {
      // in parent
      close(pfd[1]); // close write-end
      // read from read-end
      FILE *ovs;
      if((ovs = fdopen(pfd[0], "r")) == NULL) {
	myLog(LOG_ERR, "readContainerInterfaces: fdopen() failed : %s", strerror(errno));
	return 0;
      }
      char line[MAX_PROC_LINE_CHARS];
      while(fgets(line, MAX_PROC_LINE_CHARS, ovs)) containerLinkCB(vm, line);
      fclose(ovs);
      wait(NULL); // block here until child is done
    }

    return vm->interfaces->num_adaptors;
  }
  int readInterfaces(HSP *sp, uint32_t *p_added, uint32_t *p_removed, uint32_t *p_cameup, uint32_t *p_wentdown, uint32_t *p_changed)
{
  uint32_t ad_added=0, ad_removed=0, ad_cameup=0, ad_wentdown=0, ad_changed=0;
  if(sp->adaptorList == NULL) sp->adaptorList = adaptorListNew();
  else adaptorListMarkAll(sp->adaptorList);

  // Walk the interfaces and collect the non-loopback interfaces so that we
  // have a list of MAC addresses for each interface (usually only 1).
  //
  // May need to come back and run a variation of this where we supply
  // a domain and collect the virtual interfaces for that domain in a
  // similar way.  It looks like we do that by just parsing the numbers
  // out of the interface name.
  
  int fd = socket (PF_INET, SOCK_DGRAM, 0);
  if (fd < 0) {
    fprintf (stderr, "error opening socket: %d (%s)\n", errno, strerror(errno));
    return 0;
  }

  FILE *procFile = fopen("/proc/net/dev", "r");
  if(procFile) {
    struct ifreq ifr;
    memset(&ifr, 0, sizeof(ifr));
    char line[MAX_PROC_LINE_CHARS];
    int lineNo = 0;
    while(fgets(line, MAX_PROC_LINE_CHARS, procFile)) {
      if(lineNo++ < 2) continue; // skip headers
      // the device name is always the first token before the ":"
      char buf[MAX_PROC_LINE_CHARS];
      char *p = line;
      char *devName = parseNextTok(&p, " \t:", NO, '\0', NO, buf, MAX_PROC_LINE_CHARS);
      if(devName && my_strlen(devName) < IFNAMSIZ) {
	devName = trimWhitespace(devName);
	if(devName && strlen(devName) < IFNAMSIZ) {
	  // we set the ifr_name field to make our queries
	  strncpy(ifr.ifr_name, devName, sizeof(ifr.ifr_name));

	  if(debug > 1) {
	    myLog(LOG_INFO, "reading interface %s", devName);
	  }

	  // Get the flags for this interface
	  if(ioctl(fd,SIOCGIFFLAGS, &ifr) < 0) {
	    myLog(LOG_ERR, "device %s Get SIOCGIFFLAGS failed : %s",
		  devName,
		  strerror(errno));
	  }
	  else {
	    int up = (ifr.ifr_flags & IFF_UP) ? YES : NO;
	    int loopback = (ifr.ifr_flags & IFF_LOOPBACK) ? YES : NO;
	    int promisc =  (ifr.ifr_flags & IFF_PROMISC) ? YES : NO;
	    int bond_master = (ifr.ifr_flags & IFF_MASTER) ? YES : NO;
	    int bond_slave = (ifr.ifr_flags & IFF_SLAVE) ? YES : NO;
	    //int hasBroadcast = (ifr.ifr_flags & IFF_BROADCAST);
	    //int pointToPoint = (ifr.ifr_flags & IFF_POINTOPOINT);

	    // used to ignore loopback interfaces here, and interfaces
	    // that are currently marked down, but now those are
	    // filtered at the point where we roll together the
	    // counters, or build the list for export
	      
	    // Get the MAC Address for this interface
	    if(ioctl(fd,SIOCGIFHWADDR, &ifr) < 0) {
	      myLog(LOG_ERR, "device %s Get SIOCGIFHWADDR failed : %s",
		    devName,
		    strerror(errno));
	    }
	    
	    // for now just assume that each interface has only one MAC.  It's not clear how we can
	    // learn multiple MACs this way anyhow.  It seems like there is just one per ifr record.
	    // find or create a new "adaptor" entry
	    SFLAdaptor *adaptor = adaptorListGet(sp->adaptorList, devName);
	    if(adaptor == NULL) {
	      ad_added++;
	      adaptor = adaptorListAdd(sp->adaptorList, devName, (u_char *)&ifr.ifr_hwaddr.sa_data, sizeof(HSPAdaptorNIO));
	    }
	    
	    // clear the mark so we don't free it below
	    adaptor->marked = NO;
	    
	    // this flag might belong in the adaptorNIO struct
	    adaptor->promiscuous = promisc;
	    
	    // remember some useful flags in the userData structure
	    HSPAdaptorNIO *adaptorNIO = (HSPAdaptorNIO *)adaptor->userData;
	    if(adaptorNIO->up != up) {
	      if(up) ad_cameup++;
	      else ad_wentdown++;
	      if(debug) {
		myLog(LOG_INFO, "adaptor %s %s",
		      adaptor->deviceName,
		      up ? "came up" : "went down");
	      }
	    }
	    adaptorNIO->up = up;
	    adaptorNIO->loopback = loopback;
	    adaptorNIO->bond_master = bond_master;
	    adaptorNIO->bond_slave = bond_slave;
	    adaptorNIO->vlan = HSP_VLAN_ALL; // may be modified below
#ifdef HSP_SWITCHPORT_REGEX
	    if(regexec(&sp->swp_regex, devName, 0, NULL, 0) == 0) {
	      adaptorNIO->switchPort = YES;
	    }
#endif
	    // Try and get the ifIndex for this interface
	    if(ioctl(fd,SIOCGIFINDEX, &ifr) < 0) {
	      // only complain about this if we are debugging
	      if(debug) {
		myLog(LOG_ERR, "device %s Get SIOCGIFINDEX failed : %s",
		      devName,
		      strerror(errno));
	      }
	    }
	    else {
	      adaptor->ifIndex = ifr.ifr_ifindex;
	    }
	    
	    // Try to get the IP address for this interface
	    if(ioctl(fd,SIOCGIFADDR, &ifr) < 0) {
	      // only complain about this if we are debugging
	      if(debug) {
		myLog(LOG_ERR, "device %s Get SIOCGIFADDR failed : %s",
		      devName,
		      strerror(errno));
	      }
	    }
	    else {
	      if (ifr.ifr_addr.sa_family == AF_INET) {
		struct sockaddr_in *s = (struct sockaddr_in *)&ifr.ifr_addr;
		// IP addr is now s->sin_addr
		adaptorNIO->ipAddr.type = SFLADDRESSTYPE_IP_V4;
		adaptorNIO->ipAddr.address.ip_v4.addr = s->sin_addr.s_addr;
	      }
	      //else if (ifr.ifr_addr.sa_family == AF_INET6) {
	      // not sure this ever happens - on a linux system IPv6 addresses
	      // are picked up from /proc/net/if_inet6
	      // struct sockaddr_in6 *s = (struct sockaddr_in6 *)&ifr.ifr_addr;
	      // IP6 addr is now s->sin6_addr;
	      //}
	    }

	    // use ethtool to get info about direction/speed and more
	    if(read_ethtool_info(&ifr, fd, adaptor) == YES) {
	      ad_changed++;
	    }
	  }
	}
      }
    }
    fclose(procFile);
  }
  
  close (fd);

  // now remove and free any that are still marked
  ad_removed = adaptorListFreeMarked(sp->adaptorList);

  // check in case any of the survivors are specific
  // to a particular VLAN
  readVLANs(sp);

  // now that we have the evidence gathered together, we can
  // set the L3 address priorities (used for auto-selecting
  // the sFlow-agent-address if requrired to by the config.
  setAddressPriorities(sp);

  // now we can read IPv6 addresses too - they come from a
  // different place. Depending on the address priorities this
  // may cause the adaptor's best-choice ipAddress to be
  // overwritten.
  readIPv6Addresses(sp);

  if(p_added) *p_added = ad_added;
  if(p_removed) *p_removed = ad_removed;
  if(p_cameup) *p_cameup = ad_cameup;
  if(p_wentdown) *p_wentdown = ad_wentdown;
  if(p_changed) *p_changed = ad_changed;

  return sp->adaptorList->num_adaptors;
}