xref: /original-bsd/sys/netiso/tp_pcb.c (revision bafc759a)
1 /*-
2  * Copyright (c) 1991 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  *
7  *	@(#)tp_pcb.c	7.22 (Berkeley) 10/02/91
8  */
9 
10 /***********************************************************
11 				Copyright IBM Corporation 1987
12 
13                       All Rights Reserved
14 
15 Permission to use, copy, modify, and distribute this software and its
16 documentation for any purpose and without fee is hereby granted,
17 provided that the above copyright notice appear in all copies and that
18 both that copyright notice and this permission notice appear in
19 supporting documentation, and that the name of IBM not be
20 used in advertising or publicity pertaining to distribution of the
21 software without specific, written prior permission.
22 
23 IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
29 SOFTWARE.
30 
31 ******************************************************************/
32 
33 /*
34  * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
35  */
36 /*
37  * ARGO TP
38  *
39  * $Header: tp_pcb.c,v 5.4 88/11/18 17:28:24 nhall Exp $
40  * $Source: /usr/argo/sys/netiso/RCS/tp_pcb.c,v $
41  *
42  *
43  * This is the initialization and cleanup stuff -
44  * for the tp machine in general as well as  for the individual pcbs.
45  * tp_init() is called at system startup.  tp_attach() and tp_getref() are
46  * called when a socket is created.  tp_detach() and tp_freeref()
47  * are called during the closing stage and/or when the reference timer
48  * goes off.
49  * tp_soisdisconnecting() and tp_soisdisconnected() are tp-specific
50  * versions of soisconnect*
51  * and are called (obviously) during the closing phase.
52  *
53  */
54 
55 #include "param.h"
56 #include "systm.h"
57 #include "mbuf.h"
58 #include "socket.h"
59 #include "socketvar.h"
60 #include "domain.h"
61 #include "protosw.h"
62 #include "errno.h"
63 #include "time.h"
64 #include "argo_debug.h"
65 #include "tp_param.h"
66 #include "tp_timer.h"
67 #include "tp_ip.h"
68 #include "tp_stat.h"
69 #include "tp_pcb.h"
70 #include "tp_tpdu.h"
71 #include "tp_trace.h"
72 #include "tp_meas.h"
73 #include "tp_seq.h"
74 #include "tp_clnp.h"
75 
76 /* ticks are in units of:
77  * 500 nano-fortnights ;-) or
78  * 500 ms or
79  * 1/2 second
80  */
81 
82 struct tp_conn_param tp_conn_param[] = {
83 	/* ISO_CLNS: TP4 CONNECTION LESS */
84 	{
85 		TP_NRETRANS, 	/* short p_Nretrans;  */
86 		20,		/* 10 sec */ 	/* short p_dr_ticks;  */
87 
88 		20,		/* 10 sec */ 	/* short p_cc_ticks; */
89 		20,		/* 10 sec */ 	/* short p_dt_ticks; */
90 
91 		40,		/* 20 sec */ 	/* short p_x_ticks;	 */
92 		80,		/* 40 sec */ 	/* short p_cr_ticks;*/
93 
94 		240,	/* 2 min */ 	/* short p_keepalive_ticks;*/
95 		10,		/* 5 sec */ 	/* short p_sendack_ticks;  */
96 
97 		600,	/* 5 min */ 	/* short p_ref_ticks;	*/
98 		360,	/* 3 min */ 	/* short p_inact_ticks;	*/
99 
100 		(short) 100, 			/* short p_lcdtfract */
101 		(short) TP_SOCKBUFSIZE,	/* short p_winsize */
102 		TP_TPDUSIZE, 			/* u_char p_tpdusize */
103 
104 		TPACK_WINDOW, 			/* 4 bits p_ack_strat */
105 		TPRX_USE_CW | TPRX_FASTSTART,
106 								/* 4 bits p_rx_strat*/
107 		TP_CLASS_4 | TP_CLASS_0,/* 5 bits p_class */
108 		1,						/* 1 bit xtd format */
109 		1,						/* 1 bit xpd service */
110 		1,						/* 1 bit use_checksum */
111 		0,						/* 1 bit use net xpd */
112 		0,						/* 1 bit use rcc */
113 		0,						/* 1 bit use efc */
114 		1,						/* no disc indications */
115 		0,						/* don't change params */
116 		ISO_CLNS,				/* p_netservice */
117 	},
118 	/* IN_CLNS: TP4 CONNECTION LESS */
119 	{
120 		TP_NRETRANS, 	/* short p_Nretrans;  */
121 		20,		/* 10 sec */ 	/* short p_dr_ticks;  */
122 
123 		20,		/* 10 sec */ 	/* short p_cc_ticks; */
124 		20,		/* 10 sec */ 	/* short p_dt_ticks; */
125 
126 		40,		/* 20 sec */ 	/* short p_x_ticks;	 */
127 		80,		/* 40 sec */ 	/* short p_cr_ticks;*/
128 
129 		240,	/* 2 min */ 	/* short p_keepalive_ticks;*/
130 		10,		/* 5 sec */ 	/* short p_sendack_ticks;  */
131 
132 		600,	/* 5 min */ 	/* short p_ref_ticks;	*/
133 		360,	/* 3 min */ 	/* short p_inact_ticks;	*/
134 
135 		(short) 100, 			/* short p_lcdtfract */
136 		(short) TP_SOCKBUFSIZE,	/* short p_winsize */
137 		TP_TPDUSIZE, 			/* u_char p_tpdusize */
138 
139 		TPACK_WINDOW, 			/* 4 bits p_ack_strat */
140 		TPRX_USE_CW | TPRX_FASTSTART,
141 								/* 4 bits p_rx_strat*/
142 		TP_CLASS_4,				/* 5 bits p_class */
143 		1,						/* 1 bit xtd format */
144 		1,						/* 1 bit xpd service */
145 		1,						/* 1 bit use_checksum */
146 		0,						/* 1 bit use net xpd */
147 		0,						/* 1 bit use rcc */
148 		0,						/* 1 bit use efc */
149 		1,						/* no disc indications */
150 		0,						/* don't change params */
151 		IN_CLNS,				/* p_netservice */
152 	},
153 	/* ISO_CONS: TP0 CONNECTION MODE */
154 	{
155 		TP_NRETRANS, 			/* short p_Nretrans;  */
156 		0,		/* n/a */		/* short p_dr_ticks; */
157 
158 		40,		/* 20 sec */	/* short p_cc_ticks; */
159 		0,		/* n/a */		/* short p_dt_ticks; */
160 
161 		0,		/* n/a */		/* short p_x_ticks;	*/
162 		360,	/* 3  min */	/* short p_cr_ticks;*/
163 
164 		0,		/* n/a */		/* short p_keepalive_ticks;*/
165 		0,		/* n/a */		/* short p_sendack_ticks; */
166 
167 		600,	/* for cr/cc to clear *//* short p_ref_ticks;	*/
168 		0,		/* n/a */		/* short p_inact_ticks;	*/
169 
170 		/* Use tp4 defaults just in case the user changes ONLY
171 		 * the class
172 		 */
173 		(short) 100, 			/* short p_lcdtfract */
174 		(short) TP0_SOCKBUFSIZE,	/* short p_winsize */
175 		TP0_TPDUSIZE, 			/* 8 bits p_tpdusize */
176 
177 		0, 						/* 4 bits p_ack_strat */
178 		0, 						/* 4 bits p_rx_strat*/
179 		TP_CLASS_0,				/* 5 bits p_class */
180 		0,						/* 1 bit xtd format */
181 		0,						/* 1 bit xpd service */
182 		0,						/* 1 bit use_checksum */
183 		0,						/* 1 bit use net xpd */
184 		0,						/* 1 bit use rcc */
185 		0,						/* 1 bit use efc */
186 		0,						/* no disc indications */
187 		0,						/* don't change params */
188 		ISO_CONS,				/* p_netservice */
189 	},
190 	/* ISO_COSNS: TP4 CONNECTION LESS SERVICE over CONSNS */
191 	{
192 		TP_NRETRANS, 	/* short p_Nretrans;  */
193 		40,		/* 20 sec */ 	/* short p_dr_ticks;  */
194 
195 		40,		/* 20 sec */ 	/* short p_cc_ticks; */
196 		80,		/* 40 sec */ 	/* short p_dt_ticks; */
197 
198 		120,		/* 1 min */ 	/* short p_x_ticks;	 */
199 		360,		/* 3 min */ 	/* short p_cr_ticks;*/
200 
201 		360,	/* 3 min */ 	/* short p_keepalive_ticks;*/
202 		20,		/* 10 sec */ 	/* short p_sendack_ticks;  */
203 
204 		600,	/* 5 min */ 	/* short p_ref_ticks;	*/
205 		480,	/* 4 min */ 	/* short p_inact_ticks;	*/
206 
207 		(short) 100, 			/* short p_lcdtfract */
208 		(short) TP0_SOCKBUFSIZE,	/* short p_winsize */
209 		TP0_TPDUSIZE, 			/* u_char p_tpdusize */
210 
211 		TPACK_WINDOW, 			/* 4 bits p_ack_strat */
212 		TPRX_USE_CW ,			/* No fast start */
213 								/* 4 bits p_rx_strat*/
214 		TP_CLASS_4 | TP_CLASS_0,/* 5 bits p_class */
215 		0,						/* 1 bit xtd format */
216 		1,						/* 1 bit xpd service */
217 		1,						/* 1 bit use_checksum */
218 		0,						/* 1 bit use net xpd */
219 		0,						/* 1 bit use rcc */
220 		0,						/* 1 bit use efc */
221 		0,						/* no disc indications */
222 		0,						/* don't change params */
223 		ISO_COSNS,				/* p_netservice */
224 	},
225 };
226 
227 #ifdef INET
228 int		in_putnetaddr();
229 int		in_getnetaddr();
230 int		in_cmpnetaddr();
231 int 	in_putsufx();
232 int 	in_getsufx();
233 int 	in_recycle_tsuffix();
234 int 	tpip_mtu();
235 int 	in_pcbbind();
236 int 	in_pcbconnect();
237 int 	in_pcbdisconnect();
238 int 	in_pcbdetach();
239 int 	in_pcballoc();
240 int 	tpip_output();
241 int 	tpip_output_dg();
242 struct inpcb	tp_inpcb;
243 #endif INET
244 #ifdef ISO
245 int		iso_putnetaddr();
246 int		iso_getnetaddr();
247 int		iso_cmpnetaddr();
248 int 	iso_putsufx();
249 int 	iso_getsufx();
250 int 	iso_recycle_tsuffix();
251 int		tpclnp_mtu();
252 int		iso_pcbbind();
253 int		iso_pcbconnect();
254 int		iso_pcbdisconnect();
255 int 	iso_pcbdetach();
256 int 	iso_pcballoc();
257 int 	tpclnp_output();
258 int 	tpclnp_output_dg();
259 int		iso_nlctloutput();
260 struct isopcb	tp_isopcb;
261 #endif ISO
262 #ifdef TPCONS
263 int		iso_putnetaddr();
264 int		iso_getnetaddr();
265 int		iso_cmpnetaddr();
266 int 	iso_putsufx();
267 int 	iso_getsufx();
268 int 	iso_recycle_tsuffix();
269 int		iso_pcbbind();
270 int		tpcons_pcbconnect();
271 int		tpclnp_mtu();
272 int		iso_pcbdisconnect();
273 int 	iso_pcbdetach();
274 int 	iso_pcballoc();
275 int 	tpcons_output();
276 struct isopcb	tp_isopcb;
277 #endif TPCONS
278 
279 
280 struct nl_protosw nl_protosw[] = {
281 	/* ISO_CLNS */
282 #ifdef ISO
283 	{ AF_ISO, iso_putnetaddr, iso_getnetaddr, iso_cmpnetaddr,
284 		iso_putsufx, iso_getsufx,
285 		iso_recycle_tsuffix,
286 		tpclnp_mtu, iso_pcbbind, iso_pcbconnect,
287 		iso_pcbdisconnect,	iso_pcbdetach,
288 		iso_pcballoc,
289 		tpclnp_output, tpclnp_output_dg, iso_nlctloutput,
290 		(caddr_t) &tp_isopcb,
291 		},
292 #else
293 	{ 0 },
294 #endif ISO
295 	/* IN_CLNS */
296 #ifdef INET
297 	{ AF_INET, in_putnetaddr, in_getnetaddr, in_cmpnetaddr,
298 		in_putsufx, in_getsufx,
299 		in_recycle_tsuffix,
300 		tpip_mtu, in_pcbbind, in_pcbconnect,
301 		in_pcbdisconnect,	in_pcbdetach,
302 		in_pcballoc,
303 		tpip_output, tpip_output_dg, /* nl_ctloutput */ NULL,
304 		(caddr_t) &tp_inpcb,
305 		},
306 #else
307 	{ 0 },
308 #endif INET
309 	/* ISO_CONS */
310 #if defined(ISO) && defined(TPCONS)
311 	{ AF_ISO, iso_putnetaddr, iso_getnetaddr, iso_cmpnetaddr,
312 		iso_putsufx, iso_getsufx,
313 		iso_recycle_tsuffix,
314 		tpclnp_mtu, iso_pcbbind, tpcons_pcbconnect,
315 		iso_pcbdisconnect,	iso_pcbdetach,
316 		iso_pcballoc,
317 		tpcons_output, tpcons_output, iso_nlctloutput,
318 		(caddr_t) &tp_isopcb,
319 		},
320 #else
321 	{ 0 },
322 #endif ISO_CONS
323 	/* End of protosw marker */
324 	{ 0 }
325 };
326 
327 u_long tp_sendspace = 1024 * 4;
328 u_long tp_recvspace = 1024 * 4;
329 
330 /*
331  * NAME:  tp_init()
332  *
333  * CALLED FROM:
334  *  autoconf through the protosw structure
335  *
336  * FUNCTION:
337  *  initialize tp machine
338  *
339  * RETURNS:  Nada
340  *
341  * SIDE EFFECTS:
342  *
343  * NOTES:
344  */
345 int
346 tp_init()
347 {
348 	static int 	init_done=0;
349 	void	 	tp_timerinit();
350 
351 	if (init_done++)
352 		return 0;
353 
354 
355 	/* FOR INET */
356 	tp_inpcb.inp_next = tp_inpcb.inp_prev = &tp_inpcb;
357 	/* FOR ISO */
358 	tp_isopcb.isop_next = tp_isopcb.isop_prev = &tp_isopcb;
359 
360     tp_start_win = 2;
361 
362 	tp_timerinit();
363 	bzero((caddr_t)&tp_stat, sizeof(struct tp_stat));
364 	return 0;
365 }
366 
367 /*
368  * NAME: 	tp_soisdisconnecting()
369  *
370  * CALLED FROM:
371  *  tp.trans
372  *
373  * FUNCTION and ARGUMENTS:
374  *  Set state of the socket (so) to reflect that fact that we're disconnectING
375  *
376  * RETURNS: 	Nada
377  *
378  * SIDE EFFECTS:
379  *
380  * NOTES:
381  *  This differs from the regular soisdisconnecting() in that the latter
382  *  also sets the SS_CANTRECVMORE and SS_CANTSENDMORE flags.
383  *  We don't want to set those flags because those flags will cause
384  *  a SIGPIPE to be delivered in sosend() and we don't like that.
385  *  If anyone else is sleeping on this socket, wake 'em up.
386  */
387 void
388 tp_soisdisconnecting(so)
389 	register struct socket *so;
390 {
391 	soisdisconnecting(so);
392 	so->so_state &= ~SS_CANTSENDMORE;
393 	IFPERF(sototpcb(so))
394 		register struct tp_pcb *tpcb = sototpcb(so);
395 		u_int 	fsufx, lsufx;
396 
397 		bcopy ((caddr_t)tpcb->tp_fsuffix, (caddr_t)&fsufx, sizeof(u_int) );
398 		bcopy ((caddr_t)tpcb->tp_lsuffix, (caddr_t)&lsufx, sizeof(u_int) );
399 
400 		tpmeas(tpcb->tp_lref, TPtime_close, &time, fsufx, lsufx, tpcb->tp_fref);
401 		tpcb->tp_perf_on = 0; /* turn perf off */
402 	ENDPERF
403 }
404 
405 
406 /*
407  * NAME: tp_soisdisconnected()
408  *
409  * CALLED FROM:
410  *	tp.trans
411  *
412  * FUNCTION and ARGUMENTS:
413  *  Set state of the socket (so) to reflect that fact that we're disconnectED
414  *  Set the state of the reference structure to closed, and
415  *  recycle the suffix.
416  *  Start a reference timer.
417  *
418  * RETURNS:	Nada
419  *
420  * SIDE EFFECTS:
421  *
422  * NOTES:
423  *  This differs from the regular soisdisconnected() in that the latter
424  *  also sets the SS_CANTRECVMORE and SS_CANTSENDMORE flags.
425  *  We don't want to set those flags because those flags will cause
426  *  a SIGPIPE to be delivered in sosend() and we don't like that.
427  *  If anyone else is sleeping on this socket, wake 'em up.
428  */
429 void
430 tp_soisdisconnected(tpcb)
431 	register struct tp_pcb	*tpcb;
432 {
433 	register struct socket	*so = tpcb->tp_sock;
434 
435 	soisdisconnecting(so);
436 	so->so_state &= ~SS_CANTSENDMORE;
437 	IFPERF(tpcb)
438 		register struct tp_pcb *ttpcb = sototpcb(so);
439 		u_int 	fsufx, lsufx;
440 
441 		/* CHOKE */
442 		bcopy ((caddr_t)ttpcb->tp_fsuffix, (caddr_t)&fsufx, sizeof(u_int) );
443 		bcopy ((caddr_t)ttpcb->tp_lsuffix, (caddr_t)&lsufx, sizeof(u_int) );
444 
445 		tpmeas(ttpcb->tp_lref, TPtime_close,
446 		   &time, &lsufx, &fsufx, ttpcb->tp_fref);
447 		tpcb->tp_perf_on = 0; /* turn perf off */
448 	ENDPERF
449 
450 	tpcb->tp_refstate = REF_FROZEN;
451 	tp_recycle_tsuffix(tpcb);
452 	tp_etimeout(tpcb, TM_reference, (int)tpcb->tp_refer_ticks);
453 }
454 
455 /*
456  * NAME:	tp_freeref()
457  *
458  * CALLED FROM:
459  *  tp.trans when the reference timer goes off, and
460  *  from tp_attach() and tp_detach() when a tpcb is partially set up but not
461  *  set up enough to have a ref timer set for it, and it's discarded
462  *  due to some sort of error or an early close()
463  *
464  * FUNCTION and ARGUMENTS:
465  *  Frees the reference represented by (r) for re-use.
466  *
467  * RETURNS: Nothing
468  *
469  * SIDE EFFECTS:
470  *
471  * NOTES:	better be called at clock priority !!!!!
472  */
473 void
474 tp_freeref(n)
475 RefNum n;
476 {
477 	register struct tp_ref *r = tp_ref + n;
478 	register struct tp_pcb *tpcb;
479 
480 	tpcb = r->tpr_pcb;
481 	IFDEBUG(D_TIMER)
482 		printf("tp_freeref called for ref %d pcb %x maxrefopen %d\n",
483 		n, tpcb, tp_refinfo.tpr_maxopen);
484 	ENDDEBUG
485 	IFTRACE(D_TIMER)
486 		tptrace(TPPTmisc, "tp_freeref ref maxrefopen pcb",
487 		n, tp_refinfo.tpr_maxopen, tpcb, 0);
488 	ENDTRACE
489 	if (tpcb == 0)
490 		return;
491 	IFDEBUG(D_CONN)
492 		printf("tp_freeref: CLEARING tpr_pcb 0x%x\n", tpcb);
493 	ENDDEBUG
494 	r->tpr_pcb = (struct tp_pcb *)0;
495 	tpcb->tp_refstate = REF_FREE;
496 
497 	for (r = tp_ref + tp_refinfo.tpr_maxopen; r > tp_ref; r--)
498 		if (r->tpr_pcb)
499 			break;
500 	tp_refinfo.tpr_maxopen = r - tp_ref;
501 	tp_refinfo.tpr_numopen--;
502 
503 	IFDEBUG(D_TIMER)
504 		printf("tp_freeref ends w/ maxrefopen %d\n", tp_refinfo.tpr_maxopen);
505 	ENDDEBUG
506 }
507 
508 /*
509  * NAME:  tp_getref()
510  *
511  * CALLED FROM:
512  *  tp_attach()
513  *
514  * FUNCTION and ARGUMENTS:
515  *  obtains the next free reference and allocates the appropriate
516  *  ref structure, links that structure to (tpcb)
517  *
518  * RETURN VALUE:
519  *	a reference number
520  *  or TP_ENOREF
521  *
522  * SIDE EFFECTS:
523  *
524  * NOTES:
525  */
526 u_long
527 tp_getref(tpcb)
528 	register struct tp_pcb *tpcb;
529 {
530 	register struct tp_ref	*r, *rlim;
531 	register int 			i;
532 	caddr_t obase;
533 	unsigned size;
534 
535 	if (++tp_refinfo.tpr_numopen < tp_refinfo.tpr_size)
536 		for (r = tp_refinfo.tpr_base, rlim = r + tp_refinfo.tpr_size;
537 								++r < rlim; ) 	/* tp_ref[0] is never used */
538 			if (r->tpr_pcb == 0)
539 				goto got_one;
540 	/* else have to allocate more space */
541 
542 	obase = (caddr_t)tp_refinfo.tpr_base;
543 	size = tp_refinfo.tpr_size * sizeof(struct tp_ref);
544 	r = (struct tp_ref *) malloc(size + size, M_PCB, M_NOWAIT);
545 	if (r == 0)
546 		return (--tp_refinfo.tpr_numopen, TP_ENOREF);
547 	tp_refinfo.tpr_base = tp_ref = r;
548 	tp_refinfo.tpr_size *= 2;
549 	bcopy(obase, (caddr_t)r, size);
550 	free(obase, M_PCB);
551 	r = (struct tp_ref *)(size + (caddr_t)r);
552 	bzero((caddr_t)r, size);
553 
554 got_one:
555 	r->tpr_pcb = tpcb;
556 	tpcb->tp_refstate = REF_OPENING;
557 	i = r - tp_refinfo.tpr_base;
558 	if (tp_refinfo.tpr_maxopen < i)
559 		tp_refinfo.tpr_maxopen = i;
560 	return (u_long)i;
561 }
562 
563 /*
564  * NAME: tp_set_npcb()
565  *
566  * CALLED FROM:
567  *	tp_attach(), tp_route_to()
568  *
569  * FUNCTION and ARGUMENTS:
570  *  given a tpcb, allocate an appropriate lower-lever npcb, freeing
571  *  any old ones that might need re-assigning.
572  */
573 tp_set_npcb(tpcb)
574 register struct tp_pcb *tpcb;
575 {
576 	register struct socket *so = tpcb->tp_sock;
577 	int error;
578 
579 	if (tpcb->tp_nlproto && tpcb->tp_npcb) {
580 		short so_state = so->so_state;
581 		so->so_state &= ~SS_NOFDREF;
582 		tpcb->tp_nlproto->nlp_pcbdetach(tpcb->tp_npcb);
583 		so->so_state = so_state;
584 	}
585 	tpcb->tp_nlproto = &nl_protosw[tpcb->tp_netservice];
586 	/* xx_pcballoc sets so_pcb */
587 	error = tpcb->tp_nlproto->nlp_pcballoc(so, tpcb->tp_nlproto->nlp_pcblist);
588 	tpcb->tp_npcb = so->so_pcb;
589 	so->so_pcb = (caddr_t)tpcb;
590 	return (error);
591 }
592 /*
593  * NAME: tp_attach()
594  *
595  * CALLED FROM:
596  *	tp_usrreq, PRU_ATTACH
597  *
598  * FUNCTION and ARGUMENTS:
599  *  given a socket (so) and a protocol family (dom), allocate a tpcb
600  *  and ref structure, initialize everything in the structures that
601  *  needs to be initialized.
602  *
603  * RETURN VALUE:
604  *  0 ok
605  *  EINVAL if DEBUG(X) in is on and a disaster has occurred
606  *  ENOPROTOOPT if TP hasn't been configured or if the
607  *   socket wasn't created with tp as its protocol
608  *  EISCONN if this socket is already part of a connection
609  *  ETOOMANYREFS if ran out of tp reference numbers.
610  *  E* whatever error is returned from soreserve()
611  *    for from the network-layer pcb allocation routine
612  *
613  * SIDE EFFECTS:
614  *
615  * NOTES:
616  */
617 tp_attach(so, protocol)
618 	struct socket 			*so;
619 	int 					protocol;
620 {
621 	register struct tp_pcb	*tpcb;
622 	int 					error = 0;
623 	int 					dom = so->so_proto->pr_domain->dom_family;
624 	u_long					lref;
625 	extern struct tp_conn_param tp_conn_param[];
626 
627 	IFDEBUG(D_CONN)
628 		printf("tp_attach:dom 0x%x so 0x%x ", dom, so);
629 	ENDDEBUG
630 	IFTRACE(D_CONN)
631 		tptrace(TPPTmisc, "tp_attach:dom so", dom, so, 0, 0);
632 	ENDTRACE
633 
634 	if (so->so_pcb != NULL) {
635 		return EISCONN;	/* socket already part of a connection*/
636 	}
637 
638 	if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0)
639 		error = soreserve(so, tp_sendspace, tp_recvspace);
640 		/* later an ioctl will allow reallocation IF still in closed state */
641 
642 	if (error)
643 		goto bad2;
644 
645 	MALLOC(tpcb, struct tp_pcb *, sizeof(*tpcb), M_PCB, M_NOWAIT);
646 	if (tpcb == NULL) {
647 		error = ENOBUFS;
648 		goto bad2;
649 	}
650 	bzero( (caddr_t)tpcb, sizeof (struct tp_pcb) );
651 
652 	if ( ((lref = tp_getref(tpcb)) &  TP_ENOREF) != 0 ) {
653 		error = ETOOMANYREFS;
654 		goto bad3;
655 	}
656 	tpcb->tp_lref = lref;
657 	tpcb->tp_sock =  so;
658 	tpcb->tp_domain = dom;
659 	/* tpcb->tp_proto = protocol; someday maybe? */
660 	if (protocol && protocol<ISOPROTO_TP4) {
661 		tpcb->tp_netservice = ISO_CONS;
662 		tpcb->tp_snduna = (SeqNum) -1;/* kludge so the pseudo-ack from the CR/CC
663 								 * will generate correct fake-ack values
664 								 */
665 	} else {
666 		tpcb->tp_netservice = (dom== AF_INET)?IN_CLNS:ISO_CLNS;
667 		/* the default */
668 	}
669 	tpcb->_tp_param = tp_conn_param[tpcb->tp_netservice];
670 
671 	tpcb->tp_state = TP_CLOSED;
672 	tpcb->tp_vers  = TP_VERSION;
673 	tpcb->tp_notdetached = 1;
674 
675 		   /* Spec says default is 128 octets,
676 			* that is, if the tpdusize argument never appears, use 128.
677 			* As the initiator, we will always "propose" the 2048
678 			* size, that is, we will put this argument in the CR
679 			* always, but accept what the other side sends on the CC.
680 			* If the initiator sends us something larger on a CR,
681 			* we'll respond w/ this.
682 			* Our maximum is 4096.  See tp_chksum.c comments.
683 			*/
684 	tpcb->tp_cong_win =
685 		tpcb->tp_l_tpdusize = 1 << tpcb->tp_tpdusize;
686 
687 	tpcb->tp_seqmask  = TP_NML_FMT_MASK;
688 	tpcb->tp_seqbit  =  TP_NML_FMT_BIT;
689 	tpcb->tp_seqhalf  =  tpcb->tp_seqbit >> 1;
690 
691 	/* attach to a network-layer protoswitch */
692 	if ( error =  tp_set_npcb(tpcb))
693 		goto bad4;
694 	ASSERT( tpcb->tp_nlproto->nlp_afamily == tpcb->tp_domain);
695 
696 	/* nothing to do for iso case */
697 	if( dom == AF_INET )
698 		sotoinpcb(so)->inp_ppcb = (caddr_t) tpcb;
699 
700 	return 0;
701 
702 bad4:
703 	IFDEBUG(D_CONN)
704 		printf("BAD4 in tp_attach, so 0x%x\n", so);
705 	ENDDEBUG
706 	tp_freeref(tpcb->tp_lref);
707 
708 bad3:
709 	IFDEBUG(D_CONN)
710 		printf("BAD3 in tp_attach, so 0x%x\n", so);
711 	ENDDEBUG
712 
713 	free((caddr_t)tpcb, M_PCB); /* never a cluster  */
714 
715 bad2:
716 	IFDEBUG(D_CONN)
717 		printf("BAD2 in tp_attach, so 0x%x\n", so);
718 	ENDDEBUG
719 	so->so_pcb = 0;
720 
721 /*bad:*/
722 	IFDEBUG(D_CONN)
723 		printf("BAD in tp_attach, so 0x%x\n", so);
724 	ENDDEBUG
725 	return error;
726 }
727 
728 /*
729  * NAME:  tp_detach()
730  *
731  * CALLED FROM:
732  *	tp.trans, on behalf of a user close request
733  *  and when the reference timer goes off
734  * (if the disconnect  was initiated by the protocol entity
735  * rather than by the user)
736  *
737  * FUNCTION and ARGUMENTS:
738  *  remove the tpcb structure from the list of active or
739  *  partially active connections, recycle all the mbufs
740  *  associated with the pcb, ref structure, sockbufs, etc.
741  *  Only free the ref structure if you know that a ref timer
742  *  wasn't set for this tpcb.
743  *
744  * RETURNS:  Nada
745  *
746  * SIDE EFFECTS:
747  *
748  * NOTES:
749  *  tp_soisdisconnected() was already when this is called
750  */
751 void
752 tp_detach(tpcb)
753 	register struct tp_pcb 	*tpcb;
754 {
755 	void					tp_freeref(), tp_rsyflush();
756 	register struct socket	 *so = tpcb->tp_sock;
757 
758 	IFDEBUG(D_CONN)
759 		printf("tp_detach(tpcb 0x%x, so 0x%x)\n",
760 			tpcb,so);
761 	ENDDEBUG
762 	IFTRACE(D_CONN)
763 		tptraceTPCB(TPPTmisc, "tp_detach tpcb so lsufx",
764 			tpcb, so, *(u_short *)(tpcb->tp_lsuffix), 0);
765 	ENDTRACE
766 
767 	IFDEBUG(D_CONN)
768 		printf("so_snd at 0x%x so_rcv at 0x%x\n", &so->so_snd, &so->so_rcv);
769 		dump_mbuf(so->so_snd.sb_mb, "so_snd at detach ");
770 		printf("about to call LL detach, nlproto 0x%x, nl_detach 0x%x\n",
771 				tpcb->tp_nlproto, tpcb->tp_nlproto->nlp_pcbdetach);
772 	ENDDEBUG
773 
774 	if (tpcb->tp_Xsnd.sb_mb) {
775 		printf("Unsent Xdata on detach; would panic");
776 		sbflush(&tpcb->tp_Xsnd);
777 	}
778 	if (tpcb->tp_ucddata)
779 		m_freem(tpcb->tp_ucddata);
780 
781 	IFDEBUG(D_CONN)
782 		printf("reassembly info cnt %d rsyq 0x%x\n",
783 		    tpcb->tp_rsycnt, tpcb->tp_rsyq);
784 	ENDDEBUG
785 	if (tpcb->tp_rsyq)
786 		tp_rsyflush(tpcb);
787 
788 	if (tpcb->tp_next) {
789 		remque(tpcb);
790 		tpcb->tp_next = tpcb->tp_prev = 0;
791 	}
792 	tpcb->tp_notdetached = 0;
793 
794 	IFDEBUG(D_CONN)
795 		printf("calling (...nlproto->...)(0x%x, so 0x%x)\n",
796 			tpcb->tp_npcb, so);
797 		printf("so 0x%x so_head 0x%x,  qlen %d q0len %d qlimit %d\n",
798 		so,  so->so_head,
799 		so->so_q0len, so->so_qlen, so->so_qlimit);
800 	ENDDEBUG
801 
802 	(tpcb->tp_nlproto->nlp_pcbdetach)(tpcb->tp_npcb);
803 				/* does an so->so_pcb = 0; sofree(so) */
804 
805 	IFDEBUG(D_CONN)
806 		printf("after xxx_pcbdetach\n");
807 	ENDDEBUG
808 
809 	if (tpcb->tp_state == TP_LISTENING) {
810 		register struct tp_pcb **tt;
811 		for (tt = &tp_listeners; *tt; tt = &((*tt)->tp_nextlisten))
812 			if (*tt == tpcb)
813 				break;
814 		if (*tt)
815 			*tt = tpcb->tp_nextlisten;
816 		else
817 			printf("tp_detach from listen: should panic\n");
818 	}
819 	if (tpcb->tp_refstate == REF_OPENING ) {
820 		/* no connection existed here so no reference timer will be called */
821 		IFDEBUG(D_CONN)
822 			printf("SETTING ref %d to REF_FREE\n", tpcb->tp_lref);
823 		ENDDEBUG
824 
825 		tp_freeref(tpcb->tp_lref);
826 	}
827 #ifdef TP_PERF_MEAS
828 	/*
829 	 * Get rid of the cluster mbuf allocated for performance measurements, if
830 	 * there is one.  Note that tpcb->tp_perf_on says nothing about whether or
831 	 * not a cluster mbuf was allocated, so you have to check for a pointer
832 	 * to one (that is, we need the TP_PERF_MEASs around the following section
833 	 * of code, not the IFPERFs)
834 	 */
835 	if (tpcb->tp_p_mbuf) {
836 		register struct mbuf *m = tpcb->tp_p_mbuf;
837 		struct mbuf *n;
838 		IFDEBUG(D_PERF_MEAS)
839 			printf("freeing tp_p_meas 0x%x  ", tpcb->tp_p_meas);
840 		ENDDEBUG
841 		do {
842 		    MFREE(m, n);
843 		    m = n;
844 		} while (n);
845 		tpcb->tp_p_meas = 0;
846 		tpcb->tp_p_mbuf = 0;
847 	}
848 #endif TP_PERF_MEAS
849 
850 	IFDEBUG(D_CONN)
851 		printf( "end of detach, NOT single, tpcb 0x%x\n", tpcb);
852 	ENDDEBUG
853 	/* free((caddr_t)tpcb, M_PCB); WHere to put this ? */
854 }
855 
856 struct que {
857 	struct tp_pcb *next;
858 	struct tp_pcb *prev;
859 } tp_bound_pcbs =
860 {(struct tp_pcb *)&tp_bound_pcbs, (struct tp_pcb *)&tp_bound_pcbs};
861 
862 u_short tp_unique;
863 
864 tp_tselinuse(tlen, tsel, siso, reuseaddr)
865 caddr_t tsel;
866 register struct sockaddr_iso *siso;
867 {
868 	struct tp_pcb *b = tp_bound_pcbs.next, *l = tp_listeners;
869 	register struct tp_pcb *t;
870 
871 	for (;;) {
872 		if (b != (struct tp_pcb *)&tp_bound_pcbs) {
873 			t = b; b = t->tp_next;
874 		} else if (l) {
875 			t = l; l = t->tp_nextlisten;
876 		} else
877 			break;
878 		if (tlen == t->tp_lsuffixlen && bcmp(tsel, t->tp_lsuffix, tlen) == 0) {
879 			if (t->tp_flags & TPF_GENERAL_ADDR) {
880 				if (siso == 0 || reuseaddr == 0)
881 					return 1;
882 			} else if (siso) {
883 				if (siso->siso_family == t->tp_domain &&
884 					t->tp_nlproto->nlp_cmpnetaddr(t->tp_npcb, siso, TP_LOCAL))
885 						return 1;
886 			} else if (reuseaddr == 0)
887 						return 1;
888 		}
889 	}
890 	return 0;
891 
892 }
893 
894 
895 tp_pcbbind(tpcb, nam)
896 register struct tp_pcb *tpcb;
897 register struct mbuf *nam;
898 {
899 	register struct sockaddr_iso *siso = 0;
900 	int tlen = 0, wrapped = 0;
901 	caddr_t tsel;
902 	u_short tutil;
903 
904 	if (tpcb->tp_state != TP_CLOSED)
905 		return (EINVAL);
906 	if (nam) {
907 		siso = mtod(nam, struct sockaddr_iso *);
908 		switch (siso->siso_family) {
909 		default:
910 			return (EAFNOSUPPORT);
911 #ifdef ISO
912 		case AF_ISO:
913 			tlen = siso->siso_tlen;
914 			tsel = TSEL(siso);
915 			if (siso->siso_nlen == 0)
916 				siso = 0;
917 			break;
918 #endif
919 #ifdef INET
920 		case AF_INET:
921 			tsel = (caddr_t)&tutil;
922 			if (tutil =  ((struct sockaddr_in *)siso)->sin_port) {
923 				tlen = 2;
924 			}
925 			if (((struct sockaddr_in *)siso)->sin_addr.s_addr == 0)
926 				siso = 0;
927 		}
928 #endif
929 	}
930 	if (tpcb->tp_lsuffixlen == 0) {
931 		if (tlen) {
932 			if (tp_tselinuse(tlen, tsel, siso,
933 								tpcb->tp_sock->so_options & SO_REUSEADDR))
934 				return (EINVAL);
935 		} else for (tsel = (caddr_t)&tp_unique, tlen = 2;;){
936 			if (tp_unique++ < ISO_PORT_RESERVED ||
937 				tp_unique > ISO_PORT_USERRESERVED) {
938 					if (wrapped++)
939 						return ESRCH;
940 					tp_unique = ISO_PORT_RESERVED;
941 			}
942 			if (tp_tselinuse(tlen, tsel, siso, 0) == 0)
943 				break;
944 		}
945 		bcopy(tsel, tpcb->tp_lsuffix, (tpcb->tp_lsuffixlen = tlen));
946 		insque(tpcb, &tp_bound_pcbs);
947 	} else {
948 		if (tlen || siso == 0)
949 			return (EINVAL);
950 	}
951 	if (siso == 0) {
952 		tpcb->tp_flags |= TPF_GENERAL_ADDR;
953 		return (0);
954 	}
955 	return tpcb->tp_nlproto->nlp_pcbbind(tpcb->tp_npcb, nam);
956 }
957