xref: /original-bsd/sys/netiso/tp_subr2.c (revision 52f91b57)
1 /***********************************************************
2 		Copyright IBM Corporation 1987
3 
4                       All Rights Reserved
5 
6 Permission to use, copy, modify, and distribute this software and its
7 documentation for any purpose and without fee is hereby granted,
8 provided that the above copyright notice appear in all copies and that
9 both that copyright notice and this permission notice appear in
10 supporting documentation, and that the name of IBM not be
11 used in advertising or publicity pertaining to distribution of the
12 software without specific, written prior permission.
13 
14 IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
15 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
16 IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
17 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
18 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
19 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20 SOFTWARE.
21 
22 ******************************************************************/
23 
24 /*
25  * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
26  */
27 /*
28  * ARGO TP
29  *
30  * $Header: tp_subr2.c,v 5.5 88/11/18 17:28:55 nhall Exp $
31  * $Source: /usr/argo/sys/netiso/RCS/tp_subr2.c,v $
32  *	@(#)tp_subr2.c	7.7 (Berkeley) 04/26/91
33  *
34  * Some auxiliary routines:
35  * 		tp_protocol_error: required by xebec- called when a combo of state,
36  *			event, predicate isn't covered for by the transition file.
37  *		tp_indicate: gives indications(signals) to the user process
38  *		tp_getoptions: initializes variables that are affected by the options
39  *          chosen.
40  */
41 
42 #ifndef lint
43 static char *rcsid = "$Header: tp_subr2.c,v 5.5 88/11/18 17:28:55 nhall Exp $";
44 #endif lint
45 
46 /* this def'n is to cause the expansion of this macro in the
47  * routine tp_local_credit :
48  */
49 #define LOCAL_CREDIT_EXPAND
50 
51 #include "param.h"
52 #include "mbuf.h"
53 #include "socket.h"
54 #include "socketvar.h"
55 #include "domain.h"
56 #include "protosw.h"
57 #include "errno.h"
58 #include "types.h"
59 #include "time.h"
60 #include "kernel.h"
61 #undef MNULL
62 #include "argo_debug.h"
63 #include "tp_param.h"
64 #include "tp_ip.h"
65 #include "iso.h"
66 #include "iso_errno.h"
67 #include "iso_pcb.h"
68 #include "tp_timer.h"
69 #include "tp_stat.h"
70 #include "tp_tpdu.h"
71 #include "tp_pcb.h"
72 #include "tp_seq.h"
73 #include "tp_trace.h"
74 #include "tp_user.h"
75 #include "cons.h"
76 
77 #include "../net/if.h"
78 #ifdef TRUE
79 #undef FALSE
80 #undef TRUE
81 #endif
82 #include "../netccitt/x25.h"
83 #include "../netccitt/pk.h"
84 #include "../netccitt/pk_var.h"
85 
86 /*
87  * NAME: 	tp_local_credit()
88  *
89  * CALLED FROM:
90  *  tp_emit(), tp_usrreq()
91  *
92  * FUNCTION and ARGUMENTS:
93  *	Computes the local credit and stashes it in tpcb->tp_lcredit.
94  *  It's a macro in the production system rather than a procdure.
95  *
96  * RETURNS:
97  *
98  * SIDE EFFECTS:
99  *
100  * NOTES:
101  *  This doesn't actually get called in a production system -
102  *  the macro gets expanded instead in place of calls to this proc.
103  *  But for debugging, we call this and that allows us to add
104  *  debugging messages easily here.
105  */
106 void
107 tp_local_credit(tpcb)
108 	struct tp_pcb *tpcb;
109 {
110 	LOCAL_CREDIT(tpcb);
111 	IFDEBUG(D_CREDIT)
112 		printf("ref 0x%x lcdt 0x%x l_tpdusize 0x%x decbit 0x%x\n",
113 			tpcb->tp_refp - tp_ref,
114 			tpcb->tp_lcredit,
115 			tpcb->tp_l_tpdusize,
116 			tpcb->tp_decbit,
117 			tpcb->tp_cong_win
118 			);
119 	ENDDEBUG
120 	IFTRACE(D_CREDIT)
121 		tptraceTPCB(TPPTmisc,
122 			"lcdt tpdusz \n",
123 			 tpcb->tp_lcredit, tpcb->tp_l_tpdusize, 0, 0);
124 	ENDTRACE
125 }
126 
127 /*
128  * NAME:  tp_protocol_error()
129  *
130  * CALLED FROM:
131  *  tp_driver(), when it doesn't know what to do with
132  * 	a combo of event, state, predicate
133  *
134  * FUNCTION and ARGUMENTS:
135  *  print error mesg
136  *
137  * RETURN VALUE:
138  *  EIO - always
139  *
140  * SIDE EFFECTS:
141  *
142  * NOTES:
143  */
144 int
145 tp_protocol_error(e,tpcb)
146 	struct tp_event	*e;
147 	struct tp_pcb	*tpcb;
148 {
149 	printf("TP PROTOCOL ERROR! tpcb 0x%x event 0x%x, state 0x%x\n",
150 		tpcb, e->ev_number, tpcb->tp_state);
151 	IFTRACE(D_DRIVER)
152 		tptraceTPCB(TPPTmisc, "PROTOCOL ERROR tpcb event state",
153 			tpcb, e->ev_number, tpcb->tp_state, 0 );
154 	ENDTRACE
155 	return EIO; /* for lack of anything better */
156 }
157 
158 
159 /* Not used at the moment */
160 ProtoHook
161 tp_drain()
162 {
163 	return 0;
164 }
165 
166 
167 /*
168  * NAME: tp_indicate()
169  *
170  * CALLED FROM:
171  * 	tp.trans when XPD arrive, when a connection is being disconnected by
172  *  the arrival of a DR or ER, and when a connection times out.
173  *
174  * FUNCTION and ARGUMENTS:
175  *  (ind) is the type of indication : T_DISCONNECT, T_XPD
176  *  (error) is an E* value that will be put in the socket structure
177  *  to be passed along to the user later.
178  * 	Gives a SIGURG to the user process or group indicated by the socket
179  * 	attached to the tpcb.
180  *
181  * RETURNS:  Rien
182  *
183  * SIDE EFFECTS:
184  *
185  * NOTES:
186  */
187 void
188 tp_indicate(ind, tpcb, error)
189 	int				ind;
190 	u_short			error;
191 	register struct tp_pcb	*tpcb;
192 {
193 	register struct socket *so = tpcb->tp_sock;
194 	IFTRACE(D_INDICATION)
195 		tptraceTPCB(TPPTindicate, ind, *(u_short *)(tpcb->tp_lsuffix),
196 			*(u_short *)(tpcb->tp_fsuffix), error,so->so_pgid);
197 	ENDTRACE
198 	IFDEBUG(D_INDICATION)
199 		char *ls, *fs;
200 		ls = tpcb->tp_lsuffix,
201 		fs = tpcb->tp_fsuffix,
202 
203 		printf(
204 "indicate 0x%x lsuf 0x%02x%02x fsuf 0x%02x%02x err 0x%x  noind 0x%x ref 0x%x\n",
205 		ind,
206 		*ls, *(ls+1), *fs, *(fs+1),
207 		error, /*so->so_pgrp,*/
208 		tpcb->tp_no_disc_indications,
209 		tpcb->tp_lref);
210 	ENDDEBUG
211 
212 	if (ind == ER_TPDU) {
213 		register struct mbuf *m;
214 		struct tp_disc_reason x;
215 
216 		if ((so->so_state & SS_CANTRCVMORE) == 0 &&
217 				(m = m_get(M_DONTWAIT, MT_OOBDATA)) != 0) {
218 
219 			x.dr_hdr.cmsg_len = m->m_len = sizeof(x);
220 			x.dr_hdr.cmsg_level = SOL_TRANSPORT;
221 			x.dr_hdr.cmsg_type= TPOPT_DISC_REASON;
222 			x.dr_reason = error;
223 			*mtod(m, struct tp_disc_reason *) = x;
224 			sbappendrecord(&tpcb->tp_Xrcv, m);
225 			error = 0;
226 		} else
227 			error = ECONNRESET;
228 	}
229 	so->so_error = error;
230 
231 	if (ind == T_DISCONNECT)  {
232 		so->so_error = ENOTCONN;
233 		if ( tpcb->tp_no_disc_indications )
234 			return;
235 	}
236 	IFTRACE(D_INDICATION)
237 		tptraceTPCB(TPPTmisc, "doing sohasoutofband(so)", so,0,0,0);
238 	ENDTRACE
239 	sohasoutofband(so);
240 }
241 
242 /*
243  * NAME : tp_getoptions()
244  *
245  * CALLED FROM:
246  * 	tp.trans whenever we go into OPEN state
247  *
248  * FUNCTION and ARGUMENTS:
249  *  sets the proper flags and values in the tpcb, to control
250  *  the appropriate actions for the given class, options,
251  *  sequence space, etc, etc.
252  *
253  * RETURNS: Nada
254  *
255  * SIDE EFFECTS:
256  *
257  * NOTES:
258  */
259 void
260 tp_getoptions(tpcb)
261 struct tp_pcb *tpcb;
262 {
263 	tpcb->tp_seqmask =
264 		tpcb->tp_xtd_format ?	TP_XTD_FMT_MASK :	TP_NML_FMT_MASK ;
265 	tpcb->tp_seqbit =
266 		tpcb->tp_xtd_format ?	TP_XTD_FMT_BIT :	TP_NML_FMT_BIT ;
267 	tpcb->tp_seqhalf = tpcb->tp_seqbit >> 1;
268 	tpcb->tp_dt_ticks =
269 		MAX(tpcb->tp_dt_ticks, (tpcb->tp_peer_acktime + 2));
270 
271 }
272 
273 /*
274  * NAME:  tp_recycle_tsuffix()
275  *
276  * CALLED FROM:
277  *  Called when a ref is frozen.
278  *
279  * FUNCTION and ARGUMENTS:
280  *  allows the suffix to be reused.
281  *
282  * RETURNS: zilch
283  *
284  * SIDE EFFECTS:
285  *
286  * NOTES:
287  */
288 void
289 tp_recycle_tsuffix(tpcb)
290 	struct tp_pcb	*tpcb;
291 {
292 	bzero((caddr_t)tpcb->tp_lsuffix, sizeof( tpcb->tp_lsuffix));
293 	bzero((caddr_t)tpcb->tp_fsuffix, sizeof( tpcb->tp_fsuffix));
294 	tpcb->tp_fsuffixlen = tpcb->tp_lsuffixlen = 0;
295 
296 	(tpcb->tp_nlproto->nlp_recycle_suffix)(tpcb->tp_npcb);
297 }
298 
299 /*
300  * NAME: tp_quench()
301  *
302  * CALLED FROM:
303  *  tp{af}_quench() when ICMP source quench or similar thing arrives.
304  *
305  * FUNCTION and ARGUMENTS:
306  *  Drop the congestion window back to 1.
307  *  Congestion window scheme:
308  *  Initial value is 1.  ("slow start" as Nagle, et. al. call it)
309  *  For each good ack that arrives, the congestion window is increased
310  *  by 1 (up to max size of logical infinity, which is to say,
311  *	it doesn't wrap around).
312  *  Source quench causes it to drop back to 1.
313  *  tp_send() uses the smaller of (regular window, congestion window).
314  *  One retransmission strategy option is to have any retransmission
315  *	cause reset the congestion window back  to 1.
316  *
317  *	(cmd) is either PRC_QUENCH: source quench, or
318  *		PRC_QUENCH2: dest. quench (dec bit)
319  *
320  * RETURNS:
321  *
322  * SIDE EFFECTS:
323  *
324  * NOTES:
325  */
326 void
327 tp_quench( tpcb, cmd )
328 	struct tp_pcb *tpcb;
329 	int cmd;
330 {
331 	IFDEBUG(D_QUENCH)
332 		printf("tp_quench tpcb 0x%x ref 0x%x sufx 0x%x\n",
333 			tpcb, tpcb->tp_lref, *(u_short *)(tpcb->tp_lsuffix));
334 		printf("cong_win 0x%x decbit 0x%x \n",
335 			tpcb->tp_cong_win, tpcb->tp_decbit);
336 	ENDDEBUG
337 	switch(cmd) {
338 		case PRC_QUENCH:
339 			tpcb->tp_cong_win = 1;
340 			IncStat(ts_quench);
341 			break;
342 		case PRC_QUENCH2:
343 			tpcb->tp_cong_win = 1; /* might as well quench source also */
344 			tpcb->tp_decbit = TP_DECBIT_CLEAR_COUNT;
345 			IncStat(ts_rcvdecbit);
346 			break;
347 	}
348 }
349 
350 
351 /*
352  * NAME:	tp_netcmd()
353  *
354  * CALLED FROM:
355  *
356  * FUNCTION and ARGUMENTS:
357  *
358  * RETURNS:
359  *
360  * SIDE EFFECTS:
361  *
362  * NOTES:
363  */
364 tp_netcmd( tpcb, cmd )
365 	struct tp_pcb *tpcb;
366 	int cmd;
367 {
368 #ifdef TPCONS
369 	struct isopcb *isop;
370 	struct pklcd *lcp;
371 
372 	if (tpcb->tp_netservice != ISO_CONS)
373 		return;
374 	isop = (struct isopcb *)tpcb->tp_npcb;
375 	lcp = (struct pklcd *)isop->isop_chan;
376 	switch (cmd) {
377 
378 	case CONN_CLOSE:
379 	case CONN_REFUSE:
380 		if (isop->isop_refcnt == 1)
381 			pk_disconnect(lcp);
382 		isop->isop_chan = 0;
383 		isop->isop_refcnt = 0;
384 		break;
385 
386 	default:
387 		printf("tp_netcmd(0x%x, 0x%x) NOT IMPLEMENTED\n", tpcb, cmd);
388 		break;
389 	}
390 #else TPCONS
391 	printf("tp_netcmd(): X25 NOT CONFIGURED!!\n");
392 #endif
393 }
394 /*
395  * CALLED FROM:
396  *  tp_ctloutput() and tp_emit()
397  * FUNCTION and ARGUMENTS:
398  * 	Convert a class mask to the highest numeric value it represents.
399  */
400 
401 int
402 tp_mask_to_num(x)
403 	u_char x;
404 {
405 	register int j;
406 
407 	for(j = 4; j>=0 ;j--) {
408 		if(x & (1<<j))
409 			break;
410 	}
411 	ASSERT( (j == 4) || (j == 0) ); /* for now */
412 	if( (j != 4) && (j != 0) ) {
413 		printf("ASSERTION ERROR: tp_mask_to_num: x 0x%x j %d\n",
414 			x, j);
415 	}
416 	IFTRACE(D_TPINPUT)
417 		tptrace(TPPTmisc, "tp_mask_to_num(x) returns j", x, j, 0, 0);
418 	ENDTRACE
419 	IFDEBUG(D_TPINPUT)
420 		printf("tp_mask_to_num(0x%x) returns 0x%x\n", x, j);
421 	ENDDEBUG
422 	return j;
423 }
424 
425 static
426 copyQOSparms(src, dst)
427 	struct tp_conn_param *src, *dst;
428 {
429 	/* copy all but the bits stuff at the end */
430 #define COPYSIZE (12 * sizeof(short))
431 
432 	bcopy((caddr_t)src, (caddr_t)dst, COPYSIZE);
433 	dst->p_tpdusize = src->p_tpdusize;
434 	dst->p_ack_strat = src->p_ack_strat;
435 	dst->p_rx_strat = src->p_rx_strat;
436 #undef COPYSIZE
437 }
438 
439 /*
440  * CALLED FROM:
441  *  tp_usrreq on PRU_CONNECT and tp_input on receipt of CR
442  *
443  * FUNCTION and ARGUMENTS:
444  * 	route directly to x.25 if the address is type 37 - GROT.
445  *  furthermore, let TP0 handle only type-37 addresses
446  *
447  *	Since this assumes that its address argument is in a mbuf, the
448  *	parameter was changed to reflect this assumtion. This also
449  *	implies that an mbuf must be allocated when this is
450  *	called from tp_input
451  *
452  * RETURNS:
453  *	errno value	 :
454  *	EAFNOSUPPORT if can't find an nl_protosw for x.25 (really could panic)
455  *	ECONNREFUSED if trying to run TP0 with non-type 37 address
456  *  possibly other E* returned from cons_netcmd()
457  * NOTE:
458  *  Would like to eliminate as much of this as possible --
459  *  only one set of defaults (let the user set the parms according
460  *  to parameters provided in the directory service).
461  *  Left here for now 'cause we don't yet have a clean way to handle
462  *  it on the passive end.
463  */
464 int
465 tp_route_to( m, tpcb, channel)
466 	struct mbuf					*m;
467 	register struct tp_pcb		*tpcb;
468 	caddr_t 					channel;
469 {
470 	register struct sockaddr_iso *siso;	/* NOTE: this may be a sockaddr_in */
471 	extern struct tp_conn_param tp_conn_param[];
472 	struct pklcd *lcp = (struct pklcd *)channel;
473 	int error = 0;
474 
475 	siso = mtod(m, struct sockaddr_iso *);
476 	IFTRACE(D_CONN)
477 		tptraceTPCB(TPPTmisc,
478 		"route_to: so  afi netservice class",
479 		tpcb->tp_sock, siso->siso_addr.isoa_genaddr[0], tpcb->tp_netservice,
480 			tpcb->tp_class);
481 	ENDTRACE
482 	IFDEBUG(D_CONN)
483 		printf("tp_route_to( m x%x, channel 0x%x, tpcb 0x%x netserv 0x%x)\n",
484 			m, channel, tpcb, tpcb->tp_netservice);
485 		printf("m->mlen x%x, m->m_data:\n", m->m_len);
486 		dump_buf(mtod(m, caddr_t), m->m_len);
487 	ENDDEBUG
488 	if (siso->siso_family != tpcb->tp_domain) {
489 		error = EAFNOSUPPORT;
490 		goto done;
491 	}
492 	IFDEBUG(D_CONN)
493 		printf("tp_route_to  calling nlp_pcbconn, netserv %d\n",
494 			tpcb->tp_netservice);
495 	ENDDEBUG
496 #ifdef TPCONS
497 	if (lcp) {
498 		struct isopcb *isop = (struct isopcb *)lcp->lcd_upnext,
499 			*isop_new = (struct isopcb *)tpcb->tp_sock->so_pcb;
500 		remque(isop_new);
501 		free(isop_new, M_PCB);
502 		tpcb->tp_sock->so_pcb = (caddr_t)isop;
503 		if (isop->isop_refcnt == 0) {
504 			extern struct isopcb tp_isopcb;
505 			remque(isop);
506 			insque(isop, &tp_isopcb);
507 			isop->isop_head = &tp_isopcb;
508 			iso_putsufx(isop, tpcb->tp_lsuffix, tpcb->tp_lsuffixlen, TP_LOCAL);
509 		}
510 		/* else there are already connections sharing this */
511 		isop->isop_refcnt++;
512 	} else
513 #endif
514 		error = (tpcb->tp_nlproto->nlp_pcbconn)(tpcb->tp_sock->so_pcb, m);
515 	if( error )
516 		goto done;
517 
518 	{
519 		register int save_netservice = tpcb->tp_netservice;
520 
521 		switch(tpcb->tp_netservice) {
522 		case ISO_COSNS:
523 		case ISO_CLNS:
524 			/* This is a kludge but seems necessary so the passive end
525 			 * can get long enough timers. sigh.
526 			if( siso->siso_addr.osinet_idi[1] == (u_char)IDI_OSINET )
527 			 */
528 #define	IDI_OSINET	0x0004	/* bcd of "0004" */
529 			if( siso->siso_addr.isoa_genaddr[2] == (char)IDI_OSINET ) {
530 				if( tpcb->tp_dont_change_params == 0) {
531 					copyQOSparms( &tp_conn_param[ISO_COSNS],
532 							&tpcb->_tp_param);
533 				}
534 				tpcb->tp_flags |= TPF_NLQOS_PDN;
535 			}
536 			/* drop through to IN_CLNS*/
537 		case IN_CLNS:
538 			if (iso_localifa(siso))
539 				tpcb->tp_flags |= TPF_PEER_ON_SAMENET;
540 			if( (tpcb->tp_class & TP_CLASS_4)==0 ) {
541 				error = EPROTOTYPE;
542 				break;
543 			}
544 			tpcb->tp_class = TP_CLASS_4;  /* IGNORE dont_change_parms */
545 			break;
546 
547 		case ISO_CONS:
548 #ifdef TPCONS
549 			tpcb->tp_flags |= TPF_NLQOS_PDN;
550 			if( tpcb->tp_dont_change_params == 0 ) {
551 				copyQOSparms( &tp_conn_param[ISO_CONS],
552 							&tpcb->_tp_param);
553 			}
554 			/*
555 			 * for use over x.25 really need a small receive window,
556 			 * need to start slowly, need small max negotiable tpdu size,
557 			 * and need to use the congestion window to the max
558 			 * IGNORES tp_dont_change_params for these!
559 			 */
560 			if( tpcb->tp_sock->so_snd.sb_hiwat > 512 ) {
561 				(void) soreserve(tpcb->tp_sock, 512, 512 );/* GAG */
562 			}
563 			tpcb->tp_rx_strat =  TPRX_USE_CW;
564 
565 			if( (tpcb->tp_nlproto != &nl_protosw[ISO_CONS]) ) {
566 				IFDEBUG(D_CONN)
567 					printf(
568 					"tp_route_to( CHANGING nlproto old 0x%x new 0x%x)\n",
569 							tpcb->tp_nlproto , &nl_protosw[ISO_CONS]);
570 				ENDDEBUG
571 				tpcb->tp_nlproto = &nl_protosw[ISO_CONS];
572 			}
573 			/* class 4 doesn't need to open a vc now - may use one already
574 			 * opened or may open one only when it sends a pkt.
575 			 */
576 #else TPCONS
577 			error = ECONNREFUSED;
578 #endif TPCONS
579 			break;
580 		default:
581 			error = EPROTOTYPE;
582 		}
583 
584 		ASSERT( save_netservice == tpcb->tp_netservice);
585 	}
586 	if (error) {
587 		tp_netcmd( tpcb, CONN_CLOSE);
588 		goto done;
589 	}
590 	{	/* start with the global rtt, rtv stats */
591 		register int i =
592 		   (int) tpcb->tp_flags & (TPF_PEER_ON_SAMENET | TPF_NLQOS_PDN);
593 
594 		tpcb->tp_rtt = tp_stat.ts_rtt[i];
595 		tpcb->tp_rtv = tp_stat.ts_rtv[i];
596 	}
597 done:
598 	IFDEBUG(D_CONN)
599 		printf("tp_route_to  returns 0x%x\n", error);
600 	ENDDEBUG
601 	IFTRACE(D_CONN)
602 		tptraceTPCB(TPPTmisc, "route_to: returns: error netserv class", error,
603 			tpcb->tp_netservice, tpcb->tp_class, 0);
604 	ENDTRACE
605 	return error;
606 }
607 
608 
609 /* class zero version */
610 void
611 tp0_stash( tpcb, e )
612 	register struct tp_pcb		*tpcb;
613 	register struct tp_event	*e;
614 {
615 #ifndef lint
616 #define E e->ATTR(DT_TPDU)
617 #else lint
618 #define E e->ev_union.EV_DT_TPDU
619 #endif lint
620 
621 	register struct sockbuf *sb = &tpcb->tp_sock->so_rcv;
622 	register struct isopcb *isop = (struct isopcb *)tpcb->tp_npcb;
623 
624 	IFPERF(tpcb)
625 		PStat(tpcb, Nb_from_ll) += E.e_datalen;
626 		tpmeas(tpcb->tp_lref, TPtime_from_ll, &e->e_time,
627 				E.e_seq, PStat(tpcb, Nb_from_ll), E.e_datalen);
628 	ENDPERF
629 
630 	IFDEBUG(D_STASH)
631 		printf("stash EQ: seq 0x%x datalen 0x%x eot 0x%x",
632 		E.e_seq, E.e_datalen, E.e_eot);
633 	ENDDEBUG
634 
635 	IFTRACE(D_STASH)
636 		tptraceTPCB(TPPTmisc, "stash EQ: seq len eot",
637 		E.e_seq, E.e_datalen, E.e_eot, 0);
638 	ENDTRACE
639 
640 	if ( E.e_eot ) {
641 		register struct mbuf *n = E.e_data;
642 		n->m_flags |= M_EOR;
643 		n->m_act = MNULL; /* set on tp_input */
644 	}
645 	sbappend(sb, E.e_data);
646 	IFDEBUG(D_STASH)
647 		dump_mbuf(sb->sb_mb, "stash 0: so_rcv after appending");
648 	ENDDEBUG
649 	if (tpcb->tp_netservice != ISO_CONS)
650 		printf("tp0_stash: tp running over something wierd\n");
651 	else {
652 		register struct pklcd *lcp = (struct pklcd *)isop->isop_chan;
653 		pk_flowcontrol(lcp, sbspace(sb) <= 0, 1);
654 	}
655 }
656 
657 void
658 tp0_openflow(tpcb)
659 register struct tp_pcb *tpcb;
660 {
661 	register struct isopcb *isop = (struct isopcb *)tpcb->tp_npcb;
662 	if (tpcb->tp_netservice != ISO_CONS)
663 		printf("tp0_openflow: tp running over something wierd\n");
664 	else {
665 		register struct pklcd *lcp = (struct pklcd *)isop->isop_chan;
666 		if (lcp->lcd_rxrnr_condition)
667 			pk_flowcontrol(lcp, 0, 0);
668 	}
669 }
670 #ifndef TPCONS
671 static
672 pk_flowcontrol() {}
673 #endif
674 
675 #ifdef TP_PERF_MEAS
676 /*
677  * CALLED FROM:
678  *  tp_ctloutput() when the user sets TPOPT_PERF_MEAS on
679  *  and tp_newsocket() when a new connection is made from
680  *  a listening socket with tp_perf_on == true.
681  * FUNCTION and ARGUMENTS:
682  *  (tpcb) is the usual; this procedure gets a clear cluster mbuf for
683  *  a tp_pmeas structure, and makes tpcb->tp_p_meas point to it.
684  * RETURN VALUE:
685  *  ENOBUFS if it cannot get a cluster mbuf.
686  */
687 
688 int
689 tp_setup_perf(tpcb)
690 	register struct tp_pcb *tpcb;
691 {
692 	register struct mbuf *q;
693 
694 	if( tpcb->tp_p_meas == 0 ) {
695 		MGET(q, M_WAITOK, MT_PCB);
696 		if (q == 0)
697 			return ENOBUFS;
698 		MCLGET(q, M_WAITOK);
699 		if ((q->m_flags & M_EXT) == 0) {
700 			(void) m_free(q);
701 			return ENOBUFS;
702 		}
703 		q->m_len = sizeof (struct tp_pmeas);
704 		tpcb->tp_p_mbuf = q;
705 		tpcb->tp_p_meas = mtod(q, struct tp_pmeas *);
706 		bzero( (caddr_t)tpcb->tp_p_meas, sizeof (struct tp_pmeas) );
707 		IFDEBUG(D_PERF_MEAS)
708 			printf(
709 			"tpcb 0x%x so 0x%x ref 0x%x tp_p_meas 0x%x tp_perf_on 0x%x\n",
710 				tpcb, tpcb->tp_sock, tpcb->tp_lref,
711 				tpcb->tp_p_meas, tpcb->tp_perf_on);
712 		ENDDEBUG
713 		tpcb->tp_perf_on = 1;
714 	}
715 	return 0;
716 }
717 #endif TP_PERF_MEAS
718 
719 #ifdef ARGO_DEBUG
720 dump_addr (addr)
721 	register struct sockaddr *addr;
722 {
723 	switch( addr->sa_family ) {
724 		case AF_INET:
725 			dump_inaddr((struct sockaddr_in *)addr);
726 			break;
727 #ifdef ISO
728 		case AF_ISO:
729 			dump_isoaddr((struct sockaddr_iso *)addr);
730 			break;
731 #endif ISO
732 		default:
733 			printf("BAD AF: 0x%x\n", addr->sa_family);
734 			break;
735 	}
736 }
737 
738 #define	MAX_COLUMNS	8
739 /*
740  *	Dump the buffer to the screen in a readable format. Format is:
741  *
742  *		hex/dec  where hex is the hex format, dec is the decimal format.
743  *		columns of hex/dec numbers will be printed, followed by the
744  *		character representations (if printable).
745  */
746 Dump_buf(buf, len)
747 caddr_t	buf;
748 int		len;
749 {
750 	int		i,j;
751 
752 	printf("Dump buf 0x%x len 0x%x\n", buf, len);
753 	for (i = 0; i < len; i += MAX_COLUMNS) {
754 		printf("+%d:\t", i);
755 		for (j = 0; j < MAX_COLUMNS; j++) {
756 			if (i + j < len) {
757 				printf("%x/%d\t", buf[i+j]&0xff, buf[i+j]);
758 			} else {
759 				printf("	");
760 			}
761 		}
762 
763 		for (j = 0; j < MAX_COLUMNS; j++) {
764 			if (i + j < len) {
765 				if (((buf[i+j]) > 31) && ((buf[i+j]) < 128))
766 					printf("%c", buf[i+j]&0xff);
767 				else
768 					printf(".");
769 			}
770 		}
771 		printf("\n");
772 	}
773 }
774 
775 
776 #endif ARGO_DEBUG
777 
778