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