DTNTunnel.cc

Go to the documentation of this file.
00001 /*
00002  *    Copyright 2004-2006 Intel Corporation
00003  * 
00004  *    Licensed under the Apache License, Version 2.0 (the "License");
00005  *    you may not use this file except in compliance with the License.
00006  *    You may obtain a copy of the License at
00007  * 
00008  *        http://www.apache.org/licenses/LICENSE-2.0
00009  * 
00010  *    Unless required by applicable law or agreed to in writing, software
00011  *    distributed under the License is distributed on an "AS IS" BASIS,
00012  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  *    See the License for the specific language governing permissions and
00014  *    limitations under the License.
00015  */
00016 
00017 
00018 #include <stdio.h>
00019 #include <unistd.h>
00020 #include <errno.h>
00021 #include <strings.h>
00022 #include <stdlib.h>
00023 #include <sys/time.h>
00024 #include <time.h>
00025 #include <sys/socket.h>
00026 
00027 #include "dtn_api.h"
00028 #include "APIEndpointIDOpt.h"
00029 
00030 #include <oasys/util/Getopt.h>
00031 
00032 #include "DTNTunnel.h"
00033 #include "TCPTunnel.h"
00034 #include "UDPTunnel.h"
00035 
00036 namespace dtntunnel {
00037 
00038 template <>
00039 DTNTunnel* oasys::Singleton<DTNTunnel>::instance_ = 0;
00040 
00041 //----------------------------------------------------------------------
00042 DTNTunnel::DTNTunnel()
00043     : Logger("DTNTunnel", "/dtntunnel"),
00044       loglevelstr_(""),
00045       loglevel_(LOG_DEFAULT_THRESHOLD),
00046       logfile_("-"),
00047       send_lock_("/dtntunnel", oasys::Mutex::TYPE_RECURSIVE, true),
00048       listen_(false),
00049       custody_(false),
00050       expiration_(600),
00051       tcp_(false),
00052       udp_(false),
00053       local_addr_(INADDR_ANY),
00054       local_port_(0),
00055       remote_addr_(INADDR_NONE),
00056       remote_port_(0),
00057       delay_(1000),
00058       max_size_(32 * 1024)
00059 {
00060     memset(&local_eid_, 0, sizeof(local_eid_));
00061     memset(&dest_eid_,  0, sizeof(dest_eid_));
00062 }
00063 
00064 //----------------------------------------------------------------------
00065 void
00066 DTNTunnel::get_options(int argc, char* argv[])
00067 {
00068     oasys::Getopt opts;
00069     
00070     opts.addopt(
00071         new oasys::StringOpt('o', "output", &logfile_, "<output>",
00072                              "file name for error logging output "
00073                              "(- indicates stdout)"));
00074 
00075     opts.addopt(
00076         new oasys::StringOpt('l', NULL, &loglevelstr_, "<level>",
00077                              "default log level [debug|warn|info|crit]"));
00078 
00079     opts.addopt(
00080         new oasys::BoolOpt('L', "listen", &listen_,
00081                            "run in listen mode for incoming CONN bundles"));
00082     
00083     opts.addopt(
00084         new oasys::BoolOpt('c', "custody", &custody_, "use custody transfer"));
00085     
00086     opts.addopt(
00087         new oasys::UIntOpt('e', "expiration", &expiration_, "<secs>",
00088                            "expiration time"));
00089     
00090     opts.addopt(
00091         new oasys::BoolOpt('t', "tcp", &tcp_,
00092                            "proxy for TCP connections"));
00093     
00094     opts.addopt(
00095         new oasys::BoolOpt('u', "udp", &udp_,
00096                            "proxy for UDP traffic"));
00097     
00098     bool dest_eid_set = false;
00099     opts.addopt(
00100         new dtn::APIEndpointIDOpt('d', "dest_eid", &dest_eid_, "<eid>",
00101                                   "destination endpoint id", &dest_eid_set));
00102     
00103     opts.addopt(
00104         new dtn::APIEndpointIDOpt("local_eid_override", &local_eid_, "<eid>",
00105                                   "local endpoint id"));
00106     
00107     opts.addopt(
00108         new oasys::InAddrOpt("laddr", &local_addr_, "<addr>",
00109                              "local address to listen on"));
00110     
00111     opts.addopt(
00112         new oasys::UInt16Opt("lport", &local_port_, "<port>",
00113                              "local port to listen on"));
00114     
00115     opts.addopt(
00116         new oasys::InAddrOpt("rhost", &remote_addr_, "<addr>",
00117                              "remote host/address to proxy for"));
00118     
00119     opts.addopt(
00120         new oasys::UInt16Opt("rport", &remote_port_, "<port>",
00121                              "remote port to proxy"));
00122     
00123     opts.addopt(
00124         new oasys::UIntOpt('D', "delay", &delay_, "<millisecs>",
00125                            "nagle delay in msecs for stream transports (e.g. tcp)"));
00126     
00127     opts.addopt(
00128         new oasys::UIntOpt('z', "max_size", &max_size_, "<bytes>",
00129                            "maximum bundle size for stream transports (e.g. tcp)"));
00130     
00131     int remainder = opts.getopt(argv[0], argc, argv);
00132     if (remainder != argc) {
00133         fprintf(stderr, "invalid argument '%s'\n", argv[remainder]);
00134  usage:
00135         opts.usage(argv[0]);
00136         exit(1);
00137     }
00138     
00139 #define CHECK_OPT(_condition, _err) \
00140     if ((_condition)) { \
00141         fprintf(stderr, "error: " _err "\n"); \
00142         goto usage; \
00143     }
00144     
00145     //
00146     // TODO?:  couldn't we use these in listen mode to over-ride
00147     // what is provided at the sender? -kfall.  Also: mcast for udp?
00148     //
00149     if (listen_) {
00150         CHECK_OPT(dest_eid_set, "setting destination eid is "
00151                   "meaningless in listen mode");
00152         CHECK_OPT((tcp_ != false) || (udp_ != false), "setting tcp or udp is "
00153                   "meaningless in listen mode");
00154         CHECK_OPT(local_port_  != 0,  "setting local port is "
00155                   "meaningless in listen mode");
00156         CHECK_OPT(remote_addr_ != INADDR_NONE, "setting remote host is "
00157                   "meaningless in listen mode");
00158         CHECK_OPT(remote_port_ != 0,  "setting remote port is "
00159                   "meaningless in listen mode");
00160     } else {
00161         CHECK_OPT(!dest_eid_set, "must set destination eid "
00162                   "or be in listen mode");
00163         CHECK_OPT((tcp_ == false) && (udp_ == false),
00164                   "must set either tcp or udp mode");
00165         CHECK_OPT((tcp_ != false) && (udp_ != false),
00166                   "cannot set both tcp and udp mode");
00167         CHECK_OPT(local_addr_  == INADDR_NONE, "local addr is invalid");
00168         CHECK_OPT(local_port_  == 0,  "must set local port");
00169         CHECK_OPT(remote_addr_ == INADDR_NONE, "must set remote host");
00170         CHECK_OPT(remote_port_ == 0,  "must set remote port");
00171     }
00172     
00173 #undef CHECK_OPT
00174 }
00175 
00176 //----------------------------------------------------------------------
00177 void
00178 DTNTunnel::init_log()
00179 {
00180     // Parse the debugging level argument
00181     if (loglevelstr_.length() == 0) 
00182     {
00183         loglevel_ = oasys::LOG_NOTICE;
00184     }
00185     else 
00186     {
00187         loglevel_ = oasys::str2level(loglevelstr_.c_str());
00188         if (loglevel_ == oasys::LOG_INVALID) 
00189         {
00190             fprintf(stderr, "invalid level value '%s' for -l option, "
00191                     "expected debug | info | warning | error | crit\n",
00192                     loglevelstr_.c_str());
00193             exit(1);
00194         }
00195     }
00196     oasys::Log::init(logfile_.c_str(), loglevel_, "", "~/.dtndebug");
00197 }
00198 
00199 //----------------------------------------------------------------------
00200 void
00201 DTNTunnel::init_tunnel()
00202 {
00203     tcptunnel_ = new TCPTunnel();
00204     udptunnel_ = new UDPTunnel();
00205 
00206     if (!listen_) {
00207         if (tcp_) {
00208             tcptunnel_->add_listener(local_addr_, local_port_,
00209                                      remote_addr_, remote_port_);
00210         } else if (udp_) {
00211             udptunnel_->add_listener(local_addr_, local_port_,
00212                                      remote_addr_, remote_port_);
00213         }
00214     }
00215 }
00216 
00217 //----------------------------------------------------------------------
00218 void
00219 DTNTunnel::init_registration()
00220 {
00221     int err = dtn_open(&recv_handle_);
00222     if (err != DTN_SUCCESS) {
00223         log_crit("can't open recv handle to daemon: %s",
00224                  dtn_strerror(err));
00225         exit(1);
00226     }
00227     
00228     err = dtn_open(&send_handle_);
00229     if (err != DTN_SUCCESS) {
00230         log_crit("can't open send handle to daemon: %s",
00231                  dtn_strerror(err));
00232         exit(1);
00233     }
00234 
00235     if (local_eid_.uri[0] == '\0') {
00236         err = dtn_build_local_eid(recv_handle_, &local_eid_, "dtntunnel");
00237         if (err != DTN_SUCCESS) {
00238             log_crit("can't build local eid: %s",
00239                      dtn_strerror(dtn_errno(recv_handle_)));
00240             exit(1);
00241         }
00242     }
00243 
00244     log_debug("using local endpoint id %s", local_eid_.uri);
00245 
00246     u_int32_t regid;
00247     err = dtn_find_registration(recv_handle_, &local_eid_, &regid);
00248     if (err == 0) {
00249         log_notice("found existing registration id %d, calling dtn_bind",
00250                    regid);
00251         
00252         err = dtn_bind(recv_handle_, regid);
00253         if (err != 0) {
00254             log_crit("error in dtn_bind: %s", 
00255                      dtn_strerror(dtn_errno(recv_handle_)));
00256             exit(1);
00257         }
00258     } else if (dtn_errno(recv_handle_) == DTN_ENOTFOUND) {
00259         dtn_reg_info_t reginfo;
00260         memset(&reginfo, 0, sizeof(reginfo));
00261         dtn_copy_eid(&reginfo.endpoint, &local_eid_);
00262         reginfo.failure_action = DTN_REG_DEFER;
00263         reginfo.expiration     = 60 * 60 * 24; // 1 day
00264 
00265         err = dtn_register(recv_handle_, &reginfo, &regid);
00266         if (err != 0) {
00267             log_crit("error in dtn_register: %s",
00268                      dtn_strerror(dtn_errno(recv_handle_)));
00269             exit(1);
00270         }
00271     } else {
00272         log_crit("error in dtn_find_registration: %s",
00273                  dtn_strerror(dtn_errno(recv_handle_)));
00274         exit(1);
00275     }
00276 }
00277 
00278 //----------------------------------------------------------------------
00279 int
00280 DTNTunnel::send_bundle(dtn::APIBundle* bundle, dtn_endpoint_id_t* dest_eid)
00281 {
00282     // lock to coordinate access to the send_handle_ from multiple
00283     // client threads
00284     oasys::ScopeLock l(&send_lock_, "DTNTunnel::send_bundle");
00285     
00286     dtn_bundle_spec_t spec;
00287     memset(&spec, 0, sizeof(spec));
00288     dtn_copy_eid(&spec.source, &local_eid_);
00289     dtn_copy_eid(&spec.dest,   dest_eid);
00290     spec.priority   = COS_NORMAL;
00291     spec.dopts      = DOPTS_NONE;
00292     if (custody_) {
00293         spec.dopts |= DOPTS_CUSTODY;
00294     }
00295     spec.expiration = expiration_;
00296 
00297     dtn_bundle_payload_t payload;
00298     memset(&payload, 0, sizeof(payload));
00299 
00300     int err = dtn_set_payload(&payload,
00301                               DTN_PAYLOAD_MEM,
00302                               bundle->payload_.buf(),
00303                               bundle->payload_.len());
00304     if (err != 0) {
00305         log_err("error setting payload: %s",
00306                 dtn_strerror(dtn_errno(recv_handle_)));
00307         return err;
00308     }
00309     
00310     dtn_bundle_id_t bundle_id;
00311     memset(&bundle_id, 0, sizeof(bundle_id));
00312 
00313     err = dtn_send(send_handle_, &spec, &payload, &bundle_id);
00314 
00315     if (err != 0) {
00316         return dtn_errno(send_handle_);
00317     }
00318     
00319     return DTN_SUCCESS;
00320 }
00321 
00322 //----------------------------------------------------------------------
00323 int
00324 DTNTunnel::handle_bundle(dtn_bundle_spec_t* spec,
00325                          dtn_bundle_payload_t* payload)
00326 {
00327     dtn::APIBundle* b = new dtn::APIBundle();
00328     b->spec_ = *spec;
00329     ASSERT(payload->location == DTN_PAYLOAD_MEM);
00330 
00331     int len = payload->buf.buf_len;
00332 
00333     if (len < (int)sizeof(BundleHeader)) {
00334         log_err("too short bundle: len %d < sizeof bundle header", len);
00335         delete b;
00336         return -1;
00337     }
00338     
00339     char* dst = b->payload_.buf(len);
00340     memcpy(dst, payload->buf.buf_val, len);
00341     b->payload_.set_len(len);
00342     
00343     BundleHeader* hdr = (BundleHeader*)dst;
00344     
00345     switch (hdr->protocol_) {
00346     case IPPROTO_UDP: udptunnel_->handle_bundle(b); break;
00347     case IPPROTO_TCP: tcptunnel_->handle_bundle(b); break;
00348     default:
00349         log_err("unknown protocol %d in %d byte tunnel bundle",
00350                 hdr->protocol_, len);
00351         delete b;
00352         return -1;
00353     }
00354 
00355     return 0;
00356 }
00357 
00358 //----------------------------------------------------------------------
00359 int
00360 DTNTunnel::main(int argc, char* argv[])
00361 {
00362     get_options(argc, argv);
00363     init_log();
00364     log_notice("DTNTunnel starting up...");
00365 
00366     init_tunnel();
00367     init_registration();
00368 
00369     log_debug("DTNTunnel starting receive loop...");
00370     
00371     while (1) {
00372         dtn_bundle_spec_t    spec;
00373         dtn_bundle_payload_t payload;
00374 
00375         memset(&spec,    0, sizeof(spec));
00376         memset(&payload, 0, sizeof(payload));
00377     
00378         log_debug("calling dtn_recv...");
00379         int err = dtn_recv(recv_handle_, &spec, DTN_PAYLOAD_MEM, &payload,
00380                            DTN_TIMEOUT_INF);
00381         if (err != 0) {
00382             log_err("error in dtn_recv: %s",
00383                     dtn_strerror(dtn_errno(recv_handle_)));
00384             break;
00385         }
00386 
00387         log_info("got %d byte bundle", payload.buf.buf_len);
00388 
00389         handle_bundle(&spec, &payload);
00390 
00391         dtn_free_payload(&payload);
00392     }
00393 
00394     dtn_close(recv_handle_);
00395     dtn_close(send_handle_);
00396     
00397     return 0;
00398 }
00399 
00400 } // namespace dtntunnel
00401 
00402 int
00403 main(int argc, char** argv)
00404 {
00405     dtntunnel::DTNTunnel::create();
00406     dtntunnel::DTNTunnel::instance()->main(argc, argv);
00407 }

Generated on Thu Jun 7 12:54:27 2007 for DTN Reference Implementation by  doxygen 1.5.1