bool Discovery::processMessage(const NodeAddr& remote, const char *message, std::vector<NodeDevice> &newNodes) { bool sendNow = false; std::string act((const char*)message); NodeDevice nd(remote); nd.name = std::string((const char*)&message[act.length() + 1]); nd.id = std::string((const char*)&message[act.length() + nd.name.length() + 2]); nd.sinceVitalSign = 0; // check if we somehow received our own broadcast if (nd.id == getHwAddress()) { return false; } if ("ULLTRA_DEV_R" == act) { // register auto ex = m_discovered.find(nd.id); if (ex != m_discovered.end()) { // already there? ex->second.sinceVitalSign = 0; return false; } LOG(logINFO) << "Discovered node " << nd; m_discovered.insert(std::pair<std::string, NodeDevice>(nd.id, nd)); send("ULLTRA_DEV_R", nd); // send a discovery message back sendNow = true; newNodes.push_back(m_discovered.find(nd.id)->second); } else if ("ULLTRA_DEV_Z" == act) { // unregister auto it = m_discovered.find(nd.id); if (it != m_discovered.end()) { LOG(logINFO) << "Lost node " << nd; if (onNodeLost) onNodeLost(it->second); m_discovered.erase(m_discovered.find(nd.id)); sendNow = true; } } else if ("ULLTRA_DEV_U" == act) { // heartbeat LOG(logINFO) << "Heartbeat node " << nd; //ProcessHeartbeat(remote.sin_addr, ((HeartBeatData*)&recvBuffer[10])); } else if (m_customHandlers.find(act) != m_customHandlers.end() && m_customHandlers[act]) { auto ex = m_discovered.find(nd.id); if (ex == m_discovered.end()) { LOG(logDEBUG1) << "Received custom message " << act << " from unknown " << nd; return false; } ex->second.sinceVitalSign = 0; LOG(logINFO) << "Custom handler for message " << act << " from node " << nd; m_customHandlers[act](ex->second); } else { LOG(logDEBUG1) << "Received unknown message from " << nd; } return sendNow; }
static Boolean getInterfaceInfo(char* ifaceName, InterfaceInfo* ifaceInfo) { int res; res = interfaceExists(ifaceName); if (res == -1) { return FALSE; } else if (res == 0) { ERROR("Interface %s does not exist.\n", ifaceName); return FALSE; } res = getInterfaceAddress(ifaceName, ifaceInfo->addressFamily, &ifaceInfo->afAddress); if (res == -1) { return FALSE; } ifaceInfo->hasAfAddress = res; res = getHwAddress(ifaceName, (unsigned char*)ifaceInfo->hwAddress, 6); if (res == -1) { return FALSE; } ifaceInfo->hasHwAddress = res; res = getInterfaceFlags(ifaceName, &ifaceInfo->flags); if (res == -1) { return FALSE; } return TRUE; }
bool Discovery::start(int broadcastPort) { if (m_broadcastPort) return false; auto hwid = getHwAddress(); if (hwid.empty()) { LOG(logERROR) << "Not hardware id available!"; return false; } LOG(logDEBUG) << "HWID: " << hwid; m_broadcastPort = broadcastPort; m_updateCounter = 0; m_receiver = SimpleUdpReceiver::create(broadcastPort, true); if (!m_receiver) { LOG(logERROR) << ("Error: could not create receiver socket!") << lastError(); return false; } SOCKET soc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (soc == -1) { LOG(logERROR) << ("Error: could not create broadcast socket!") << lastError(); return false; } #ifdef _WIN32 char yes = 1; #else int yes = 1; #endif // allow broadcast int rv = setsockopt(soc, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes)); if (rv == -1) { LOG(logERROR) << ("Error: could not set broadcast socket options!") << lastError(); return false; } m_soc = soc; return true; }
bool Discovery::start(int broadcastPort) { if (m_broadcastPort) return false; // LOG(logDEBUG) << "default bind address set to " << NodeDevice::localAny; m_broadcastPort = broadcastPort; m_updateCounter = 0; auto hwid = getHwAddress(); if (hwid.empty()) { LOG(logERROR) << "Not hardware id available!"; return false; } LOG(logDEBUG4) << "HWID: " << hwid; initBroadcast(broadcastPort); //initMulticast(broadcastPort+1); return true; }
/*******************************************************************-o-****** * setup_engineID * * Parameters: * **eidp * *text Printable (?) text to be plugged into the snmpEngineID. * * Return: * Length of allocated engineID string in bytes, -OR- * -1 on error. * * * Create an snmpEngineID using text and the local IP address. If eidp * is defined, use it to return a pointer to the newly allocated data. * Otherwise, use the result to define engineID defined in this module. * * Line syntax: * engineID <text> | NULL * * XXX What if a node has multiple interfaces? * XXX What if multiple engines all choose the same address? * (answer: You're screwed, because you might need a kul database * which is dependant on the current engineID. Enumeration and other * tricks won't work). */ int setup_engineID(u_char ** eidp, const char *text) { int enterpriseid = htonl(NETSNMP_ENTERPRISE_OID), netsnmpoid = htonl(NETSNMP_OID), localsetup = (eidp) ? 0 : 1; /* * Use local engineID if *eidp == NULL. */ #ifdef HAVE_GETHOSTNAME u_char buf[SNMP_MAXBUF_SMALL]; struct hostent *hent = NULL; #endif u_char *bufp = NULL; size_t len; int localEngineIDType = engineIDType; int tmpint; time_t tmptime; engineIDIsSet = 1; #ifdef HAVE_GETHOSTNAME #ifdef AF_INET6 /* * see if they selected IPV4 or IPV6 support */ if ((ENGINEID_TYPE_IPV6 == localEngineIDType) || (ENGINEID_TYPE_IPV4 == localEngineIDType)) { /* * get the host name and save the information */ gethostname((char *) buf, sizeof(buf)); hent = gethostbyname((char *) buf); if (hent && hent->h_addrtype == AF_INET6) { localEngineIDType = ENGINEID_TYPE_IPV6; } else { /* * Not IPV6 so we go with default */ localEngineIDType = ENGINEID_TYPE_IPV4; } } #else /* * No IPV6 support. Check if they selected IPV6 engineID type. * If so make it IPV4 instead */ if (ENGINEID_TYPE_IPV6 == localEngineIDType) { localEngineIDType = ENGINEID_TYPE_IPV4; } if (ENGINEID_TYPE_IPV4 == localEngineIDType) { /* * get the host name and save the information */ gethostname((char *) buf, sizeof(buf)); hent = gethostbyname((char *) buf); } #endif #endif /* HAVE_GETHOSTNAME */ /* * Determine if we have text and if so setup our localEngineIDType * * appropriately. */ if (NULL != text) { engineIDType = localEngineIDType = ENGINEID_TYPE_TEXT; } /* * Determine length of the engineID string. */ len = 5; /* always have 5 leading bytes */ switch (localEngineIDType) { case ENGINEID_TYPE_TEXT: if (NULL == text) { snmp_log(LOG_ERR, "Can't set up engineID of type text from an empty string.\n"); return -1; } len += strlen(text); /* 5 leading bytes+text. No NULL char */ break; #if defined(IFHWADDRLEN) && defined(SIOCGIFHWADDR) case ENGINEID_TYPE_MACADDR: /* MAC address */ len += 6; /* + 6 bytes for MAC address */ break; #endif case ENGINEID_TYPE_IPV4: /* IPv4 */ len += 4; /* + 4 byte IPV4 address */ break; case ENGINEID_TYPE_IPV6: /* IPv6 */ len += 16; /* + 16 byte IPV6 address */ break; case ENGINEID_TYPE_NETSNMP_RND: /* Net-SNMP specific encoding */ if (engineID) /* already setup, keep current value */ return engineIDLength; if (oldEngineID) { len = oldEngineIDLength; } else { len += sizeof(int) + sizeof(time_t); } break; default: snmp_log(LOG_ERR, "Unknown EngineID type requested for setup (%d). Using IPv4.\n", localEngineIDType); localEngineIDType = ENGINEID_TYPE_IPV4; /* make into IPV4 */ len += 4; /* + 4 byte IPv4 address */ break; } /* switch */ /* * Allocate memory and store enterprise ID. */ if ((bufp = (u_char *) malloc(len)) == NULL) { snmp_log_perror("setup_engineID malloc"); return -1; } if (localEngineIDType == ENGINEID_TYPE_NETSNMP_RND) /* * we must use the net-snmp enterprise id here, regardless */ memcpy(bufp, &netsnmpoid, sizeof(netsnmpoid)); /* XXX Must be 4 bytes! */ else memcpy(bufp, &enterpriseid, sizeof(enterpriseid)); /* XXX Must be 4 bytes! */ bufp[0] |= 0x80; /* * Store the given text -OR- the first found IP address * -OR- the MAC address -OR- random elements * (the latter being the recommended default) */ switch (localEngineIDType) { case ENGINEID_TYPE_NETSNMP_RND: if (oldEngineID) { /* * keep our previous notion of the engineID */ memcpy(bufp, oldEngineID, oldEngineIDLength); } else { /* * Here we've desigend our own ENGINEID that is not based on * an address which may change and may even become conflicting * in the future like most of the default v3 engineID types * suffer from. * * Ours is built from 2 fairly random elements: a random number and * the current time in seconds. This method suffers from boxes * that may not have a correct clock setting and random number * seed at startup, but few OSes should have that problem. */ bufp[4] = ENGINEID_TYPE_NETSNMP_RND; tmpint = random(); memcpy(bufp + 5, &tmpint, sizeof(tmpint)); tmptime = time(NULL); memcpy(bufp + 5 + sizeof(tmpint), &tmptime, sizeof(tmptime)); } break; case ENGINEID_TYPE_TEXT: bufp[4] = ENGINEID_TYPE_TEXT; memcpy((char *) bufp + 5, (text), strlen(text)); break; #ifdef HAVE_GETHOSTNAME #ifdef AF_INET6 case ENGINEID_TYPE_IPV6: bufp[4] = ENGINEID_TYPE_IPV6; memcpy(bufp + 5, hent->h_addr_list[0], hent->h_length); break; #endif #endif #if defined(IFHWADDRLEN) && defined(SIOCGIFHWADDR) case ENGINEID_TYPE_MACADDR: { int x; bufp[4] = ENGINEID_TYPE_MACADDR; /* * use default NIC if none provided */ if (NULL == engineIDNic) { x = getHwAddress(DEFAULT_NIC, (char *)&bufp[5]); } else { x = getHwAddress((char *)engineIDNic, (char *)&bufp[5]); } if (0 != x) /* * function failed fill MAC address with zeros */ { memset(&bufp[5], 0, 6); } } break; #endif case ENGINEID_TYPE_IPV4: default: bufp[4] = ENGINEID_TYPE_IPV4; #ifdef HAVE_GETHOSTNAME if (hent && hent->h_addrtype == AF_INET) { memcpy(bufp + 5, hent->h_addr_list[0], hent->h_length); } else { /* Unknown address type. Default to 127.0.0.1. */ bufp[5] = 127; bufp[6] = 0; bufp[7] = 0; bufp[8] = 1; } #else /* HAVE_GETHOSTNAME */ /* * Unknown address type. Default to 127.0.0.1. */ bufp[5] = 127; bufp[6] = 0; bufp[7] = 0; bufp[8] = 1; #endif /* HAVE_GETHOSTNAME */ break; } /* * Pass the string back to the calling environment, or use it for * our local engineID. */ if (localsetup) { SNMP_FREE(engineID); engineID = bufp; engineIDLength = len; } else { *eidp = bufp; } return len; } /* end setup_engineID() */
bool Discovery::send(const std::string &msg, const NodeDevice &node) { bool isMulticast = !node.exists(); char data[500]; char *dp = &data[0]; auto devName = UP::getDeviceName(); // create 0-seperated string list strcpy(dp, msg.c_str()); dp += msg.length() + 1; strcpy(dp, devName.c_str()); dp += devName.length() + 1; strcpy(dp, getHwAddress().c_str()); dp += getHwAddress().length() + 1; int dataLen = dp - &data[0]; if (!isMulticast) { auto na = node.getAddr(m_broadcastPort); bool ipv6 = (na.getFamily() == AF_INET6); if (sendto(ipv6 ? m_socMulticast : m_socBroadcast4, data, dataLen, 0, (struct sockaddr*)&na, sizeof(na)) != dataLen) { LOG(logERROR) << "Could not send broadcast message to " << node << lastError(); return false; } } else { // ipv4 udp broadcast if (m_socBroadcast4 != -1) { struct sockaddr_in addr4; memset(&addr4, 0, sizeof(addr4)); addr4.sin_family = AF_INET; addr4.sin_port = htons(m_broadcastPort); addr4.sin_addr.s_addr = htonl(INADDR_BROADCAST); if (sendto(m_socBroadcast4, data, dataLen, 0, (struct sockaddr*)&addr4, sizeof(addr4)) != dataLen) { LOG(logERROR) << "Could not send broadcast message on INADDR_BROADCAST! " << lastError(); return false; } // Windows broadcast fix (for ipv4): send broadcast on each host addr char ac[1000]; if (gethostname(ac, sizeof(ac)) == -1) return false; // TODO bug gethostbyname causes seg fault on windows in release (maybe mongoose?) addrinfo *ai; if (getaddrinfo(ac, NULL, NULL, &ai) != 0) { return false; } for (auto cai = ai; cai != 0; cai = cai->ai_next) { struct in_addr daddr; memcpy(&daddr, cai->ai_addr, std::min(sizeof(daddr), (size_t)cai->ai_addrlen)); addr4.sin_addr.s_addr = daddr.s_addr | (255) << (8 * 3); std::string ip4(inet_ntoa(addr4.sin_addr)); //std::cout << "broadcast message to " << ip4 << ":" << ntohs(addr4.sin_port) << "" << std::endl; if (sendto(m_socBroadcast4, data, dataLen, 0, (struct sockaddr*)&addr4, sizeof(addr4)) != dataLen) { LOG(logERROR) << "Could not send broadcast message to " << ip4 << "!" << std::endl; } } freeaddrinfo(ai); } // ipv6 multicast if (m_socMulticast != -1) { if (sendto(m_socMulticast, data, dataLen, 0, (struct sockaddr *)&m_multicastAddrSend, sizeof(m_multicastAddrSend)) != dataLen) { LOG(logERROR) << "sending ipv6 multicast message on " << m_multicastAddrSend << " failed! " << lastError(); return false; } } // send to explicit nodes for (NodeDevice n : m_explicitNodes) { send(msg, n); } } return true; }
void Discovery::send(const std::string &msg, const NodeDevice &node) { SOCKET soc = (SOCKET)m_soc; char data[500]; char hostname[32]; gethostname(hostname, 31); auto hwAddress = getHwAddress(); char *dp = &data[0]; // create 0-seperated string list strcpy(dp, msg.c_str()); dp += strlen(dp) + 1; strcpy(dp, hostname); dp += strlen(hostname) + 1; strcpy(dp, hwAddress.c_str()); dp += hwAddress.length() + 1; int dataLen = dp - &data[0]; struct sockaddr_in s; memset(&s, 0, sizeof(struct sockaddr_in)); s.sin_family = AF_INET; s.sin_port = htons(m_broadcastPort); s.sin_addr.s_addr = node.exists() ? node.addr.s_addr : htonl(INADDR_BROADCAST); int sent = sendto(soc, data, dataLen, 0, (struct sockaddr*)&s, sizeof(struct sockaddr_in)); if (sent != dataLen) { LOG(logERROR) << "Could not send broadcast message!"; } //#ifdef _WIN32 // win32 broadcast fix // use this on linux too? char ac[200]; if (gethostname(ac, sizeof(ac)) == -1) return; struct hostent *phe = gethostbyname(ac); if (phe == 0) return; for (int i = 0; phe->h_addr_list[i] != 0; ++i) { struct in_addr addr; memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr)); addr.s_addr = addr.s_addr | (255) << (8 * 3); s.sin_addr.s_addr = addr.s_addr | (255) << (8 * 3); std::string ip4(inet_ntoa(s.sin_addr)); //std::cout << "broadcast message to " << ip4 << ":" << ntohs(s.sin_port) << "!" << std::endl; sent = sendto(soc, data, dataLen, 0, (struct sockaddr*)&s, sizeof(struct sockaddr_in)); if (sent != dataLen) { LOG(logERROR) << "Could not send broadcast message to " << ip4 << "!" << std::endl; } } //#else // inet_aton("10.0.0.255", &s.sin_addr); // sendto(soc, data, 16 + 32, 0, (struct sockaddr*)&s, sizeof(struct sockaddr_in)); //#endif }
bool Discovery::update(time_t now) { bool sendNow = false; std::map<std::string, NodeDevice> newlyDiscovered; while (true) { struct sockaddr_in remote; int len; const uint8_t * recvBuffer = m_receiver->receive(len, remote); // would block? if (len == -1 || !recvBuffer) break; if (len < 20) continue; std::string act((const char*)recvBuffer); NodeDevice nd; nd.name = std::string((const char*)&recvBuffer[act.length() + 1]); nd.id = std::string((const char*)&recvBuffer[act.length() + nd.name.length() + 2]); nd.addr = remote.sin_addr; // check if we somehow received our own broadcast if (nd.id == getHwAddress()) { continue; } if ("ULLTRA_DEV_R" == act) { // register auto &exN = getNode(nd.id); // already there? if (exN.exists()) { exN.vitalSign = now; continue; } nd.vitalSign = now; if (newlyDiscovered.find(nd.id) == newlyDiscovered.end()) { newlyDiscovered[nd.id] = nd; LOG(logINFO) << "Discovered node " << nd; send("ULLTRA_DEV_R", nd); sendNow = true; } } else if ("ULLTRA_DEV_Z" == act) { // unregister for (auto it = m_discovered.begin(); it != m_discovered.end(); it++) { if (it->id == nd.id || it->addr.s_addr == nd.addr.s_addr) { { LOG(logINFO) << "Lost node " << nd; } sendNow = true; if (onNodeLost) onNodeLost(*it); m_discovered.erase(it); break; } } } else if ("ULLTRA_DEV_H" == act) { // heartbeat LOG(logINFO) << "Heartbeat node " << nd; //ProcessHeartbeat(remote.sin_addr, ((HeartBeatData*)&recvBuffer[10])); } else if (m_customHandlers.find(act) != m_customHandlers.end() && m_customHandlers[act]) { // only accept custom messages from known nodes auto &exN = getNode(nd.id); if (!exN.exists() && newlyDiscovered.find(nd.id) == newlyDiscovered.end()) { { LOG(logDEBUG1) << "Received custom message " << act << " from unknown " << nd; } continue; } exN.vitalSign = now; { LOG(logINFO) << "Custom handler for message " << act << " from node " << nd; } m_customHandlers[act](nd); exN.vitalSign = UP::getMicroSeconds(); } else { { LOG(logDEBUG1) << "Received unknown message from " << nd; } } } // auto-purge dead nodes for (auto it = m_discovered.begin(); it != m_discovered.end(); it++) { if(difftime(now, it->vitalSign) > (UlltraProto::BroadcastInterval*3)) { LOG(logINFO) << "Dead node " << (*it); m_discovered.erase(it); sendNow = true; break; } } // broadcast a discovery packet every 10 updates or if something changed // e.g. if a new node appears make current node visible // this needs to be done beffore onNodeDiscovered() call! if (difftime(now, m_lastBroadcast) >= UlltraProto::BroadcastInterval || sendNow) { usleep(1000 * 100); send("ULLTRA_DEV_R"); m_lastBroadcast = now; usleep(1000 * 100); } for (auto &nd : newlyDiscovered) { if (onNodeDiscovered) onNodeDiscovered(nd.second); m_discovered.push_back(nd.second); } m_updateCounter++; return true; }