xref: /csrg-svn/sys/netiso/tp_pcb.c (revision 51251)
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.20 (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 /*
328  * NAME:  tp_init()
329  *
330  * CALLED FROM:
331  *  autoconf through the protosw structure
332  *
333  * FUNCTION:
334  *  initialize tp machine
335  *
336  * RETURNS:  Nada
337  *
338  * SIDE EFFECTS:
339  *
340  * NOTES:
341  */
342 int
343 tp_init()
344 {
345 	static int 	init_done=0;
346 	void	 	tp_timerinit();
347 
348 	if (init_done++)
349 		return 0;
350 
351 
352 	/* FOR INET */
353 	tp_inpcb.inp_next = tp_inpcb.inp_prev = &tp_inpcb;
354 	/* FOR ISO */
355 	tp_isopcb.isop_next = tp_isopcb.isop_prev = &tp_isopcb;
356 
357     tp_start_win = 2;
358 
359 	tp_timerinit();
360 	bzero((caddr_t)&tp_stat, sizeof(struct tp_stat));
361 	return 0;
362 }
363 
364 /*
365  * NAME: 	tp_soisdisconnecting()
366  *
367  * CALLED FROM:
368  *  tp.trans
369  *
370  * FUNCTION and ARGUMENTS:
371  *  Set state of the socket (so) to reflect that fact that we're disconnectING
372  *
373  * RETURNS: 	Nada
374  *
375  * SIDE EFFECTS:
376  *
377  * NOTES:
378  *  This differs from the regular soisdisconnecting() in that the latter
379  *  also sets the SS_CANTRECVMORE and SS_CANTSENDMORE flags.
380  *  We don't want to set those flags because those flags will cause
381  *  a SIGPIPE to be delivered in sosend() and we don't like that.
382  *  If anyone else is sleeping on this socket, wake 'em up.
383  */
384 void
385 tp_soisdisconnecting(so)
386 	register struct socket *so;
387 {
388 	soisdisconnecting(so);
389 	so->so_state &= ~SS_CANTSENDMORE;
390 	IFPERF(sototpcb(so))
391 		register struct tp_pcb *tpcb = sototpcb(so);
392 		u_int 	fsufx, lsufx;
393 
394 		bcopy ((caddr_t)tpcb->tp_fsuffix, (caddr_t)&fsufx, sizeof(u_int) );
395 		bcopy ((caddr_t)tpcb->tp_lsuffix, (caddr_t)&lsufx, sizeof(u_int) );
396 
397 		tpmeas(tpcb->tp_lref, TPtime_close, &time, fsufx, lsufx, tpcb->tp_fref);
398 		tpcb->tp_perf_on = 0; /* turn perf off */
399 	ENDPERF
400 }
401 
402 
403 /*
404  * NAME: tp_soisdisconnected()
405  *
406  * CALLED FROM:
407  *	tp.trans
408  *
409  * FUNCTION and ARGUMENTS:
410  *  Set state of the socket (so) to reflect that fact that we're disconnectED
411  *  Set the state of the reference structure to closed, and
412  *  recycle the suffix.
413  *  Start a reference timer.
414  *
415  * RETURNS:	Nada
416  *
417  * SIDE EFFECTS:
418  *
419  * NOTES:
420  *  This differs from the regular soisdisconnected() in that the latter
421  *  also sets the SS_CANTRECVMORE and SS_CANTSENDMORE flags.
422  *  We don't want to set those flags because those flags will cause
423  *  a SIGPIPE to be delivered in sosend() and we don't like that.
424  *  If anyone else is sleeping on this socket, wake 'em up.
425  */
426 void
427 tp_soisdisconnected(tpcb)
428 	register struct tp_pcb	*tpcb;
429 {
430 	register struct socket	*so = tpcb->tp_sock;
431 
432 	soisdisconnecting(so);
433 	so->so_state &= ~SS_CANTSENDMORE;
434 	IFPERF(tpcb)
435 		register struct tp_pcb *ttpcb = sototpcb(so);
436 		u_int 	fsufx, lsufx;
437 
438 		/* CHOKE */
439 		bcopy ((caddr_t)ttpcb->tp_fsuffix, (caddr_t)&fsufx, sizeof(u_int) );
440 		bcopy ((caddr_t)ttpcb->tp_lsuffix, (caddr_t)&lsufx, sizeof(u_int) );
441 
442 		tpmeas(ttpcb->tp_lref, TPtime_close,
443 		   &time, &lsufx, &fsufx, ttpcb->tp_fref);
444 		tpcb->tp_perf_on = 0; /* turn perf off */
445 	ENDPERF
446 
447 	tpcb->tp_refstate = REF_FROZEN;
448 	tp_recycle_tsuffix(tpcb);
449 	tp_etimeout(tpcb, TM_reference, (int)tpcb->tp_refer_ticks);
450 }
451 
452 /*
453  * NAME:	tp_freeref()
454  *
455  * CALLED FROM:
456  *  tp.trans when the reference timer goes off, and
457  *  from tp_attach() and tp_detach() when a tpcb is partially set up but not
458  *  set up enough to have a ref timer set for it, and it's discarded
459  *  due to some sort of error or an early close()
460  *
461  * FUNCTION and ARGUMENTS:
462  *  Frees the reference represented by (r) for re-use.
463  *
464  * RETURNS: Nothing
465  *
466  * SIDE EFFECTS:
467  *
468  * NOTES:	better be called at clock priority !!!!!
469  */
470 void
471 tp_freeref(n)
472 RefNum n;
473 {
474 	register struct tp_ref *r = tp_ref + n;
475 	register struct tp_pcb *tpcb;
476 
477 	tpcb = r->tpr_pcb;
478 	IFDEBUG(D_TIMER)
479 		printf("tp_freeref called for ref %d pcb %x maxrefopen %d\n",
480 		n, tpcb, tp_refinfo.tpr_maxopen);
481 	ENDDEBUG
482 	IFTRACE(D_TIMER)
483 		tptrace(TPPTmisc, "tp_freeref ref maxrefopen pcb",
484 		n, tp_refinfo.tpr_maxopen, tpcb, 0);
485 	ENDTRACE
486 	if (tpcb == 0)
487 		return;
488 	IFDEBUG(D_CONN)
489 		printf("tp_freeref: CLEARING tpr_pcb 0x%x\n", tpcb);
490 	ENDDEBUG
491 	r->tpr_pcb = (struct tp_pcb *)0;
492 	tpcb->tp_refstate = REF_FREE;
493 
494 	for (r = tp_ref + tp_refinfo.tpr_maxopen; r > tp_ref; r--)
495 		if (r->tpr_pcb)
496 			break;
497 	tp_refinfo.tpr_maxopen = r - tp_ref;
498 	tp_refinfo.tpr_numopen--;
499 
500 	IFDEBUG(D_TIMER)
501 		printf("tp_freeref ends w/ maxrefopen %d\n", tp_refinfo.tpr_maxopen);
502 	ENDDEBUG
503 }
504 
505 /*
506  * NAME:  tp_getref()
507  *
508  * CALLED FROM:
509  *  tp_attach()
510  *
511  * FUNCTION and ARGUMENTS:
512  *  obtains the next free reference and allocates the appropriate
513  *  ref structure, links that structure to (tpcb)
514  *
515  * RETURN VALUE:
516  *	a reference number
517  *  or TP_ENOREF
518  *
519  * SIDE EFFECTS:
520  *
521  * NOTES:
522  */
523 u_long
524 tp_getref(tpcb)
525 	register struct tp_pcb *tpcb;
526 {
527 	register struct tp_ref	*r, *rlim;
528 	register int 			i;
529 	caddr_t obase;
530 	unsigned size;
531 
532 	if (++tp_refinfo.tpr_numopen < tp_refinfo.tpr_size)
533 		for (r = tp_refinfo.tpr_base, rlim = r + tp_refinfo.tpr_size;
534 								++r < rlim; ) 	/* tp_ref[0] is never used */
535 			if (r->tpr_pcb == 0)
536 				goto got_one;
537 	/* else have to allocate more space */
538 
539 	obase = (caddr_t)tp_refinfo.tpr_base;
540 	size = tp_refinfo.tpr_size * sizeof(struct tp_ref);
541 	r = (struct tp_ref *) malloc(size + size, M_PCB, M_NOWAIT);
542 	if (r == 0)
543 		return (--tp_refinfo.tpr_numopen, TP_ENOREF);
544 	tp_refinfo.tpr_base = tp_ref = r;
545 	tp_refinfo.tpr_size *= 2;
546 	bcopy(obase, (caddr_t)r, size);
547 	free(obase, M_PCB);
548 	r = (struct tp_ref *)(size + (caddr_t)r);
549 	bzero((caddr_t)r, size);
550 
551 got_one:
552 	r->tpr_pcb = tpcb;
553 	tpcb->tp_refstate = REF_OPENING;
554 	i = r - tp_refinfo.tpr_base;
555 	if (tp_refinfo.tpr_maxopen < i)
556 		tp_refinfo.tpr_maxopen = i;
557 	return (u_long)i;
558 }
559 
560 /*
561  * NAME: tp_set_npcb()
562  *
563  * CALLED FROM:
564  *	tp_attach(), tp_route_to()
565  *
566  * FUNCTION and ARGUMENTS:
567  *  given a tpcb, allocate an appropriate lower-lever npcb, freeing
568  *  any old ones that might need re-assigning.
569  */
570 tp_set_npcb(tpcb)
571 register struct tp_pcb *tpcb;
572 {
573 	register struct socket *so = tpcb->tp_sock;
574 	int error;
575 
576 	if (tpcb->tp_nlproto && tpcb->tp_npcb) {
577 		short so_state = so->so_state;
578 		so->so_state &= ~SS_NOFDREF;
579 		tpcb->tp_nlproto->nlp_pcbdetach(tpcb->tp_npcb);
580 		so->so_state = so_state;
581 	}
582 	tpcb->tp_nlproto = &nl_protosw[tpcb->tp_netservice];
583 	/* xx_pcballoc sets so_pcb */
584 	error = tpcb->tp_nlproto->nlp_pcballoc(so, tpcb->tp_nlproto->nlp_pcblist);
585 	tpcb->tp_npcb = so->so_pcb;
586 	so->so_pcb = (caddr_t)tpcb;
587 	return (error);
588 }
589 /*
590  * NAME: tp_attach()
591  *
592  * CALLED FROM:
593  *	tp_usrreq, PRU_ATTACH
594  *
595  * FUNCTION and ARGUMENTS:
596  *  given a socket (so) and a protocol family (dom), allocate a tpcb
597  *  and ref structure, initialize everything in the structures that
598  *  needs to be initialized.
599  *
600  * RETURN VALUE:
601  *  0 ok
602  *  EINVAL if DEBUG(X) in is on and a disaster has occurred
603  *  ENOPROTOOPT if TP hasn't been configured or if the
604  *   socket wasn't created with tp as its protocol
605  *  EISCONN if this socket is already part of a connection
606  *  ETOOMANYREFS if ran out of tp reference numbers.
607  *  E* whatever error is returned from soreserve()
608  *    for from the network-layer pcb allocation routine
609  *
610  * SIDE EFFECTS:
611  *
612  * NOTES:
613  */
614 tp_attach(so, protocol)
615 	struct socket 			*so;
616 	int 					protocol;
617 {
618 	register struct tp_pcb	*tpcb;
619 	int 					error;
620 	int 					dom = so->so_proto->pr_domain->dom_family;
621 	u_long					lref;
622 	extern struct tp_conn_param tp_conn_param[];
623 
624 	IFDEBUG(D_CONN)
625 		printf("tp_attach:dom 0x%x so 0x%x ", dom, so);
626 	ENDDEBUG
627 	IFTRACE(D_CONN)
628 		tptrace(TPPTmisc, "tp_attach:dom so", dom, so, 0, 0);
629 	ENDTRACE
630 
631 	if (so->so_pcb != NULL) {
632 		return EISCONN;	/* socket already part of a connection*/
633 	}
634 
635 	error = soreserve(so, 2 * TP_SOCKBUFSIZE, TP_SOCKBUFSIZE);
636 		/* later an ioctl will allow reallocation IF still in closed state */
637 
638 	if (error)
639 		goto bad2;
640 
641 	MALLOC(tpcb, struct tp_pcb *, sizeof(*tpcb), M_PCB, M_NOWAIT);
642 	if (tpcb == NULL) {
643 		error = ENOBUFS;
644 		goto bad2;
645 	}
646 	bzero( (caddr_t)tpcb, sizeof (struct tp_pcb) );
647 
648 	if ( ((lref = tp_getref(tpcb)) &  TP_ENOREF) != 0 ) {
649 		error = ETOOMANYREFS;
650 		goto bad3;
651 	}
652 	tpcb->tp_lref = lref;
653 	tpcb->tp_sock =  so;
654 	tpcb->tp_domain = dom;
655 	/* tpcb->tp_proto = protocol; someday maybe? */
656 	if (protocol && protocol<ISOPROTO_TP4) {
657 		tpcb->tp_netservice = ISO_CONS;
658 		tpcb->tp_snduna = (SeqNum) -1;/* kludge so the pseudo-ack from the CR/CC
659 								 * will generate correct fake-ack values
660 								 */
661 	} else {
662 		tpcb->tp_netservice = (dom== AF_INET)?IN_CLNS:ISO_CLNS;
663 		/* the default */
664 	}
665 	tpcb->_tp_param = tp_conn_param[tpcb->tp_netservice];
666 
667 	tpcb->tp_state = TP_CLOSED;
668 	tpcb->tp_vers  = TP_VERSION;
669 	tpcb->tp_notdetached = 1;
670 
671 		   /* Spec says default is 128 octets,
672 			* that is, if the tpdusize argument never appears, use 128.
673 			* As the initiator, we will always "propose" the 2048
674 			* size, that is, we will put this argument in the CR
675 			* always, but accept what the other side sends on the CC.
676 			* If the initiator sends us something larger on a CR,
677 			* we'll respond w/ this.
678 			* Our maximum is 4096.  See tp_chksum.c comments.
679 			*/
680 	tpcb->tp_cong_win =
681 		tpcb->tp_l_tpdusize = 1 << tpcb->tp_tpdusize;
682 
683 	tpcb->tp_seqmask  = TP_NML_FMT_MASK;
684 	tpcb->tp_seqbit  =  TP_NML_FMT_BIT;
685 	tpcb->tp_seqhalf  =  tpcb->tp_seqbit >> 1;
686 
687 	/* attach to a network-layer protoswitch */
688 	if ( error =  tp_set_npcb(tpcb))
689 		goto bad4;
690 	ASSERT( tpcb->tp_nlproto->nlp_afamily == tpcb->tp_domain);
691 
692 	/* nothing to do for iso case */
693 	if( dom == AF_INET )
694 		sotoinpcb(so)->inp_ppcb = (caddr_t) tpcb;
695 
696 	return 0;
697 
698 bad4:
699 	IFDEBUG(D_CONN)
700 		printf("BAD4 in tp_attach, so 0x%x\n", so);
701 	ENDDEBUG
702 	tp_freeref(tpcb->tp_lref);
703 
704 bad3:
705 	IFDEBUG(D_CONN)
706 		printf("BAD3 in tp_attach, so 0x%x\n", so);
707 	ENDDEBUG
708 
709 	free((caddr_t)tpcb, M_PCB); /* never a cluster  */
710 
711 bad2:
712 	IFDEBUG(D_CONN)
713 		printf("BAD2 in tp_attach, so 0x%x\n", so);
714 	ENDDEBUG
715 	so->so_pcb = 0;
716 
717 /*bad:*/
718 	IFDEBUG(D_CONN)
719 		printf("BAD in tp_attach, so 0x%x\n", so);
720 	ENDDEBUG
721 	return error;
722 }
723 
724 /*
725  * NAME:  tp_detach()
726  *
727  * CALLED FROM:
728  *	tp.trans, on behalf of a user close request
729  *  and when the reference timer goes off
730  * (if the disconnect  was initiated by the protocol entity
731  * rather than by the user)
732  *
733  * FUNCTION and ARGUMENTS:
734  *  remove the tpcb structure from the list of active or
735  *  partially active connections, recycle all the mbufs
736  *  associated with the pcb, ref structure, sockbufs, etc.
737  *  Only free the ref structure if you know that a ref timer
738  *  wasn't set for this tpcb.
739  *
740  * RETURNS:  Nada
741  *
742  * SIDE EFFECTS:
743  *
744  * NOTES:
745  *  tp_soisdisconnected() was already when this is called
746  */
747 void
748 tp_detach(tpcb)
749 	register struct tp_pcb 	*tpcb;
750 {
751 	void					tp_freeref(), tp_rsyflush();
752 	register struct socket	 *so = tpcb->tp_sock;
753 
754 	IFDEBUG(D_CONN)
755 		printf("tp_detach(tpcb 0x%x, so 0x%x)\n",
756 			tpcb,so);
757 	ENDDEBUG
758 	IFTRACE(D_CONN)
759 		tptraceTPCB(TPPTmisc, "tp_detach tpcb so lsufx",
760 			tpcb, so, *(u_short *)(tpcb->tp_lsuffix), 0);
761 	ENDTRACE
762 
763 	IFDEBUG(D_CONN)
764 		printf("so_snd at 0x%x so_rcv at 0x%x\n", &so->so_snd, &so->so_rcv);
765 		dump_mbuf(so->so_snd.sb_mb, "so_snd at detach ");
766 		printf("about to call LL detach, nlproto 0x%x, nl_detach 0x%x\n",
767 				tpcb->tp_nlproto, tpcb->tp_nlproto->nlp_pcbdetach);
768 	ENDDEBUG
769 
770 	if (tpcb->tp_Xsnd.sb_mb) {
771 		printf("Unsent Xdata on detach; would panic");
772 		sbflush(&tpcb->tp_Xsnd);
773 	}
774 	if (tpcb->tp_ucddata)
775 		m_freem(tpcb->tp_ucddata);
776 
777 	IFDEBUG(D_CONN)
778 		printf("reassembly info cnt %d rsyq 0x%x\n",
779 		    tpcb->tp_rsycnt, tpcb->tp_rsyq);
780 	ENDDEBUG
781 	if (tpcb->tp_rsyq)
782 		tp_rsyflush(tpcb);
783 
784 	if (tpcb->tp_next) {
785 		remque(tpcb);
786 		tpcb->tp_next = tpcb->tp_prev = 0;
787 	}
788 	tpcb->tp_notdetached = 0;
789 
790 	IFDEBUG(D_CONN)
791 		printf("calling (...nlproto->...)(0x%x, so 0x%x)\n",
792 			tpcb->tp_npcb, so);
793 		printf("so 0x%x so_head 0x%x,  qlen %d q0len %d qlimit %d\n",
794 		so,  so->so_head,
795 		so->so_q0len, so->so_qlen, so->so_qlimit);
796 	ENDDEBUG
797 
798 	(tpcb->tp_nlproto->nlp_pcbdetach)(tpcb->tp_npcb);
799 				/* does an so->so_pcb = 0; sofree(so) */
800 
801 	IFDEBUG(D_CONN)
802 		printf("after xxx_pcbdetach\n");
803 	ENDDEBUG
804 
805 	if (tpcb->tp_state == TP_LISTENING) {
806 		register struct tp_pcb **tt;
807 		for (tt = &tp_listeners; *tt; tt = &((*tt)->tp_nextlisten))
808 			if (*tt == tpcb)
809 				break;
810 		if (*tt)
811 			*tt = tpcb->tp_nextlisten;
812 		else
813 			printf("tp_detach from listen: should panic\n");
814 	}
815 	if (tpcb->tp_refstate == REF_OPENING ) {
816 		/* no connection existed here so no reference timer will be called */
817 		IFDEBUG(D_CONN)
818 			printf("SETTING ref %d to REF_FREE\n", tpcb->tp_lref);
819 		ENDDEBUG
820 
821 		tp_freeref(tpcb->tp_lref);
822 	}
823 #ifdef TP_PERF_MEAS
824 	/*
825 	 * Get rid of the cluster mbuf allocated for performance measurements, if
826 	 * there is one.  Note that tpcb->tp_perf_on says nothing about whether or
827 	 * not a cluster mbuf was allocated, so you have to check for a pointer
828 	 * to one (that is, we need the TP_PERF_MEASs around the following section
829 	 * of code, not the IFPERFs)
830 	 */
831 	if (tpcb->tp_p_mbuf) {
832 		register struct mbuf *m = tpcb->tp_p_mbuf;
833 		struct mbuf *n;
834 		IFDEBUG(D_PERF_MEAS)
835 			printf("freeing tp_p_meas 0x%x  ", tpcb->tp_p_meas);
836 		ENDDEBUG
837 		do {
838 		    MFREE(m, n);
839 		    m = n;
840 		} while (n);
841 		tpcb->tp_p_meas = 0;
842 		tpcb->tp_p_mbuf = 0;
843 	}
844 #endif TP_PERF_MEAS
845 
846 	IFDEBUG(D_CONN)
847 		printf( "end of detach, NOT single, tpcb 0x%x\n", tpcb);
848 	ENDDEBUG
849 	/* free((caddr_t)tpcb, M_PCB); WHere to put this ? */
850 }
851 
852 struct que {
853 	struct tp_pcb *next;
854 	struct tp_pcb *prev;
855 } tp_bound_pcbs =
856 {(struct tp_pcb *)&tp_bound_pcbs, (struct tp_pcb *)&tp_bound_pcbs};
857 
858 u_short tp_unique;
859 
860 tp_tselinuse(tlen, tsel, siso, reuseaddr)
861 caddr_t tsel;
862 register struct sockaddr_iso *siso;
863 {
864 	struct tp_pcb *b = tp_bound_pcbs.next, *l = tp_listeners;
865 	register struct tp_pcb *t;
866 
867 	for (;;) {
868 		if (b != (struct tp_pcb *)&tp_bound_pcbs) {
869 			t = b; b = t->tp_next;
870 		} else if (l) {
871 			t = l; l = t->tp_nextlisten;
872 		} else
873 			break;
874 		if (tlen == t->tp_lsuffixlen && bcmp(tsel, t->tp_lsuffix, tlen) == 0) {
875 			if (t->tp_flags & TPF_GENERAL_ADDR) {
876 				if (siso == 0 || reuseaddr == 0)
877 					return 1;
878 			} else if (siso) {
879 				if (siso->siso_family == t->tp_domain &&
880 					t->tp_nlproto->nlp_cmpnetaddr(t->tp_npcb, siso, TP_LOCAL))
881 						return 1;
882 			} else if (reuseaddr == 0)
883 						return 1;
884 		}
885 	}
886 	return 0;
887 
888 }
889 
890 
891 tp_pcbbind(tpcb, nam)
892 register struct tp_pcb *tpcb;
893 register struct mbuf *nam;
894 {
895 	register struct sockaddr_iso *siso = 0;
896 	int tlen = 0, wrapped = 0;
897 	caddr_t tsel;
898 	u_short tutil;
899 
900 	if (tpcb->tp_state != TP_CLOSED)
901 		return (EINVAL);
902 	if (nam) {
903 		siso = mtod(nam, struct sockaddr_iso *);
904 		switch (siso->siso_family) {
905 		default:
906 			return (EAFNOSUPPORT);
907 #ifdef ISO
908 		case AF_ISO:
909 			tlen = siso->siso_tlen;
910 			tsel = TSEL(siso);
911 			if (siso->siso_nlen == 0)
912 				siso = 0;
913 			break;
914 #endif
915 #ifdef INET
916 		case AF_INET:
917 			tsel = (caddr_t)&tutil;
918 			if (tutil =  ((struct sockaddr_in *)siso)->sin_port) {
919 				tlen = 2;
920 			}
921 			if (((struct sockaddr_in *)siso)->sin_addr.s_addr == 0)
922 				siso = 0;
923 		}
924 #endif
925 	}
926 	if (tpcb->tp_lsuffixlen == 0) {
927 		if (tlen) {
928 			if (tp_tselinuse(tlen, tsel, siso,
929 								tpcb->tp_sock->so_options & SO_REUSEADDR))
930 				return (EINVAL);
931 		} else for (tsel = (caddr_t)&tp_unique, tlen = 2;;){
932 			if (tp_unique++ < ISO_PORT_RESERVED ||
933 				tp_unique > ISO_PORT_USERRESERVED) {
934 					if (wrapped++)
935 						return ESRCH;
936 					tp_unique = ISO_PORT_RESERVED;
937 			}
938 			if (tp_tselinuse(tlen, tsel, siso, 0) == 0)
939 				break;
940 		}
941 		bcopy(tsel, tpcb->tp_lsuffix, (tpcb->tp_lsuffixlen = tlen));
942 		insque(tpcb, &tp_bound_pcbs);
943 	} else {
944 		if (tlen || siso == 0)
945 			return (EINVAL);
946 	}
947 	if (siso == 0) {
948 		tpcb->tp_flags |= TPF_GENERAL_ADDR;
949 		return (0);
950 	}
951 	return tpcb->tp_nlproto->nlp_pcbbind(tpcb->tp_npcb, nam);
952 }
953