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