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