bool SchedulerBase::CreateSignalChannel(int *outSigId, SignalCallback callback, void *userdata) { LogAssert(IsMainThread()); if (!LogVerify(outSigId) || !LogVerify(callback)) return false; *outSigId = -1; // Create a set of pipes int fdPipe[2]; int flags; int ret = pipe(fdPipe); if (ret != 0) { gLog.ErrnoError(errno, "Unable to create pipe for signaling"); return false; } FileDescriptor pipeRead(fdPipe[0]); FileDescriptor pipeWrite(fdPipe[1]); flags = fcntl(pipeRead, F_GETFL); flags = flags | O_NONBLOCK; if (-1 == fcntl(pipeRead, F_SETFL, flags)) { gLog.LogError("Failed to set read pipe to non-blocking."); return false; } flags = fcntl(pipeWrite, F_GETFL); flags = flags | O_NONBLOCK; if (-1 == fcntl(pipeWrite, F_SETFL, flags)) { gLog.LogError("Failed to set write pipe to non-blocking."); return false; } if (!watchSocket(pipeRead)) return false; schedulerSignalItem item; item.callback = callback; item.userdata = userdata; item.fdWrite = pipeWrite; item.fdRead = pipeRead; m_signals[item.fdRead] = item; pipeWrite.Detach(); pipeRead.Detach(); *outSigId = item.fdWrite; gLog.Optional(Log::TimerDetail, "Created signal channel from %d to %d .", item.fdWrite, item.fdRead); return true; }
bool ReadWriteLock::UnLock() { if (!LogVerify(m_initialized)) return false; if (!LogVerify(!pthread_rwlock_unlock(&m_Lock))) return false; return true; }
bool QuickLock::UnLock() { if (!LogVerify(m_initialized)) return false; if (!LogVerify(!pthread_mutex_unlock(&m_Lock))) return false; return true; }
int AutoQuickLock::LockWait(WaitCondition &condition, uint32_t msTimeout) { if (!LogVerify(m_isLockedByMe)) return -1; return m_quickLock->LockWait(condition, msTimeout); }
bool AutoQuickLock::UnLock() { if (!LogVerify(m_isLockedByMe)) return false; m_isLockedByMe = false; return m_quickLock->UnLock(); }
bool AutoReadWriteLock::UnLock() { if (!LogVerify(m_lockedByMeType != AutoReadWriteLock::None)) return false; m_lockedByMeType = AutoReadWriteLock::None; return m_rwLock->UnLock(); }
void AutoQuickLock::SignalAndUnlock(WaitCondition &condition) { if (!LogVerify(m_isLockedByMe)) return; m_isLockedByMe = false; m_quickLock->SignalAndUnlock(condition); }
void QuickLock::Destroy() { if (m_initialized) { LogVerify(!pthread_mutex_destroy(&m_Lock)); m_initialized = false; } }
void Beacon::SetDefMinTxInterval(uint32_t val) { LogAssert(m_scheduler->IsMainThread()); if (!LogVerify(val != 0)) // v10/4.1 return; m_initialSessionParams.desiredMinTx = val; }
void Beacon::SetDefMulti(uint8_t val) { LogAssert(m_scheduler->IsMainThread()); if (!LogVerify(val != 0)) // v10/4.1 return; m_initialSessionParams.detectMulti = val; }
void ReadWriteLock::Destroy() { if (m_initialized) { LogVerify(!pthread_rwlock_destroy(&m_Lock)); m_initialized = false; } }
bool AutoQuickLock::Lock() { if (!LogVerify(!m_isLockedByMe)) return true; // only call once if (!m_quickLock->Lock()) return false; m_isLockedByMe = true; return true; }
void WaitCondition::Signal() { if (!m_initDone) { LogAssertFalse("signaling on uninitialized signal"); return; } LogVerify(!pthread_cond_signal(&m_condition)); }
bool AutoReadWriteLock::WriteLock() { if (!LogVerify(m_lockedByMeType == AutoReadWriteLock::None)) return true; // only call once if (!m_rwLock->WriteLock()) return false; m_lockedByMeType = AutoReadWriteLock::Write; return true; }
void Beacon::KillSession(Session *session) { if (!LogVerify(session)) return; LogAssert(m_scheduler->IsMainThread()); LogVerify(1 == m_discMap.erase(session->GetLocalDiscriminator())); LogVerify(1 == m_IdMap.erase(session->GetId())); LogVerify(1 == m_sourceMap.erase(SourceMapKey(session->GetRemoteAddress(), session->GetLocalAddress()))); LogOptional(Log::Session, "Removed session %s to %s id=%d.", session->GetLocalAddress().ToString(), session->GetRemoteAddress().ToString(), session->GetId()); delete session; }
bool SchedulerBase::SetSocketCallback(int socket, Scheduler::SocketCallback callback, void *userdata) { LogAssert(IsMainThread()); if (!LogVerify(callback) || !LogVerify(socket != -1)) return false; if (!watchSocket(socket)) return false; schedulerSocketItem item; item.callback = callback; item.userdata = userdata; item.socket = socket; m_sockets[socket] = item; return true; }
void QuickLock::SignalAndUnlock(WaitCondition &condition) { if (!LogVerify(m_initialized)) return; condition.Signal(); // Unlocking after is safer, but may be more expensive. In the simple case it // could be unlocked before the signal. There are more complicated cases // involving multiple waiters with different expectations, where this could be a // problems. For now we err on the side of safety. UnLock(); }
bool QuickLock::Create() { if (!LogVerify(!m_initialized)) return false; if (pthread_mutex_init(&m_Lock, NULL)) { LogAssertFalse("pthread_mutex_init failed"); return false; } m_initialized = true; return true; }
bool ReadWriteLock::Create() { if (!LogVerify(!m_initialized)) return false; if (pthread_rwlock_init(&m_Lock, NULL)) { LogAssertFalse("pthread_rwlock_init failed"); return false; } m_initialized = true; return true; }
bool WaitCondition::Init() { if (m_initDone) { LogAssertFalse("WaitCondition::Init called more than once."); return true; } if (LogVerify(!pthread_cond_init(&m_condition, NULL))) m_initDone = true; return m_initDone; }
/** * This needs a to match compare. * * @return size_t */ size_t sockAddrBase::hash() const { /** * Invalid always hashes to a constant ... 0 seems as good as any? */ if (!IsValid()) return 0; else if (IsIPv4()) { // May not be the 'best' hash, but it should work. We add DNSP_HASHINIT // So that address 0.0.0.0 does not collide with !IsValid(). if (!m_allowPort) return getIPv4Storage()->sin_addr.s_addr + DNSP_HASHINIT; else { const sockaddr_in *storage = getIPv4Storage(); return hashlittle(&storage->sin_port, sizeof(storage->sin_port), storage->sin_addr.s_addr + DNSP_HASHINIT); } } else { const sockaddr_in6 *storage = getIPv6Storage(); // Below is to make sure that we can use hashword. This should never fail, but // without the 'if' some compilers issue a warning. if (!LogVerify((sizeof(in6_addr) % 4) == 0)) return 0; // May not be the 'best' hash, but it should work. uint32_t port = m_allowPort ? storage->sin6_port : 0; uint32_t hash = hashword(reinterpret_cast<const uint32_t *>(&storage->sin6_addr), sizeof(in6_addr) / 4, DNSP_HASHINIT + port); return (hash + storage->sin6_scope_id + storage->sin6_flowinfo); } }
bool SchedulerBase::Run() { uint32_t iter=0; TimeSpec timeout; TimeSpec immediate; bool gotEvents; if (!LogVerify(IsMainThread())) return false; m_isStarted = true; // Start with a quick event check. timeout = immediate; while (true) { iter++; if (m_wantsShutdown) break; // // Get event, or timeout. // gLog.Optional(Log::TimerDetail, "checking events (%u)", iter); gotEvents = waitForEvents(timeout); // By default the next event check is immediately. timeout = immediate; // // High priority timers, if any, get handled now // while (!m_wantsShutdown && expireTimer(Timer::Priority::Hi)) { //nothing } if (m_wantsShutdown) break; // // Handle any events. // if (gotEvents) { int socketId; gLog.Optional(Log::TimerDetail, "Handling events (%u)", iter); while (-1 != (socketId = getNextSocketEvent())) { // we have a socket event .. is it a socket or a signal? SocketItemHashMap::iterator foundSocket; SignalItemHashMap::iterator foundSignal; if (m_sockets.end() != (foundSocket = m_sockets.find(socketId))) { if (LogVerify(foundSocket->second.callback != NULL)) foundSocket->second.callback(socketId, foundSocket->second.userdata); } else if (m_signals.end() != (foundSignal = m_signals.find(socketId))) { if (LogVerify(foundSignal->second.callback != NULL)) { // 'Drain' the pipe. char drain[128]; int result; size_t reads = 0; while ( 0 < (result = ::read(socketId, drain, sizeof(drain)))) reads++; if (reads == 0 && result < 0) gLog.LogError("Failed to read from pipe %d: %s", socketId, strerror(errno)); else if (result == 0) gLog.LogError("Signaling pipe write end for %d closed", socketId); foundSignal->second.callback(foundSignal->second.fdWrite, foundSignal->second.userdata); } } else { gLog.Optional(Log::TimerDetail, "Socket (%d) signaled with no handler (%u).", socketId, iter); } if (m_wantsShutdown) break; } if (m_wantsShutdown) break; } // // Handle a low priority timer if there are no events. // TODO: starvation is a potential problem for low priority timers. // if (!gotEvents && !expireTimer(Timer::Priority::Low)) { // No events and no more timers, so we are ready to sleep again. timeout = getNextTimerTimeout(); } if (m_wantsShutdown) break; } // while true return true; }
int QuickLock::LockWait(WaitCondition &condition, uint32_t msTimeout) { if (!LogVerify(m_initialized)) return -1; return condition.Wait(&m_Lock, msTimeout); }
/** * Call from any thread to trigger handleSelfMessage on main thread. * * * @return bool */ bool Beacon::triggerSelfMessage() { if (!LogVerify(m_selfSignalId != -1) || !LogVerify(m_scheduler)) return false; return m_scheduler->Signal(m_selfSignalId); }
static bool doLoadScript(const char *path, const SockAddr &connectAddr) { ifstream file; string line; int lines = 0; vector<char> buffer; const char *seps = " \t"; file.open(path); if (!file.is_open()) { fprintf(stderr, "Failed to open file <%s> : %s\n", path, ErrnoToString()); return false; } buffer.reserve(MaxCommandSize); while (getline(file, line), file.good()) { size_t pos = 0; lines++; if (line.empty()) continue; if (line[0] == '#') continue; // Parse the command line. This doe not currently handle quoted parameters. If // we ever have a command that takes, for example, a file name, then this will // need to be fixed. buffer.resize(0); pos = line.find_first_not_of(seps, 0); while (pos != string::npos) { size_t sepPos = line.find_first_of(seps, pos); LogVerify(sepPos != pos); size_t end = (sepPos == string::npos) ? line.length() : sepPos; size_t bufpos = buffer.size(); buffer.resize(bufpos + end - pos); memcpy(&buffer[bufpos], &line[pos], end - pos); buffer.push_back('\0'); if (sepPos == string::npos) break; pos = line.find_first_not_of(seps, end); } if (buffer.size() != 0) { fprintf(stdout, " Command <%s>\n", line.c_str()); // buffer is double null terminated. buffer.push_back('\0'); if (!SendData(&buffer.front(), buffer.size(), connectAddr, " ")) return false; } } if (!file.eof()) { fprintf(stderr, "Failed to read from file <%s>. %d lines processed: %s\n", path, lines, ErrnoToString()); return false; } file.close(); return true; }
const char* sockAddrBase::ToString(bool includePort /*true*/) const { in_port_t port = includePort ? Port() : 0; if (IsIPv4()) { if (port == 0) return Ip4ToString(getIPv4Storage()->sin_addr); else return Ip4ToString(getIPv4Storage()->sin_addr, port); } else if (IsIPv6()) { const sockaddr_in6 *storage = getIPv6Storage(); if (storage->sin6_scope_id == 0 && !port) { // Just IPv6 address char *buffer; size_t bufsize = GetSmallTLSBuffer(&buffer); if (!bufsize) return "<memerror>"; if (!inet_ntop(AF_INET6, &storage->sin6_addr, buffer, bufsize)) return "<Invalid Address>"; return buffer; } else { // composite address char ifNameBuf[IF_NAMESIZE]; char addrStrBuf[INET6_ADDRSTRLEN]; const char *ifName = NULL; const char *addrStr = NULL; if (storage->sin6_scope_id) { ifName = if_indextoname(storage->sin6_scope_id, ifNameBuf); if (!ifName) ifName = "???"; } addrStr = inet_ntop(AF_INET6, &storage->sin6_addr, addrStrBuf, sizeof(addrStrBuf)); if (!addrStr) addrStr = "<Invalid IPv6>"; if (port) { if (ifName) return FormatShortStr("[%s%%%s]:%d", addrStr, ifName, (int)port); else return FormatShortStr("[%s]:%d", addrStr, (int)port); } else { if (ifName) return FormatShortStr("%s%%%s", addrStr, ifName); else { LogVerify(false); // should never happen return FormatShortStr("%s", addrStr); } } } } else return "<Invalid Address>"; }
WaitCondition::~WaitCondition() { if (m_initDone) LogVerify(!pthread_cond_destroy(&m_condition)); }
void Beacon::handleListenSocket(Socket &socket) { SockAddr sourceAddr; IpAddr destIpAddr, sourceIpAddr; uint8_t ttl; BfdPacket packet; bool found; Session *session = NULL; if (!m_packet.DoRecvMsg(socket)) { gLog.ErrnoError(m_packet.GetLastError(), "Error receiving on BFD listen socket"); return; } sourceAddr = m_packet.GetSrcAddress(); if (!LogVerify(sourceAddr.IsValid())) return; sourceIpAddr = IpAddr(sourceAddr); destIpAddr = m_packet.GetDestAddress(); if (!destIpAddr.IsValid()) { gLog.LogError("Could not get destination address for packet from %s.", sourceAddr.ToString()); return; } ttl = m_packet.GetTTLorHops(&found); if (!found) { gLog.LogError("Could not get ttl for packet from %s.", sourceAddr.ToString()); return; } LogOptional(Log::Packet, "Received bfd packet %zu bytes from %s to %s", m_packet.GetDataSize(), sourceAddr.ToString(), destIpAddr.ToString()); // // Check ip specific stuff. See draft-ietf-bfd-v4v6-1hop-11.txt // // Port if (m_strictPorts) { if (sourceAddr.Port() < bfd::MinSourcePort) // max port is max value, so no need to check { LogOptional(Log::Discard, "Discard packet: bad source port %s to %s", sourceAddr.ToString(), destIpAddr.ToString()); return; } } // TTL assumes that all control packets are from neighbors. if (ttl != 255) { gLog.Optional(Log::Discard, "Discard packet: bad ttl/hops %hhu", ttl); return; } if (!Session::InitialProcessControlPacket(m_packet.GetData(), m_packet.GetDataSize(), packet)) { gLog.Optional(Log::Discard, "Discard packet"); return; } // We have a (partially) valid packet ... now find the correct session. if (packet.header.yourDisc != 0) { DiscMapIt found = m_discMap.find(packet.header.yourDisc); if (found == m_discMap.end()) { if (gLog.LogTypeEnabled(Log::DiscardDetail)) Session::LogPacketContents(packet, false, true, sourceAddr, destIpAddr); gLog.Optional(Log::Discard, "Discard packet: no session found for yourDisc <%u>.", packet.header.yourDisc); return; } session = found->second; if (session->GetRemoteAddress() != sourceIpAddr) { if (gLog.LogTypeEnabled(Log::DiscardDetail)) Session::LogPacketContents(packet, false, true, sourceAddr, destIpAddr); LogOptional(Log::Discard, "Discard packet: mismatched yourDisc <%u> and ip <from %s to %s>.", packet.header.yourDisc, sourceAddr.ToString(), destIpAddr.ToString()); return; } } else { // No discriminator session = findInSourceMap(sourceIpAddr, destIpAddr); if (NULL == session) { // No session yet .. create one !? if (!m_allowAnyPassiveIP && m_allowedPassiveIP.find(sourceIpAddr) == m_allowedPassiveIP.end()) { if (gLog.LogTypeEnabled(Log::DiscardDetail)) Session::LogPacketContents(packet, false, true, sourceAddr, destIpAddr); LogOptional(Log::Discard, "Ignoring unauthorized bfd packets from %s", sourceAddr.ToString()); return; } session = addSession(sourceIpAddr, destIpAddr); if (!session) return; if (!session->StartPassiveSession(sourceAddr, destIpAddr)) { gLog.LogError("Failed to add new session for local %s to remote %s id=%d.", destIpAddr.ToString(), sourceAddr.ToString(), session->GetId()); KillSession(session); } LogOptional(Log::Session, "Added new session for local %s to remote %s id=%d.", destIpAddr.ToString(), sourceAddr.ToString(), session->GetId()); } } // // We have a session that can handle the rest. // session->ProcessControlPacket(packet, sourceAddr.Port()); }