text: /*
   * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The OpenAirInterface Software Alliance licenses this file to You under
   * the OAI Public License, Version 1.1  (the "License"); you may not use this
   * file except in compliance with the License. You may obtain a copy of the
   * License at
   *
   *      http://www.openairinterface.org/?page_id=698
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   *-------------------------------------------------------------------------------
   * For more information about the OpenAirInterface (OAI) Software Alliance:
   *      contact@openairinterface.org
   */
  
  /*! \file udp.hpp
    \brief
    \author Lionel Gauthier
    \company Eurecom
    \email: lionel.gauthier@eurecom.fr
  */
  #ifndef FILE_UDP_HPP_SEEN
  #define FILE_UDP_HPP_SEEN
  
  #include "conversions.hpp"
  #include "endpoint.hpp"
  #include "itti.hpp"
  #include "thread_sched.hpp"
  
  #include <arpa/inet.h>
  #include <inttypes.h>
  #include <sys/socket.h>
  
  #include <stdint.h>
  #include <iostream>
  #include <map>
  #include <memory>
  #include <string>
  #include <system_error>
  #include <thread>
  #include <utility>
  #include <vector>
  
  class udp_application {
   public:
    virtual void handle_receive(
        char* recv_buffer, const std::size_t bytes_transferred,
        const endpoint& r_endpoint);
    virtual void start_receive(
        udp_application* gtp_stack,
        const util::thread_sched_params& sched_params);
  };
  
  class udp_server {
   public:
    udp_server(const struct in_addr& address, const uint16_t port_num)
        : app_(nullptr), port_(port_num) {
      socket_ = create_socket(address, port_);
      if (socket_ > 0) {
        Logger::udp().debug(
            "udp_server::udp_server(%s:%d)", conv::toString(address).c_str(),
            port_);
        sa_family = AF_INET;
      } else {
        Logger::udp().error(
            "udp_server::udp_server(%s:%d)", conv::toString(address).c_str(),
            port_);
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
        throw std::system_error(
            socket_, std::generic_category(), "GTPV1-U socket creation failed!");
      }
    }
  
    udp_server(const struct in6_addr& address, const uint16_t port_num)
        : app_(nullptr), port_(port_num) {
      socket_ = create_socket(address, port_);
      if (socket_ > 0) {
        Logger::udp().debug(
            "udp_server::udp_server(%s:%d)", conv::toString(address).c_str(),
            port_);
        sa_family = AF_INET6;
      } else {
        Logger::udp().error(
            "udp_server::udp_server(%s:%d)", conv::toString(address).c_str(),
            port_);
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
        throw std::system_error(
            socket_, std::generic_category(), "GTPV1-U socket creation failed!");
      }
    }
  
    udp_server(const char* address, const uint16_t port_num)
        : app_(nullptr), port_(port_num) {
      socket_ = create_socket(address, port_);
      if (socket_ > 0) {
        Logger::udp().debug("udp_server::udp_server(%s:%d)", address, port_);
      } else {
        Logger::udp().error("udp_server::udp_server(%s:%d)", address, port_);
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
        throw std::system_error(
            socket_, std::generic_category(), "GTPV1-U socket creation failed!");
      }
    }
  
    ~udp_server() { close(socket_); }
  
    void udp_read_loop(const util::thread_sched_params& thread_sched_params);
  
    void async_send_to(
        const char* send_buffer, const ssize_t num_bytes,
        const endpoint& r_endpoint) {
      ssize_t bytes_written = sendto(
          socket_, send_buffer, num_bytes, 0,
          (struct sockaddr*) &r_endpoint.addr_storage,
          r_endpoint.addr_storage_len);
      if (bytes_written != num_bytes) {
        Logger::udp().error("sendto failed(%d:%s)\n", errno, strerror(errno));
      }
    }
  
    void async_send_to(
        const char* send_buffer, const ssize_t num_bytes,
        const struct sockaddr_in& r_endpoint) {
      ssize_t bytes_written = sendto(
          socket_, send_buffer, num_bytes, 0, (struct sockaddr*) &r_endpoint,
          sizeof(struct sockaddr_in));
      if (bytes_written != num_bytes) {
        Logger::udp().error("sendto failed(%d:%s)\n", errno, strerror(errno));
      }
    }
  
    void async_send_to(
        const char* send_buffer, const ssize_t num_bytes,
        const struct sockaddr_in6& r_endpoint) {
      ssize_t bytes_written = sendto(
          socket_, send_buffer, num_bytes, 0, (struct sockaddr*) &r_endpoint,
          sizeof(struct sockaddr_in6));
      if (bytes_written != num_bytes) {
        Logger::udp().error("sendto failed(%d:%s)\n", errno, strerror(errno));
      }
    }
  
    void start_receive(
        udp_application* gtp_stack,
        const util::thread_sched_params& sched_params);
  
   protected:
    int create_socket(const struct in_addr& address, const uint16_t port);
    int create_socket(const struct in6_addr& address, const uint16_t port);
    int create_socket(const char* address, const uint16_t port_num);
  
    // void handle_receive(const int& error, std::size_t bytes_transferred);
  
    static void handle_send(
        const char*, /*buffer*/
        const int& /*error*/, std::size_t /*bytes_transferred*/) {}
  
    udp_application* app_;
    std::thread thread_;
    int socket_;
    uint16_t port_;
    sa_family_t sa_family;
  #define UDP_RECV_BUFFER_SIZE 8192
    char recv_buffer_[UDP_RECV_BUFFER_SIZE];
  };
  
  #endif /* FILE_UDP_HPP_SEEN */
  