text: /*
   *
   * Copyright (C) 2011, 2015 Cisco Systems, Inc.
   * Copyright (C) 2015 CBA research group, Technical University of Catalonia.
   *
   * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
   *
   * 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.
   *
   */
  
  #include <signal.h>
  #include <sys/stat.h>
  #include <time.h>
  #include <unistd.h>
  
  
  #include "oor.h"
  #include "cmdline.h"
  #include "iface_list.h"
  #include "control/oor_control.h"
  #include "control/lisp_xtr.h"
  #include "control/lisp_ms.h"
  #include "data-plane/data-plane.h"
  #include "net_mgr/net_mgr.h"
  #include "lib/htable_ptrs.h"
  #include "lib/oor_log.h"
  #include "lib/nonces_table.h"
  #include "lib/sockets.h"
  #include "lib/timers.h"
  #include "lib/routing_tables_lib.h"
  #include "liblisp/liblisp.h"
  #include "lib/shash.h"
  #include "lib/generic_list.h"
  
  #ifdef VPP
  #include "lib/vpp_api/vpp_api.h"
  #endif
  
  #ifdef VPNAPI
  #include "oor_jni.h"
  #endif
  
  #ifdef __APPLE__
  #include <syslog.h>
  #include "lib/interfaces_lib.h"
  #else
  #include <linux/capability.h>
  #include <linux/netlink.h>
  #include <linux/rtnetlink.h>
  #include <sys/prctl.h>
  #endif
  
  #if !defined(ANDROID) && !defined(OPENWRT)
  #include "config/oor_api_internals.h"
  #endif
  #ifdef OPENWRT
  #include "config/oor_config_uci.h"
  #else
  #include "config/oor_config_confuse.h"
  #endif
  
  #ifdef ANDROID
  #define CAP_TO_MASK(x)      (1 << ((x) & 31))
  #endif
  
  // system calls - look to libc for function to system call mapping
  #ifndef __APPLE__
  extern int capset(cap_user_header_t header, cap_user_data_t data);
  extern int capget(cap_user_header_t header, const cap_user_data_t data);
  #endif
  
  /* config paramaters */
  char    *config_file                        = NULL;
  int      debug_level                        = -1;
  int      default_rloc_afi                   = AF_UNSPEC;
  int      daemonize                          = FALSE;
  
  uint32_t iseed                              = 0;  /* initial random number generator */
  
  /* various globals */
  pid_t  pid                                  = 0;    /* child pid */
  pid_t  sid                                  = 0;
  
  /* sockets (fds)  */
  int     ipv4_data_input_fd                  = -1;
  int     ipv6_data_input_fd                  = -1;
  int     netlink_fd                          = -1;
  
  sockmstr_t *smaster = NULL;
  oor_ctrl_dev_t *ctrl_dev;
  oor_ctrl_t *lctrl;
  
  #ifdef VPNAPI
  int oor_running;
  #endif
  
  #if !defined(ANDROID) && !defined(OPENWRT)
  /* OOR's API connection structure */
  oor_api_connection_t oor_api_connection;
  #endif
  
  htable_nonces_t *nonces_ht; //<uint64_t, oor_timer_t>
  htable_ptrs_t *ptrs_to_timers_ht; //<pointer, glist_t of timers>
  
  
  /*
   *  Check for superuser privileges
   */
  #if !defined(ANDROID) && !defined(__APPLE__)
  int
  check_capabilities()
  {
      struct __user_cap_header_struct cap_header;
      struct __user_cap_data_struct cap_data;
      
      cap_header.pid = getpid();
      cap_header.version = _LINUX_CAPABILITY_VERSION;
      
      /* Check if oor is already running: /var/run/oor.pid */
      
      if (capget(&cap_header, &cap_data) < 0)
      {
          OOR_LOG(LCRIT, "Could not retrieve capabilities");
          return BAD;
      }
      
      OOR_LOG(LDBG_1, "Rights: Effective [%u] Permitted  [%u]", cap_data.effective, cap_data.permitted);
      
      /* check for capabilities */
      if(  (cap_data.effective & CAP_TO_MASK(CAP_NET_ADMIN)) && (cap_data.effective & CAP_TO_MASK(CAP_NET_RAW))  )  {
      }
      else {
          OOR_LOG(LCRIT, "Insufficient rights, you need CAP_NET_ADMIN and CAP_NET_RAW. See README");
          return BAD;
      }
      
      return GOOD;
  }
  #else
  int
  check_capabilities()
  {
      if (geteuid() != 0) {
          OOR_LOG(LCRIT,"Running Open Overlay Router requires superuser privileges! Exiting...\n");
          return (BAD);
      }
      
      return (GOOD);
  }
  #endif
  
  
  void
  signal_handler(int sig) {
      switch (sig) {
          case SIGHUP:
              /* TODO: SIGHUP should trigger reloading the configuration file */
              OOR_LOG(LDBG_1, "Received SIGHUP signal.");
              break;
          case SIGTERM:
              /* SIGTERM is the default signal sent by 'kill'. Exit cleanly */
              OOR_LOG(LDBG_1, "Received SIGTERM signal. Cleaning up...");
              exit_cleanup();
              break;
          case SIGINT:
              /* SIGINT is sent by pressing Ctrl-C. Exit cleanly */
              OOR_LOG(LDBG_1, "Terminal interrupt. Cleaning up...");
              exit_cleanup();
              break;
          default:
              OOR_LOG(LDBG_1,"Unhandled signal (%d)", sig);
              exit(EXIT_FAILURE);
      }
  }
  
  /*
   *  exit_cleanup()
   *
   *  Close opened sockets and file descriptors
   */
  void
  exit_cleanup(void) {
      OOR_LOG(LDBG_2,"Exit Cleanup");
      
      // Order is important
      ctrl_destroy(lctrl);
      
      if (data_plane){
          data_plane->datap_uninit();
      }
      
      if (net_mgr){
          net_mgr->netm_uninit();
      }
      
      ifaces_destroy();
      
      sockmstr_destroy(smaster);
      
      oor_timers_destroy();
      
      htable_ptrs_destroy(ptrs_to_timers_ht);
      htable_nonces_destroy(nonces_ht);
      
      close_log_file();
  #ifndef VPNAPI
      OOR_LOG(LINF,"Exiting ...");
      exit(EXIT_SUCCESS);
  #else
      jni_uninit();
  #endif
  #ifdef VPP
      vpp_uninit_api();
  #endif
  }
  
  /*
   *  handle_oor_command_line --
   */
  static void
  handle_oor_command_line(int argc, char **argv)
  {
      struct gengetopt_args_info args_info;
      
      if (cmdline_parser(argc, argv, &args_info) != 0) {
          exit_cleanup();
      }
      
      if (args_info.config_file_given) {
          config_file = strdup(args_info.config_file_arg);
      }else{
  #ifdef OPENWRT
          config_file = strdup("/etc/config/oor");
  #else
          config_file = strdup("/etc/oor.conf");
  #endif
      }
      
      if (args_info.daemonize_given) {
          if (config_file[0] != '/'){
              OOR_LOG(LCRIT, "Couldn't find config file %s. If you are useing OOR in daemon mode, please indicate a full path file.", config_file);
          }
          daemonize = TRUE;
      }
      
      if (args_info.debug_given) {
          debug_level = args_info.debug_arg;
      }
      if (args_info.afi_given) {
          switch (args_info.afi_arg) {
              case 0: /* afi given = 4 */
                  default_rloc_afi = AF_INET;
                  break;
              case 1: /* afi given = 6 */
                  default_rloc_afi = AF_INET6;
                  break;
              default:
                  OOR_LOG(LINF, "AFI must be IPv4 (-a 4) or IPv6 (-a 6)\n");
                  break;
          }
      } else {
          default_rloc_afi = AF_UNSPEC;
      }
  #ifdef VPP
      default_rloc_afi = AF_INET;
      OOR_LOG(LINF, "VPP: Disabling IPv6 RLOCs\n");
  #endif
      
      cmdline_parser_free(&args_info);
  }
  
  static void
  demonize_start()
  {
      if (daemonize) {
          OOR_LOG(LDBG_1, "Starting the daemonizing process");
          if ((pid = fork()) < 0) {
              exit_cleanup();
          }
          umask(0);
          if (pid > 0){
              exit(EXIT_SUCCESS);
          }
          if ((sid = setsid()) < 0){
              exit_cleanup();
          }
          if ((chdir("/")) < 0){
              exit_cleanup();
          }
          close(STDIN_FILENO);
          close(STDOUT_FILENO);
          close(STDERR_FILENO);
      }
  }
  
  static void
  setup_signal_handlers()
  {
      signal(SIGHUP,  signal_handler);
      signal(SIGTERM, signal_handler);
      signal(SIGINT,  signal_handler);
      signal(SIGQUIT, signal_handler);
  }
  
  static int
  parse_config_file()
  {
      int err;
      err = handle_config_file();
      
      return (err);
  }
  
  static int
  initial_setup()
  {
      
      int selected = 0;
      
  #ifdef OPENWRT
      OOR_LOG(LINF,"Open Overlay Router %s compiled for openWRT\n", OOR_VERSION);
      selected ++;
  #endif
      
  #ifdef VPP
      OOR_LOG(LINF,"Open Overlay Router %s compiled for linux with VPP data plane\n", OOR_VERSION);
      selected ++;
  #endif
      
  #ifdef ANDROID
  #ifdef VPNAPI
      OOR_LOG(LINF,"Open Overlay Router %s compiled for not rooted Android\n", OOR_VERSION);
      selected ++;
  #else
      OOR_LOG(LINF,"Open Overlay Router %s compiled for rooted Android\n", OOR_VERSION);
      selected ++;
  #endif
  #endif
      
  #ifdef __APPLE__
      OOR_LOG(LINF,"Open Overlay Router %s compiled for iOS\n", OOR_VERSION);
      selected ++;
  #endif
      
      if (selected > 1){
          OOR_LOG(LERR,"Wrong platform selection");
          return (BAD);
      }else if (selected == 0){
          OOR_LOG(LINF,"Open Overlay Router %s compiled for linux\n", OOR_VERSION);
      }
      
  #if UINTPTR_MAX == 0xffffffff
      OOR_LOG(LDBG_1,"x32 system");
  #elif UINTPTR_MAX == 0xffffffffffffffff
      OOR_LOG(LDBG_1,"x64 system");
  #else
      OOR_LOG(LERR,"Unknow system. Please contact the Open Overlay Router team providing your hardware");
  #endif
      
  #if !defined(VPNAPI) && !defined(__APPLE__)
      if (check_capabilities() != GOOD){
          return (BAD);
      }
  #endif
      
      /* Initialize the random number generator  */
      iseed = (unsigned int) time(NULL);
      srandom(iseed);
      setup_signal_handlers();
      
      /* Initialize hash table that control timers */
      nonces_ht = htable_nonces_new();
      ptrs_to_timers_ht = htable_ptrs_new();
      
  #ifdef VPP
      if (vpp_is_enabled() == FALSE || vpp_init_api() != GOOD){
          return (BAD);
      }
  #endif
      return(GOOD);
  }
  
  #if !defined(VPNAPI) && !defined(__APPLE__)
  int
  main(int argc, char **argv)
  {
      oor_dev_type_e dev_type;
      tr_abstract_device *tunnel_router;
      
      if (initial_setup() != GOOD){
          exit(EXIT_SUCCESS);
      }
  
      handle_oor_command_line(argc, argv);
      
      /* Check config file exists */
      if (access( config_file, F_OK ) == -1 ) {
          OOR_LOG(LCRIT, "Couldn't find config file %s.", config_file);
          exit(EXIT_SUCCESS);
      }
  
      /* see if we need to daemonize, and if so, do it */
      demonize_start();
      
      /* Detect the data plane type */
      data_plane_select();
      net_mgr_select();
      
      /* create socket master, timer wheel, initialize interfaces */
      smaster = sockmstr_create();
      oor_timers_init();
      if (ifaces_init() != GOOD){
          exit_cleanup();
      }
      /* create control. Only one instance for now */
      if ((lctrl = ctrl_create())==NULL){
          exit_cleanup();
      }
      /* parse config and create ctrl_dev */
      if (parse_config_file() != GOOD){
          exit_cleanup();
      }
      dev_type = ctrl_dev_mode(ctrl_dev);
  #ifdef VPP
      if (dev_type != xTR_MODE){
          OOR_LOG(LERR, "VPP is only supported in xTR mode");
          exit_cleanup();
      }
  #endif
      if (dev_type == xTR_MODE || dev_type == RTR_MODE || dev_type == MN_MODE) {
          OOR_LOG(LDBG_2, "Configuring data plane");
          tunnel_router = lisp_tr_abstract_cast(ctrl_dev);
          if (data_plane->datap_init(dev_type,tr_encap_type(&tunnel_router->tr),tr_allowed_dst_eids(&tunnel_router->tr))!=GOOD){
              data_plane = NULL;
              exit_cleanup();
          }
          OOR_LOG(LDBG_1, "Data plane initialized");
      }
      
      /* The control should be initialized after data plane */
      if (ctrl_init(lctrl) != GOOD){
          exit_cleanup();
      }
      if (net_mgr->netm_init() != GOOD){
          exit_cleanup();
      }
      /* run lisp control device xtr/ms */
      ctrl_dev_run(ctrl_dev);
      
      OOR_LOG(LINF,"\n\n Open Overlay Router (%s): started... \n\n",OOR_VERSION);
      
  #if !defined(ANDROID) && !defined(OPENWRT)
      /* Initialize API for external access */
      oor_api_init_server(&oor_api_connection);
      
      for (;;) {
          sockmstr_wait_on_all_read(smaster);
          sockmstr_process_all(smaster);
          oor_api_loop(&oor_api_connection);
      }
  #else
      for (;;) {
          sockmstr_wait_on_all_read(smaster);
          sockmstr_process_all(smaster);
      }
  #endif
      
      /* event_loop returned: bad! */
      OOR_LOG(LINF, "Exiting...");
      exit_cleanup();
      return(0);
  }
  #endif
  
  #ifdef VPNAPI
  JNIEXPORT jint JNICALL Java_org_openoverlayrouter_noroot_OOR_1JNI_oor_1start
  (JNIEnv *env, jobject thisObj, jint vpn_tun_fd, jstring storage_path)
  {
      oor_dev_type_e dev_type;
      jintArray fd_list;
      uint32_t iseed = 0;  /* initial random number generator */
      pid_t pid = 0;    /* child pid */
      pid_t sid = 0;
      char log_file[1024];
      const char *path = NULL;
      tr_abstract_device *tunnel_router;
      memset (log_file,0,sizeof(char)*1024);
      initial_setup();
      jni_init(env,thisObj);
      /* create socket master, timer wheel, initialize interfaces */
      smaster = sockmstr_create();
      oor_timers_init();
      /* create control. Only one instance for now */
      lctrl = ctrl_create();
      
      /* Detect the data plane type */
      data_plane_select();
      net_mgr_select();
      if (ifaces_init()!=GOOD){
          exit_cleanup();
          close(vpn_tun_fd);
          return (BAD);
      }
      
      /** parse config and create ctrl_dev **/
      /* obtain the configuration file */
      path = (*env)->GetStringUTFChars(env, storage_path, 0);
      config_file = calloc(1024, sizeof(char));
      strcat(config_file,path);
      strcat(config_file,"oor.conf");
      strcat(log_file,path);
      strcat(log_file,"oor.log");
      (*env)->ReleaseStringUTFChars(env, storage_path, path);
      open_log_file(log_file);
      OOR_LOG(LDBG_2, "Default selected afi: %d",default_rloc_afi);
      if (parse_config_file()!=GOOD){
          exit_cleanup();
          close(vpn_tun_fd);
          return (BAD);
      }
      dev_type = ctrl_dev_mode(ctrl_dev);
      if (dev_type == xTR_MODE || dev_type == RTR_MODE || dev_type == MN_MODE) {
          OOR_LOG(LDBG_2, "Configuring data plane");
          tunnel_router = lisp_tr_abstract_cast(ctrl_dev);
          if (data_plane->datap_init(dev_type, tr_encap_type(&tunnel_router->tr), vpn_tun_fd) != GOOD){
              data_plane = NULL;
              return (BAD);
          }
          OOR_LOG(LDBG_1, "Data plane initialized");
          
      }
      ctrl_init(lctrl);
      if (net_mgr->netm_init() != GOOD){
          exit_cleanup();
      }
      return (GOOD);
  }
  
  JNIEXPORT void JNICALL Java_org_openoverlayrouter_noroot_OOR_1JNI_oor_1loop(JNIEnv * env, jclass cl)
  {
      oor_running = TRUE;
      ctrl_dev_run(ctrl_dev);
      
      /* EVENT LOOP */
      while (oor_running) {
          sockmstr_wait_on_all_read(smaster);
          sockmstr_process_all(smaster);
      }
      /* event_loop returned: bad! */
      exit_cleanup();
  }
  
  JNIEXPORT void JNICALL Java_org_openoverlayrouter_noroot_OOR_1JNI_oor_1exit(JNIEnv * env,
                                                                              jclass cl)
  {
      oor_running = false;
  }
  
  #endif
  
  #ifdef __APPLE__
  
  int oor_running;
  
  char *logPath;
  char *confPath;
  
  void setLogPath(const char *path) {
      logPath = (char *)path;
  }
  
  void setConfPath(const char *path) {
      confPath = (char *)path;
  }
  
  int oor_start() {
      
      oor_dev_type_e dev_type;
      char log_file[1024];
      tr_abstract_device *tunnel_router;
      memset (log_file,0,sizeof(char)*1024);
      initial_setup();
      /* create socket master, timer wheel, initialize interfaces */
      smaster = sockmstr_create();
      oor_timers_init();
      
      // create control. Only one instance for now
      lctrl = ctrl_create();
      
      // Detect the data plane type
      data_plane_select();
      net_mgr_select();
      if (ifaces_init()!=GOOD){
          exit_cleanup();
          return (BAD);
      }
      
      // parse config and create ctrl_dev
      // obtain the configuration file
      config_file = calloc(1024, sizeof(char));
      strcat(config_file,confPath);
      strcat(log_file,logPath);
      open_log_file(log_file);
      if (parse_config_file()!=GOOD){
          exit_cleanup();
          return (BAD);
      }
      
      dev_type = ctrl_dev_mode(ctrl_dev);
      if (dev_type == xTR_MODE || dev_type == RTR_MODE || dev_type == MN_MODE) {
          OOR_LOG(LDBG_2, "Configuring data plane");
          tunnel_router = lisp_tr_abstract_cast(ctrl_dev);
          if (data_plane->datap_init(dev_type, tr_encap_type(&tunnel_router->tr)) != GOOD){
              data_plane = NULL;
              return (BAD);
          }
          OOR_LOG(LDBG_1, "Data plane initialized");
          
      }
      
      ctrl_init(lctrl);
      
      if (net_mgr->netm_init() != GOOD){
          exit_cleanup();
      }
      return (GOOD);
  }
  
  void trigtimer() {
      
  }
  
  void oor_loop() {
      oor_running = TRUE;
      ctrl_dev_run(ctrl_dev);
      // EVENT LOOP
      while (oor_running) {
          sockmstr_wait_on_all_read(smaster);
          sockmstr_process_all(smaster);
      }
      // event_loop returned: bad!
      exit_cleanup();
      
  }
  
  void oor_stop() {
      oor_running = false;
      close_log_file();
  }
  
  #endif
  
  /*
   * Editor modelines
   *
   * vi: set shiftwidth=4 tabstop=4 expandtab:
   * :indentSize=4:tabSize=4:noTabs=true:
   */
  