00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
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
00147
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
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_, ®id);
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(®info, 0, sizeof(reginfo));
00261 dtn_copy_eid(®info.endpoint, &local_eid_);
00262 reginfo.failure_action = DTN_REG_DEFER;
00263 reginfo.expiration = 60 * 60 * 24;
00264
00265 err = dtn_register(recv_handle_, ®info, ®id);
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
00283
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 }
00401
00402 int
00403 main(int argc, char** argv)
00404 {
00405 dtntunnel::DTNTunnel::create();
00406 dtntunnel::DTNTunnel::instance()->main(argc, argv);
00407 }