EMANE  1.0.1
rawtransport.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013-2014,2016 - Adjacent Link LLC, Bridgewater, New
3  * Jersey
4  * Copyright (c) 2008-2010 - DRS CenGen, LLC, Columbia, Maryland
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in
15  * the documentation and/or other materials provided with the
16  * distribution.
17  * * Neither the name of DRS CenGen, LLC nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 #include "rawtransport.h"
36 
37 #include "emane/downstreampacket.h"
40 #include "emane/startexception.h"
41 
44 
45 #include <sstream>
46 
47 namespace
48 {
49  const int PCAP_SNAPLEN = 0xFFFF;
50  const int PCAP_PROMISC = 1;
51 
52  const int AddressType = AF_PACKET;
53 
54  const std::string DEVICE_PREFIX = "";
55 
56  const int PCAP_TIMEOUT = 0;
57 
58  struct sockaddr_ll_t {
59  std::uint16_t sll_family;
60  std::uint16_t sll_protocol;
61  std::uint32_t sll_ifindex;
62  std::uint16_t sll_type;
63  std::uint8_t sll_pkttype;
64  std::uint8_t sll_halen;
65  std::uint8_t sll_addr[8];
66  }__attribute__((packed));
67 }
68 
69 
71  PlatformServiceProvider * pPlatformService):
72  EthernetTransport(id, pPlatformService),
73  thread_{},
74  pPcapHandle_{},
75  pBitPool_{},
76  u64BitRate_{}
77 {
78  memset(&macAddr_, 0x0, sizeof(macAddr_));
79 }
80 
82 {
83  // close pcap handle
84  // Moves pcap_close call to the destructor
85  // instead of the stop call because having it
86  // there caused emanetransportd to hang on a
87  // ctrl-c
88  if (pPcapHandle_ != NULL)
89  {
90  pcap_close(pPcapHandle_);
91 
92  pPcapHandle_ = NULL;
93  }
94 
95  if(pBitPool_ != NULL)
96  {
97  delete pBitPool_;
98 
99  pBitPool_ = NULL;
100  }
101 }
102 
103 
105 {
106  // create bit pool
107  pBitPool_ = new Utils::BitPool(pPlatformService_, id_);
108 
109  auto & configRegistrar = registrar.configurationRegistrar();
110 
111  configRegistrar.registerNonNumeric<std::string>("device",
113  {},
114  "Device to use as the raw packet entry point.");
115 
116  configRegistrar.registerNumeric<std::uint64_t>("bitrate",
118  {0},
119  "Transport bitrate in bps. This is the total allowable"
120  " throughput for the transport combined in both directions"
121  " (upstream and downstream). A value of 0 disables the"
122  " bitrate feature.");
123 
124  configRegistrar.registerNumeric<bool>("broadcastmodeenable",
126  {false},
127  "Broadcast all packets to all NEMs.");
128 
129 
130  configRegistrar.registerNumeric<bool>("arpcacheenable",
132  {true},
133  "Enable ARP request/reply monitoring to map Ethernet address to NEM.");
134 
135 }
136 
137 
138 
140 {
141  for(const auto & item : update)
142  {
143  if(item.first == "bitrate")
144  {
145  // value is in bits
146  u64BitRate_ = item.second[0].asUINT64();
147 
149  INFO_LEVEL,
150  "TRANSPORTI %03hu Transports::Raw::RawTransport %s %s: %ju",
151  id_,
152  __func__,
153  item.first.c_str(),
154  u64BitRate_);
155  }
156  else if(item.first == "device")
157  {
158  sTargetDevice_ = item.second[0].asString();
159 
161  INFO_LEVEL,
162  "TRANSPORTI %03hu RawTransport %s %s: %s",
163  id_,
164  __func__,
165  item.first.c_str(),
166  sTargetDevice_.c_str());
167  }
168  else if(item.first == "broadcastmodeenable")
169  {
170  bBroadcastMode_ = item.second[0].asBool();
171 
173  INFO_LEVEL,
174  "TRANSPORTI %03d RawTransport %s %s: %d",
175  id_,
176  __func__,
177  item.first.c_str(),
179  }
180  else if(item.first == "arpcacheenable")
181  {
182  bArpCacheMode_ = item.second[0].asBool();
183 
185  INFO_LEVEL,
186  "TRANSPORTI %03d RawTransport %s %s: %d",
187  id_,
188  __func__,
189  item.first.c_str(),
191  }
192  else
193  {
194  throw makeException<ConfigureException>("RawTransport: "
195  "Unexpected configuration item %s",
196  item.first.c_str());
197 
198  }
199  }
200 }
201 
202 
203 
205 {
206  bool bMacResolved{false};
207 
208  // pcap error buff
209  char errbuf[PCAP_ERRBUF_SIZE]={};
210 
211  // pcap interface list
212  struct pcap_if *iflist;
213 
214  // device name
215  std::string sDeviceName = DEVICE_PREFIX;
216 
217  // get all available interfaces
218  if (pcap_findalldevs (&iflist, errbuf) < 0 || iflist == NULL)
219  {
220  std::stringstream ssDescription;
221  ssDescription<<"could not get interface list "<< errbuf<<std::ends;
222  throw StartException(ssDescription.str());
223  }
224 
225 
226  // each interface
227  struct pcap_if *ifp = iflist;
228 
229  // walk through all available interfaces
230  while (ifp)
231  {
232  // name match
233  if(sTargetDevice_ == ifp->name)
234  {
235  // add adapter name to device prefix
236  sDeviceName += ifp->name;
237 
238  // address list
239  pcap_addr_t *ap = ifp->addresses;
240 
241  // for each address
242  while (ap && !bMacResolved)
243  {
244  // mac addr
245  if(ap->addr->sa_family == AddressType)
246  {
247  struct sockaddr_ll_t *s = (struct sockaddr_ll_t *) ap->addr;
248 
249  memcpy(macAddr_.bytes.buff, &s->sll_addr[0], Utils::ETH_ALEN);
250 
252  DEBUG_LEVEL,
253  "TRANSPORTI %03d RawTransport %s adapter %s, hw addr %s",
254  id_,
255  __func__,
256  ifp->name,
257  ethaddr_to_string(&macAddr_).c_str());
258 
259  // set resolved flag
260  bMacResolved = true;
261  }
262  // next addr
263  ap = ap->next;
264  }
265  }
266  // next interface
267  ifp = ifp->next;
268  }
269 
270  // free interface list
271  pcap_freealldevs(iflist);
272 
273  // mac address not resolved
274  if(bMacResolved == false)
275  {
276  std::stringstream ssDescription;
277  ssDescription<<"could not resolve our mac address "<< sTargetDevice_ << " " << errbuf<<std::ends;
278  throw StartException(ssDescription.str());
279  }
280 
281  // open pcap handle
282  if((pPcapHandle_ = pcap_open_live(sDeviceName.c_str(), PCAP_SNAPLEN, PCAP_PROMISC, PCAP_TIMEOUT, errbuf)) == NULL)
283  {
284  std::stringstream ssDescription;
285  ssDescription<<"could not open device "<< sDeviceName << " " << errbuf<<std::ends;
286  throw StartException(ssDescription.str());
287  }
288 
289  // set datalink type, this covers 10/100/1000
290  if(pcap_set_datalink(pPcapHandle_, DLT_EN10MB) < 0)
291  {
292  std::stringstream ssDescription;
293  ssDescription<<"could not set datalink type on device "<< sDeviceName << " " << errbuf<<std::ends;
294  throw StartException(ssDescription.str());
295  }
296 
297  // currently unsupported by winpcap
298  if(pcap_setdirection(pPcapHandle_, PCAP_D_IN) < 0)
299  {
300  std::stringstream ssDescription;
301  ssDescription<<"could not set direction on device "<< sDeviceName << " " << errbuf<<std::ends;
302  throw StartException(ssDescription.str());
303  }
304 
305  pBitPool_->setMaxSize(u64BitRate_);
306 
307  // start pcap read thread
308  thread_ = std::thread(&RawTransport::readDevice,this);
309 }
310 
311 
312 
314 {
315  // cancel read thread
316  if(thread_.joinable())
317  {
318  ThreadUtils::cancel(thread_);
319 
320  thread_.join();
321  }
322 }
323 
324 
326  throw()
327 {}
328 
329 
330 
332  const ControlMessages &msgs)
333 {
334  // we are not in the running state - the infrastructure should protect
335  // against this but currently it does not for transports.
336  if(!pPcapHandle_)
337  {
338  return;
339  }
340 
341  // frame sanity check
342  if(verifyFrame(pkt.get(), pkt.length()) < 0)
343  {
345  ERROR_LEVEL,
346  "TRANSPORTI %03d RawTransport %s ethernet frame error",
347  id_,
348  __func__);
349  }
350 
351  handleUpstreamControl(msgs);
352 
353  // get packet info
354  const PacketInfo & pktInfo{pkt.getPacketInfo()};
355 
356  // ether header
357  const Utils::EtherHeader * pEtherHeader = (const Utils::EtherHeader*) pkt.get();
358 
359  // update arp cache
360  updateArpCache(pEtherHeader, pktInfo.getSource());
361 
362  // send packet
363  if(pcap_sendpacket(pPcapHandle_, (const std::uint8_t*) pkt.get(), pkt.length()) < 0)
364  {
366  ERROR_LEVEL,
367  "TRANSPORTI %03d RawTransport %s pcap_sendpacket error %s",
368  id_,
369  __func__,
370  pcap_geterr(pPcapHandle_));
371  }
372  else
373  {
375  DEBUG_LEVEL,
376  "TRANSPORTI %03d RawTransport %s src %hu, dst %hu, dscp %hhu, length %zu",
377  id_,
378  __func__,
379  pktInfo.getSource(),
380  pktInfo.getDestination(),
381  pktInfo.getPriority(),
382  pkt.length());
383 
384  // drain the bit pool converting bytes to bits
385  const size_t sizePending = pBitPool_->get(pkt.length() * 8);
386 
387  // check for bitpool error
388  if(sizePending != 0)
389  {
391  ERROR_LEVEL,
392  "TRANSPORTI %03d RawTransport %s bitpool request error %zd of %zd",
393  id_,
394  __func__,
395  sizePending, pkt.length() * 8);
396  }
397  }
398 }
399 
400 
402 {
403  handleUpstreamControl(msgs);
404 }
405 
406 void EMANE::Transports::Raw::RawTransport::handleUpstreamControl(const ControlMessages & msgs)
407 {
408  for(const auto & pMessage : msgs)
409  {
410  switch(pMessage->getId())
411  {
413  {
414  const auto pSerializedControlMessage =
415  static_cast<const Controls::SerializedControlMessage *>(pMessage);
416 
417  switch(pSerializedControlMessage->getSerializedId())
418  {
419  default:
421  DEBUG_LEVEL,
422  "TRANSPORTI %03hu RawTransport::%s unknown serialized msg id %hu, ignore",
423  id_,
424  __func__,
425  pSerializedControlMessage->getSerializedId());
426 
427  }
428  }
429  break;
430 
431  default:
433  DEBUG_LEVEL,
434  "TRANSPORTI %03hu RawTransport::%s unknown msg id %hu, ignore",
435  id_,
436  __func__,
437  pMessage->getId());
438  }
439  }
440 }
441 
442 
443 
444 void EMANE::Transports::Raw::RawTransport::readDevice()
445 {
446  const std::uint8_t* buf = NULL;
447 
448  struct pcap_pkthdr *pcap_hdr = NULL;
449 
450  int iPcapResult{};
451 
452  while(1)
453  {
454  // get frame, blocks here
455  iPcapResult = pcap_next_ex(pPcapHandle_, &pcap_hdr, &buf);
456 
457  // error
458  if (iPcapResult < 0)
459  {
461  ERROR_LEVEL,
462  "TRANSPORTI %03d RawTransport %s pcap_next_ex error %s",
463  id_,
464  __func__,
465  pcap_geterr(pPcapHandle_));
466 
467  // done
468  break;
469  }
470  // time out
471  else if (iPcapResult == 0)
472  {
473  continue;
474  }
475  // success
476  else if(iPcapResult == 1)
477  {
478  // frame sanity check
479  if(verifyFrame(buf, pcap_hdr->caplen) < 0)
480  {
482  ERROR_LEVEL,
483  "TRANSPORTI %03d RawTransport %s frame error",
484  id_,
485  __func__);
486  }
487  else
488  {
489  const Utils::EtherHeader *pEtherHeader = (const Utils::EtherHeader *) buf;
490 
491  NEMId nemDestination;
492 
493  std::uint8_t dscp{};
494 
495  // get dst and dscp values from frame
496  if(parseFrame(pEtherHeader, nemDestination, dscp) < 0)
497  {
499  ERROR_LEVEL,
500  "TRANSPORTI %03d RawTransport %s frame parse error",
501  id_,
502  __func__);
503  }
504  else
505  {
507  DEBUG_LEVEL,
508  "TRANSPORTI %03d RawTransport %s src %hu, dst %hu, dscp %hhu, length %u",
509  id_,
510  __func__,
511  id_,
512  nemDestination,
513  dscp,
514  pcap_hdr->caplen);
515 
516  // create downstream packet with packet info
517  DownstreamPacket pkt(PacketInfo (id_, nemDestination, dscp,Clock::now()), buf, pcap_hdr->caplen);
518 
520 
521  // drain the bit pool converting bytes to bits
522  const size_t sizePending = pBitPool_->get(pcap_hdr->caplen * 8);
523 
524  // check for bitpool error
525  if(sizePending != 0)
526  {
528  ERROR_LEVEL,
529  "TRANSPORTI %03d RawTransport %s bitpool request error %zd of %u",
530  id_,
531  __func__,
532  sizePending,
533  pcap_hdr->caplen * 8);
534  }
535  }
536  }
537  }
538  }
539 }
540 
541 
A Packet class that allows upstream processing to strip layer headers as the packet travels up the st...
Implementation of a rate limiting bit pool.
Definition: bitpool.h:52
The Registrar interface provides access to all of the emulator registrars.
Definition: registrar.h:50
A Serialized Control Message is used to encapsulate Serializable control messages as they traverse pr...
std::uint8_t sll_halen
Definition: rawtransport.cc:62
DECLARE_TRANSPORT(EMANE::Transports::Raw::RawTransport)
void configure(const ConfigurationUpdate &items) override
virtual ConfigurationRegistrar & configurationRegistrar()=0
std::uint16_t sll_family
Definition: rawtransport.cc:57
#define LOGGER_VERBOSE_LOGGING(logger, level, fmt, args...)
struct EtherAddrBytes bytes
Definition: netutils.h:423
RawTransport(NEMId id, PlatformServiceProvider *pPlatformService)
Definition: rawtransport.cc:70
std::list< const ControlMessage * > ControlMessages
void registerNonNumeric(const std::string &sName, const ConfigurationProperties &properties=ConfigurationProperties::NONE, const std::initializer_list< T > &values={}, const std::string &sUsage="", std::size_t minOccurs=1, std::size_t maxOccurs=1, const std::string &sRegexPattern={})
std::uint8_t sll_pkttype
Definition: rawtransport.cc:61
std::uint8_t sll_addr[8]
Definition: rawtransport.cc:63
void setMaxSize(std::uint64_t u64NewSize)
Definition: bitpool.inl:65
virtual int parseFrame(const Utils::EtherHeader *pEthHeader, EMANE::NEMId &dst, std::uint8_t &dscp)
The PlatformServiceProvider interface provides access to emulator services.
struct sockaddr_in_t __attribute__((__may_alias__))
Store source, destination, creation time and priority information for a packet.
Definition: packetinfo.h:50
std::uint16_t sll_protocol
Definition: rawtransport.cc:58
std::uint16_t sll_type
Definition: rawtransport.cc:60
void initialize(Registrar &registrar) override
void processUpstreamPacket(UpstreamPacket &pkt, const ControlMessages &msgs) override
const PacketInfo & getPacketInfo() const
Specialized packet the allows downstream processing to add layer specific headers as the packet trave...
int cancel(std::thread &thread)
Definition: threadutils.h:65
std::uint64_t get(std::uint64_t u64Request, bool bFullFill=true)
Definition: bitpool.inl:97
std::uint16_t NEMId
Definition: types.h:52
PlatformServiceProvider * pPlatformService_
Raw Device EMANE Transport.
Definition: rawtransport.h:59
const void * get() const
virtual LogServiceProvider & logService()=0
const std::uint16_t ETH_ALEN
Ethernet hardware address length.
Definition: netutils.h:335
Component start exception is used to indicate an exception during transition to the start state...
virtual int verifyFrame(const void *buf, size_t len)
std::vector< ConfigurationNameAnyValues > ConfigurationUpdate
std::string ethaddr_to_string(const EtherAddr *addr)
Definition: netutils.h:508
#define LOGGER_STANDARD_LOGGING(logger, level, fmt, args...)
Definition of the ethernet frame header.
Definition: netutils.h:434
void sendDownstreamPacket(DownstreamPacket &pkt, const ControlMessages &msgs=empty)
void updateArpCache(const Utils::EtherHeader *pEthHeader, EMANE::NEMId nemId)
std::uint8_t buff[ETH_ALEN]
Definition: netutils.h:399
void processUpstreamControl(const ControlMessages &msgs) override
std::uint32_t sll_ifindex
Definition: rawtransport.cc:59