1 #include "stg/pinger.h"
3 #include "stg/common.h"
14 #include <sys/types.h>
15 #include <sys/socket.h>
16 #include <netinet/in.h>
17 #include <arpa/inet.h>
23 extern volatile time_t stgTime;
26 //-----------------------------------------------------------------------------
27 STG_PINGER::STG_PINGER(unsigned d)
29 m_isRunningRecver(false),
30 m_isRunningSender(false),
35 memset(&m_pmSend, 0, sizeof(m_pmSend));
37 //-----------------------------------------------------------------------------
38 bool STG_PINGER::Start()
40 auto* proto = getprotobyname("ICMP");
41 m_sendSocket = socket(PF_INET, SOCK_RAW, proto->p_proto);
42 m_recvSocket = socket(PF_INET, SOCK_RAW, proto->p_proto);
43 m_pid = static_cast<uint32_t>(getpid()) % 65535;
44 if (m_sendSocket < 0 || m_recvSocket < 0)
46 m_errorStr = "Cannot create socket.";
50 m_sendThread = std::jthread([this](auto token){ RunSendPing(std::move(token)); });
51 m_recvThread = std::jthread([this](auto token){ RunRecvPing(std::move(token)); });
55 //-----------------------------------------------------------------------------
56 bool STG_PINGER::Stop()
59 m_sendThread.request_stop();
60 if (m_isRunningRecver)
62 //5 seconds to thread stops itself
63 for (size_t i = 0; i < 25; i++)
66 SendPing(0x0100007f);//127.0.0.1
68 if (!m_isRunningRecver)
71 std::this_thread::sleep_for(std::chrono::milliseconds(200));
75 if (m_isRunningSender)
77 //5 seconds to thread stops itself
78 for (size_t i = 0; i < 25; i++)
80 if (!m_isRunningSender)
83 std::this_thread::sleep_for(std::chrono::milliseconds(200));
89 return !m_isRunningSender && !m_isRunningRecver;
91 //-----------------------------------------------------------------------------
92 void STG_PINGER::AddIP(uint32_t ip)
94 std::lock_guard lock(m_mutex);
95 m_pingIPs.insert(std::make_pair(ip, 0));
97 //-----------------------------------------------------------------------------
98 void STG_PINGER::DelIP(uint32_t ip)
100 std::lock_guard lock(m_mutex);
101 auto it = m_pingIPs.find(ip);
102 if (it != m_pingIPs.end())
105 //-----------------------------------------------------------------------------
106 void STG_PINGER::PrintAllIP()
108 std::lock_guard lock(m_mutex);
109 for (auto kv : m_pingIPs)
111 uint32_t ip = kv.first;
112 time_t t = kv.second;
113 std::string s = std::to_string(t);
114 printf("ip = %s, time = %9s\n", inet_ntostring(ip).c_str(), s.c_str());
117 //-----------------------------------------------------------------------------
118 bool STG_PINGER::GetIPTime(uint32_t ip, time_t& t) const
120 std::lock_guard lock(m_mutex);
122 auto it = m_pingIPs.find(ip);
123 if (it == m_pingIPs.end())
129 //-----------------------------------------------------------------------------
130 uint16_t STG_PINGER::PingCheckSum(const void* data, int len)
132 const auto* buf = static_cast<const uint16_t*>(data);
135 for ( sum = 0; len > 1; len -= 2 )
139 sum += *reinterpret_cast<const uint8_t*>(buf);
141 sum = (sum >> 16) + (sum & 0xFFFF);
146 //-----------------------------------------------------------------------------
147 bool STG_PINGER::SendPing(uint32_t ip)
149 struct sockaddr_in addr;
150 memset(&addr, 0, sizeof(addr));
151 addr.sin_family = AF_INET;
153 addr.sin_addr.s_addr = ip;
155 memset(&m_pmSend, 0, sizeof(m_pmSend));
156 m_pmSend.hdr.type = ICMP_ECHO;
157 m_pmSend.hdr.un.echo.id = static_cast<uint16_t>(m_pid);
158 memcpy(m_pmSend.msg, &ip, sizeof(ip));
160 m_pmSend.hdr.checksum = PingCheckSum(&m_pmSend, sizeof(m_pmSend));
162 if (sendto(m_sendSocket, &m_pmSend, sizeof(m_pmSend), 0, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) <= 0 )
164 m_errorStr = "Send ping error: " + std::string(strerror(errno));
171 //-----------------------------------------------------------------------------
172 uint32_t STG_PINGER::RecvPing()
174 struct sockaddr_in addr;
178 memset(buf, 0, sizeof(buf));
179 socklen_t len = sizeof(addr);
181 if (recvfrom(m_recvSocket, &buf, sizeof(buf), 0, reinterpret_cast<struct sockaddr*>(&addr), &len))
183 auto* ip = static_cast<struct IP_HDR *>(static_cast<void *>(buf));
184 auto* icmp = static_cast<struct ICMP_HDR *>(static_cast<void *>(buf + ip->ihl * 4));
186 if (icmp->un.echo.id != m_pid)
189 ipAddr = *static_cast<uint32_t*>(static_cast<void *>(buf + sizeof(ICMP_HDR) + ip->ihl * 4));
194 //-----------------------------------------------------------------------------
195 void STG_PINGER::RunSendPing(std::stop_token token)
198 sigfillset(&signalSet);
199 pthread_sigmask(SIG_BLOCK, &signalSet, NULL);
201 m_isRunningSender = true;
203 while (!token.stop_requested())
207 std::lock_guard lock(m_mutex);
211 for (const auto& kv : ips)
220 currTime = lastPing = time(NULL);
223 while (currTime - lastPing < m_delay && !token.stop_requested())
228 currTime = time(NULL);
230 std::this_thread::sleep_for(std::chrono::milliseconds(200));
234 m_isRunningSender = false;
236 //-----------------------------------------------------------------------------
237 void STG_PINGER::RunRecvPing(std::stop_token token)
240 sigfillset(&signalSet);
241 pthread_sigmask(SIG_BLOCK, &signalSet, NULL);
243 m_isRunningRecver = true;
245 while (!token.stop_requested())
247 uint32_t ip = RecvPing();
251 std::lock_guard lock(m_mutex);
252 auto treeIterUpper = m_pingIPs.upper_bound(ip);
253 auto treeIterLower = m_pingIPs.lower_bound(ip);
254 while (treeIterUpper != treeIterLower)
257 treeIterLower->second = stgTime;
259 treeIterLower->second = time(NULL);
266 m_isRunningRecver = false;
268 //-----------------------------------------------------------------------------