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