A Discrete-Event Network Simulator
API
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
v4ping.cc
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License version 2 as
5  * published by the Free Software Foundation;
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15  */
16 #include "v4ping.h"
17 #include "ns3/icmpv4.h"
18 #include "ns3/assert.h"
19 #include "ns3/log.h"
20 #include "ns3/ipv4-address.h"
21 #include "ns3/socket.h"
22 #include "ns3/uinteger.h"
23 #include "ns3/boolean.h"
24 #include "ns3/inet-socket-address.h"
25 #include "ns3/packet.h"
26 #include "ns3/trace-source-accessor.h"
27 
28 namespace ns3 {
29 
30 NS_LOG_COMPONENT_DEFINE ("V4Ping");
32 
33 TypeId
35 {
36  static TypeId tid = TypeId ("ns3::V4Ping")
38  .AddConstructor<V4Ping> ()
39  .AddAttribute ("Remote",
40  "The address of the machine we want to ping.",
42  MakeIpv4AddressAccessor (&V4Ping::m_remote),
43  MakeIpv4AddressChecker ())
44  .AddAttribute ("Verbose",
45  "Produce usual output.",
46  BooleanValue (false),
47  MakeBooleanAccessor (&V4Ping::m_verbose),
48  MakeBooleanChecker ())
49  .AddAttribute ("Interval", "Wait interval seconds between sending each packet.",
50  TimeValue (Seconds (1)),
51  MakeTimeAccessor (&V4Ping::m_interval),
52  MakeTimeChecker ())
53  .AddAttribute ("Size", "The number of data bytes to be sent, real packet will be 8 (ICMP) + 20 (IP) bytes longer.",
54  UintegerValue (56),
55  MakeUintegerAccessor (&V4Ping::m_size),
56  MakeUintegerChecker<uint32_t> (16))
57  .AddTraceSource ("Rtt",
58  "The rtt calculated by the ping.",
60  ;
61  return tid;
62 }
63 
65  : m_interval (Seconds (1)),
66  m_size (56),
67  m_socket (0),
68  m_seq (0),
69  m_verbose (false),
70  m_recv (0)
71 {
72 }
74 {
75 }
76 
77 void
79 {
80  NS_LOG_FUNCTION (this);
81  m_socket = 0;
83 }
84 
85 uint32_t
87 {
88  Ptr<Node> node = GetNode ();
89  for (uint32_t i = 0; i < node->GetNApplications (); ++i)
90  {
91  if (node->GetApplication (i) == this)
92  {
93  return i;
94  }
95  }
96  NS_ASSERT_MSG (false, "forgot to add application to node");
97  return 0; // quiet compiler
98 }
99 
100 void
102 {
103  NS_LOG_FUNCTION (this << socket);
104  while (m_socket->GetRxAvailable () > 0)
105  {
106  Address from;
107  Ptr<Packet> p = m_socket->RecvFrom (0xffffffff, 0, from);
108  NS_LOG_DEBUG ("recv " << p->GetSize () << " bytes");
111  NS_ASSERT (realFrom.GetPort () == 1); // protocol should be icmp.
112  Ipv4Header ipv4;
113  p->RemoveHeader (ipv4);
114  uint32_t recvSize = p->GetSize ();
115  NS_ASSERT (ipv4.GetProtocol () == 1); // protocol should be icmp.
116  Icmpv4Header icmp;
117  p->RemoveHeader (icmp);
118  if (icmp.GetType () == Icmpv4Header::ECHO_REPLY)
119  {
120  Icmpv4Echo echo;
121  p->RemoveHeader (echo);
122  std::map<uint16_t, Time>::iterator i = m_sent.find (echo.GetSequenceNumber ());
123 
124  if (i != m_sent.end () && echo.GetIdentifier () == 0)
125  {
126  uint32_t * buf = new uint32_t [m_size];
127  uint32_t dataSize = echo.GetDataSize ();
128  uint32_t nodeId;
129  uint32_t appId;
130  if (dataSize == m_size)
131  {
132  echo.GetData ((uint8_t *)buf);
133  Read32 ((const uint8_t *) &buf[0], nodeId);
134  Read32 ((const uint8_t *) &buf[1], appId);
135 
136  if (nodeId == GetNode ()->GetId () &&
137  appId == GetApplicationId ())
138  {
139  Time sendTime = i->second;
140  NS_ASSERT (Simulator::Now () >= sendTime);
141  Time delta = Simulator::Now () - sendTime;
142 
143  m_sent.erase (i);
144  m_avgRtt.Update (delta.GetMilliSeconds ());
145  m_recv++;
146  m_traceRtt (delta);
147 
148  if (m_verbose)
149  {
150  std::cout << recvSize << " bytes from " << realFrom.GetIpv4 () << ":"
151  << " icmp_seq=" << echo.GetSequenceNumber ()
152  << " ttl=" << (unsigned)ipv4.GetTtl ()
153  << " time=" << delta.GetMilliSeconds () << " ms\n";
154  }
155  }
156  }
157  delete[] buf;
158  }
159  }
160  }
161 }
162 
163 // Writes data to buffer in little-endian format; least significant byte
164 // of data is at lowest buffer address
165 void
166 V4Ping::Write32 (uint8_t *buffer, const uint32_t data)
167 {
168  buffer[0] = (data >> 0) & 0xff;
169  buffer[1] = (data >> 8) & 0xff;
170  buffer[2] = (data >> 16) & 0xff;
171  buffer[3] = (data >> 24) & 0xff;
172 }
173 
174 // Writes data from a little-endian formatted buffer to data
175 void
176 V4Ping::Read32 (const uint8_t *buffer, uint32_t &data)
177 {
178  data = (buffer[3] << 24) + (buffer[2] << 16) + (buffer[1] << 8) + buffer[0];
179 }
180 
181 void
183 {
185  Ptr<Packet> p = Create<Packet> ();
186  Icmpv4Echo echo;
187  echo.SetSequenceNumber (m_seq);
188  m_seq++;
189  echo.SetIdentifier (0);
190 
191  //
192  // We must write quantities out in some form of network order. Since there
193  // isn't an htonl to work with we just follow the convention in pcap traces
194  // (where any difference would show up anyway) and borrow that code. Don't
195  // be too surprised when you see that this is a little endian convention.
196  //
197  uint8_t* data = new uint8_t[m_size];
198  for (uint32_t i = 0; i < m_size; ++i) data[i] = 0;
199  NS_ASSERT (m_size >= 16);
200 
201  uint32_t tmp = GetNode ()->GetId ();
202  Write32 (&data[0 * sizeof(uint32_t)], tmp);
203 
204  tmp = GetApplicationId ();
205  Write32 (&data[1 * sizeof(uint32_t)], tmp);
206 
207  Ptr<Packet> dataPacket = Create<Packet> ((uint8_t *) data, m_size);
208  echo.SetData (dataPacket);
209  p->AddHeader (echo);
210  Icmpv4Header header;
211  header.SetType (Icmpv4Header::ECHO);
212  header.SetCode (0);
213  if (Node::ChecksumEnabled ())
214  {
215  header.EnableChecksum ();
216  }
217  p->AddHeader (header);
218  m_sent.insert (std::make_pair (m_seq - 1, Simulator::Now ()));
219  m_socket->Send (p, 0);
221  delete[] data;
222 }
223 
224 void
226 {
227  NS_LOG_FUNCTION (this);
228 
230  if (m_verbose)
231  {
232  std::cout << "PING " << m_remote << " 56(84) bytes of data.\n";
233  }
234 
235  m_socket = Socket::CreateSocket (GetNode (), TypeId::LookupByName ("ns3::Ipv4RawSocketFactory"));
236  NS_ASSERT (m_socket != 0);
237  m_socket->SetAttribute ("Protocol", UintegerValue (1)); // icmp
240  int status;
241  status = m_socket->Bind (src);
242  NS_ASSERT (status != -1);
244  status = m_socket->Connect (dst);
245  NS_ASSERT (status != -1);
246 
247  Send ();
248 }
249 void
251 {
252  NS_LOG_FUNCTION (this);
253  m_next.Cancel ();
254  m_socket->Close ();
255 
256  if (m_verbose)
257  {
258  std::ostringstream os;
259  os.precision (4);
260  os << "--- " << m_remote << " ping statistics ---\n"
261  << m_seq << " packets transmitted, " << m_recv << " received, "
262  << ((m_seq - m_recv) * 100 / m_seq) << "% packet loss, "
263  << "time " << (Simulator::Now () - m_started).GetMilliSeconds () << "ms\n";
264 
265  if (m_avgRtt.Count () > 0)
266  os << "rtt min/avg/max/mdev = " << m_avgRtt.Min () << "/" << m_avgRtt.Avg () << "/"
267  << m_avgRtt.Max () << "/" << m_avgRtt.Stddev ()
268  << " ms\n";
269  std::cout << os.str ();
270  }
271 }
272 
273 
274 } // namespace ns3