Goby v2
ip_gateway.cpp
1 // Copyright 2009-2018 Toby Schneider (http://gobysoft.org/index.wt/people/toby)
2 // GobySoft, LLC (2013-)
3 // Massachusetts Institute of Technology (2007-2014)
4 //
5 //
6 // This file is part of the Goby Underwater Autonomy Project Binaries
7 // ("The Goby Binaries").
8 //
9 // The Goby Binaries are free software: you can redistribute them and/or modify
10 // them under the terms of the GNU General Public License as published by
11 // the Free Software Foundation, either version 2 of the License, or
12 // (at your option) any later version.
13 //
14 // The Goby Binaries are distributed in the hope that they will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with Goby. If not, see <http://www.gnu.org/licenses/>.
21 
22 #include <linux/if_tun.h>
23 #include <net/if.h>
24 #include <sys/socket.h>
25 
26 #include <boost/bimap.hpp>
27 #include <boost/circular_buffer.hpp>
28 
29 #include "dccl/arithmetic/field_codec_arithmetic.h"
30 
31 #include "goby/acomms/acomms_constants.h"
32 #include "goby/acomms/amac.h"
33 #include "goby/acomms/connect.h"
34 #include "goby/acomms/ip_codecs.h"
35 #include "goby/acomms/protobuf/modem_message.pb.h"
36 #include "goby/pb/application.h"
37 #include "goby/util/binary.h"
38 
39 #include "ip_gateway_config.pb.h"
40 
41 enum
42 {
43  IPV4_ADDRESS_BITS = 32,
44  MIN_IPV4_HEADER_LENGTH = 5, // number of 32-bit words
45  UDP_HEADER_SIZE = 8,
46  ICMP_HEADER_SIZE = 8,
47  ICMP_TYPE = 250,
48  IPV4_VERSION = 4,
49  ICMP_CODE = 0
50 };
51 
52 int tun_alloc(char* dev);
53 int tun_config(const char* dev, const char* host, unsigned cidr_prefix, unsigned mtu);
54 
55 using namespace goby::common::logger;
56 
57 namespace goby
58 {
59 namespace acomms
60 {
62 {
63  public:
65  ~IPGateway();
66 
67  private:
68  void init_dccl();
69  void init_tun();
70 
71  void loop();
72  void receive_packets();
73 
74  void handle_udp_packet(const goby::acomms::protobuf::IPv4Header& ip_hdr,
75  const goby::acomms::protobuf::UDPHeader& udp_hdr,
76  const std::string& payload);
77  void write_udp_packet(goby::acomms::protobuf::IPv4Header& ip_hdr,
78  goby::acomms::protobuf::UDPHeader& udp_hdr, const std::string& payload);
79  void write_icmp_control_message(const protobuf::IPGatewayICMPControl& control_msg);
80 
81  void write_icmp_packet(goby::acomms::protobuf::IPv4Header& ip_hdr,
83  const std::string& payload);
84 
85  void icmp_report_queue();
86 
87  std::pair<int, int> to_src_dest_pair(int srcdest);
88  int from_src_dest_pair(std::pair<int, int> src_dest);
89 
90  int ipv4_to_goby_address(const std::string& ipv4_address);
91  std::string goby_address_to_ipv4(int modem_id);
92 
93  void handle_data_request(const protobuf::ModemTransmission& m);
94  void handle_initiate_transmission(const protobuf::ModemTransmission& m);
95  void handle_modem_receive(const goby::acomms::protobuf::ModemTransmission& m);
96 
97  int ac_freq(int srcdest);
98 
99  private:
101  dccl::Codec dccl_goby_nh_, dccl_ip_, dccl_udp_, dccl_icmp_;
102  int tun_fd_;
103  int total_addresses_;
104  goby::uint32 local_address_; // in host byte order
105  int local_modem_id_;
106  goby::uint32 netmask_; // in host byte order
107 
109 
110  // map goby_port to udp_port
111  boost::bimap<int, int> port_map_;
112  int dynamic_port_index_;
113  std::vector<int> dynamic_udp_fd_;
114 
115  int ip_mtu_; // the MTU on the tun interface, which is slightly different than the Goby MTU specified in the config file since the IP and Goby NetworkHeader are different sizes.
116 
117  // maps destination goby address to message buffer
118  std::map<int, boost::circular_buffer<std::string> > outgoing_;
119 };
120 } // namespace acomms
121 } // namespace goby
122 
123 goby::acomms::IPGateway::IPGateway(goby::acomms::protobuf::IPGatewayConfig* cfg)
124  : Application(cfg), cfg_(*cfg), dccl_goby_nh_("ip_gateway_id_codec_0"),
125  dccl_ip_("ip_gateway_id_codec_1"), dccl_udp_("ip_gateway_id_codec_2"),
126  dccl_icmp_("ip_gateway_id_codec_3"), tun_fd_(-1),
127  total_addresses_((1 << (IPV4_ADDRESS_BITS - cfg_.cidr_netmask_prefix())) -
128  1), // minus one since we don't need to use .255 as broadcast
129  local_address_(0), local_modem_id_(0), netmask_(0),
130  dynamic_port_index_(cfg_.static_udp_port_size())
131 {
132  for (int d = 0; d < total_addresses_; ++d)
133  {
134  for (int s = 0; s < total_addresses_; ++s)
135  {
136  int S = from_src_dest_pair(std::make_pair(s, d));
137  std::cout << std::setw(8) << S;
138  if (s != 0 && s != d)
139  {
140  std::pair<int, int> sd = to_src_dest_pair(S);
141  std::cout << "(" << sd.first << "," << sd.second << ")";
142  assert(sd.first == s && sd.second == d);
143  }
144  else
145  std::cout << "(" << s << "," << d << ")";
146  }
147 
148  std::cout << std::endl;
149  }
150 
151  init_dccl();
152  init_tun();
153 
154  Application::subscribe(&IPGateway::handle_data_request, this,
155  "DataRequest" + goby::util::as<std::string>(local_modem_id_));
156  Application::subscribe(&IPGateway::handle_modem_receive, this,
157  "Rx" + goby::util::as<std::string>(local_modem_id_));
158 
160  &IPGateway::handle_initiate_transmission);
161  cfg_.mutable_mac_cfg()->set_modem_id(local_modem_id_);
162  mac_.startup(cfg_.mac_cfg());
163 }
164 void goby::acomms::IPGateway::init_dccl()
165 {
166  if (cfg_.model_type() == protobuf::IPGatewayConfig::AUTONOMY_COLLABORATION)
167  {
168  if (cfg_.gamma_autonomy() > 1 || cfg_.gamma_autonomy() < 0)
169  glog.is(DIE) && glog << "gamma_autonomy must be [0, 1]" << std::endl;
170 
171  if (cfg_.gamma_collaboration() > cfg_.gamma_autonomy() || cfg_.gamma_collaboration() < 0)
172  glog.is(DIE) && glog << "gamma_collaboration must be [0, gamma_autonomy]" << std::endl;
173  }
174 
175  dccl::dlog.connect(dccl::logger::INFO, &std::cout);
176 
177  dccl_arithmetic_load(&dccl_goby_nh_);
178 
179  dccl::arith::protobuf::ArithmeticModel addr_model;
180  addr_model.set_name("goby.acomms.NetworkHeader.AddrModel");
181 
182  for (int i = 0, n = (total_addresses_ - 1) * (total_addresses_ - 1); i <= n; ++i)
183  {
184  if (i != n)
185  {
186  int freq = 10;
187  switch (cfg_.model_type())
188  {
189  case protobuf::IPGatewayConfig::UNIFORM:
190  // already set to uniform value
191  break;
192  case protobuf::IPGatewayConfig::AUTONOMY_COLLABORATION: freq = ac_freq(i); break;
193  }
194 
195  addr_model.add_value_bound(i);
196  addr_model.add_frequency(freq);
197  }
198  else
199  {
200  addr_model.add_value_bound(n);
201  }
202  }
203 
204  addr_model.set_eof_frequency(0);
205  addr_model.set_out_of_range_frequency(0);
206  glog.is(DEBUG1) && glog << addr_model.DebugString() << std::endl;
207 
208  dccl::arith::ModelManager::set_model(addr_model);
209 
210  if (cfg_.total_ports() < cfg_.static_udp_port_size())
211  glog.is(DIE) &&
212  glog << "total_ports must be at least as many as the static_udp_ports defined"
213  << std::endl;
214 
215  for (int i = 0, n = cfg_.total_ports(); i < n; ++i)
216  {
217  if (i < cfg_.static_udp_port_size())
218  port_map_.insert(boost::bimap<int, int>::value_type(i, cfg_.static_udp_port(i)));
219  else
220  port_map_.insert(boost::bimap<int, int>::value_type(i, -i));
221  }
222 
223  dccl::arith::protobuf::ArithmeticModel port_model;
224  port_model.set_name("goby.acomms.NetworkHeader.PortModel");
225  for (int i = 0, n = cfg_.total_ports(); i <= n; ++i)
226  {
227  if (i != n)
228  {
229  int freq = 10;
230  port_model.add_value_bound(i);
231  port_model.add_frequency(freq);
232  }
233  else
234  {
235  port_model.add_value_bound(n);
236  }
237  }
238  port_model.set_eof_frequency(0);
239  port_model.set_out_of_range_frequency(0);
240  dccl::arith::ModelManager::set_model(port_model);
241 
242  dccl_goby_nh_.load<goby::acomms::protobuf::NetworkHeader>();
243  dccl_ip_.load<goby::acomms::protobuf::IPv4Header>();
244  dccl_udp_.load<goby::acomms::protobuf::UDPHeader>();
245  dccl_icmp_.load<goby::acomms::protobuf::ICMPHeader>();
246 
247  dccl_goby_nh_.info_all(&std::cout);
248 }
249 
250 void goby::acomms::IPGateway::init_tun()
251 {
252  char tun_name[IFNAMSIZ];
253 
254  std::string desired_tun_name = "tun";
255  if (cfg_.has_tun_number())
256  desired_tun_name += goby::util::as<std::string>(cfg_.tun_number());
257  else
258  desired_tun_name += "%d";
259 
260  strcpy(tun_name, desired_tun_name.c_str());
261  tun_fd_ = tun_alloc(tun_name);
262  if (tun_fd_ < 0)
263  glog.is(DIE) && glog << "Could not allocate tun interface. Check permissions?" << std::endl;
264 
265  ip_mtu_ = cfg_.mtu() - dccl_goby_nh_.max_size<goby::acomms::protobuf::NetworkHeader>() +
266  MIN_IPV4_HEADER_LENGTH * 4;
267 
268  int ret = tun_config(tun_name, cfg_.local_ipv4_address().c_str(), cfg_.cidr_netmask_prefix(),
269  ip_mtu_);
270  if (ret < 0)
271  glog.is(DIE) && glog << "Could not configure tun interface. Check IP address: "
272  << cfg_.local_ipv4_address()
273  << " and netmask prefix: " << cfg_.cidr_netmask_prefix() << std::endl;
274 
275  in_addr local_addr;
276  inet_aton(cfg_.local_ipv4_address().c_str(), &local_addr);
277  local_address_ = ntohl(local_addr.s_addr);
278  netmask_ = 0xFFFFFFFF - ((1 << (IPV4_ADDRESS_BITS - cfg_.cidr_netmask_prefix())) - 1);
279  local_modem_id_ = ipv4_to_goby_address(cfg_.local_ipv4_address());
280 }
281 
282 goby::acomms::IPGateway::~IPGateway() { dccl_arithmetic_unload(&dccl_goby_nh_); }
283 
284 void goby::acomms::IPGateway::loop()
285 {
286  mac_.do_work();
287  receive_packets();
288 }
289 
290 void goby::acomms::IPGateway::handle_udp_packet(const goby::acomms::protobuf::IPv4Header& ip_hdr,
291  const goby::acomms::protobuf::UDPHeader& udp_hdr,
292  const std::string& payload)
293 {
294  glog.is(VERBOSE) && glog << "Received UDP Packet. IPv4 Header: " << ip_hdr.DebugString()
295  << "UDP Header: " << udp_hdr << "Payload (" << payload.size()
296  << " bytes): " << goby::util::hex_encode(payload) << std::endl;
297 
298  int src = ipv4_to_goby_address(ip_hdr.source_ip_address());
299  int dest = ipv4_to_goby_address(ip_hdr.dest_ip_address());
300 
302  net_header.set_protocol(goby::acomms::protobuf::NetworkHeader::UDP);
303  net_header.set_srcdest_addr(from_src_dest_pair(std::make_pair(src, dest)));
304 
305  // map destination first - we need this mapping to exist on the other end
306  // if we map the source first, we might use the source mapping when source port == dest port
307  int dest_port = 0, src_port = 0;
308  boost::bimap<int, int>::right_map::const_iterator dest_it =
309  port_map_.right.find(udp_hdr.dest_port());
310  if (dest_it != port_map_.right.end())
311  {
312  dest_port = dest_it->second;
313  }
314  else
315  {
316  glog.is(WARN) && glog << "No mapping for destination UDP port: " << udp_hdr.dest_port()
317  << ". Unable to send packet." << std::endl;
318  return;
319  }
320 
321  boost::bimap<int, int>::right_map::const_iterator src_it =
322  port_map_.right.find(udp_hdr.source_port());
323  if (src_it != port_map_.right.end())
324  {
325  src_port = src_it->second;
326  }
327  else
328  {
329  // on transmit, try to map the source port to a dynamic port
330  if (cfg_.total_ports() == cfg_.static_udp_port_size())
331  {
332  glog.is(WARN) && glog << "No mapping for source UDP port: " << udp_hdr.source_port()
333  << " and we have no dynamic ports allocated (static_udp_port "
334  "size == total_ports)"
335  << std::endl;
336  return;
337  }
338  else
339  {
340  boost::bimap<int, int>::left_map::iterator dyn_port_it =
341  port_map_.left.find(dynamic_port_index_);
342  port_map_.left.replace_data(dyn_port_it, udp_hdr.source_port());
343  net_header.mutable_udp()->add_srcdest_port(dynamic_port_index_);
344  ++dynamic_port_index_;
345  if (dynamic_port_index_ >= cfg_.total_ports())
346  dynamic_port_index_ = cfg_.static_udp_port_size();
347  }
348  }
349 
350  net_header.mutable_udp()->add_srcdest_port(src_port);
351  net_header.mutable_udp()->add_srcdest_port(dest_port);
352 
353  glog.is(VERBOSE) && glog << "NetHeader: " << net_header.DebugString() << std::endl;
354 
355  std::string nh;
356  dccl_goby_nh_.encode(&nh, net_header);
357 
358  std::map<int, boost::circular_buffer<std::string> >::iterator it = outgoing_.find(dest);
359  if (it == outgoing_.end())
360  {
361  std::pair<std::map<int, boost::circular_buffer<std::string> >::iterator, bool> itboolpair =
362  outgoing_.insert(
363  std::make_pair(dest, boost::circular_buffer<std::string>(cfg_.queue_size())));
364  it = itboolpair.first;
365  }
366 
367  it->second.push_back(nh + payload);
368  icmp_report_queue();
369 }
370 
371 void goby::acomms::IPGateway::handle_initiate_transmission(const protobuf::ModemTransmission& m)
372 {
373  publish(m, "Tx" + goby::util::as<std::string>(local_modem_id_));
374 }
375 
376 void goby::acomms::IPGateway::receive_packets()
377 {
378  while (true)
379  {
380  fd_set rd_set;
381  FD_ZERO(&rd_set);
382  FD_SET(tun_fd_, &rd_set);
383 
384  timeval tout = {0};
385  int ret = select(tun_fd_ + 1, &rd_set, 0, 0, &tout);
386 
387  if (ret < 0 && errno != EINTR)
388  {
389  glog.is(WARN) && glog << "Could not select on tun fd." << std::endl;
390  return;
391  }
392  else if (ret > 0)
393  {
394  char buffer[ip_mtu_ + 1];
395  int len = read(tun_fd_, buffer, ip_mtu_);
396 
397  if (len < 0)
398  {
399  glog.is(WARN) && glog << "tun read error." << std::endl;
400  }
401  else if (len == 0)
402  {
403  glog.is(DIE) && glog << "tun reached EOF." << std::endl;
404  }
405  else
406  {
408  unsigned short ip_header_size = (buffer[0] & 0xF) * 4;
409  unsigned short version = ((buffer[0] >> 4) & 0xF);
410  if (version == 4)
411  {
412  std::string header_data(buffer, ip_header_size);
413  dccl_ip_.decode(header_data, &ip_hdr);
414  glog.is(DEBUG2) && glog << "Received " << len << " bytes. " << std::endl;
415  switch (ip_hdr.protocol())
416  {
417  default:
418  glog.is(DEBUG1) && glog << "IPv4 Protocol " << ip_hdr.protocol()
419  << " is not supported." << std::endl;
420  break;
421  case IPPROTO_UDP:
422  {
424  std::string udp_header_data(&buffer[ip_header_size], UDP_HEADER_SIZE);
425  dccl_udp_.decode(udp_header_data, &udp_hdr);
426  handle_udp_packet(ip_hdr, udp_hdr,
427  std::string(&buffer[ip_header_size + UDP_HEADER_SIZE],
428  ip_hdr.total_length() - ip_header_size -
429  UDP_HEADER_SIZE));
430  break;
431  }
432  case IPPROTO_ICMP:
433  {
435  std::string icmp_header_data(&buffer[ip_header_size], ICMP_HEADER_SIZE);
436  dccl_icmp_.decode(icmp_header_data, &icmp_hdr);
437  glog.is(DEBUG1) && glog << "Received ICMP Packet with header: "
438  << icmp_hdr.ShortDebugString() << std::endl;
439  glog.is(DEBUG1) && glog << "ICMP sending is not supported."
440  << std::endl;
441 
442  break;
443  }
444  }
445  }
446  }
447  }
448  else
449  {
450  // no select items
451  return;
452  }
453  }
454 }
455 
456 void goby::acomms::IPGateway::handle_data_request(const protobuf::ModemTransmission& orig_msg)
457 {
458  if (cfg_.has_only_rate() && cfg_.only_rate() != orig_msg.rate())
459  return;
460 
461  protobuf::ModemTransmission msg = orig_msg;
462 
463  bool had_data = false;
464  while ((unsigned)msg.frame_size() < msg.max_num_frames())
465  {
466  if (msg.dest() != goby::acomms::QUERY_DESTINATION_ID)
467  {
468  std::map<int, boost::circular_buffer<std::string> >::iterator it =
469  outgoing_.find(msg.dest());
470  if (it == outgoing_.end())
471  {
472  break;
473  }
474  else if (it->second.size() == 0)
475  {
476  break;
477  }
478  else
479  {
480  msg.set_ack_requested(false);
481  msg.add_frame(it->second.front());
482  it->second.pop_front();
483  had_data = true;
484  }
485  }
486  else
487  {
488  // TODO: not fair - prioritizes lower valued destinations
489  for (std::map<int, boost::circular_buffer<std::string> >::iterator
490  it = outgoing_.begin(),
491  end = outgoing_.end();
492  it != end; ++it)
493  {
494  if (it->second.size() == 0)
495  {
496  continue;
497  }
498  else
499  {
500  msg.set_dest(it->first);
501  msg.set_ack_requested(false);
502  msg.add_frame(it->second.front());
503  it->second.pop_front();
504  had_data = true;
505  break;
506  }
507  }
508  break;
509  }
510  }
511 
512  publish(msg, "DataResponse" + goby::util::as<std::string>(local_modem_id_));
513  if (had_data)
514  icmp_report_queue();
515 }
516 
517 void goby::acomms::IPGateway::handle_modem_receive(
519 {
520  if (cfg_.has_only_rate() && cfg_.only_rate() != modem_msg.rate())
521  return;
522 
523  for (int i = 0, n = modem_msg.frame_size(); i < n; ++i)
524  {
527 
529  std::string frame = modem_msg.frame(i);
530 
531  try
532  {
533  dccl_goby_nh_.decode(&frame, &net_header); // strips used bytes off frame
534  }
535  catch (goby::Exception& e)
536  {
537  glog.is(WARN) && glog << "Could not decode header: " << e.what() << std::endl;
538  continue;
539  }
540 
541  glog.is(VERBOSE) && glog << "NetHeader: " << net_header.DebugString() << std::endl;
542 
543  ip_hdr.set_ihl(MIN_IPV4_HEADER_LENGTH);
544  ip_hdr.set_version(IPV4_VERSION);
545  ip_hdr.set_ecn(0);
546  ip_hdr.set_dscp(0);
547  ip_hdr.set_total_length(MIN_IPV4_HEADER_LENGTH * 4 + UDP_HEADER_SIZE + frame.size());
548  ip_hdr.set_identification(0);
549  ip_hdr.mutable_flags_frag_offset()->set_dont_fragment(false);
550  ip_hdr.mutable_flags_frag_offset()->set_more_fragments(false);
551  ip_hdr.mutable_flags_frag_offset()->set_fragment_offset(0);
552  ip_hdr.set_ttl(63);
553  ip_hdr.set_protocol(net_header.protocol());
554 
555  std::pair<int, int> src_dest = to_src_dest_pair(net_header.srcdest_addr());
556  ip_hdr.set_source_ip_address(goby_address_to_ipv4(src_dest.first));
557  ip_hdr.set_dest_ip_address(goby_address_to_ipv4(src_dest.second));
558 
559  if (net_header.udp().srcdest_port_size() == 2)
560  {
561  boost::bimap<int, int>::left_map::iterator src_it =
562  port_map_.left.find(net_header.udp().srcdest_port(0));
563  int source_port = src_it->second;
564  if (source_port < 0)
565  {
566  // get an unused UDP port
567  int fd = socket(AF_INET, SOCK_DGRAM, 0);
568  struct sockaddr_in addr;
569  memset((char*)&addr, 0, sizeof(addr));
570  addr.sin_family = AF_INET;
571  addr.sin_addr.s_addr = htonl(INADDR_ANY);
572  addr.sin_port = htons(0);
573  bind(fd, (struct sockaddr*)&addr, sizeof(addr));
574 
575  struct sockaddr_in sa;
576  socklen_t sa_len;
577  sa_len = sizeof(sa);
578  getsockname(fd, (struct sockaddr*)&sa, &sa_len);
579  source_port = ntohs(sa.sin_port);
580  dynamic_udp_fd_.push_back(fd);
581 
582  port_map_.left.replace_data(src_it, source_port);
583  }
584 
585  boost::bimap<int, int>::left_map::const_iterator dest_it =
586  port_map_.left.find(net_header.udp().srcdest_port(1));
587  int dest_port = dest_it->second;
588  if (dest_port < 0)
589  {
590  glog.is(WARN) &&
591  glog << "No mapping for destination port: " << net_header.udp().srcdest_port(1)
592  << ", cannot write packet" << std::endl;
593  continue;
594  }
595 
596  udp_hdr.set_source_port(source_port);
597  udp_hdr.set_dest_port(dest_port);
598  }
599  else
600  {
601  glog.is(WARN) && glog << "Bad srcdest_port field, must have two values." << std::endl;
602  continue;
603  }
604 
605  udp_hdr.set_length(UDP_HEADER_SIZE + frame.size());
606  write_udp_packet(ip_hdr, udp_hdr, frame);
607  }
608 }
609 
610 void goby::acomms::IPGateway::write_udp_packet(goby::acomms::protobuf::IPv4Header& ip_hdr,
612  const std::string& payload)
613 {
614  // set checksum 0 for calculation
615  ip_hdr.set_header_checksum(0);
616  udp_hdr.set_checksum(0);
617 
618  std::string ip_hdr_data, udp_hdr_data;
619  dccl_udp_.encode(&udp_hdr_data, udp_hdr);
620  dccl_ip_.encode(&ip_hdr_data, ip_hdr);
621 
622  enum
623  {
624  NET_SHORT_BYTES = 2,
625  NET_LONG_BYTES = 4
626  };
627  enum
628  {
629  IPV4_SOURCE_ADDR_OFFSET = 12,
630  IPV4_DEST_ADDR_OFFSET = 16,
631  IPV4_CS_OFFSET = 10
632  };
633  enum
634  {
635  UDP_LENGTH_OFFSET = 4,
636  UDP_CS_OFFSET = 6
637  };
638 
639  std::string udp_pseudo_header = ip_hdr_data.substr(IPV4_SOURCE_ADDR_OFFSET, NET_LONG_BYTES) +
640  ip_hdr_data.substr(IPV4_DEST_ADDR_OFFSET, NET_LONG_BYTES) +
641  char(0) + char(IPPROTO_UDP) +
642  udp_hdr_data.substr(UDP_LENGTH_OFFSET, NET_SHORT_BYTES);
643  assert(udp_pseudo_header.size() == 12);
644 
645  uint16_t ip_checksum = net_checksum(ip_hdr_data);
646  uint16_t udp_checksum = net_checksum(udp_pseudo_header + udp_hdr_data + payload);
647 
648  ip_hdr_data[IPV4_CS_OFFSET] = (ip_checksum >> 8) & 0xFF;
649  ip_hdr_data[IPV4_CS_OFFSET + 1] = ip_checksum & 0xFF;
650 
651  udp_hdr_data[UDP_CS_OFFSET] = (udp_checksum >> 8) & 0xFF;
652  udp_hdr_data[UDP_CS_OFFSET + 1] = udp_checksum & 0xFF;
653 
654  std::string packet(ip_hdr_data + udp_hdr_data + payload);
655  unsigned len = write(tun_fd_, packet.c_str(), packet.size());
656  if (len < packet.size())
657  glog.is(WARN) && glog << "Failed to write all " << packet.size() << " bytes." << std::endl;
658 }
659 
660 void goby::acomms::IPGateway::icmp_report_queue()
661 {
662  protobuf::IPGatewayICMPControl control_msg;
663  control_msg.set_type(protobuf::IPGatewayICMPControl::QUEUE_REPORT);
664  control_msg.set_address(cfg_.local_ipv4_address());
665 
666  int total_messages = 0;
667 
668  for (std::map<int, boost::circular_buffer<std::string> >::const_iterator it = outgoing_.begin(),
669  end = outgoing_.end();
670  it != end; ++it)
671  {
672  int size = it->second.size();
673  if (size)
674  {
676  control_msg.mutable_queue_report()->add_queue();
677  q->set_dest(it->first);
678  q->set_size(size);
679  total_messages += size;
680  }
681  }
682  write_icmp_control_message(control_msg);
683 
684  // skip the MACManager and directly initiate the transmission
685  if (total_messages > 0 && cfg_.bypass_mac())
686  {
687  if (cfg_.has_bypass_mac_slot())
688  {
689  handle_initiate_transmission(cfg_.bypass_mac_slot());
690  }
691  else
692  {
694  m.set_src(local_modem_id_);
695  m.set_type(protobuf::ModemTransmission::DATA);
696  if (cfg_.has_only_rate())
697  m.set_rate(cfg_.only_rate());
698  handle_initiate_transmission(m);
699  }
700  }
701 }
702 
703 void goby::acomms::IPGateway::write_icmp_control_message(
704  const protobuf::IPGatewayICMPControl& control_msg)
705 {
706  std::string control_data;
707  control_msg.SerializeToString(&control_data);
708 
709  glog.is(DEBUG1) && glog << "Writing ICMP Control message: " << control_msg.DebugString()
710  << std::endl;
711 
713  ip_hdr.set_ihl(MIN_IPV4_HEADER_LENGTH);
714  ip_hdr.set_version(IPV4_VERSION);
715  ip_hdr.set_ecn(0);
716  ip_hdr.set_dscp(0);
717  ip_hdr.set_total_length(MIN_IPV4_HEADER_LENGTH * 4 + ICMP_HEADER_SIZE + control_data.size());
718  ip_hdr.set_identification(0);
719  ip_hdr.mutable_flags_frag_offset()->set_dont_fragment(false);
720  ip_hdr.mutable_flags_frag_offset()->set_more_fragments(false);
721  ip_hdr.mutable_flags_frag_offset()->set_fragment_offset(0);
722  ip_hdr.set_ttl(63);
723  ip_hdr.set_protocol(IPPROTO_ICMP);
724  ip_hdr.set_source_ip_address(cfg_.local_ipv4_address());
725  ip_hdr.set_dest_ip_address(cfg_.local_ipv4_address());
726 
728  icmp_hdr.set_type(ICMP_TYPE);
729  icmp_hdr.set_code(ICMP_CODE);
730 
731  write_icmp_packet(ip_hdr, icmp_hdr, control_data);
732 }
733 
734 void goby::acomms::IPGateway::write_icmp_packet(goby::acomms::protobuf::IPv4Header& ip_hdr,
736  const std::string& payload)
737 {
738  // set checksum 0 for calculation
739  ip_hdr.set_header_checksum(0);
740  icmp_hdr.set_checksum(0);
741  icmp_hdr.set_short1(0);
742  icmp_hdr.set_short2(0);
743 
744  std::string ip_hdr_data, icmp_hdr_data;
745  dccl_icmp_.encode(&icmp_hdr_data, icmp_hdr);
746  dccl_ip_.encode(&ip_hdr_data, ip_hdr);
747 
748  enum
749  {
750  NET_SHORT_BYTES = 2,
751  NET_LONG_BYTES = 4
752  };
753  enum
754  {
755  IPV4_SOURCE_ADDR_OFFSET = 12,
756  IPV4_DEST_ADDR_OFFSET = 16,
757  IPV4_CS_OFFSET = 10
758  };
759  enum
760  {
761  ICMP_CS_OFFSET = 2
762  };
763 
764  uint16_t ip_checksum = net_checksum(ip_hdr_data);
765  uint16_t icmp_checksum = net_checksum(icmp_hdr_data + payload);
766 
767  ip_hdr_data[IPV4_CS_OFFSET] = (ip_checksum >> 8) & 0xFF;
768  ip_hdr_data[IPV4_CS_OFFSET + 1] = ip_checksum & 0xFF;
769 
770  icmp_hdr_data[ICMP_CS_OFFSET] = (icmp_checksum >> 8) & 0xFF;
771  icmp_hdr_data[ICMP_CS_OFFSET + 1] = icmp_checksum & 0xFF;
772 
773  std::string packet(ip_hdr_data + icmp_hdr_data + payload);
774  unsigned len = write(tun_fd_, packet.c_str(), packet.size());
775  if (len < packet.size())
776  glog.is(WARN) && glog << "Failed to write all " << packet.size() << " bytes." << std::endl;
777 }
778 
779 // src
780 // 0 1 2 3
781 // ------------
782 // d 0 | x 0 1 2
783 // e 1 | x x 3 4
784 // s 2 | x 5 x 6
785 // t 3 | x 7 8 x
786 std::pair<int, int> goby::acomms::IPGateway::to_src_dest_pair(int srcdest)
787 {
788  int src = (srcdest - 1) % (total_addresses_ - 2) + 1;
789  int dest = (srcdest - 1) / (total_addresses_ - 2);
790  if (src >= dest)
791  ++src;
792 
793  return std::make_pair(src, dest);
794 }
795 
796 int goby::acomms::IPGateway::from_src_dest_pair(std::pair<int, int> src_dest)
797 {
798  int src = src_dest.first;
799  int dest = src_dest.second;
800 
801  if (src == dest || src == goby::acomms::BROADCAST_ID)
802  return -1;
803 
804  return dest * (total_addresses_ - 2) + src - (src > dest ? 1 : 0);
805 }
806 
807 int goby::acomms::IPGateway::ipv4_to_goby_address(const std::string& ipv4_address)
808 {
809  in_addr remote_addr;
810  inet_aton(ipv4_address.c_str(), &remote_addr);
811  goby::uint32 remote_address = ntohl(remote_addr.s_addr);
812  int modem_id = remote_address & ~netmask_;
813 
814  // broadcast conventions differ
815  if (modem_id + netmask_ == 0xFFFFFFFF)
816  modem_id = goby::acomms::BROADCAST_ID;
817  return modem_id;
818 }
819 
820 std::string goby::acomms::IPGateway::goby_address_to_ipv4(int modem_id)
821 {
822  goby::uint32 address = 0;
823  if (modem_id == goby::acomms::BROADCAST_ID)
824  address = (local_address_ & netmask_) + ~netmask_;
825  else
826  address = (local_address_ & netmask_) + modem_id;
827 
828  in_addr ret_addr;
829  ret_addr.s_addr = htonl(address);
830  return std::string(inet_ntoa(ret_addr));
831 }
832 
833 int goby::acomms::IPGateway::ac_freq(int srcdest)
834 {
835  std::pair<int, int> sd = to_src_dest_pair(srcdest);
836  int s = sd.first;
837  int d = sd.second;
838  int N = total_addresses_ + 1;
839  const int scale_factor = N * N * 100;
840 
841  double p0 = 1.0 / (N - 2);
842  double p1 = 1.0 / (N - 3);
843  double p2 = 1.0 / (N * N - 6 * N + 9);
844 
845  if (s == d || s == goby::acomms::BROADCAST_ID)
846  return 0;
847  else if (s == cfg_.gateway_id())
848  return scale_factor * p0 * (1 - cfg_.gamma_autonomy());
849  else if (d == cfg_.gateway_id())
850  return scale_factor * p1 * (cfg_.gamma_autonomy() - cfg_.gamma_collaboration());
851  else
852  return scale_factor * p2 * (cfg_.gamma_collaboration());
853 }
854 
855 int main(int argc, char* argv[])
856 {
857  dccl::FieldCodecManager::add<goby::acomms::IPGatewayEmptyIdentifierCodec<0xF000> >(
858  "ip_gateway_id_codec_0");
859  dccl::FieldCodecManager::add<goby::acomms::IPGatewayEmptyIdentifierCodec<0xF001> >(
860  "ip_gateway_id_codec_1");
861  dccl::FieldCodecManager::add<goby::acomms::IPGatewayEmptyIdentifierCodec<0xF002> >(
862  "ip_gateway_id_codec_2");
863  dccl::FieldCodecManager::add<goby::acomms::IPGatewayEmptyIdentifierCodec<0xF003> >(
864  "ip_gateway_id_codec_3");
865  dccl::FieldCodecManager::add<goby::acomms::NetShortCodec>("net.short");
866  dccl::FieldCodecManager::add<goby::acomms::IPv4AddressCodec>("ip.v4.address");
867  dccl::FieldCodecManager::add<goby::acomms::IPv4FlagsFragOffsetCodec>("ip.v4.flagsfragoffset");
868 
870  goby::run<goby::acomms::IPGateway>(argc, argv, &cfg);
871 }
872 
873 // https://www.kernel.org/doc/Documentation/networking/tuntap.txt
874 // char *dev should be the name of the device with a format string (e.g.
875 // "tun%d"), but (as far as I can see) this can be any valid network device name.
876 // Note that the character pointer becomes overwritten with the real device name
877 // (e.g. "tun0")
878 int tun_alloc(char* dev)
879 {
880  struct ifreq ifr;
881  int fd, err;
882 
883  if ((fd = open("/dev/net/tun", O_RDWR)) < 0)
884  return fd;
885 
886  memset(&ifr, 0, sizeof(ifr));
887 
888  /* Flags: IFF_TUN - TUN device (no Ethernet headers)
889  * IFF_TAP - TAP device
890  *
891  * IFF_NO_PI - Do not provide packet information
892  */
893  ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
894  if (*dev)
895  strncpy(ifr.ifr_name, dev, IFNAMSIZ);
896 
897  if ((err = ioctl(fd, TUNSETIFF, (void*)&ifr)) < 0)
898  {
899  close(fd);
900  return err;
901  }
902 
903  strcpy(dev, ifr.ifr_name);
904  return fd;
905 }
906 
907 // from https://stackoverflow.com/questions/5308090/set-ip-address-using-siocsifaddr-ioctl
908 int tun_config(const char* dev, const char* host, unsigned cidr_prefix, unsigned mtu)
909 {
910  // set address
911 
912  struct ifreq ifr;
913  struct sockaddr_in* sai = (struct sockaddr_in*)&ifr.ifr_addr;
914  int sockfd;
915 
916  sockfd = socket(AF_INET, SOCK_DGRAM, 0);
917 
918  memset(&ifr, 0, sizeof(ifr));
919 
920  strncpy(ifr.ifr_name, dev, IFNAMSIZ);
921 
922  sai->sin_family = AF_INET;
923  sai->sin_port = 0;
924 
925  int ret = inet_aton(host, &sai->sin_addr);
926  if (ret == 0)
927  return -1;
928 
929  if (ioctl(sockfd, SIOCSIFADDR, &ifr) == -1)
930  return -1;
931 
932  if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) == -1)
933  return -1;
934 
935  ifr.ifr_mtu = mtu;
936 
937  if (ioctl(sockfd, SIOCSIFMTU, &ifr) == -1)
938  return -1;
939 
940  ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
941 
942  if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) == -1)
943  return -1;
944 
945  // set mask
946 
947  struct ifreq ifr_mask;
948  struct sockaddr_in* sai_mask = (struct sockaddr_in*)&ifr_mask.ifr_addr;
949  memset(&ifr_mask, 0, sizeof(ifr_mask));
950  strncpy(ifr_mask.ifr_name, dev, IFNAMSIZ);
951 
952  sai_mask->sin_family = AF_INET;
953  sai_mask->sin_port = 0;
954 
955  if (cidr_prefix > IPV4_ADDRESS_BITS)
956  return -1;
957 
958  sai_mask->sin_addr.s_addr = htonl(0xFFFFFFFF - ((1 << (IPV4_ADDRESS_BITS - cidr_prefix)) - 1));
959 
960  if (ioctl(sockfd, SIOCSIFNETMASK, &ifr_mask) == -1)
961  return -1;
962 
963  close(sockfd);
964 
965  return 0;
966 }
Base class provided for users to generate applications that participate in the Goby publish/subscribe...
Definition: application.h:49
void set_name(const std::string &s)
Set the name of the application that the logger is serving.
Definition: flex_ostream.h:67
void startup(const protobuf::MACConfig &cfg)
Starts the MAC with given configuration.
Definition: mac_manager.cpp:65
google::protobuf::uint32 uint32
an unsigned 32 bit integer
void do_work()
Allows the MAC timer to do its work. Does not block. If you prefer more control you can directly cont...
Definition: mac_manager.h:76
const int BROADCAST_ID
special modem id for the broadcast destination - no one is assigned this address. Analogous to 192...
provides an API to the goby-acomms MAC library. MACManager is essentially a std::list<protobuf::Modem...
Definition: mac_manager.h:51
void connect(Signal *signal, Slot slot)
connect a signal to a slot (e.g. function pointer)
Definition: connect.h:36
common::FlexOstream glog
Access the Goby logger through this object.
The global namespace for the Goby project.
boost::signals2::signal< void(const protobuf::ModemTransmission &m)> signal_initiate_transmission
Signals when it is time for this platform to begin transmission of an acoustic message at the start o...
Definition: mac_manager.h:91
void bind(ModemDriverBase &driver, QueueManager &queue_manager)
binds the driver link-layer callbacks to the QueueManager
Definition: bind.h:43
simple exception class for goby applications
Definition: exception.h:32
const int QUERY_DESTINATION_ID
special modem id used internally to goby-acomms for indicating that the MAC layer (amac) is agnostic ...