HardwareEthernetInterface.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 <linux/sockios.h>
18 #include <linux/filter.h>
20 
21 // Custom includes
22 
23 #define prefix_
24 //-/////////////////////////////////////////////////////////////////////////////////////////////////
25 
26 //-/////////////////////////////////////////////////////////////////////////////////////////////////
27 // senf::emu::detail::HardwareEthernetInterfaceNet
28 
29 prefix_ senf::emu::detail::HardwareEthernetInterfaceNet::HardwareEthernetInterfaceNet()
30  : socket (senf::noinit), source (socket), sink (socket),
31  annotatorRx_(true, true), annotatorTx_(false, true),
32  netOutput (annotatorRx_.output), netInput (annotatorTx_.input)
33 {
34  senf::ppi::connect(source.output, annotatorRx_.input);
35  senf::ppi::connect(annotatorTx_.output, sink.input);
36 }
37 
38 prefix_ void senf::emu::detail::HardwareEthernetInterfaceNet::assignSockets(ConnectedMMapPacketSocketHandle & socket_)
39 {
40  source.handle(socket_);
41  sink.handle(socket_);
42 
43  if (!socket_) {
44  if(socket) {
45  socket.close();
46  }
47  } else {
48  socket = socket_;
49  }
50 }
51 
52 prefix_ unsigned senf::emu::detail::HardwareEthernetInterfaceNet::rcvBuf()
53 {
54  if (socket)
55  return socket.protocol().rcvbuf();
56  return 0;
57 }
58 
59 prefix_ void senf::emu::detail::HardwareEthernetInterfaceNet::rcvBuf(unsigned rcvbuf)
60 {
61  if (socket)
62  socket.protocol().rcvbuf(rcvbuf);
63 }
64 
65 prefix_ unsigned senf::emu::detail::HardwareEthernetInterfaceNet::sndBuf()
66 {
67  if (socket)
68  return socket.protocol().sndbuf();
69  return 0;
70 }
71 
72 prefix_ void senf::emu::detail::HardwareEthernetInterfaceNet::sndBuf(unsigned sndbuf)
73 {
74  if (socket)
75  socket.protocol().sndbuf(sndbuf);
76 }
77 
78 prefix_ void senf::emu::detail::HardwareEthernetInterfaceNet::setupBPF(senf::MACAddress const & hwAddr, bool srcOnly)
79 {
80  if (!socket)
81  return;
82 
83  // BPF: tcpdump -i eth1 -dd 'not ether src <self>
84  static struct sock_filter BPF_code_src[] = {
85  { 0x20, 0, 0, 0x00000008 },
86  { 0x15, 0, 3, (boost::uint32_t(hwAddr[3]) << 24) | (boost::uint32_t(hwAddr[2]) << 16) | (boost::uint32_t(hwAddr[1]) << 8) | boost::uint32_t(hwAddr[0]) },
87  { 0x28, 0, 0, 0x00000006 },
88  { 0x15, 0, 1, (boost::uint32_t(hwAddr[5]) << 8) | boost::uint32_t(hwAddr[4])},
89  { 0x06, 0, 0, 0x00000000 },
90  { 0x06, 0, 0, 0x0000ffff }
91  };
92 
93  // BPF: tcpdump -i eth1 -dd 'not ether src <self> and not ether dst <self>'
94  static struct sock_filter BPF_code_src_dst[] = {
95  { 0x20, 0, 0, 0x00000008 },
96  { 0x15, 0, 3, (boost::uint32_t(hwAddr[3]) << 24) | (boost::uint32_t(hwAddr[2]) << 16) | (boost::uint32_t(hwAddr[1]) << 8) | boost::uint32_t(hwAddr[0]) },
97  { 0x28, 0, 0, 0x00000006 },
98  { 0x15, 0, 1, (boost::uint32_t(hwAddr[5]) << 8) | boost::uint32_t(hwAddr[4])},
99  { 0x20, 0, 0, 0x00000002 },
100  { 0x15, 0, 3, (boost::uint32_t(hwAddr[3]) << 24) | (boost::uint32_t(hwAddr[2]) << 16) | (boost::uint32_t(hwAddr[1]) << 8) | boost::uint32_t(hwAddr[0]) },
101  { 0x28, 0, 0, 0x00000000 },
102  { 0x15, 0, 1, (boost::uint32_t(hwAddr[5]) << 8) | boost::uint32_t(hwAddr[4])},
103  { 0x06, 0, 0, 0x00000000 },
104  { 0x06, 0, 0, 0x0000ffff }
105  };
106 
107  if (srcOnly)
108  socket.protocol().attachSocketFilter(BPF_code_src);
109  else
110  socket.protocol().attachSocketFilter(BPF_code_src_dst);
111 }
112 
113 prefix_ void senf::emu::detail::HardwareEthernetInterfaceNet::clearBPF()
114 {
115  if (!socket)
116  return;
117 
118  socket.protocol().detachSocketFilter();
119 }
120 
121 
122 
123 //-/////////////////////////////////////////////////////////////////////////////////////////////////
124 // senf::emu::HardwareEthernetInterface
125 
126 namespace {
127  struct DisableInterfaceGuard
128  {
129  DisableInterfaceGuard(senf::emu::Interface & iface)
130  : iface_ (iface), enabled_ (iface_.enabled())
131  { if (enabled_) iface_.disable(); }
132 
133  ~DisableInterfaceGuard()
134  { if (enabled_) iface_.enable(); }
135 
136  senf::emu::Interface & iface_;
137  bool enabled_;
138  };
139 }
140 
142  : EthernetInterface (netOutput, netInput), dev_ (dev), ctrl_ (dev_),
143  rcvBufSize_ (1024), sndBufSize_ (96*1024), qlen_ (512), pvid_(VLanId::None), accessMode_(false)
144 {
147 
148  namespace fty = console::factory;
149 
150  consoleDir()
151  .add("mmapStats", fty::Command(&HardwareEthernetInterface::dumpMmapStats, this)
152  .doc("dumps and resets the MMAP RX/TX statistics"));
153  consoleDir()
154  .add("sndBuf", fty::Command(
156  .doc( "set the send socket buffer size in bytes"));
157  consoleDir()
158  .add("sndBuf", fty::Command(
160  .doc( "get the send socket buffer size in bytes"));
161  consoleDir()
162  .add("rcvBuf", fty::Command(
164  .doc( "set the receive socket buffer size in bytes"));
165  consoleDir()
166  .add("rcvBuf", fty::Command(
168  .doc( "get the receive socket buffer size in bytes"));
169  consoleDir()
170  .add("qlen", fty::Command(
171  SENF_MEMBINDFNP(void, HardwareEthernetInterface, qlen, (unsigned)))
172  .doc("set mmap read queue length in # of packets"));
173  consoleDir()
174  .add("qlen", fty::Command(
175  SENF_MEMBINDFNP(unsigned, HardwareEthernetInterface, qlen, () const))
176  .doc("get mmap read queue length in # of packets"));
177  consoleDir()
178  .add("maxBurst", fty::Command(
180  .doc("set max burst rate"));
181  consoleDir()
182  .add("maxBurst", fty::Command(
183  SENF_MEMBINDFNP(unsigned, HardwareEthernetInterface, maxBurst, () const))
184  .doc("get max burst rate"));
185  consoleDir()
186  .add("pvid", fty::Command(
187  SENF_MEMBINDFNP(bool, HardwareEthernetInterface, pvid, (VLanId const &, bool)))
188  .doc( "enables filtering for a specific PVID (VLAN ID must be 0...4095)"));
189  consoleDir()
190  .add("pvid", fty::Command(
192  .doc( "report the currently configured PVID (-1 means none)"));
193 
194 
195  console::provideDirectory(interfaceDir(),"by-device").add(device(), fty::Link(consoleDir()));
196 
197  initialId_ = id();
198  annotatorRx_.id(initialId_);
199 
200  if (ctrl_.isUp())
201  init_sockets();
202 }
203 
205 {
206  try {
207  console::provideDirectory(interfaceDir(),"by-device").remove(device());
208  } catch (...) {};
209 }
210 
212  const
213 {
214  return dev_;
215 }
216 
217 prefix_ void senf::emu::HardwareEthernetInterface::v_mcAdd(MACAddress const & address)
218 {
219  if (HardwareEthernetInterfaceNet::socket.valid())
220  HardwareEthernetInterfaceNet::socket.protocol().mcAdd( dev_, address);
221 }
222 
223 prefix_ void senf::emu::HardwareEthernetInterface::v_mcDrop(MACAddress const & address)
224 {
225  if (HardwareEthernetInterfaceNet::socket.valid())
226  HardwareEthernetInterfaceNet::socket.protocol().mcDrop( dev_, address);
227 }
228 
229 prefix_ void senf::emu::HardwareEthernetInterface::init_sockets()
230 {
231  std::string vlanDevice (device() + "." + senf::str(pvid_));
232 
233  if (!promisc() and pvid_) {
234  // if there exists a VLAN interface, remove it first
235  try {
236  ctrl_.delVLAN(pvid_.id());
237  }
238  catch (...) {
239  }
240  ctrl_.addVLAN(pvid_.id());
241  NetdeviceController(vlanDevice).up();
242  }
243 
244  ConnectedMMapPacketSocketHandle socket_ (((promisc() or !pvid_) ? device() : vlanDevice),
245  qlen_, SENF_EMU_MAXMTU);
246 
247  socket_.protocol().rcvbuf( rcvBufSize_);
248  socket_.protocol().sndbuf( sndBufSize_);
249  // socket_.protocol().sndLowat(SENF_EMU_MAXMTU);
250 
251  HardwareEthernetInterfaceNet::assignSockets(socket_);
252  if (promisc()) {
253  HardwareEthernetInterfaceNet::setupBPF(id(), true); // SRC only
254  }
255 
256  if (promisc() and pvid_) {
257  if (accessMode_) {
258  annotatorRx_.insertTag(pvid_);
259  annotatorTx_.removeTag(pvid_);
260  } else {
261  annotatorRx_.removeTag(pvid_);
262  annotatorTx_.insertTag(pvid_);
263  }
264  } else {
265  annotatorRx_.clearTag();
266  annotatorTx_.clearTag();
267  }
268 
269  // switch to promisc rx method, which works around possibily misconfigured VLAN offloading
270  annotatorRx_.promisc(promisc());
271 }
272 
273 prefix_ void senf::emu::HardwareEthernetInterface::close_sockets()
274 {
275  try {
276  if (!promisc() and pvid_) {
277  ctrl_.delVLAN(pvid_.id());
278  }
279  }
280  catch (...) {
281  }
282 
284  HardwareEthernetInterfaceNet::assignSockets(skt);
285 }
286 
288 {
289  if (!enabled()) {
290  // we might have a new ifindex_ (i.e. after USB Ethernet unplug/plug)
291  ctrl_.reset(device());
292  // did our ID change ?
293  if (initialId_ != id()) {
294  // if so, reset it to the initial Id (i.e. USB Ethernet)
295  SENF_LOG((senf::log::IMPORTANT)("Id (MAC Address) of device " << device() << " has changed to " << id() << ". Resetting it back to " << initialId_));
296  // iface must be down before changing Id
297  ctrl_.down();
298  // reset to old Id here
299  id(initialId_);
300  }
301  // make sure the interface is 'up'
302  ctrl_.up();
303  // open the MMAP sockets
304  init_sockets();
305  }
306 }
307 
309 {
310  if (enabled()) {
311  close_sockets();
312  try {
313  ctrl_.down();
314  }
315  catch (...) {
316  }
317  }
318 }
319 
320 prefix_ bool senf::emu::HardwareEthernetInterface::v_enabled()
321  const
322 {
323  return HardwareEthernetInterfaceNet::socket.valid();
324 }
325 
326 prefix_ void senf::emu::HardwareEthernetInterface::v_id(MACAddress const & mac)
327 {
328  DisableInterfaceGuard guard (*this);
329  ctrl_.hardwareAddress(mac);
330  annotatorRx_.id(id());
331 }
332 
333 prefix_ senf::MACAddress senf::emu::HardwareEthernetInterface::v_id()
334  const
335 {
336  return ctrl_.hardwareAddress();
337 }
338 
339 prefix_ bool senf::emu::HardwareEthernetInterface::v_promisc()
340  const
341 {
342  return ctrl_.promisc();
343 }
344 
345 prefix_ void senf::emu::HardwareEthernetInterface::v_promisc(bool p)
346 {
347  close_sockets();
348  ctrl_.promisc(p);
349  init_sockets();
350 }
351 
352 prefix_ bool senf::emu::HardwareEthernetInterface::v_annotationMode()
353  const
354 {
355  return annotatorRx_.annotate();
356 }
357 
358 prefix_ void senf::emu::HardwareEthernetInterface::v_annotationMode(bool a)
359 {
360  annotatorRx_.annotate(a);
361 }
362 
364  const
365 {
366  return ctrl_.mtu();
367 }
368 
370 {
371  // nowadays, let's assume Gbit Interfaces ;)
372  unsigned speed (1000);
373 
374  // this seems to fail for virtual interfaces (i.e. kvm)
375  try {
376  speed = ctrl_.speed();
377  }
378  catch(...) {};
379 
380  if ((speed < 1000) and (v > 1500)) {
381  SENF_THROW_SYSTEM_EXCEPTION("EthernetController: interface/link in non-Jumbo mode. MTU > 1500 not allowed.");
382  return;
383  }
384 
385  ctrl_.mtu(v);
386 }
387 
388 prefix_ void senf::emu::HardwareEthernetInterface::v_flushRxQueues()
389 {
390  HardwareEthernetInterfaceNet::source.flush();
391 }
392 
393 prefix_ void senf::emu::HardwareEthernetInterface::v_flushTxQueues()
394 {
395  HardwareEthernetInterfaceNet::sink.flush();
396 }
397 
399 {
400  // need to cache sndBufSize_ so we can (re-)apply the value in v_enable()
401  sndBufSize_ = std::max(sndbuf, 2048u);
402  HardwareEthernetInterfaceNet::sndBuf( sndBufSize_);
403 }
404 
406 {
407  // need to cache sndBufSize_ so we can (re-)apply the value in v_enable()
408  return sndBufSize_ = HardwareEthernetInterfaceNet::sndBuf();
409 }
410 
412 {
413  // need to cache rcvBufSize_ so we can (re-)apply the value in v_enable()
414  rcvBufSize_ = std::max(rcvbuf, 4096u);
415  HardwareEthernetInterfaceNet::rcvBuf( rcvBufSize_);
416 }
417 
419 {
420  // need to cache rcvBufSize_ so we can (re-)apply the value in v_enable()
421  return rcvBufSize_ = HardwareEthernetInterfaceNet::rcvBuf();
422 }
423 
425 {
426  if (!accessMode and p.stag())
427  return false;
428 
429  close_sockets();
430  pvid_ = p;
431  accessMode_ = accessMode;
432  init_sockets();
433  return true;
434 }
435 
437 {
438  return HardwareEthernetInterfaceNet::annotatorRx_.vlanMismatch();
439 }
440 
442 {
443  return HardwareEthernetInterfaceNet::annotatorTx_.vlanMismatch();
444 }
445 
447  const
448 {
449  return qlen_;
450 }
451 
453 {
454  if (qlen_ == qlen)
455  return;
456  // need to cache rxqlen_ so we can (re-)apply the value in v_enable()
457  qlen_ = qlen;
458 
459  if (enabled()) {
460  close_sockets();
461  init_sockets();
462  }
463 }
464 
466 {
467  return source.dropped();
468 }
469 
470 prefix_ std::tuple<unsigned,unsigned,unsigned> senf::emu::HardwareEthernetInterface::txDropped()
471 {
472  return sink.dropped();
473 }
474 
476  const
477 {
478  return source.maxBurst();
479 }
480 
482 {
483  source.maxBurst(maxBurst);
484 }
485 
487 {
488  if (HardwareEthernetInterfaceNet::socket.valid()) {
489  auto rs (HardwareEthernetInterfaceNet::socket.protocol().rxStats());
490  os << "MMAP Rx (fd=" << socket.protocol().fd() << ", avail " << socket.protocol().available() << ") stats: "; rs.dump(os);
491  auto ts (HardwareEthernetInterfaceNet::socket.protocol().txStats());
492  os << " MMAP Tx (fd=" << socket.protocol().fd() << ") stats: "; ts.dump(os);
493  } else {
494  os << "Socket closed. No stats available.";
495  }
496  os << std::endl;
497 }
498 
500  const
501 {
502  if (HardwareEthernetInterfaceNet::socket)
503  return socket.protocol().interfaceDead();
504  return false; // do not call us if the iface is not active
505 }
506 
507 
508 #ifdef SENF_DEBUG
509 
510 prefix_ unsigned senf::emu::HardwareEthernetInterface::burstMax()
511 {
512  return source.burstMax();
513 }
514 
515 #endif
516 
517 //-/////////////////////////////////////////////////////////////////////////////////////////////////
518 #undef prefix_
519 
520 
521 // Local Variables:
522 // mode: c++
523 // fill-column: 100
524 // comment-column: 40
525 // c-file-style: "senf"
526 // indent-tabs-mode: nil
527 // ispell-local-dictionary: "american"
528 // compile-command: "scons -u test"
529 // End:
bool promisc() const
Get interface promiscuous status.
#define SENF_EMU_MAXMTU
Definition: config.hh:40
Interface API base class
#define SENF_MEMBINDFNP(ret, cls, fn, args)
std::uint8_t mac[6]
#define SENF_THROW_SYSTEM_EXCEPTION(desc)
void addVLAN(std::uint16_t vlanId)
bool enabled() const
true, if interface enabled
void reset(std::string const &interface_name)
noinit
std::tuple< unsigned, unsigned, unsigned > txDropped()
std::int32_t max
console::DirectoryNode & consoleDir() const
Access interface console directory.
bool stag() const
std::string const & device() const
NodeType & add(std::string const &name, boost::shared_ptr< NodeType > node)
virtual unsigned v_mtu() const override
Called to get interface MTU.
MACAddress const & id() const
Get interface MAC Address.
#define prefix_
MACAddress hardwareAddress() const
void delVLAN(std::uint16_t vlanId)
console::DirectoryNode & interfaceDir()
std::string const & v_device() const override
ProtocolClientSocketHandle< ConnectedMMapPacketSocketProtocol< QueueReadPolicy, QueueWritePolicy > > ConnectedMMapPacketSocketHandle
std::pair< unsigned, unsigned > rxDropped()
virtual void v_disable() override
Called to disable interface.
#define SENF_LOG(args)
HardwareEthernetInterface public header.
virtual void v_enable() override
Called to enable interface.