EmulatedInterface.cc
Go to the documentation of this file.
1 //
2 // Copyright (c) 2020 Fraunhofer Institute for Applied Information Technology (FIT)
3 // Network Research Group (NET)
4 // Schloss Birlinghoven, 53754 Sankt Augustin, GERMANY
5 // Contact: support@wiback.org
6 //
7 // This file is part of the SENF code tree.
8 // It is licensed under the 3-clause BSD License (aka New BSD License).
9 // See LICENSE.txt in the top level directory for details or visit
10 // https://opensource.org/licenses/BSD-3-Clause
11 //
12 
13 
17 #include "EmulatedInterface.hh"
18 
19 // Custom includes
20 #include "Annotations.hh"
22 
23 #define prefix_
24 //-/////////////////////////////////////////////////////////////////////////////////////////////////
25 
26 std::uint32_t senf::emu::EmulatedInterface::emuInterfaceIndex = 0;
27 
28 //-/////////////////////////////////////////////////////////////////////////////////////////////////
29 // senf::emu::detail::EmulatedInterfaceReceiveFilter
30 
31 prefix_ void senf::emu::detail::EmulatedInterfaceReceiveFilter::request()
32 {
33  PacketHeader p (input());
35 
36  Interface & iface (receiver_.EmulatedReceiver::basei::interface());
37  auto const & ifaceId (iface.id());
38 
39  if (e) {
40  // Drop self-looped packets
41  if (e->source() == ifaceId)
42  return;
43 
44  // Drop all packets not addressed to us
45  if (! iface.receiver().promisc() ) {
46  MACAddress dst (e->destination());
47  if (! dst.multicast() && dst != ifaceId)
48  return;
49  }
50  } else { //non-data frames (i.e no Ethernet header in stack)
51  // TODO: make this more generic:
52  // Currently this covers only WLAN non-data frames.
53  // Are more generic solution might be needed if we want to
54  // have similar mgt frames for DVB (or WiMAX) as well
56 
57  if (! w)
58  return;
59  // Drop self-looped packets
60  if (w->sourceAddress() == ifaceId)
61  return;
62  }
63 
64  // Cry loudly if someone else sends with our own node id
65  if (Interface::nodeId() == p->nodeId() && iface.index() == p->interfaceIndex())
66  SENF_LOG((senf::log::CRITICAL)("Duplicate nodeId,ifIndex: "
67  << p->nodeId() << "," << p->interfaceIndex()));
68 
69  p.annotation<annotations::Timestamp>().fromSocketProtocol(receiver_.EmulatedReceiver::emui::interface().emulationSocket().protocol());
70  p.annotation<annotations::Interface>().value = ifaceId;
71 
72  output(p);
73 }
74 
75 //-/////////////////////////////////////////////////////////////////////////////////////////////////
76 // senf::emu::detail::EmulatedInterfaceTransmitFilter
77 
78 prefix_ void senf::emu::detail::EmulatedInterfaceTransmitFilter::request()
79 {
80  Packet p (input());
81  if (mtu_) {
82  Packet::size_type sz (transmitter_.v_emulatedPayloadSize(p));
83  if (sz > mtu_) {
85  "Dropping emulated packet: emulated packet size " << sz
86  << " exceeds device MTU " << mtu_));
87  return;
88  }
89  }
90 
91  timeval tv;
92  gettimeofday( &tv, NULL);
93  PacketHeader ph (PacketHeader::createBefore(p));
94  ph->timestamp() = (tv.tv_sec * 1000000000ULL + tv.tv_usec * 1000ULL);
95  ph->sequenceNumber() = seqNr_++;
96  ph->nodeId() = Interface::nodeId();
97  ph->interfaceIndex() = transmitter_.basei::interface().index();
98  ph.finalizeThis();
99  output(ph);
100 }
101 
102 //-/////////////////////////////////////////////////////////////////////////////////////////////////
103 // senf::emu::detail::DroppedPacketChecker
104 
105 prefix_ senf::emu::detail::DroppedPacketChecker::DroppedPacketChecker()
106  : droppedPackets_ (0u), lastDroppedPackets_ (0u), factor_ (0.0f)
107 {
108  registerEvent(statisticsTimer_, &DroppedPacketChecker::statisticsUpdate);
109 }
110 
111 prefix_ void senf::emu::detail::DroppedPacketChecker::v_handlePacket(PacketHeader const & p)
112 {
113  boost::uint32_t seqNo (p->sequenceNumber());
114  SeqNoMap::iterator i (seqNos_.find(std::make_pair(p->nodeId(),p->interfaceIndex())));
115  if (i == seqNos_.end()) {
116  seqNos_.insert(
117  std::make_pair(
118  std::make_pair(p->nodeId().value(), p->interfaceIndex().value()),
119  seqNo ));
120  return;
121  }
122  if (seqNo > i->second+1) {
124  ("Dropped packet from nodeId,ifIndex " <<
125  i->first.first << "," << i->first.second << " "
126  "detected at sequence number " << seqNo));
127  SENF_LOG((senf::log::NOTICE)(dumpPacketInfo(p)));
128  ++ droppedPackets_;
129  ++ lastDroppedPackets_;
130  }
131  else if (seqNo <= i->second ) {
133  ("Packet reversal or duplicate packet from nodeId,ifIndex " <<
134  i->first.first << "," << i->first.second << " "
135  "detected at sequence number " << seqNo));
136  }
137  i->second = seqNo;
138 }
139 
140 prefix_ void senf::emu::detail::DroppedPacketChecker::
141 startStatistics(ClockService::clock_type pollInterval)
142 {
143  statisticsTimer_.interval(pollInterval);
144  factor_ = double(ClockService::in_nanoseconds(pollInterval)) /
145  double(ClockService::in_nanoseconds(ClockService::seconds(1)));
146 }
147 
148 prefix_ void senf::emu::detail::DroppedPacketChecker::statisticsUpdate()
149 {
150  signals.droppedPacketsPerSecond(lastDroppedPackets_/factor_);
151  signals.numPeers(seqNos_.size());
152  lastDroppedPackets_ = 0;
153 }
154 
155 //-/////////////////////////////////////////////////////////////////////////////////////////////////
156 // senf::emu::detail::DelayTimeChecker
157 
158 prefix_
159 senf::emu::detail::DelayTimeChecker::DelayTimeChecker(ClockService::clock_type maxDelay)
160  : maxDelay_ (maxDelay), delayedPackets_ (0u), maxPacketDelay_ (0u), minDelayStat_ (0u),
161  totalDelayStat_ (0u), maxDelayStat_ (0u), nPackets_ (0u)
162 {
163  registerEvent(statisticsTimer_, &DelayTimeChecker::statisticsUpdate);
164 }
165 
166 prefix_ void
167 senf::emu::detail::DelayTimeChecker::startStatistics(ClockService::clock_type pollInterval)
168 {
169  statisticsTimer_.interval(pollInterval);
170 }
171 
172 prefix_ void senf::emu::detail::DelayTimeChecker::v_handlePacket(PacketHeader const & p)
173 {
174  ClockService::clock_type sendTime (p->timestamp().value());
175  ClockService::clock_type recvTime (p.annotation<annotations::Timestamp>().as_clock_type());
176  ClockService::clock_type delay (recvTime - sendTime);
177  if (nPackets_ == 0 || delay < minDelayStat_)
178  minDelayStat_ = delay;
179  if (delay > maxDelayStat_)
180  maxDelayStat_ = delay;
181  totalDelayStat_ += delay;
182  ++nPackets_;
183  if (delay > maxPacketDelay_)
184  maxPacketDelay_ = delay;
185  if (delay < ClockService::clock_type(0)) {
187  ("Detected time reversal (\?\?) in packet from nodeId,ifIndex " <<
188  p->nodeId() << "," << p->interfaceIndex()));
189  SENF_LOG((senf::log::NOTICE)(dumpPacketInfo(p)));
190  }
191  else if (delay > maxDelay_) {
193  ("Detected excessive packet delay of "
194  << ClockService::reltime(delay)
195  << " in packet from nodeId,ifIndex "
196  << p->nodeId() << "," << p->interfaceIndex()));
197  SENF_LOG((senf::log::NOTICE)(dumpPacketInfo(p)));
198  ++ delayedPackets_;
199  }
200 }
201 
202 prefix_ void senf::emu::detail::DelayTimeChecker::statisticsUpdate()
203 {
204  if (nPackets_ == 0)
205  nPackets_ = 1;
206  signals.packetDelay(
207  double(ClockService::in_nanoseconds(minDelayStat_)) /
208  ClockService::in_nanoseconds(ClockService::seconds(1)),
209  double(ClockService::in_nanoseconds(totalDelayStat_)) /
210  nPackets_ / ClockService::in_nanoseconds(ClockService::seconds(1)),
211  double(ClockService::in_nanoseconds(maxDelayStat_)) /
212  ClockService::in_nanoseconds(ClockService::seconds(1)));
213  minDelayStat_ = totalDelayStat_ = maxDelayStat_ = ClockService::clock_type(0);
214  nPackets_ = 0u;
215 }
216 
217 //-/////////////////////////////////////////////////////////////////////////////////////////////////
218 // senf::emu::EmulatedInterface
219 
221 {
222  namespace fty = console::factory;
223 
224  interface().consoleDir()
225  .add("emulationAddress",
226  fty::Command(SENF_MEMBINDFNP(UDPClientHandle::Address, EmulatedInterface,
227  emulationAddress, () const))
228  .doc("The emulation address is the multicast address and port to which all emulated\n"
229  "Network traffic is sent. All emulated interfaces with the same emulation address\n"
230  "Are connected to the same network segment.")
231  .overloadDoc("Get current emulation address") );
232  interface().consoleDir()
233  .add("emulationInterface",
234  fty::Command(SENF_MEMBINDFNP(void, EmulatedInterface,
235  emulationInterface, (std::string const &)))
236  .arg("iface","name of interface for emulation network traffic")
237  .doc("All emulation traffic is sent and received on this interface.")
238  .overloadDoc("Set the emulation interface.") );
239  interface().consoleDir()
240  .add("emulationInterface",
241  fty::Command(SENF_MEMBINDFNP(std::string const &, EmulatedInterface,
242  emulationInterface, () const))
243  .overloadDoc("Get current emulation interface.") );
244 }
245 
247 {
248  if (address_ && ! enabled_)
249  openSocket();
250  enabled_ = true;
251 }
252 
254 {
255  if (address_ && enabled_)
256  closeSocket();
257  enabled_ = false;
258 }
259 
260 prefix_ void
261 senf::emu::EmulatedInterface::emulationAddress(UDPClientHandle::Address const & address)
262 {
263  if (address_ && enabled_)
264  closeSocket();
265  address_ = address;
266  if (address_ && enabled_)
267  openSocket();
268 }
269 
270 prefix_ void senf::emu::EmulatedInterface::openSocket()
271 {
272  socket_ = UDPClientHandle();
273  socket_.blocking(false);
274  socket_.protocol().sndbuf( 48*1024);
275  socket_.protocol().rcvbuf( 128*1024);
277 }
278 
279 prefix_ void senf::emu::EmulatedInterface::closeSocket()
280 {
282  socket_.close();
283  socket_ = UDPClientHandle(senf::noinit);
284 }
285 
286 //-/////////////////////////////////////////////////////////////////////////////////////////////////
287 // senf::emu::EmulatedReceiver
288 
290  : filter_ (*this), dropper_(0u), receiverJack (advance_.output), promisc_ (false), annotationMode_(false)
291 {
292  ppi::connect(source_, filter_);
293  ppi::connect(filter_, dropChecker_);
294  ppi::connect(dropChecker_, delayChecker_);
295  ppi::connect(delayChecker_, dropper_);
296  ppi::connect(dropper_, advance_);
297 
298  source_.maxBurst(8);
299 }
300 
302 {
303  namespace kw = console::kw;
304  namespace fty = console::factory;
305 
306  basei::interface()
307  .startStatistics.connect(membind(&EmulatedReceiver::startStatistics, this));
308  basei::interface()
309  .registerStatistics("in-droppedPacketsPerSecond", dropChecker_.signals.droppedPacketsPerSecond);
310  basei::interface()
311  .registerStatistics("in-packetDelay", delayChecker_.signals.packetDelay);
312  basei::interface()
313  .registerStatistics("in-numPeers", dropChecker_.signals.numPeers);
314 
315  basei::interface().consoleDir()
316  .add("maxDelay",
317  fty::Command(SENF_MEMBINDFNP(void, EmulatedReceiver,
319  .arg("delay", "Maximum allowed packet delay",
320  kw::parser=senf::parseClockServiceInterval)
321  .doc("Get/set maximum allowed packet delay.\n"
322  "Packets which take longer to be delivered will be logged.") );
323  basei::interface().consoleDir()
324  .add("maxDelay",
326  maxDelay, () const))
327  .formatter(senf::formatClockServiceInterval) );
328  basei::interface().consoleDir()
329  .add("delayedPackets", fty::Command(&EmulatedReceiver::delayedPackets, this)
330  .doc("Number of packets with delay > maxDelay.") );
331  basei::interface().consoleDir()
332  .add("maxPacketDelay", fty::Command(&EmulatedReceiver::maxPacketDelay, this)
333  .doc("Maximum packet delay encountered.") );
334  basei::interface().consoleDir()
335  .add("droppedPackets", fty::Command(&EmulatedReceiver::droppedPackets, this)
336  .doc("Number of dropped packets.") );
337 
338  basei::interface().consoleDir()
339  .add("lossrate",
340  fty::Command(SENF_MEMBINDFNP(void, EmulatedReceiver,
341  lossRate, (double)))
342  .arg("lossRate", "loss rate of packet dropper")
343  .doc("Get/set the loss rate of the packet dropper.") );
344  basei::interface().consoleDir()
345  .add("lossrate",
346  fty::Command(SENF_MEMBINDFNP(double, EmulatedReceiver,
347  lossRate, () const)) );
348 }
349 
351 {
352  EmulatedInterface & iface (emui::interface());
353  iface.emulationSocket().protocol().reuseaddr(true);
354  iface.emulationSocket().bind(iface.emulationAddress());
355  if (!iface.emulationInterface().empty()) {
356  iface.emulationSocket().protocol().bindInterface(iface.emulationInterface());
357  iface.emulationSocket().protocol().mcAddMembership(iface.emulationAddress().address(),
358  iface.emulationInterface());
359  } else
360  iface.emulationSocket().protocol().mcAddMembership(iface.emulationAddress().address());
361  iface.emulationSocket().protocol().mcLoop(true);
362  source_.handle(iface.emulationSocket());
363 }
364 
365 //-/////////////////////////////////////////////////////////////////////////////////////////////////
366 // senf::emu::EmulatedTransmitter
367 
369  : transmitterJack(delayer_.input), filter_(*this)
370 {
371  ppi::connect(delayer_, filter_);
372  ppi::connect(filter_, sink_);
373 }
374 
376 {
377  namespace fty = console::factory;
378 
379  basei::interface().consoleDir()
380  .add("delay",
381  fty::Command(SENF_MEMBINDFNP(void, EmulatedTransmitter,
383  .arg("delay", "transmitting delay",
384  console::kw::parser=senf::parseClockServiceInterval)
385  .doc("TODO.\n"
386  ".") ); //TODO: delay doc for console
387  basei::interface().consoleDir()
388  .add("delay",
390  delay, () const))
391  .formatter(senf::formatClockServiceInterval) );
392 }
393 
395 {
396  EmulatedInterface & iface (emui::interface());
397  if (!iface.emulationInterface().empty())
398  iface.emulationSocket().protocol().bindInterface(iface.emulationInterface());
399  sink_.handle(iface.emulationSocket());
400  sink_.writer().target(emui::interface().emulationAddress());
401 }
402 
404 {
405  EthernetPacket ethp (packet.find<EthernetPacket>(nothrow));
406  if (!ethp)
407  return 0;
408  Packet pp (ethp.next(nothrow));
409  return pp ? pp.size() : 0u;
410 }
411 
412 //-/////////////////////////////////////////////////////////////////////////////////////////////////
413 // senf::emu::EmulatedWiredInterface
414 
416 {
417  namespace fty = console::factory;
418 
420  interface().consoleDir()
421  .add("emulationAddress",
422  fty::Command(SENF_MEMBINDFNP(void, EmulatedWiredInterface,
423  emulationAddress, (UDPClientHandle::Address const &)))
424  .arg("address", "emulation multicast address")
425  .overloadDoc("Change emulation address.") );
426 }
427 
428 //-/////////////////////////////////////////////////////////////////////////////////////////////////
429 // senf::emu::EmulatedWiredReceiver
430 
432 {
434 }
435 
436 //-/////////////////////////////////////////////////////////////////////////////////////////////////
437 // senf::emu::EmulatedWiredTransmitter
438 
440 {
442 }
443 
444 //-/////////////////////////////////////////////////////////////////////////////////////////////////
445 #undef prefix_
446 
447 
448 // Local Variables:
449 // mode: c++
450 // fill-column: 100
451 // comment-column: 40
452 // c-file-style: "senf"
453 // indent-tabs-mode: nil
454 // ispell-local-dictionary: "american"
455 // compile-command: "scons -u test"
456 // End:
void startStatistics(ClockService::clock_type pollInterval)
Start statistics generation or change interval.
config::time_type clock_type
#define prefix_
ClockService::clock_type maxDelay() const
Get current maxDelay() value.
#define SENF_MEMBINDFNP(ret, cls, fn, args)
ConcretePacket< PacketHeaderType > PacketHeader
PacketHeader packet typedef.
Definition: Packets.hh:85
Emulated interface base-class for transmit capable interfaces.
virtual void v_initEmulationInterface()=0
Called to initialize the emulated interface.
EmulatedInterface public header.
std::string const & emulationInterface() const
Get interface on which emulated traffic is sent/received.
boost::function< R(Args)> membind(R(T::*fn)(Args), T *ob)
OtherPacket find() const
Annotations public header.
noinit
Emulated interface base-class.
WLANPacket_MgtFrameType::packet WLANPacket_MgtFrame
virtual Packet::size_type v_emulatedPayloadSize(Packet packet)
Called to get size of payload.
senf::detail::packet::size_type size_type
Emulated interface base-class for receive capable interfaces.
void initEmulatedReceiver()
Initialize receiver.
unsigned delayedPackets() const
Number of packets with delay > maxDelay()
void disableEmulatedInterface()
Disable the interface.
nothrow
void emulationInterface(std::string const &interface)
Set interface on which to send/receive emulated traffic.
void initEmulatedTransmitter()
Initialize transmitter.
senf::UDPv4ClientSocketHandle UDPClientHandle
UDPClientHandle emulationSocket() const
Get the emulation socket handle.
virtual void v_deinitEmulationInterface()=0
Called to shut down the emulated interface.
void enableEmulatedInterface()
Enable the interface.
Emulated interface base-class for wired interfaces.
size_type size() const
ClockService::clock_type maxPacketDelay() const
Maximum packet delay encountered.
ConcretePacket< EthernetPacketType > EthernetPacket
#define SENF_LOG(args)
UDPClientHandle::Address emulationAddress() const
Get current emulated traffic multicast address.
unsigned droppedPackets() const
Number of dropped packets.
ClockService::clock_type delay() const
get the sender delay