xref: /original-bsd/sys/netiso/tp_pcb.c (revision 381fb7ab)
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.24 (Berkeley) 05/27/92
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_rhiwat = so->so_rcv.sb_hiwat;
660 	/* tpcb->tp_proto = protocol; someday maybe? */
661 	if (protocol && protocol<ISOPROTO_TP4) {
662 		tpcb->tp_netservice = ISO_CONS;
663 		tpcb->tp_snduna = (SeqNum) -1;/* kludge so the pseudo-ack from the CR/CC
664 								 * will generate correct fake-ack values
665 								 */
666 	} else {
667 		tpcb->tp_netservice = (dom== AF_INET)?IN_CLNS:ISO_CLNS;
668 		/* the default */
669 	}
670 	tpcb->_tp_param = tp_conn_param[tpcb->tp_netservice];
671 
672 	tpcb->tp_state = TP_CLOSED;
673 	tpcb->tp_vers  = TP_VERSION;
674 	tpcb->tp_notdetached = 1;
675 
676 		   /* Spec says default is 128 octets,
677 			* that is, if the tpdusize argument never appears, use 128.
678 			* As the initiator, we will always "propose" the 2048
679 			* size, that is, we will put this argument in the CR
680 			* always, but accept what the other side sends on the CC.
681 			* If the initiator sends us something larger on a CR,
682 			* we'll respond w/ this.
683 			* Our maximum is 4096.  See tp_chksum.c comments.
684 			*/
685 	tpcb->tp_cong_win =
686 		tpcb->tp_l_tpdusize = 1 << tpcb->tp_tpdusize;
687 
688 	tpcb->tp_seqmask  = TP_NML_FMT_MASK;
689 	tpcb->tp_seqbit  =  TP_NML_FMT_BIT;
690 	tpcb->tp_seqhalf  =  tpcb->tp_seqbit >> 1;
691 
692 	/* attach to a network-layer protoswitch */
693 	if ( error =  tp_set_npcb(tpcb))
694 		goto bad4;
695 	ASSERT( tpcb->tp_nlproto->nlp_afamily == tpcb->tp_domain);
696 
697 	/* nothing to do for iso case */
698 	if( dom == AF_INET )
699 		sotoinpcb(so)->inp_ppcb = (caddr_t) tpcb;
700 
701 	return 0;
702 
703 bad4:
704 	IFDEBUG(D_CONN)
705 		printf("BAD4 in tp_attach, so 0x%x\n", so);
706 	ENDDEBUG
707 	tp_freeref(tpcb->tp_lref);
708 
709 bad3:
710 	IFDEBUG(D_CONN)
711 		printf("BAD3 in tp_attach, so 0x%x\n", so);
712 	ENDDEBUG
713 
714 	free((caddr_t)tpcb, M_PCB); /* never a cluster  */
715 
716 bad2:
717 	IFDEBUG(D_CONN)
718 		printf("BAD2 in tp_attach, so 0x%x\n", so);
719 	ENDDEBUG
720 	so->so_pcb = 0;
721 
722 /*bad:*/
723 	IFDEBUG(D_CONN)
724 		printf("BAD in tp_attach, so 0x%x\n", so);
725 	ENDDEBUG
726 	return error;
727 }
728 
729 /*
730  * NAME:  tp_detach()
731  *
732  * CALLED FROM:
733  *	tp.trans, on behalf of a user close request
734  *  and when the reference timer goes off
735  * (if the disconnect  was initiated by the protocol entity
736  * rather than by the user)
737  *
738  * FUNCTION and ARGUMENTS:
739  *  remove the tpcb structure from the list of active or
740  *  partially active connections, recycle all the mbufs
741  *  associated with the pcb, ref structure, sockbufs, etc.
742  *  Only free the ref structure if you know that a ref timer
743  *  wasn't set for this tpcb.
744  *
745  * RETURNS:  Nada
746  *
747  * SIDE EFFECTS:
748  *
749  * NOTES:
750  *  tp_soisdisconnected() was already when this is called
751  */
752 void
753 tp_detach(tpcb)
754 	register struct tp_pcb 	*tpcb;
755 {
756 	void					tp_freeref(), tp_rsyflush();
757 	register struct socket	 *so = tpcb->tp_sock;
758 
759 	IFDEBUG(D_CONN)
760 		printf("tp_detach(tpcb 0x%x, so 0x%x)\n",
761 			tpcb,so);
762 	ENDDEBUG
763 	IFTRACE(D_CONN)
764 		tptraceTPCB(TPPTmisc, "tp_detach tpcb so lsufx",
765 			tpcb, so, *(u_short *)(tpcb->tp_lsuffix), 0);
766 	ENDTRACE
767 
768 	IFDEBUG(D_CONN)
769 		printf("so_snd at 0x%x so_rcv at 0x%x\n", &so->so_snd, &so->so_rcv);
770 		dump_mbuf(so->so_snd.sb_mb, "so_snd at detach ");
771 		printf("about to call LL detach, nlproto 0x%x, nl_detach 0x%x\n",
772 				tpcb->tp_nlproto, tpcb->tp_nlproto->nlp_pcbdetach);
773 	ENDDEBUG
774 
775 	if (tpcb->tp_Xsnd.sb_mb) {
776 		printf("Unsent Xdata on detach; would panic");
777 		sbflush(&tpcb->tp_Xsnd);
778 	}
779 	if (tpcb->tp_ucddata)
780 		m_freem(tpcb->tp_ucddata);
781 
782 	IFDEBUG(D_CONN)
783 		printf("reassembly info cnt %d rsyq 0x%x\n",
784 		    tpcb->tp_rsycnt, tpcb->tp_rsyq);
785 	ENDDEBUG
786 	if (tpcb->tp_rsyq)
787 		tp_rsyflush(tpcb);
788 
789 	if (tpcb->tp_next) {
790 		remque(tpcb);
791 		tpcb->tp_next = tpcb->tp_prev = 0;
792 	}
793 	tpcb->tp_notdetached = 0;
794 
795 	IFDEBUG(D_CONN)
796 		printf("calling (...nlproto->...)(0x%x, so 0x%x)\n",
797 			tpcb->tp_npcb, so);
798 		printf("so 0x%x so_head 0x%x,  qlen %d q0len %d qlimit %d\n",
799 		so,  so->so_head,
800 		so->so_q0len, so->so_qlen, so->so_qlimit);
801 	ENDDEBUG
802 
803 	(tpcb->tp_nlproto->nlp_pcbdetach)(tpcb->tp_npcb);
804 				/* does an so->so_pcb = 0; sofree(so) */
805 
806 	IFDEBUG(D_CONN)
807 		printf("after xxx_pcbdetach\n");
808 	ENDDEBUG
809 
810 	if (tpcb->tp_state == TP_LISTENING) {
811 		register struct tp_pcb **tt;
812 		for (tt = &tp_listeners; *tt; tt = &((*tt)->tp_nextlisten))
813 			if (*tt == tpcb)
814 				break;
815 		if (*tt)
816 			*tt = tpcb->tp_nextlisten;
817 		else
818 			printf("tp_detach from listen: should panic\n");
819 	}
820 	if (tpcb->tp_refstate == REF_OPENING ) {
821 		/* no connection existed here so no reference timer will be called */
822 		IFDEBUG(D_CONN)
823 			printf("SETTING ref %d to REF_FREE\n", tpcb->tp_lref);
824 		ENDDEBUG
825 
826 		tp_freeref(tpcb->tp_lref);
827 	}
828 #ifdef TP_PERF_MEAS
829 	/*
830 	 * Get rid of the cluster mbuf allocated for performance measurements, if
831 	 * there is one.  Note that tpcb->tp_perf_on says nothing about whether or
832 	 * not a cluster mbuf was allocated, so you have to check for a pointer
833 	 * to one (that is, we need the TP_PERF_MEASs around the following section
834 	 * of code, not the IFPERFs)
835 	 */
836 	if (tpcb->tp_p_mbuf) {
837 		register struct mbuf *m = tpcb->tp_p_mbuf;
838 		struct mbuf *n;
839 		IFDEBUG(D_PERF_MEAS)
840 			printf("freeing tp_p_meas 0x%x  ", tpcb->tp_p_meas);
841 		ENDDEBUG
842 		do {
843 		    MFREE(m, n);
844 		    m = n;
845 		} while (n);
846 		tpcb->tp_p_meas = 0;
847 		tpcb->tp_p_mbuf = 0;
848 	}
849 #endif TP_PERF_MEAS
850 
851 	IFDEBUG(D_CONN)
852 		printf( "end of detach, NOT single, tpcb 0x%x\n", tpcb);
853 	ENDDEBUG
854 	/* free((caddr_t)tpcb, M_PCB); WHere to put this ? */
855 }
856 
857 struct que {
858 	struct tp_pcb *next;
859 	struct tp_pcb *prev;
860 } tp_bound_pcbs =
861 {(struct tp_pcb *)&tp_bound_pcbs, (struct tp_pcb *)&tp_bound_pcbs};
862 
863 u_short tp_unique;
864 
865 tp_tselinuse(tlen, tsel, siso, reuseaddr)
866 caddr_t tsel;
867 register struct sockaddr_iso *siso;
868 {
869 	struct tp_pcb *b = tp_bound_pcbs.next, *l = tp_listeners;
870 	register struct tp_pcb *t;
871 
872 	for (;;) {
873 		if (b != (struct tp_pcb *)&tp_bound_pcbs) {
874 			t = b; b = t->tp_next;
875 		} else if (l) {
876 			t = l; l = t->tp_nextlisten;
877 		} else
878 			break;
879 		if (tlen == t->tp_lsuffixlen && bcmp(tsel, t->tp_lsuffix, tlen) == 0) {
880 			if (t->tp_flags & TPF_GENERAL_ADDR) {
881 				if (siso == 0 || reuseaddr == 0)
882 					return 1;
883 			} else if (siso) {
884 				if (siso->siso_family == t->tp_domain &&
885 					t->tp_nlproto->nlp_cmpnetaddr(t->tp_npcb, siso, TP_LOCAL))
886 						return 1;
887 			} else if (reuseaddr == 0)
888 						return 1;
889 		}
890 	}
891 	return 0;
892 
893 }
894 
895 
896 tp_pcbbind(tpcb, nam)
897 register struct tp_pcb *tpcb;
898 register struct mbuf *nam;
899 {
900 	register struct sockaddr_iso *siso = 0;
901 	int tlen = 0, wrapped = 0;
902 	caddr_t tsel;
903 	u_short tutil;
904 
905 	if (tpcb->tp_state != TP_CLOSED)
906 		return (EINVAL);
907 	if (nam) {
908 		siso = mtod(nam, struct sockaddr_iso *);
909 		switch (siso->siso_family) {
910 		default:
911 			return (EAFNOSUPPORT);
912 #ifdef ISO
913 		case AF_ISO:
914 			tlen = siso->siso_tlen;
915 			tsel = TSEL(siso);
916 			if (siso->siso_nlen == 0)
917 				siso = 0;
918 			break;
919 #endif
920 #ifdef INET
921 		case AF_INET:
922 			tsel = (caddr_t)&tutil;
923 			if (tutil =  ((struct sockaddr_in *)siso)->sin_port) {
924 				tlen = 2;
925 			}
926 			if (((struct sockaddr_in *)siso)->sin_addr.s_addr == 0)
927 				siso = 0;
928 		}
929 #endif
930 	}
931 	if (tpcb->tp_lsuffixlen == 0) {
932 		if (tlen) {
933 			if (tp_tselinuse(tlen, tsel, siso,
934 								tpcb->tp_sock->so_options & SO_REUSEADDR))
935 				return (EINVAL);
936 		} else {
937 			for (tsel = (caddr_t)&tutil, tlen = 2;;){
938 				if (tp_unique++ < ISO_PORT_RESERVED ||
939 					tp_unique > ISO_PORT_USERRESERVED) {
940 						if (wrapped++)
941 							return ESRCH;
942 						tp_unique = ISO_PORT_RESERVED;
943 				}
944 				tutil = htons(tp_unique);
945 				if (tp_tselinuse(tlen, tsel, siso, 0) == 0)
946 					break;
947 			}
948 			if (siso) switch (siso->siso_family) {
949 #ifdef ISO
950 				case AF_ISO:
951 					bcopy(tsel, TSEL(siso), tlen);
952 					siso->siso_tlen = tlen;
953 					break;
954 #endif
955 #ifdef INET
956 				case AF_INET:
957 					((struct sockaddr_in *)siso)->sin_port = tutil;
958 #endif
959 				}
960 		}
961 		bcopy(tsel, tpcb->tp_lsuffix, (tpcb->tp_lsuffixlen = tlen));
962 		insque(tpcb, &tp_bound_pcbs);
963 	} else {
964 		if (tlen || siso == 0)
965 			return (EINVAL);
966 	}
967 	if (siso == 0) {
968 		tpcb->tp_flags |= TPF_GENERAL_ADDR;
969 		return (0);
970 	}
971 	return tpcb->tp_nlproto->nlp_pcbbind(tpcb->tp_npcb, nam);
972 }
973