1 /*
2 * Copyright (c) 1998,1999,2000
3 * Traakan, Inc., Los Altos, CA
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice unmodified, this list of conditions, and the following
11 * disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 /*
30 * Project: NDMJOB
31 * Ident: $Id: $
32 *
33 * Description:
34 *
35 *
36 * ndmis_connect_status transitions
37 *
38 * Event relative to ------ before ------ ------ after -------
39 * "mine" end point MINE PEER REMO MINE PEER REMO
40 * ====================================================================
41 * LISTEN/LOCAL IDLE IDLE IDLE LISTEN IDLE EXCLUDE
42 * LISTEN/TCP IDLE IDLE IDLE LISTEN REMOTE LISTEN
43 *
44 * CONNECT/LOCAL IDLE LISTEN EXCLUDE CONN'ED ACC'ED EXCLUDE
45 * CONNECT/TCP IDLE IDLE IDLE CONN'ED REMOTE CONN'ED
46 *
47 * tcp_accept() LISTEN REMOTE LISTEN ACC'ED REMOTE ACC'ED
48 *
49 */
50
51
52 #if 0
53 DATA TAPE
54 END ========== LOCAL ============ END
55 POINT POINT
56 REMOTE
57 #endif
58
59
60 #include "ndmagents.h"
61
62
ndmis_reinit_remote(struct ndm_session * sess)63 int ndmis_reinit_remote(struct ndm_session* sess)
64 {
65 struct ndm_image_stream* is = sess->plumb.image_stream;
66 struct ndm_tape_agent* ta = sess->tape_acb;
67
68 NDMOS_MACRO_ZEROFILL(&is->remote);
69
70 ndmchan_initialize(&is->remote.listen_chan, "image-stream-listen");
71 ndmchan_initialize(&is->remote.sanity_chan, "image-stream-sanity");
72 ndmchan_initialize(&is->chan, "image-stream");
73 if (!is->buf) {
74 is->buflen = ta->mover_state.record_size;
75 is->buf = NDMOS_API_MALLOC(is->buflen);
76 if (!is->buf) { return -1; }
77 NDMOS_MACRO_ZEROFILL_SIZE(is->buf, is->buflen);
78 }
79 ndmchan_setbuf(&is->chan, is->buf, is->buflen);
80
81 return 0;
82 }
83
84
85 /*
86 * Initialization and Cleanup
87 ****************************************************************
88 */
89
90 /* Initialize -- Set data structure to know value, ignore current value */
ndmis_initialize(struct ndm_session * sess)91 int ndmis_initialize(struct ndm_session* sess)
92 {
93 sess->plumb.image_stream = NDMOS_API_MALLOC(sizeof(struct ndm_image_stream));
94 if (!sess->plumb.image_stream) return -1;
95 NDMOS_MACRO_ZEROFILL(sess->plumb.image_stream);
96 NDMOS_MACRO_ZEROFILL(&sess->plumb.image_stream->chan);
97
98 ndmis_reinit_remote(sess);
99
100 sess->plumb.image_stream->data_ep.name = "DATA";
101 sess->plumb.image_stream->tape_ep.name = "TAPE";
102
103 return 0;
104 }
105
106 /* Commission -- Get agent ready. Entire session has been initialize()d */
ndmis_commission(struct ndm_session * sess)107 int ndmis_commission(struct ndm_session* sess) { return 0; }
108
109 /* Decommission -- Discard agent */
ndmis_decommission(struct ndm_session * sess)110 int ndmis_decommission(struct ndm_session* sess) { return 0; }
111
112 /* Destroy -- Destroy agent */
ndmis_destroy(struct ndm_session * sess)113 int ndmis_destroy(struct ndm_session* sess)
114 {
115 if (!sess->plumb.image_stream) { return 0; }
116
117 if (sess->plumb.image_stream->buf) {
118 NDMOS_API_FREE(sess->plumb.image_stream->buf);
119 }
120 NDMOS_API_FREE(sess->plumb.image_stream);
121 sess->plumb.image_stream = NULL;
122
123 return 0;
124 }
125
126 /* Belay -- Cancel partially issued activation/start */
ndmis_belay(struct ndm_session * sess)127 int ndmis_belay(struct ndm_session* sess) { return 0; }
128
129
130 /*
131 * Semantic actions -- called from ndma_dispatch()
132 ****************************************************************
133 */
134
ndmis_audit_data_listen(struct ndm_session * sess,ndmp9_addr_type addr_type,char * reason)135 ndmp9_error ndmis_audit_data_listen(struct ndm_session* sess,
136 ndmp9_addr_type addr_type,
137 char* reason)
138 {
139 struct ndm_image_stream* is;
140 struct ndmis_end_point* mine_ep;
141 struct ndmis_end_point* peer_ep;
142
143 /*
144 * We are about to start using an Image Stream so allocate it.
145 * Only do this when not allocated yet.
146 */
147 if (!sess->plumb.image_stream) {
148 if (ndmis_initialize(sess)) { return NDMP9_NO_MEM_ERR; }
149 }
150
151 is = sess->plumb.image_stream;
152 mine_ep = &is->data_ep;
153 peer_ep = &is->tape_ep;
154
155 return ndmis_audit_ep_listen(sess, addr_type, reason, mine_ep, peer_ep);
156 }
157
ndmis_audit_tape_listen(struct ndm_session * sess,ndmp9_addr_type addr_type,char * reason)158 ndmp9_error ndmis_audit_tape_listen(struct ndm_session* sess,
159 ndmp9_addr_type addr_type,
160 char* reason)
161 {
162 struct ndm_image_stream* is;
163 struct ndmis_end_point* mine_ep;
164 struct ndmis_end_point* peer_ep;
165
166 /*
167 * We are about to start using an Image Stream so allocate it.
168 * Only do this when not allocated yet.
169 */
170 if (!sess->plumb.image_stream) {
171 if (ndmis_initialize(sess)) { return NDMP9_NO_MEM_ERR; }
172 }
173
174 is = sess->plumb.image_stream;
175 mine_ep = &is->tape_ep;
176 peer_ep = &is->data_ep;
177
178 return ndmis_audit_ep_listen(sess, addr_type, reason, mine_ep, peer_ep);
179 }
180
ndmis_audit_data_connect(struct ndm_session * sess,ndmp9_addr_type addr_type,char * reason)181 ndmp9_error ndmis_audit_data_connect(struct ndm_session* sess,
182 ndmp9_addr_type addr_type,
183 char* reason)
184 {
185 struct ndm_image_stream* is;
186 struct ndmis_end_point* mine_ep;
187 struct ndmis_end_point* peer_ep;
188
189 /*
190 * We are about to start using an Image Stream so allocate it.
191 * Only do this when not allocated yet.
192 */
193 if (!sess->plumb.image_stream) {
194 if (ndmis_initialize(sess)) { return NDMP9_NO_MEM_ERR; }
195 }
196
197 is = sess->plumb.image_stream;
198 mine_ep = &is->data_ep;
199 peer_ep = &is->tape_ep;
200
201 return ndmis_audit_ep_listen(sess, addr_type, reason, mine_ep, peer_ep);
202 }
203
ndmis_audit_tape_connect(struct ndm_session * sess,ndmp9_addr_type addr_type,char * reason)204 ndmp9_error ndmis_audit_tape_connect(struct ndm_session* sess,
205 ndmp9_addr_type addr_type,
206 char* reason)
207 {
208 struct ndm_image_stream* is;
209 struct ndmis_end_point* mine_ep;
210 struct ndmis_end_point* peer_ep;
211
212 /*
213 * We are about to start using an Image Stream so allocate it.
214 * Only do this when not allocated yet.
215 */
216 if (!sess->plumb.image_stream) {
217 if (ndmis_initialize(sess)) { return NDMP9_NO_MEM_ERR; }
218 }
219
220 is = sess->plumb.image_stream;
221 mine_ep = &is->tape_ep;
222 peer_ep = &is->data_ep;
223
224 return ndmis_audit_ep_listen(sess, addr_type, reason, mine_ep, peer_ep);
225 }
226
ndmis_data_listen(struct ndm_session * sess,ndmp9_addr_type addr_type,ndmp9_addr * ret_addr,char * reason)227 ndmp9_error ndmis_data_listen(struct ndm_session* sess,
228 ndmp9_addr_type addr_type,
229 ndmp9_addr* ret_addr,
230 char* reason)
231 {
232 struct ndm_image_stream* is = sess->plumb.image_stream;
233 struct ndmis_end_point* mine_ep = &is->data_ep;
234 struct ndmis_end_point* peer_ep = &is->tape_ep;
235
236 return ndmis_ep_listen(sess, addr_type, ret_addr, reason, mine_ep, peer_ep);
237 }
238
ndmis_tape_listen(struct ndm_session * sess,ndmp9_addr_type addr_type,ndmp9_addr * ret_addr,char * reason)239 ndmp9_error ndmis_tape_listen(struct ndm_session* sess,
240 ndmp9_addr_type addr_type,
241 ndmp9_addr* ret_addr,
242 char* reason)
243 {
244 struct ndm_image_stream* is = sess->plumb.image_stream;
245 struct ndmis_end_point* mine_ep = &is->tape_ep;
246 struct ndmis_end_point* peer_ep = &is->data_ep;
247
248 return ndmis_ep_listen(sess, addr_type, ret_addr, reason, mine_ep, peer_ep);
249 }
250
ndmis_data_connect(struct ndm_session * sess,ndmp9_addr * addr,char * reason)251 ndmp9_error ndmis_data_connect(struct ndm_session* sess,
252 ndmp9_addr* addr,
253 char* reason)
254 {
255 struct ndm_image_stream* is = sess->plumb.image_stream;
256 struct ndmis_end_point* mine_ep = &is->data_ep;
257 struct ndmis_end_point* peer_ep = &is->tape_ep;
258 ndmp9_error error;
259
260 error = ndmis_ep_connect(sess, addr, reason, mine_ep, peer_ep);
261 #ifndef NDMOS_OPTION_NO_TAPE_AGENT
262 if (error == NDMP9_NO_ERR) {
263 if (peer_ep->connect_status == NDMIS_CONN_ACCEPTED &&
264 peer_ep->addr_type == NDMP9_ADDR_LOCAL) {
265 ndmta_quantum(sess);
266 }
267 }
268 #endif /* !NDMOS_OPTION_NO_TAPE_AGENT */
269 return error;
270 }
271
ndmis_tape_connect(struct ndm_session * sess,ndmp9_addr * addr,char * reason)272 ndmp9_error ndmis_tape_connect(struct ndm_session* sess,
273 ndmp9_addr* addr,
274 char* reason)
275 {
276 struct ndm_image_stream* is = sess->plumb.image_stream;
277 struct ndmis_end_point* mine_ep = &is->tape_ep;
278 struct ndmis_end_point* peer_ep = &is->data_ep;
279
280 return ndmis_ep_connect(sess, addr, reason, mine_ep, peer_ep);
281 }
282
ndmis_ep_start(struct ndm_session * sess,int chan_mode,struct ndmis_end_point * mine_ep,struct ndmis_end_point * peer_ep)283 int ndmis_ep_start(struct ndm_session* sess,
284 int chan_mode,
285 struct ndmis_end_point* mine_ep,
286 struct ndmis_end_point* peer_ep)
287 {
288 struct ndm_image_stream* is = sess->plumb.image_stream;
289
290 if (mine_ep->connect_status != NDMIS_CONN_CONNECTED &&
291 mine_ep->connect_status != NDMIS_CONN_ACCEPTED) {
292 return -1;
293 }
294
295 if (mine_ep->transfer_mode != NDMCHAN_MODE_IDLE) { return -2; }
296
297 if (mine_ep->addr_type == NDMP9_ADDR_LOCAL) {
298 ndmchan_start_resident(&is->chan);
299 if (chan_mode == NDMCHAN_MODE_WRITE) {
300 peer_ep->transfer_mode = NDMCHAN_MODE_READ;
301 } else {
302 peer_ep->transfer_mode = NDMCHAN_MODE_WRITE;
303 }
304 } else if (chan_mode == NDMCHAN_MODE_WRITE) {
305 ndmchan_pending_to_write(&is->chan);
306 } else if (chan_mode == NDMCHAN_MODE_READ) {
307 ndmchan_pending_to_read(&is->chan);
308 } else {
309 return -3;
310 }
311
312 mine_ep->transfer_mode = chan_mode;
313
314 return 0;
315 }
316
ndmis_data_start(struct ndm_session * sess,int chan_mode)317 int ndmis_data_start(struct ndm_session* sess, int chan_mode)
318 {
319 struct ndm_image_stream* is = sess->plumb.image_stream;
320 struct ndmis_end_point* mine_ep = &is->data_ep;
321 struct ndmis_end_point* peer_ep = &is->tape_ep;
322
323 return ndmis_ep_start(sess, chan_mode, mine_ep, peer_ep);
324 }
325
ndmis_tape_start(struct ndm_session * sess,int chan_mode)326 int ndmis_tape_start(struct ndm_session* sess, int chan_mode)
327 {
328 struct ndm_image_stream* is = sess->plumb.image_stream;
329 struct ndmis_end_point* mine_ep = &is->tape_ep;
330 struct ndmis_end_point* peer_ep = &is->data_ep;
331
332 return ndmis_ep_start(sess, chan_mode, mine_ep, peer_ep);
333 }
334
ndmis_data_close(struct ndm_session * sess)335 int ndmis_data_close(struct ndm_session* sess)
336 {
337 struct ndm_image_stream* is = sess->plumb.image_stream;
338
339 if (is) {
340 return ndmis_ep_close(sess, &is->data_ep, &is->tape_ep);
341 } else {
342 return 0;
343 }
344 }
345
ndmis_tape_close(struct ndm_session * sess)346 int ndmis_tape_close(struct ndm_session* sess)
347 {
348 struct ndm_image_stream* is = sess->plumb.image_stream;
349
350 if (is) {
351 return ndmis_ep_close(sess, &is->tape_ep, &is->data_ep);
352 } else {
353 return 0;
354 }
355 }
356
357
358 /*
359 * Quantum -- get a bit of work done
360 ****************************************************************
361 */
362
ndmis_quantum(struct ndm_session * sess)363 int ndmis_quantum(struct ndm_session* sess)
364 {
365 struct ndm_image_stream* is = sess->plumb.image_stream;
366 struct ndmis_end_point* mine_ep;
367 int rc;
368
369 if (is->remote.connect_status != NDMIS_CONN_LISTEN)
370 return 0; /* did nothing */
371
372 if (!is->remote.listen_chan.ready) return 0; /* did nothing */
373
374 /* now this is going to get hard */
375
376 if (is->data_ep.connect_status == NDMIS_CONN_LISTEN) {
377 mine_ep = &is->data_ep;
378 /* assert (is->tape_ep.connect_status == NDMIS_CONN_REMOTE); */
379 } else if (is->tape_ep.connect_status == NDMIS_CONN_LISTEN) {
380 mine_ep = &is->tape_ep;
381 /* assert (is->data_ep.connect_status == NDMIS_CONN_REMOTE); */
382 } else {
383 assert(0);
384 return -1;
385 }
386
387 rc = ndmis_tcp_accept(sess);
388 if (rc == 0) {
389 mine_ep->connect_status = NDMIS_CONN_ACCEPTED;
390 is->remote.connect_status = NDMIS_CONN_ACCEPTED;
391 } else {
392 mine_ep->connect_status = NDMIS_CONN_BOTCHED;
393 is->remote.connect_status = NDMIS_CONN_BOTCHED;
394 }
395
396 return 1; /* did something */
397 }
398
399
400 /*
401 * ndmis_end_point oriented helper routines
402 ****************************************************************
403 */
404
ndmis_audit_ep_listen(struct ndm_session * sess,ndmp9_addr_type addr_type,char * reason,struct ndmis_end_point * mine_ep,struct ndmis_end_point * peer_ep)405 ndmp9_error ndmis_audit_ep_listen(struct ndm_session* sess,
406 ndmp9_addr_type addr_type,
407 char* reason,
408 struct ndmis_end_point* mine_ep,
409 struct ndmis_end_point* peer_ep)
410 {
411 ndmp9_error error = NDMP9_NO_ERR;
412 char* reason_end;
413
414 sprintf(reason, "IS %s_LISTEN: ", mine_ep->name);
415 reason_end = reason;
416 while (*reason_end) reason_end++;
417
418 if (mine_ep->connect_status != NDMIS_CONN_IDLE) {
419 sprintf(reason_end, "%s not idle", mine_ep->name);
420 error = NDMP9_ILLEGAL_STATE_ERR;
421 goto out;
422 }
423 if (peer_ep->connect_status != NDMIS_CONN_IDLE) {
424 sprintf(reason_end, "%s not idle", peer_ep->name);
425 error = NDMP9_ILLEGAL_STATE_ERR;
426 goto out;
427 }
428
429 switch (addr_type) {
430 case NDMP9_ADDR_LOCAL:
431 break;
432
433 case NDMP9_ADDR_TCP:
434 break;
435
436 default:
437 strcpy(reason_end, "unknown addr_type");
438 error = NDMP9_ILLEGAL_ARGS_ERR;
439 goto out;
440 }
441
442 out:
443 if (error == NDMP9_NO_ERR)
444 strcpy(reason_end, "OK");
445 else
446 ndmalogf(sess, 0, 2, "listen %s messy mcs=%d pcs=%d", mine_ep->name,
447 mine_ep->connect_status, peer_ep->connect_status);
448
449 return error;
450 }
451
ndmis_audit_ep_connect(struct ndm_session * sess,ndmp9_addr_type addr_type,char * reason,struct ndmis_end_point * mine_ep,struct ndmis_end_point * peer_ep)452 ndmp9_error ndmis_audit_ep_connect(struct ndm_session* sess,
453 ndmp9_addr_type addr_type,
454 char* reason,
455 struct ndmis_end_point* mine_ep,
456 struct ndmis_end_point* peer_ep)
457 {
458 ndmp9_error error = NDMP9_NO_ERR;
459 char* reason_end;
460
461 sprintf(reason, "IS %s_CONNECT: ", mine_ep->name);
462 reason_end = reason;
463 while (*reason_end) reason_end++;
464
465 if (mine_ep->connect_status != NDMIS_CONN_IDLE) {
466 sprintf(reason_end, "%s not idle", mine_ep->name);
467 error = NDMP9_ILLEGAL_STATE_ERR;
468 goto out;
469 }
470
471 switch (addr_type) {
472 case NDMP9_ADDR_LOCAL:
473 if (peer_ep->connect_status != NDMIS_CONN_LISTEN) {
474 sprintf(reason_end, "LOCAL %s not LISTEN", peer_ep->name);
475 error = NDMP9_ILLEGAL_STATE_ERR;
476 goto out;
477 }
478 if (peer_ep->addr_type != NDMP9_ADDR_LOCAL) {
479 sprintf(reason_end, "LOCAL %s not LOCAL", peer_ep->name);
480 error = NDMP9_ILLEGAL_STATE_ERR;
481 goto out;
482 }
483 break;
484
485 case NDMP9_ADDR_TCP:
486 if (peer_ep->connect_status != NDMIS_CONN_IDLE) {
487 sprintf(reason_end, "LOCAL %s not IDLE", peer_ep->name);
488 error = NDMP9_ILLEGAL_STATE_ERR;
489 goto out;
490 }
491 break;
492
493 default:
494 strcpy(reason_end, "unknown addr_type");
495 error = NDMP9_ILLEGAL_ARGS_ERR;
496 goto out;
497 }
498
499 out:
500 if (error == NDMP9_NO_ERR) strcpy(reason_end, "OK");
501
502 return error;
503 }
504
505
ndmis_ep_listen(struct ndm_session * sess,ndmp9_addr_type addr_type,ndmp9_addr * ret_addr,char * reason,struct ndmis_end_point * mine_ep,struct ndmis_end_point * peer_ep)506 ndmp9_error ndmis_ep_listen(struct ndm_session* sess,
507 ndmp9_addr_type addr_type,
508 ndmp9_addr* ret_addr,
509 char* reason,
510 struct ndmis_end_point* mine_ep,
511 struct ndmis_end_point* peer_ep)
512 {
513 struct ndm_image_stream* is = sess->plumb.image_stream;
514 char* reason_end;
515 ndmp9_error error;
516
517 error = ndmis_audit_ep_listen(sess, addr_type, reason, mine_ep, peer_ep);
518 if (error != NDMP9_NO_ERR) return error;
519
520 reason_end = reason;
521 while (*reason_end && *reason_end != ':') reason_end++;
522 *reason_end++ = ':';
523 *reason_end++ = ' ';
524 *reason_end = 0;
525
526 NDMOS_MACRO_ZEROFILL(ret_addr);
527 ret_addr->addr_type = addr_type;
528
529 switch (addr_type) {
530 case NDMP9_ADDR_LOCAL:
531 mine_ep->addr_type = NDMP9_ADDR_LOCAL;
532 mine_ep->connect_status = NDMIS_CONN_LISTEN;
533 is->remote.connect_status = NDMIS_CONN_EXCLUDE;
534 break;
535
536 case NDMP9_ADDR_TCP:
537 if (ndmis_tcp_listen(sess, ret_addr) != 0) {
538 strcpy(reason_end, "TCP listen() failed");
539 error = NDMP9_CONNECT_ERR;
540 goto out;
541 }
542 mine_ep->addr_type = NDMP9_ADDR_TCP;
543 mine_ep->connect_status = NDMIS_CONN_LISTEN;
544 peer_ep->connect_status = NDMIS_CONN_REMOTE;
545 break;
546
547 default:
548 reason = "unknown addr_type (bad)";
549 error = NDMP9_ILLEGAL_ARGS_ERR;
550 goto out;
551 }
552
553 out:
554 if (error == NDMP9_NO_ERR) strcpy(reason_end, "OK");
555
556 return error;
557 }
558
ndmis_ep_connect(struct ndm_session * sess,ndmp9_addr * addr,char * reason,struct ndmis_end_point * mine_ep,struct ndmis_end_point * peer_ep)559 ndmp9_error ndmis_ep_connect(struct ndm_session* sess,
560 ndmp9_addr* addr,
561 char* reason,
562 struct ndmis_end_point* mine_ep,
563 struct ndmis_end_point* peer_ep)
564 {
565 struct ndm_image_stream* is = sess->plumb.image_stream;
566 ndmp9_addr_type addr_type = addr->addr_type;
567 char* reason_end;
568 ndmp9_error error;
569
570 error = ndmis_audit_ep_connect(sess, addr_type, reason, mine_ep, peer_ep);
571 if (error != NDMP9_NO_ERR) return error;
572
573 reason_end = reason;
574 while (*reason_end && *reason_end != ':') reason_end++;
575 *reason_end++ = ':';
576 *reason_end++ = ' ';
577 *reason_end = 0;
578
579 switch (addr_type) {
580 case NDMP9_ADDR_LOCAL:
581 mine_ep->addr_type = NDMP9_ADDR_LOCAL;
582 mine_ep->connect_status = NDMIS_CONN_CONNECTED;
583 peer_ep->connect_status = NDMIS_CONN_ACCEPTED;
584 is->remote.connect_status = NDMIS_CONN_EXCLUDE;
585 break;
586
587 case NDMP9_ADDR_TCP:
588 if (ndmis_tcp_connect(sess, addr) != 0) {
589 strcpy(reason_end, "TCP connect() failed");
590 error = NDMP9_CONNECT_ERR;
591 goto out;
592 }
593 mine_ep->addr_type = NDMP9_ADDR_TCP;
594 mine_ep->connect_status = NDMIS_CONN_CONNECTED;
595 peer_ep->connect_status = NDMIS_CONN_REMOTE;
596 break;
597
598 default:
599 reason = "unknown addr_type (bad)";
600 error = NDMP9_ILLEGAL_ARGS_ERR;
601 goto out;
602 }
603
604 out:
605 return error;
606 }
607
ndmis_ep_close(struct ndm_session * sess,struct ndmis_end_point * mine_ep,struct ndmis_end_point * peer_ep)608 int ndmis_ep_close(struct ndm_session* sess,
609 struct ndmis_end_point* mine_ep,
610 struct ndmis_end_point* peer_ep)
611 {
612 struct ndm_image_stream* is = sess->plumb.image_stream;
613 char* save_name = mine_ep->name;
614
615 switch (mine_ep->connect_status) {
616 case NDMIS_CONN_IDLE:
617 return 0;
618
619 case NDMIS_CONN_BOTCHED:
620 case NDMIS_CONN_REMOTE:
621 case NDMIS_CONN_EXCLUDE:
622 goto messy;
623
624 case NDMIS_CONN_LISTEN:
625 switch (mine_ep->addr_type) {
626 default:
627 goto messy;
628
629 case NDMP9_ADDR_LOCAL:
630 ndmis_reinit_remote(sess);
631 if (peer_ep->connect_status != NDMIS_CONN_IDLE) goto messy;
632 break;
633
634 case NDMP9_ADDR_TCP:
635 ndmis_tcp_close(sess);
636 if (peer_ep->connect_status != NDMIS_CONN_REMOTE) goto messy;
637 peer_ep->connect_status = NDMIS_CONN_IDLE;
638 break;
639 }
640 break;
641
642 case NDMIS_CONN_ACCEPTED:
643 switch (mine_ep->addr_type) {
644 default:
645 goto messy;
646
647 case NDMP9_ADDR_LOCAL:
648 if (peer_ep->connect_status != NDMIS_CONN_CONNECTED) goto messy;
649 peer_ep->connect_status = NDMIS_CONN_DISCONNECTED;
650 is->chan.eof = 1;
651 if (mine_ep->transfer_mode == NDMCHAN_MODE_READ)
652 is->chan.error = 1; /* EPIPE */
653 break;
654
655 case NDMP9_ADDR_TCP:
656 ndmis_tcp_close(sess);
657 if (peer_ep->connect_status != NDMIS_CONN_REMOTE) goto messy;
658 peer_ep->connect_status = NDMIS_CONN_IDLE;
659 break;
660 }
661 break;
662
663 case NDMIS_CONN_CONNECTED:
664 switch (mine_ep->addr_type) {
665 default:
666 goto messy;
667
668 case NDMP9_ADDR_LOCAL:
669 if (peer_ep->connect_status != NDMIS_CONN_ACCEPTED) goto messy;
670 peer_ep->connect_status = NDMIS_CONN_DISCONNECTED;
671 is->chan.eof = 1;
672 if (mine_ep->transfer_mode == NDMCHAN_MODE_READ)
673 is->chan.error = 1; /* EPIPE */
674 break;
675
676 case NDMP9_ADDR_TCP:
677 ndmis_tcp_close(sess);
678 if (peer_ep->connect_status != NDMIS_CONN_REMOTE) goto messy;
679 peer_ep->connect_status = NDMIS_CONN_IDLE;
680 break;
681 }
682 break;
683
684 case NDMIS_CONN_DISCONNECTED: /* peer close()d first */
685 ndmis_reinit_remote(sess);
686 break;
687
688 case NDMIS_CONN_CLOSED:
689 goto messy;
690 }
691
692 NDMOS_MACRO_ZEROFILL(mine_ep);
693 mine_ep->name = save_name;
694
695 return 0;
696
697 messy:
698 ndmalogf(sess, 0, 2, "close %s messy mcs=%d pcs=%d", mine_ep->name,
699 mine_ep->connect_status, peer_ep->connect_status);
700 NDMOS_MACRO_ZEROFILL(mine_ep);
701 mine_ep->name = save_name;
702 return -1;
703 }
704
705 /*
706 * ADDR_TCP helper routines
707 ****************************************************************
708 */
709
710
711 /*
712 * ndmis_tcp_listen()
713 *
714 * The tricky part of listen()ing is determining the IP
715 * address to offer, which ultimately will be used by
716 * the other (peer) side for connect()ing.
717 *
718 * We can't just bind() with INADDR_ANY (0's) because
719 * that results in a local socket with INADDR_ANY, and
720 * any inbound connection to the right port will be
721 * accept()ed by the networking system. That doesn't
722 * help us here, though, because we have to have a
723 * real IP address to offer. INADDR_ANY ain't sufficient.
724 *
725 * There is also the issue of systems with multiple
726 * network connections. We of course would like to
727 * use the network data link that is most advantageous
728 * on both sides. This may vary from job run to job
729 * run, and so any method of specifying just one
730 * is IP address ain't sufficient.
731 *
732 * The approach here uses the existing control connections,
733 * which normally precede the image stream connection,
734 * as cues for which IP address to use. So, for example,
735 * if a TAPE or DATA host has four network connections,
736 * the CONTROL agent can coax them to use a specific one
737 * of the four by connecting to the IP address of the
738 * network wanted for the image stream.
739 *
740 * If the clever rules don't work out, the fallback is to
741 * look up the host name. Right now we use ndmhost_lookup()
742 * of sess->local_info.host_name because both must work
743 * before things would progress to this point.
744 */
745
ndmis_tcp_listen(struct ndm_session * sess,struct ndmp9_addr * listen_addr)746 int ndmis_tcp_listen(struct ndm_session* sess, struct ndmp9_addr* listen_addr)
747 {
748 struct ndm_image_stream* is = sess->plumb.image_stream;
749 ndmp9_tcp_addr* tcp_addr = &listen_addr->ndmp9_addr_u.tcp_addr;
750 struct ndmconn* conn;
751 struct sockaddr c_sa;
752 struct sockaddr l_sa;
753 struct sockaddr_in* sin;
754 socklen_t len;
755 int listen_sock = -1;
756 char* what = "???";
757
758 /*
759 * Get the IP address thru which the CONTROL agent connected
760 * to this session. The CONTROL agent may influence the
761 * network used for the image-stream on multi-homed hosts
762 * simply by connecting to the prefered IP address.
763 */
764 what = "determine-conn";
765 conn = sess->plumb.control;
766 if (!conn || conn->conn_type != NDMCONN_TYPE_REMOTE) {
767 /*
768 * If CONTROL is resident, try the other
769 * control connections in hopes of finding
770 * a clue about what IP address to offer.
771 */
772 conn = sess->plumb.data;
773 if (!conn || conn->conn_type != NDMCONN_TYPE_REMOTE) {
774 conn = sess->plumb.tape;
775 if (!conn || conn->conn_type != NDMCONN_TYPE_REMOTE) { conn = 0; }
776 }
777 }
778
779 if (conn) {
780 /*
781 * We found a connection to use for determining
782 * what IP address to offer.
783 */
784 len = sizeof c_sa;
785 if (getsockname(ndmconn_fileno(conn), &c_sa, &len) < 0) {
786 /* we'll try the fallback rules */
787 conn = 0;
788 }
789 }
790
791 if (!conn) {
792 /*
793 * For whatever reason, we can't determine a good
794 * IP address from the connections. Try the boring
795 * fallback rules.
796 */
797 ndmos_sync_config_info(sess);
798
799 sin = (struct sockaddr_in*)&c_sa;
800
801 what = "ndmhost_lookup";
802 if (ndmhost_lookup(sess->config_info->hostname, sin) != 0) goto fail;
803 }
804
805 /* c_sa is a sockaddr_in for the IP address to use */
806
807 what = "socket";
808 listen_sock = socket(AF_INET, SOCK_STREAM, 0);
809 if (listen_sock < 0) goto fail;
810
811 /* could bind() to more restrictive addr based on c_sa */
812 NDMOS_MACRO_SET_SOCKADDR(&l_sa, 0, 0);
813 what = "bind";
814 if (bind(listen_sock, &l_sa, sizeof l_sa) < 0) goto fail;
815
816 what = "listen";
817 if (listen(listen_sock, 1) < 0) goto fail;
818
819 ndmos_condition_listen_socket(sess, listen_sock);
820
821 /* Get the port */
822 what = "getsockname-listen";
823 len = sizeof l_sa;
824 if (getsockname(listen_sock, &l_sa, &len) < 0) goto fail;
825
826 /*
827 * Fill in the return address
828 */
829
830 listen_addr->addr_type = NDMP9_ADDR_TCP;
831 tcp_addr = &listen_addr->ndmp9_addr_u.tcp_addr;
832
833 /* IP addr from CONTROL connection, or where ever c_sa came from */
834 sin = (struct sockaddr_in*)&c_sa;
835 tcp_addr->ip_addr = ntohl(sin->sin_addr.s_addr);
836
837 /* port from the bind() and getsockname() above */
838 sin = (struct sockaddr_in*)&l_sa;
839 tcp_addr->port = ntohs(sin->sin_port);
840
841 /*
842 * Start the listen channel
843 */
844
845 ndmchan_start_listen(&is->remote.listen_chan, listen_sock);
846
847 is->remote.connect_status = NDMIS_CONN_LISTEN;
848 is->remote.listen_addr = *listen_addr;
849
850 return 0;
851
852 fail:
853 ndmalogf(sess, 0, 2, "ndmis_tcp_listen(): %s failed", what);
854 if (listen_sock >= 0) close(listen_sock);
855
856 return -1;
857 }
858
ndmis_tcp_accept(struct ndm_session * sess)859 int ndmis_tcp_accept(struct ndm_session* sess)
860 {
861 struct ndm_image_stream* is = sess->plumb.image_stream;
862 char* what = "???";
863 ndmp9_tcp_addr* tcp_addr;
864 struct sockaddr sa;
865 struct sockaddr_in* sin = (struct sockaddr_in*)&sa;
866 socklen_t len;
867 int accept_sock = -1;
868
869 what = "remote-conn-stat";
870 if (is->remote.connect_status != NDMIS_CONN_LISTEN) goto fail;
871
872 what = "remote-list-ready";
873 if (!is->remote.listen_chan.ready) goto fail;
874
875 what = "accept";
876 len = sizeof sa;
877 accept_sock = accept(is->remote.listen_chan.fd, &sa, &len);
878
879 ndmchan_cleanup(&is->remote.listen_chan);
880
881 if (accept_sock < 0) {
882 is->remote.connect_status = NDMIS_CONN_BOTCHED;
883 goto fail;
884 }
885
886 /* write what we know, ndmis...addrs() will update if possible */
887 is->remote.peer_addr.addr_type = NDMP9_ADDR_TCP;
888 tcp_addr = &is->remote.peer_addr.ndmp9_addr_u.tcp_addr;
889 tcp_addr->ip_addr = ntohl(sin->sin_addr.s_addr);
890 tcp_addr->port = ntohs(sin->sin_port);
891
892 ndmis_tcp_green_light(sess, accept_sock, NDMIS_CONN_ACCEPTED);
893
894 return 0;
895
896 fail:
897 ndmalogf(sess, 0, 2, "ndmis_tcp_accept(): %s failed", what);
898
899 return -1;
900 }
901
ndmis_tcp_connect(struct ndm_session * sess,struct ndmp9_addr * connect_addr)902 int ndmis_tcp_connect(struct ndm_session* sess, struct ndmp9_addr* connect_addr)
903 {
904 struct ndm_image_stream* is = sess->plumb.image_stream;
905 ndmp9_tcp_addr* tcp_addr = &connect_addr->ndmp9_addr_u.tcp_addr;
906 char* what = "???";
907 struct sockaddr sa;
908 int connect_sock;
909
910 NDMOS_MACRO_SET_SOCKADDR(&sa, tcp_addr->ip_addr, tcp_addr->port);
911
912 what = "socket";
913 connect_sock = socket(AF_INET, SOCK_STREAM, 0);
914 if (connect_sock < 0) goto fail;
915
916 what = "connect";
917 if (connect(connect_sock, &sa, sizeof sa) < 0) goto fail;
918
919
920 /* write what we know, ndmis...addrs() will update if possible */
921 is->remote.peer_addr = *connect_addr;
922
923 ndmis_tcp_green_light(sess, connect_sock, NDMIS_CONN_CONNECTED);
924
925 return 0;
926
927 fail:
928 ndmalogf(sess, 0, 2, "ndmis_tcp_connect(): %s failed", what);
929 if (connect_sock >= 0) close(connect_sock);
930
931 return -1;
932 }
933
ndmis_tcp_green_light(struct ndm_session * sess,int sock,ndmis_connect_status new_status)934 int ndmis_tcp_green_light(struct ndm_session* sess,
935 int sock,
936 ndmis_connect_status new_status)
937 {
938 struct ndm_image_stream* is = sess->plumb.image_stream;
939
940 ndmos_condition_image_stream_socket(sess, sock);
941
942 ndmchan_start_pending(&is->chan, sock);
943
944 is->remote.connect_status = new_status;
945
946 ndmis_tcp_get_local_and_peer_addrs(sess);
947
948 return 0;
949 }
950
ndmis_tcp_close(struct ndm_session * sess)951 int ndmis_tcp_close(struct ndm_session* sess)
952 {
953 struct ndm_image_stream* is = sess->plumb.image_stream;
954
955 switch (is->remote.connect_status) {
956 case NDMIS_CONN_LISTEN:
957 ndmchan_cleanup(&is->remote.listen_chan);
958 break;
959
960 case NDMIS_CONN_CONNECTED:
961 case NDMIS_CONN_ACCEPTED:
962 ndmchan_cleanup(&is->chan);
963 break;
964
965 default:
966 break;
967 }
968
969 ndmis_reinit_remote(sess);
970
971 return 0;
972 }
973
ndmis_tcp_get_local_and_peer_addrs(struct ndm_session * sess)974 int ndmis_tcp_get_local_and_peer_addrs(struct ndm_session* sess)
975 {
976 struct ndm_image_stream* is = sess->plumb.image_stream;
977 char* what = "???";
978 struct sockaddr sa;
979 struct sockaddr_in* sin = (struct sockaddr_in*)&sa;
980 ndmp9_tcp_addr* tcp_addr;
981 socklen_t len;
982 int rc = 0;
983
984 len = sizeof sa;
985 what = "getpeername";
986 if (getpeername(is->chan.fd, &sa, &len) < 0) {
987 /* this is best effort */
988 ndmalogf(sess, 0, 2, "ndmis_tcp..._addrs(): %s failed", what);
989 rc = -1;
990 } else {
991 is->remote.peer_addr.addr_type = NDMP9_ADDR_TCP;
992 tcp_addr = &is->remote.peer_addr.ndmp9_addr_u.tcp_addr;
993 tcp_addr->ip_addr = ntohl(sin->sin_addr.s_addr);
994 tcp_addr->port = ntohs(sin->sin_port);
995 }
996
997 len = sizeof sa;
998 what = "getsockname";
999 if (getsockname(is->chan.fd, &sa, &len) < 0) {
1000 /* this is best effort */
1001 ndmalogf(sess, 0, 2, "ndmis_tcp..._addrs(): %s failed", what);
1002 rc = -1;
1003 } else {
1004 is->remote.local_addr.addr_type = NDMP9_ADDR_TCP;
1005 tcp_addr = &is->remote.peer_addr.ndmp9_addr_u.tcp_addr;
1006 tcp_addr->ip_addr = ntohl(sin->sin_addr.s_addr);
1007 tcp_addr->port = ntohs(sin->sin_port);
1008 }
1009
1010 return rc;
1011 }
1012