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