udp_layer.cc 7.25 KB
Newer Older
garciay's avatar
garciay committed
#include <netdb.h>
#include <unistd.h>
#include <chrono>

#include <Port.hh>

#include "loggers.hh"

garciay's avatar
garciay committed
#include "udp_layer_factory.hh"

Yann Garcia's avatar
Yann Garcia committed
#include <sys/ioctl.h>
#include <net/if.h>

YannGarcia's avatar
YannGarcia committed
udp_layer::udp_layer(const std::string & p_type, const std::string & param) : layer(p_type), PORT(p_type.c_str()), _params(), _saddr{0}, _daddr{0}, _reuse_incoming_source_adddress(false), _fd(-1), _time_key("udp_layer::Handle_Fd_Event_Readable") {
  loggers::get_instance().log(">>> udp_layer::udp_layer: %s, %s", to_string().c_str(), param.c_str());
  // Setup parameters
  params::convert(_params, param);
  params::const_iterator it = _params.find("src_ip");
  if (it == _params.cend()) {
    _params.insert(std::pair<std::string, std::string>(std::string("src_ip"), "127.0.0.1"));
  }
  it = _params.find("src_port");
  if (it == _params.cend()) {
garciay's avatar
garciay committed
    _params.insert(std::pair<std::string, std::string>(std::string("src_port"), "0")); // Dynamic binding requested
  }
  it = _params.find("dst_ip");
  if (it == _params.cend()) {
    _params.insert(std::pair<std::string, std::string>(std::string("dst_ip"), "127.0.0.1"));
  }
  it = _params.find("dst_port");
  if (it == _params.cend()) {
    _params.insert(std::pair<std::string, std::string>(std::string("dst_port"), "12345"));
YannGarcia's avatar
YannGarcia committed
  it = _params.find("reuse_incoming_source_adddress");
  if (it != _params.cend()) {
    _reuse_incoming_source_adddress = (boolean)(it->second.compare("1") == 0);
  }
  loggers::get_instance().log("udp_layer::udp_layer: _reuse_incoming_source_adddress: %d", _reuse_incoming_source_adddress);
garciay's avatar
garciay committed
  // Initialize the socket
  _saddr.sin_family = AF_INET;
Yann Garcia's avatar
Yann Garcia committed
  _saddr.sin_addr.s_addr = htonl(INADDR_ANY);
  loggers::get_instance().log("udp_layer::udp_layer: Port to listen=%d", std::atoi(_params["src_port"].c_str()));
Yann Garcia's avatar
Yann Garcia committed
  _saddr.sin_port = htons(std::atoi(_params["src_port"].c_str()));
garciay's avatar
garciay committed
  // Create socket
  _fd = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  if (_fd == -1) {
    loggers::get_instance().error("udp_layer::udp_layer: Failed to create socket");
  }
  loggers::get_instance().log("udp_layer::udp_layer: socket id: %d", _fd);
Yann Garcia's avatar
Yann Garcia committed
  int reuse = 1;
  if (::setsockopt(_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) {
garciay's avatar
garciay committed
    loggers::get_instance().warning("udp_layer::udp_layer: Failed to set SO_REUSEADDR");
  }
  // Bind it
Yann Garcia's avatar
Yann Garcia committed
  /*struct ifreq ifr;
  memset(&ifr, 0, sizeof(ifr));
  snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth1");
  if (setsockopt(_fd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr.ifr_name, strlen(ifr.ifr_name)) < 0) {
    close();
    loggers::get_instance().error("udp_layer::udp_layer: Failed to bind socket to %s", ifr.ifr_name);
  }
  loggers::get_instance().log("udp_layer::udp_layer: Bound to device %s", ifr.ifr_name);*/
garciay's avatar
garciay committed
  if(::bind(_fd, (struct sockaddr *)&_saddr, sizeof(_saddr)) < 0) {
    close();
    loggers::get_instance().error("udp_layer::udp_layer: Failed to bind socket");
  }
Yann Garcia's avatar
Yann Garcia committed
  loggers::get_instance().log("udp_layer::udp_layer: Bound on port %s", _params["src_port"].c_str());
garciay's avatar
garciay committed
  // Pass the device file handler to the polling procedure
  Handler_Add_Fd_Read(_fd);
  
  _daddr.sin_family = AF_INET;
Yann Garcia's avatar
Yann Garcia committed
  _daddr.sin_addr.s_addr = ::htonl(get_host_id(_params["dst_ip"]));
  _daddr.sin_port = ::htons(std::atoi(_params["dst_port"].c_str()));
garciay's avatar
garciay committed
  
}

udp_layer::~udp_layer() {
  loggers::get_instance().log(">>> udp_layer::~udp_layer");

  close();
}

void udp_layer::close() {
  loggers::get_instance().log(">>> udp_layer::close: %d", _fd);

  if (_fd != -1) {
    ::close(_fd);
    _fd = -1;
  }
}

void udp_layer::send_data(OCTETSTRING& data, params& params) {
  loggers::get_instance().log_msg(">>> udp_layer::send_data: ", data);
  
garciay's avatar
garciay committed
  int result = ::sendto(_fd, (const char*)static_cast<const unsigned char*>(data), data.lengthof(), 0, (struct sockaddr*)&_daddr, sizeof(_daddr));
garciay's avatar
garciay committed
  loggers::get_instance().log("udp_layer::send_data: #bytes sent: %d to %s:%d", result, ::inet_ntoa(_daddr.sin_addr), ntohs(_daddr.sin_port));
}

void udp_layer::receive_data(OCTETSTRING& data, params& params) {
  loggers::get_instance().log_msg(">>> udp_layer::receive_data: ", data);

  receive_to_all_layers(data, params);
}

garciay's avatar
garciay committed
void udp_layer::Handle_Fd_Event_Readable(int fd) {
  loggers::get_instance().log(">>> udp_layer::Handle_Fd_Event_Readable: %d", fd);

Yann Garcia's avatar
Yann Garcia committed
  unsigned char buffer[3072] = {0};
garciay's avatar
garciay committed
  struct sockaddr_in from = {0};
  socklen_t len = sizeof(struct sockaddr_in); // Length of sender's address
garciay's avatar
garciay committed
  params params;
Yann Garcia's avatar
Yann Garcia committed
  std::vector<unsigned char> acc;
  int result = ::recvfrom(_fd, buffer, 3072, 0, (struct sockaddr *)&from, &len);
  loggers::get_instance().log("udp_layer::Handle_Fd_Event_Readable: src_port = %s:%d, payload length = %d, errno = %d", ::inet_ntoa(from.sin_addr), ntohs(from.sin_port), result, errno);
  while ((result == 3072) && (errno == 0)) {
    std::copy((unsigned char*)buffer, (unsigned char*)((unsigned char*)buffer + result), std::back_inserter(acc));
    result = ::recvfrom(_fd, buffer, 3072, 0, (struct sockaddr *)&from, &len);
    loggers::get_instance().log("udp_layer::Handle_Fd_Event_Readable: src_port = %s:%d, payload length = %d, errno = %d", ::inet_ntoa(from.sin_addr), ntohs(from.sin_port), result, errno);
  } // End of 'while' statement
  if (errno < 0) {
    loggers::get_instance().warning("udp_layer::Handle_Fd_Event_Readable: Failed to read data, discard them: errno=%d", errno);
garciay's avatar
garciay committed
    return;
Yann Garcia's avatar
Yann Garcia committed
  } else {
    std::copy((unsigned char*)buffer, (unsigned char*)((unsigned char*)buffer + result), std::back_inserter(acc));
YannGarcia's avatar
YannGarcia committed
    if (_reuse_incoming_source_adddress) { // Reuse the incoming address/port for sending
      memcpy((void*)&_daddr, (const void*)&from, sizeof(struct sockaddr_in));
      loggers::get_instance().log("udp_layer::Handle_Fd_Event_Readable: New _daddr: %s:%d", ::inet_ntoa(_daddr.sin_addr), ntohs(_daddr.sin_port));
    }
Yann Garcia's avatar
Yann Garcia committed
  params.insert(std::pair<std::string, std::string>(std::string("timestamp"), std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count())));
garciay's avatar
garciay committed
  
  float duration;
  loggers::get_instance().set_start_time(_time_key);
Yann Garcia's avatar
Yann Garcia committed
  OCTETSTRING os(acc.size(), acc.data());
garciay's avatar
garciay committed
  receive_data(os, params); // TODO Check execution time for decoding operation
  loggers::get_instance().set_stop_time(_time_key, duration);
}

unsigned long udp_layer::get_host_id(const std::string& p_host_name) {
  loggers::get_instance().log(">>> udp_layer::get_host_id");

  if (p_host_name.empty()) {
garciay's avatar
garciay committed
    loggers::get_instance().warning("udp_layer::get_host_id: Wrong parameter");
garciay's avatar
garciay committed
    return INADDR_ANY;
  }
garciay's avatar
garciay committed
  unsigned long ip_addr = 0;
  if(p_host_name.compare("255.255.255.255") == 0) {
garciay's avatar
garciay committed
    loggers::get_instance().warning("udp_layer::get_host_id: Host ip is 255.255.255.255");
garciay's avatar
garciay committed
    ip_addr = 0xffffffff;
  } else {
    in_addr_t addr = ::inet_addr(p_host_name.c_str());
garciay's avatar
garciay committed
    if (addr != (in_addr_t)-1) { // host name in XX:XX:XX:XX form
garciay's avatar
garciay committed
      ip_addr = addr;
    } else { // host name in domain.com form
      struct hostent* hptr;
      if ((hptr = ::gethostbyname(p_host_name.c_str())) == 0) {
        close();
        loggers::get_instance().error("udp_layer::get_host_id: Invalid host name: %s", p_host_name.c_str());
      }
      ip_addr = *((unsigned long*)hptr->h_addr_list[0]);
garciay's avatar
garciay committed
  loggers::get_instance().log("udp_layer::get_host_id: Host name: %s, Host address: %u", p_host_name.c_str(), ip_addr);
  
Yann Garcia's avatar
Yann Garcia committed
  return ::htonl(ip_addr);
}

udp_layer_factory udp_layer_factory::_f;