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