A Discrete-Event Network Simulator
API
v4ping.cc
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License version 2 as
4  * published by the Free Software Foundation;
5  *
6  * This program is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9  * GNU General Public License for more details.
10  *
11  * You should have received a copy of the GNU General Public License
12  * along with this program; if not, write to the Free Software
13  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14  */
15 
16 #include "v4ping.h"
17 
18 #include "ns3/assert.h"
19 #include "ns3/boolean.h"
20 #include "ns3/icmpv4.h"
21 #include "ns3/inet-socket-address.h"
22 #include "ns3/ipv4-address.h"
23 #include "ns3/log.h"
24 #include "ns3/packet.h"
25 #include "ns3/socket.h"
26 #include "ns3/trace-source-accessor.h"
27 #include "ns3/uinteger.h"
28 
29 namespace ns3
30 {
31 
32 NS_LOG_COMPONENT_DEFINE("V4Ping");
33 
35 
36 TypeId
37 V4Ping::GetTypeId()
38 {
39  static TypeId tid = TypeId("ns3::V4Ping")
40  .SetParent<Application>()
41  .SetGroupName("Internet-Apps")
42  .AddConstructor<V4Ping>()
43  .AddAttribute("Remote",
44  "The address of the machine we want to ping.",
46  MakeIpv4AddressAccessor(&V4Ping::m_remote),
47  MakeIpv4AddressChecker())
48  .AddAttribute("Verbose",
49  "Produce usual output.",
50  BooleanValue(false),
51  MakeBooleanAccessor(&V4Ping::m_verbose),
53  .AddAttribute("Interval",
54  "Wait interval seconds between sending each packet.",
55  TimeValue(Seconds(1)),
56  MakeTimeAccessor(&V4Ping::m_interval),
58  .AddAttribute("Size",
59  "The number of data bytes to be sent, real packet will "
60  "be 8 (ICMP) + 20 (IP) bytes longer.",
61  UintegerValue(56),
62  MakeUintegerAccessor(&V4Ping::m_size),
63  MakeUintegerChecker<uint32_t>(16))
64  .AddTraceSource("Rtt",
65  "The rtt calculated by the ping.",
66  MakeTraceSourceAccessor(&V4Ping::m_traceRtt),
67  "ns3::Time::TracedCallback");
68  ;
69  return tid;
70 }
71 
72 V4Ping::V4Ping()
73  : m_interval(Seconds(1)),
74  m_size(56),
75  m_socket(nullptr),
76  m_seq(0),
77  m_verbose(false),
78  m_recv(0)
79 {
80  NS_LOG_FUNCTION(this);
81 }
82 
83 V4Ping::~V4Ping()
84 {
85  NS_LOG_FUNCTION(this);
86 }
87 
88 void
89 V4Ping::DoDispose()
90 {
91  NS_LOG_FUNCTION(this);
92 
93  if (m_next.IsRunning())
94  {
95  StopApplication();
96  }
97 
98  m_socket = nullptr;
100 }
101 
102 uint32_t
103 V4Ping::GetApplicationId() const
104 {
105  NS_LOG_FUNCTION(this);
106  Ptr<Node> node = GetNode();
107  for (uint32_t i = 0; i < node->GetNApplications(); ++i)
108  {
109  if (node->GetApplication(i) == this)
110  {
111  return i;
112  }
113  }
114  NS_ASSERT_MSG(false, "forgot to add application to node");
115  return 0; // quiet compiler
116 }
117 
118 void
119 V4Ping::Receive(Ptr<Socket> socket)
120 {
121  NS_LOG_FUNCTION(this << socket);
122  while (m_socket->GetRxAvailable() > 0)
123  {
124  Address from;
125  Ptr<Packet> p = m_socket->RecvFrom(0xffffffff, 0, from);
126  NS_LOG_DEBUG("recv " << p->GetSize() << " bytes");
128  InetSocketAddress realFrom = InetSocketAddress::ConvertFrom(from);
129  NS_ASSERT(realFrom.GetPort() == 1); // protocol should be icmp.
130  Ipv4Header ipv4;
131  p->RemoveHeader(ipv4);
132  uint32_t recvSize = p->GetSize();
133  NS_ASSERT(ipv4.GetProtocol() == 1); // protocol should be icmp.
134  Icmpv4Header icmp;
135  p->RemoveHeader(icmp);
136  if (icmp.GetType() == Icmpv4Header::ICMPV4_ECHO_REPLY)
137  {
138  Icmpv4Echo echo;
139  p->RemoveHeader(echo);
140  std::map<uint16_t, Time>::iterator i = m_sent.find(echo.GetSequenceNumber());
141 
142  if (i != m_sent.end() && echo.GetIdentifier() == 0)
143  {
144  uint32_t* buf = new uint32_t[m_size];
145  uint32_t dataSize = echo.GetDataSize();
146  uint32_t nodeId;
147  uint32_t appId;
148  if (dataSize == m_size)
149  {
150  echo.GetData((uint8_t*)buf);
151  Read32((const uint8_t*)&buf[0], nodeId);
152  Read32((const uint8_t*)&buf[1], appId);
153 
154  if (nodeId == GetNode()->GetId() && appId == GetApplicationId())
155  {
156  Time sendTime = i->second;
157  NS_ASSERT(Simulator::Now() >= sendTime);
158  Time delta = Simulator::Now() - sendTime;
159 
160  m_sent.erase(i);
161  m_avgRtt.Update(delta.GetMilliSeconds());
162  m_recv++;
163  m_traceRtt(delta);
164 
165  if (m_verbose)
166  {
167  std::cout << recvSize << " bytes from " << realFrom.GetIpv4() << ":"
168  << " icmp_seq=" << echo.GetSequenceNumber()
169  << " ttl=" << (unsigned)ipv4.GetTtl()
170  << " time=" << delta.As(Time::MS) << "\n";
171  }
172  }
173  }
174  delete[] buf;
175  }
176  }
177  }
178 }
179 
180 // Writes data to buffer in little-endian format; least significant byte
181 // of data is at lowest buffer address
182 void
183 V4Ping::Write32(uint8_t* buffer, const uint32_t data)
184 {
185  NS_LOG_FUNCTION(this << (void*)buffer << data);
186  buffer[0] = (data >> 0) & 0xff;
187  buffer[1] = (data >> 8) & 0xff;
188  buffer[2] = (data >> 16) & 0xff;
189  buffer[3] = (data >> 24) & 0xff;
190 }
191 
192 // Writes data from a little-endian formatted buffer to data
193 void
194 V4Ping::Read32(const uint8_t* buffer, uint32_t& data)
195 {
196  NS_LOG_FUNCTION(this << (void*)buffer << data);
197  data = (buffer[3] << 24) + (buffer[2] << 16) + (buffer[1] << 8) + buffer[0];
198 }
199 
200 void
201 V4Ping::Send()
202 {
203  NS_LOG_FUNCTION(this);
204 
205  NS_LOG_INFO("m_seq=" << m_seq);
206  Ptr<Packet> p = Create<Packet>();
207  Icmpv4Echo echo;
208  echo.SetSequenceNumber(m_seq);
209  m_seq++;
210  echo.SetIdentifier(0);
211 
212  //
213  // We must write quantities out in some form of network order. Since there
214  // isn't an htonl to work with we just follow the convention in pcap traces
215  // (where any difference would show up anyway) and borrow that code. Don't
216  // be too surprised when you see that this is a little endian convention.
217  //
218  uint8_t* data = new uint8_t[m_size];
219  for (uint32_t i = 0; i < m_size; ++i)
220  {
221  data[i] = 0;
222  }
223  NS_ASSERT(m_size >= 16);
224 
225  uint32_t tmp = GetNode()->GetId();
226  Write32(&data[0 * sizeof(uint32_t)], tmp);
227 
228  tmp = GetApplicationId();
229  Write32(&data[1 * sizeof(uint32_t)], tmp);
230 
231  Ptr<Packet> dataPacket = Create<Packet>((uint8_t*)data, m_size);
232  echo.SetData(dataPacket);
233  p->AddHeader(echo);
234  Icmpv4Header header;
235  header.SetType(Icmpv4Header::ICMPV4_ECHO);
236  header.SetCode(0);
237  if (Node::ChecksumEnabled())
238  {
239  header.EnableChecksum();
240  }
241  p->AddHeader(header);
242  m_sent.insert(std::make_pair(m_seq - 1, Simulator::Now()));
243  m_socket->Send(p, 0);
244  m_next = Simulator::Schedule(m_interval, &V4Ping::Send, this);
245  delete[] data;
246 }
247 
248 void
249 V4Ping::StartApplication()
250 {
251  NS_LOG_FUNCTION(this);
252 
253  m_started = Simulator::Now();
254  if (m_verbose)
255  {
256  std::cout << "PING " << m_remote << " - " << m_size << " bytes of data - " << m_size + 28
257  << " bytes including ICMP and IPv4 headers.\n";
258  }
259 
260  m_socket = Socket::CreateSocket(GetNode(), TypeId::LookupByName("ns3::Ipv4RawSocketFactory"));
261  NS_ASSERT(m_socket);
262  m_socket->SetAttribute("Protocol", UintegerValue(1)); // icmp
263  m_socket->SetRecvCallback(MakeCallback(&V4Ping::Receive, this));
264  InetSocketAddress src = InetSocketAddress(Ipv4Address::GetAny(), 0);
265  int status;
266  status = m_socket->Bind(src);
267  NS_ASSERT(status != -1);
268  InetSocketAddress dst = InetSocketAddress(m_remote, 0);
269  status = m_socket->Connect(dst);
270  NS_ASSERT(status != -1);
271 
272  Send();
273 }
274 
275 void
276 V4Ping::StopApplication()
277 {
278  NS_LOG_FUNCTION(this);
279 
280  if (m_next.IsRunning())
281  {
282  m_next.Cancel();
283  }
284  if (m_socket)
285  {
286  m_socket->Close();
287  }
288 
289  if (m_verbose)
290  {
291  std::ostringstream os;
292  os.precision(4);
293  os << "--- " << m_remote << " ping statistics ---\n"
294  << m_seq << " packets transmitted, " << m_recv << " received, "
295  << ((m_seq - m_recv) * 100 / m_seq) << "% packet loss, "
296  << "time " << (Simulator::Now() - m_started).As(Time::MS) << "\n";
297 
298  if (m_avgRtt.Count() > 0)
299  {
300  os << "rtt min/avg/max/mdev = " << m_avgRtt.Min() << "/" << m_avgRtt.Avg() << "/"
301  << m_avgRtt.Max() << "/" << m_avgRtt.Stddev() << " ms\n";
302  }
303  std::cout << os.str();
304  }
305 }
306 
307 } // namespace ns3
void DoDispose() override
Destructor implementation.
Definition: application.cc:85
static bool IsMatchingType(const Address &address)
static InetSocketAddress ConvertFrom(const Address &address)
Returns an InetSocketAddress which corresponds to the input Address.
static Ipv4Address GetAny()
AttributeValue implementation for Ipv4Address.
static bool ChecksumEnabled()
Definition: node.cc:290
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition: simulator.h:568
static Time Now()
Return the current simulation virtual time.
Definition: simulator.cc:199
static Ptr< Socket > CreateSocket(Ptr< Node > node, TypeId tid)
This method wraps the creation of sockets that is performed on a given node by a SocketFactory specif...
Definition: socket.cc:72
@ MS
millisecond
Definition: nstime.h:117
static TypeId LookupByName(std::string name)
Get a TypeId by name.
Definition: type-id.cc:839
static void Send(Ptr< NetDevice > dev, int level, std::string emuMode)
Definition: fd-emu-send.cc:54
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition: assert.h:66
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition: assert.h:86
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition: boolean.h:86
Ptr< const AttributeChecker > MakeBooleanChecker()
Definition: boolean.cc:124
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition: nstime.h:1424
Ptr< const AttributeAccessor > MakeUintegerAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition: uinteger.h:46
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:268
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition: log.h:275
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition: object-base.h:46
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1336
Ptr< const TraceSourceAccessor > MakeTraceSourceAccessor(T a)
Create a TraceSourceAccessor which will control access to the underlying trace source.
void(* Time)(Time oldValue, Time newValue)
TracedValue callback signature for Time.
Definition: nstime.h:848
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Callback< R, Args... > MakeCallback(R(T::*memPtr)(Args...), OBJ objPtr)
Build Callbacks for class method members which take varying numbers of arguments and potentially retu...
Definition: callback.h:707
Ptr< const AttributeChecker > MakeTimeChecker(const Time min, const Time max)
Helper to make a Time checker with bounded range.
Definition: time.cc:535
uint8_t data[writeSize]