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); }
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; }