FreeBSD/Linux Kernel Cross Reference
sys/netiso/tp_input.c
1 /* $NetBSD: tp_input.c,v 1.17 2004/02/13 17:56:17 wiz Exp $ */
2
3 /*-
4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 * @(#)tp_input.c 8.1 (Berkeley) 6/10/93
32 */
33
34 /***********************************************************
35 Copyright IBM Corporation 1987
36
37 All Rights Reserved
38
39 Permission to use, copy, modify, and distribute this software and its
40 documentation for any purpose and without fee is hereby granted,
41 provided that the above copyright notice appear in all copies and that
42 both that copyright notice and this permission notice appear in
43 supporting documentation, and that the name of IBM not be
44 used in advertising or publicity pertaining to distribution of the
45 software without specific, written prior permission.
46
47 IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
48 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
49 IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
50 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
51 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
52 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
53 SOFTWARE.
54
55 ******************************************************************/
56
57 /*
58 * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
59 */
60 /*
61 * tp_input() gets an mbuf chain from ip. Actually, not directly from ip,
62 * because ip calls a net-level routine that strips off the net header and
63 * then calls tp_input(), passing the proper type of addresses for the
64 * address family in use (how it figures out which AF is not yet determined.)
65 *
66 * Decomposing the tpdu is some of the most laughable code. The variable-length
67 * parameters and the problem of non-aligned memory references necessitates
68 * such abominations as the macros WHILE_OPTIONS (q.v. below) to loop through
69 * the header and decompose it.
70 *
71 * The routine tp_newsocket() is called when a CR comes in for a listening
72 * socket. tp_input calls sonewconn() and tp_newsocket() to set up the
73 * "child" socket. Most tpcb values are copied from the parent tpcb into the
74 * child.
75 *
76 * Also in here is tp_headersize() (grot) which tells the expected size of a tp
77 * header, to be used by other layers. It's in here because it uses the
78 * static structure tpdu_info.
79 */
80
81 #include <sys/cdefs.h>
82 __KERNEL_RCSID(0, "$NetBSD: tp_input.c,v 1.17 2004/02/13 17:56:17 wiz Exp $");
83
84 #include <sys/param.h>
85 #include <sys/systm.h>
86 #include <sys/mbuf.h>
87 #include <sys/socket.h>
88 #include <sys/socketvar.h>
89 #include <sys/domain.h>
90 #include <sys/protosw.h>
91 #include <sys/errno.h>
92 #include <sys/time.h>
93 #include <sys/kernel.h>
94
95 #include <net/if.h>
96
97 #include <netiso/iso.h>
98 #include <netiso/iso_errno.h>
99 #include <netiso/iso_pcb.h>
100 #include <netiso/tp_param.h>
101 #include <netiso/tp_timer.h>
102 #include <netiso/tp_stat.h>
103 #include <netiso/tp_pcb.h>
104 #include <netiso/argo_debug.h>
105 #include <netiso/tp_trace.h>
106 #include <netiso/tp_tpdu.h>
107 #include <netiso/tp_var.h>
108 #include <netiso/iso_var.h>
109
110 #ifdef TRUE
111 #undef FALSE
112 #undef TRUE
113 #endif
114 #include <netccitt/x25.h>
115 #include <netccitt/pk.h>
116 #include <netccitt/pk_var.h>
117
118 #include <machine/stdarg.h>
119
120 static struct socket *tp_newsocket __P((struct socket *, struct sockaddr *,
121 caddr_t, u_int, u_int));
122
123 struct mbuf *
124 tp_inputprep(m)
125 struct mbuf *m;
126 {
127 int hdrlen;
128
129 #ifdef ARGO_DEBUG
130 if (argo_debug[D_TPINPUT]) {
131 printf("tp_inputprep: m %p\n", m);
132 }
133 #endif
134
135 while (m->m_len < 1) {
136 /*
137 * The "m_free" logic if( (m = m_free(m)) == NULL ) return
138 * (struct mbuf *)0; would cause a system crash if ever
139 * executed. This logic will be executed if the first mbuf in
140 * the chain only contains a CLNP header. The m_free routine
141 * will release the mbuf containing the CLNP header from the
142 * chain and the new head of the chain will not have the
143 * M_PKTHDR bit set. This routine, tp_inputprep, will
144 * eventually call the "sbappendaddr" routine. "sbappendaddr"
145 * calls "panic" if M_PKTHDR is not set. m_pullup is a cheap
146 * way of keeping the head of the chain from being freed.
147 */
148 if ((m = m_pullup(m, 1)) == NULL)
149 return (NULL);
150 }
151 if (((long) m->m_data) & 0x3) {
152 /*
153 * If we are not 4-byte aligned, we have to be above the
154 * beginning of the mbuf, and it is ok just to slide it back.
155 */
156 caddr_t ocp = m->m_data;
157
158 m->m_data = (caddr_t) (((long) m->m_data) & ~0x3);
159 bcopy(ocp, m->m_data, (unsigned) m->m_len);
160 }
161 CHANGE_MTYPE(m, TPMT_DATA);
162
163 /*
164 * we KNOW that there is at least 1 byte in this mbuf and that it is
165 * hdr->tpdu_li XXXXXXX!
166 */
167
168 hdrlen = 1 + *mtod(m, u_char *);
169
170 /*
171 * now pull up the whole tp header
172 */
173 if (m->m_len < hdrlen) {
174 if ((m = m_pullup(m, hdrlen)) == NULL) {
175 IncStat(ts_recv_drop);
176 return (struct mbuf *) 0;
177 }
178 }
179 #ifdef ARGO_DEBUG
180 if (argo_debug[D_INPUT]) {
181 printf(
182 " at end: m %p hdr->tpdu_li 0x%x m_len 0x%x\n", m,
183 hdrlen, m->m_len);
184 }
185 #endif
186 return m;
187 }
188
189 /*
190 * begin groan -- this array and the following macros allow you to step
191 * through the parameters of the variable part of a header note that if for
192 * any reason the values of the **_TPDU macros (in tp_events.h) should
193 * change, this array has to be rearranged
194 */
195
196 #define TP_LEN_CLASS_0_INDEX 2
197 #define TP_MAX_DATA_INDEX 3
198
199 static u_char tpdu_info[][4] =
200 {
201 /* length max data len */
202 /* reg fmt xtd fmt class 0 */
203 /* UNUSED 0x0 */ { 0x0, 0x0, 0x0, 0x0 },
204 /* XPD_TPDU_type 0x1 */ { 0x5, 0x8, 0x0, TP_MAX_XPD_DATA },
205 /* XAK_TPDU_type 0x2 */ { 0x5, 0x8, 0x0, 0x0 },
206 /* GR_TPDU_type 0x3 */ { 0x0, 0x0, 0x0, 0x0 },
207 /* UNUSED 0x4 */ { 0x0, 0x0, 0x0, 0x0 },
208 /* UNUSED 0x5 */ { 0x0, 0x0, 0x0, 0x0 },
209 /* AK_TPDU_type 0x6 */ { 0x5, 0xa, 0x0, 0x0 },
210 /* ER_TPDU_type 0x7 */ { 0x5, 0x5, 0x0, 0x0 },
211 /* DR_TPDU_type 0x8 */ { 0x7, 0x7, 0x7, TP_MAX_DR_DATA },
212 /* UNUSED 0x9 */ { 0x0, 0x0, 0x0, 0x0 },
213 /* UNUSED 0xa */ { 0x0, 0x0, 0x0, 0x0 },
214 /* UNUSED 0xb */ { 0x0, 0x0, 0x0, 0x0 },
215 /* DC_TPDU_type 0xc */ { 0x6, 0x6, 0x0, 0x0 },
216 /* CC_TPDU_type 0xd */ { 0x7, 0x7, 0x7, TP_MAX_CC_DATA },
217 /* CR_TPDU_type 0xe */ { 0x7, 0x7, 0x7, TP_MAX_CR_DATA },
218 /* DT_TPDU_type 0xf */ { 0x5, 0x8, 0x3, 0x0 },
219 };
220
221 #define CHECK(Phrase, Erval, Stat, Whattodo, Loc)\
222 if (Phrase) {error = (Erval); errlen = (int)(Loc); IncStat(Stat);\
223 goto Whattodo; }
224
225 /*
226 * WHENEVER YOU USE THE FOLLOWING MACRO, BE SURE THE TPDUTYPE IS A LEGIT
227 * VALUE FIRST!
228 */
229
230 #define WHILE_OPTIONS(P, hdr, format)\
231 { caddr_t P = tpdu_info[(hdr)->tpdu_type][(format)] + (caddr_t)hdr;\
232 caddr_t PLIM = 1 + hdr->tpdu_li + (caddr_t)hdr;\
233 for (;; P += 2 + ((struct tp_vbp *)P)->tpv_len) {\
234 CHECK((P > PLIM), E_TP_LENGTH_INVAL, ts_inv_length,\
235 respond, P - (caddr_t)hdr);\
236 if (P == PLIM) break;
237
238 #define END_WHILE_OPTIONS(P) } }
239
240 /* end groan */
241
242 /*
243 * NAME: tp_newsocket()
244 *
245 * CALLED FROM:
246 * tp_input() on incoming CR, when a socket w/ the called suffix
247 * is awaiting a connection request
248 *
249 * FUNCTION and ARGUMENTS:
250 * Create a new socket structure, attach to it a new transport pcb,
251 * using a copy of the net level pcb for the parent socket.
252 * (so) is the parent socket.
253 * (fname) is the foreign address (all that's used is the nsap portion)
254 *
255 * RETURN VALUE:
256 * a new socket structure, being this end of the newly formed connection.
257 *
258 * SIDE EFFECTS:
259 * Sets a few things in the tpcb and net level pcb
260 *
261 * NOTES:
262 */
263 static struct socket *
264 tp_newsocket(so, fname, cons_channel, class_to_use, netservice)
265 struct socket *so;
266 struct sockaddr *fname;
267 caddr_t cons_channel;
268 u_int class_to_use;
269 u_int netservice;
270 {
271 struct tp_pcb *tpcb = sototpcb(so); /* old tpcb, needed
272 * below */
273 struct tp_pcb *newtpcb;
274
275 /*
276 * sonewconn() gets a new socket structure, a new lower layer pcb and
277 * a new tpcb, but the pcbs are unnamed (not bound)
278 */
279 #ifdef TPPT
280 if (tp_traceflags[D_NEWSOCK]) {
281 tptraceTPCB(TPPTmisc, "newsock: listg_so, _tpcb, so_head",
282 so, tpcb, so->so_head, 0);
283 }
284 #endif
285
286 if ((so = sonewconn(so, SS_ISCONFIRMING)) == (struct socket *) 0)
287 return so;
288 #ifdef TPPT
289 if (tp_traceflags[D_NEWSOCK]) {
290 tptraceTPCB(TPPTmisc, "newsock: after newconn so, so_head",
291 so, so->so_head, 0, 0);
292 }
293 #endif
294
295 #ifdef ARGO_DEBUG
296 if (argo_debug[D_NEWSOCK]) {
297 printf("tp_newsocket(channel %p) after sonewconn so %p \n",
298 cons_channel, so);
299 dump_addr(fname);
300 {
301 struct socket *t, *head;
302
303 head = so->so_head;
304 t = so;
305 printf("so %p so_head %p so_q0 %p, q0len %d\n",
306 t, t->so_head, t->so_q0.tqh_first, t->so_q0len);
307 while ((t = t->so_q0.tqh_first) && t != so && t != head)
308 printf("so %p so_head %p so_q0 %p, q0len %d\n",
309 t, t->so_head, t->so_q0.tqh_first,
310 t->so_q0len);
311 }
312 }
313 #endif
314
315 /*
316 * before we clobber the old tpcb ptr, get these items from the
317 * parent pcb
318 */
319 newtpcb = sototpcb(so);
320 newtpcb->_tp_param = tpcb->_tp_param;
321 newtpcb->tp_flags = tpcb->tp_flags;
322 newtpcb->tp_lcredit = tpcb->tp_lcredit;
323 newtpcb->tp_l_tpdusize = tpcb->tp_l_tpdusize;
324 newtpcb->tp_lsuffixlen = tpcb->tp_lsuffixlen;
325 bcopy(tpcb->tp_lsuffix, newtpcb->tp_lsuffix, newtpcb->tp_lsuffixlen);
326
327 if ( /* old */ tpcb->tp_ucddata) {
328 /*
329 * These data are the connect- , confirm- or disconnect-
330 * data.
331 */
332 struct mbuf *conndata;
333
334 conndata = m_copy(tpcb->tp_ucddata, 0, (int) M_COPYALL);
335 #ifdef ARGO_DEBUG
336 if (argo_debug[D_CONN]) {
337 dump_mbuf(conndata, "conndata after mcopy");
338 }
339 #endif
340 newtpcb->tp_ucddata = conndata;
341 }
342 tpcb = newtpcb;
343 tpcb->tp_state = TP_LISTENING;
344 tpcb->tp_class = class_to_use;
345 tpcb->tp_netservice = netservice;
346
347
348 ASSERT(fname != 0); /* just checking */
349 if (fname) {
350 /*
351 * tp_route_to takes its address argument in the form of an mbuf.
352 */
353 struct mbuf *m;
354 int err;
355
356 MGET(m, M_DONTWAIT, MT_SONAME); /* mbuf type used is
357 * confusing */
358 if (m) {
359 /*
360 * this seems a bit grotesque, but tp_route_to expects
361 * an mbuf * instead of simply a sockaddr; it calls the ll
362 * pcb_connect, which expects the name/addr in an mbuf as well.
363 * sigh.
364 */
365 bcopy((caddr_t) fname, mtod(m, caddr_t), fname->sa_len);
366 m->m_len = fname->sa_len;
367
368 /*
369 * grot : have to say the kernel can override params
370 * in the passive open case
371 */
372 tpcb->tp_dont_change_params = 0;
373 err = tp_route_to(m, tpcb, cons_channel);
374 m_free(m);
375
376 if (!err)
377 goto ok;
378 }
379 #ifdef ARGO_DEBUG
380 if (argo_debug[D_CONN]) {
381 printf("tp_route_to FAILED! detaching tpcb %p, so %p\n",
382 tpcb, so);
383 }
384 #endif
385 (void) tp_detach(tpcb);
386 return 0;
387 }
388 ok:
389 #ifdef ARGO_DEBUG
390 if (argo_debug[D_TPINPUT]) {
391 printf("tp_newsocket returning so %p, sototpcb(so) %p\n",
392 so, sototpcb(so));
393 }
394 #endif
395 return so;
396 }
397
398 /*
399 * NAME: tp_input()
400 *
401 * CALLED FROM: net layer input routine
402 *
403 * FUNCTION and ARGUMENTS: Process an incoming TPDU (m), finding the associated
404 * tpcb if there is one. Create the appropriate type of event and call the
405 * driver. (faddr) and (laddr) are the foreign and local addresses.
406 *
407 * When tp_input() is called we KNOW that the ENTIRE TP HEADER has been
408 * m_pullup-ed.
409 *
410 * RETURN VALUE: Nada
411 *
412 * SIDE EFFECTS: When using COSNS it may affect the state of the net-level pcb
413 *
414 * NOTE: The initial value of acktime is 2 so that we will never have a 0 value
415 * for tp_peer_acktime. It gets used in the computation of the
416 * retransmission timer value, and so it mustn't be zero. 2 seems like a
417 * reasonable minimum.
418 */
419 void
420 #if __STDC__
421 tp_input(struct mbuf *m, ...)
422 #else
423 tp_input(m, va_alist)
424 struct mbuf *m;
425 va_dcl
426 #endif
427 {
428 struct sockaddr *faddr, *laddr; /* NSAP addresses */
429 caddr_t cons_channel;
430 int (*dgout_routine) __P((struct mbuf *, ...));
431 int ce_bit;
432 struct tp_pcb *tpcb;
433 struct tpdu *hdr;
434 struct socket *so;
435 struct tp_event e;
436 int error;
437 unsigned dutype;
438 u_short dref, sref, acktime, subseq;
439 u_char preferred_class, class_to_use, pdusize;
440 u_char opt, dusize, addlopt, version = 0;
441 #ifdef TP_PERF_MEAS
442 u_char perf_meas;
443 #endif /* TP_PERF_MEAS */
444 u_char fsufxlen, lsufxlen;
445 caddr_t fsufxloc, lsufxloc;
446 int tpdu_len;
447 u_int takes_data;
448 u_int fcc_present;
449 int errlen;
450 struct tp_conn_param tpp;
451 va_list ap;
452
453 va_start(ap, m);
454 faddr = va_arg(ap, struct sockaddr *);
455 laddr = va_arg(ap, struct sockaddr *);
456 cons_channel = va_arg(ap, caddr_t);
457 /* XXX: Does va_arg does not work for function ptrs */
458 dgout_routine = (int (*) __P((struct mbuf *, ...))) va_arg(ap, void *);
459 ce_bit = va_arg(ap, int);
460 va_end(ap);
461
462 again:
463 hdr = mtod(m, struct tpdu *);
464 tpcb = 0;
465 error = errlen = tpdu_len = 0;
466 takes_data = fcc_present = FALSE;
467 acktime = 2;
468 sref = subseq = 0;
469 fsufxloc = lsufxloc = NULL;
470 fsufxlen = lsufxlen =
471 preferred_class = class_to_use = pdusize = addlopt = 0;
472 dusize = TP_DFL_TPDUSIZE;
473 #ifdef TP_PERF_MEAS
474 GET_CUR_TIME(&e.e_time);
475 perf_meas = 0;
476 #endif /* TP_PERF_MEAS */
477
478 #ifdef ARGO_DEBUG
479 if (argo_debug[D_TPINPUT]) {
480 printf("tp_input(%p, ... %p)\n", m, cons_channel);
481 }
482 #endif
483
484
485 /*
486 * get the actual tpdu length - necessary for monitoring and for
487 * checksumming
488 *
489 * Also, maybe measure the mbuf chain lengths and sizes.
490 */
491
492 {
493 struct mbuf *n = m;
494 #ifdef ARGO_DEBUG
495 int chain_length = 0;
496 #endif /* ARGO_DEBUG */
497
498 for (;;) {
499 tpdu_len += n->m_len;
500 #ifdef ARGO_DEBUG
501 if (argo_debug[D_MBUF_MEAS]) {
502 if (n->m_flags & M_EXT) {
503 IncStat(ts_mb_cluster);
504 } else {
505 IncStat(ts_mb_small);
506 }
507 chain_length++;
508 }
509 #endif
510 if (n->m_next == NULL) {
511 break;
512 }
513 n = n->m_next;
514 }
515 #ifdef ARGO_DEBUG
516 if (argo_debug[D_MBUF_MEAS]) {
517 if (chain_length > 16)
518 chain_length = 0; /* zero used for
519 * anything > 16 */
520 tp_stat.ts_mb_len_distr[chain_length]++;
521 }
522 #endif
523 }
524 #ifdef TPPT
525 if (tp_traceflags[D_TPINPUT]) {
526 tptraceTPCB(TPPTtpduin, hdr->tpdu_type, hdr, hdr->tpdu_li + 1,
527 tpdu_len, 0);
528 }
529 #endif
530
531 dref = ntohs((short) hdr->tpdu_dref);
532 sref = ntohs((short) hdr->tpdu_sref);
533 dutype = (int) hdr->tpdu_type;
534
535 #ifdef ARGO_DEBUG
536 if (argo_debug[D_TPINPUT]) {
537 printf("input: dutype 0x%x cons_channel %p dref 0x%x\n",
538 dutype, cons_channel, dref);
539 printf("input: dref 0x%x sref 0x%x\n", dref, sref);
540 }
541 #endif
542 #ifdef TPPT
543 if (tp_traceflags[D_TPINPUT]) {
544 tptrace(TPPTmisc, "channel dutype dref ",
545 cons_channel, dutype, dref, 0);
546 }
547 #endif
548
549
550 #ifdef ARGO_DEBUG
551 if ((dutype < TP_MIN_TPDUTYPE) || (dutype > TP_MAX_TPDUTYPE)) {
552 printf("BAD dutype! 0x%x, channel %p dref 0x%x\n",
553 dutype, cons_channel, dref);
554 dump_buf(m, sizeof(struct mbuf));
555
556 IncStat(ts_inv_dutype);
557 goto discard;
558 }
559 #endif /* ARGO_DEBUG */
560
561 CHECK((dutype < TP_MIN_TPDUTYPE || dutype > TP_MAX_TPDUTYPE),
562 E_TP_INV_TPDU, ts_inv_dutype, respond,
563 2);
564 /*
565 * unfortunately we can't take the address of the tpdu_type field,
566 * since it's a bit field - so we just use the constant offset 2
567 */
568
569 /*
570 * Now this isn't very neat but since you locate a pcb one way at the
571 * beginning of connection establishment, and by the dref for each
572 * tpdu after that, we have to treat CRs differently
573 */
574 if (dutype == CR_TPDU_type) {
575 u_char alt_classes = 0;
576
577 preferred_class = 1 << hdr->tpdu_CRclass;
578 opt = hdr->tpdu_CRoptions;
579
580 WHILE_OPTIONS(P, hdr, 1) /* { */
581 switch (vbptr(P)->tpv_code) {
582
583 case TPP_tpdu_size:
584 vb_getval(P, u_char, dusize);
585 #ifdef ARGO_DEBUG
586 if (argo_debug[D_TPINPUT]) {
587 printf("CR dusize 0x%x\n", dusize);
588 }
589 #endif
590 /* COS tests: NBS IA (Dec. 1987) Sec. 4.5.2.1 */
591 if (dusize < TP_MIN_TPDUSIZE || dusize > TP_MAX_TPDUSIZE)
592 dusize = TP_DFL_TPDUSIZE;
593 break;
594 case TPP_ptpdu_size:
595 switch (vbptr(P)->tpv_len) {
596 case 1:
597 pdusize = vbval(P, u_char);
598 break;
599 case 2:
600 pdusize = ntohs(vbval(P, u_short));
601 break;
602 default:;
603 #ifdef ARGO_DEBUG
604 if (argo_debug[D_TPINPUT]) {
605 printf("malformed prefered TPDU option\n");
606 }
607 #endif
608 }
609 break;
610 case TPP_addl_opt:
611 vb_getval(P, u_char, addlopt);
612 break;
613 case TPP_calling_sufx:
614 /*
615 * could use vb_getval, but we want to save the loc &
616 * len for later use
617 */
618 fsufxloc = (caddr_t) & vbptr(P)->tpv_val;
619 fsufxlen = vbptr(P)->tpv_len;
620 #ifdef ARGO_DEBUG
621 if (argo_debug[D_TPINPUT]) {
622 printf("CR fsufx:");
623 {
624 int j;
625 for (j = 0; j < fsufxlen; j++) {
626 printf(" 0x%x. ", *((caddr_t) (fsufxloc + j)));
627 }
628 printf("\n");
629 }
630 }
631 #endif
632 break;
633 case TPP_called_sufx:
634 /*
635 * could use vb_getval, but we want to save the loc &
636 * len for later use
637 */
638 lsufxloc = (caddr_t) & vbptr(P)->tpv_val;
639 lsufxlen = vbptr(P)->tpv_len;
640 #ifdef ARGO_DEBUG
641 if (argo_debug[D_TPINPUT]) {
642 printf("CR lsufx:");
643 {
644 int j;
645 for (j = 0; j < lsufxlen; j++) {
646 printf(" 0x%x. ", *((u_char *) (lsufxloc + j)));
647 }
648 printf("\n");
649 }
650 }
651 #endif
652 break;
653
654 #ifdef TP_PERF_MEAS
655 case TPP_perf_meas:
656 vb_getval(P, u_char, perf_meas);
657 break;
658 #endif /* TP_PERF_MEAS */
659
660 case TPP_vers:
661 /* not in class 0; 1 octet; in CR_TPDU only */
662 /*
663 * COS tests says if version wrong, use default
664 * version!?XXX
665 */
666 CHECK((vbval(P, u_char) != TP_VERSION),
667 E_TP_INV_PVAL, ts_inv_pval, setversion,
668 (1 + (caddr_t) & vbptr(P)->tpv_val - (caddr_t) hdr));
669 setversion:
670 version = vbval(P, u_char);
671 break;
672 case TPP_acktime:
673 vb_getval(P, u_short, acktime);
674 acktime = ntohs(acktime);
675 acktime = acktime / 500; /* convert to slowtimo
676 * ticks */
677 if ((short) acktime <= 0)
678 acktime = 2; /* don't allow a bad peer to
679 * screw us up */
680 #ifdef ARGO_DEBUG
681 if (argo_debug[D_TPINPUT]) {
682 printf("CR acktime 0x%x\n", acktime);
683 }
684 #endif
685 break;
686
687 case TPP_alt_class:
688 {
689 u_char *aclass = 0;
690 int i;
691 static u_char bad_alt_classes[5] =
692 {~0, ~3, ~5, ~0xf, ~0x1f};
693
694 aclass =
695 (u_char *) & (((struct tp_vbp *) P)->tpv_val);
696 for (i = ((struct tp_vbp *) P)->tpv_len; i > 0; i--) {
697 alt_classes |= (1 << ((*aclass++) >> 4));
698 }
699 CHECK((bad_alt_classes[hdr->tpdu_CRclass] & alt_classes),
700 E_TP_INV_PVAL, ts_inv_aclass, respond,
701 ((caddr_t) aclass) - (caddr_t) hdr);
702 #ifdef ARGO_DEBUG
703 if (argo_debug[D_TPINPUT]) {
704 printf("alt_classes 0x%x\n", alt_classes);
705 }
706 #endif
707 }
708 break;
709
710 case TPP_security:
711 case TPP_residER:
712 case TPP_priority:
713 case TPP_transdelay:
714 case TPP_throughput:
715 case TPP_addl_info:
716 case TPP_subseq:
717 default:
718 #ifdef ARGO_DEBUG
719 if (argo_debug[D_TPINPUT]) {
720 printf("param ignored CR_TPDU code= 0x%x\n",
721 vbptr(P)->tpv_code);
722 }
723 #endif
724 IncStat(ts_param_ignored);
725 break;
726
727 case TPP_checksum:
728 #ifdef ARGO_DEBUG
729 if (argo_debug[D_TPINPUT]) {
730 printf("CR before cksum\n");
731 }
732 #endif
733
734 CHECK(iso_check_csum(m, tpdu_len),
735 E_TP_INV_PVAL, ts_bad_csum, discard, 0)
736 #ifdef ARGO_DEBUG
737 if (argo_debug[D_TPINPUT]) {
738 printf("CR before cksum\n");
739 }
740 #endif
741 break;
742 }
743
744 /* } */ END_WHILE_OPTIONS(P)
745 if (lsufxlen == 0) {
746 /* can't look for a tpcb w/o any called sufx */
747 error = E_TP_LENGTH_INVAL;
748 IncStat(ts_inv_sufx);
749 goto respond;
750 } else {
751 struct tp_pcb *t;
752 /*
753 * The intention here is to trap all CR requests
754 * to a given nsap, for constructing transport
755 * service bridges at user level; so these
756 * intercepts should precede the normal listens.
757 * Phrasing the logic in this way also allows for
758 * mop-up listeners, which we don't currently implement.
759 * We also wish to have a single socket be able to
760 * listen over any network service provider,
761 * (cons or clns or ip).
762 */
763 for (t = tp_listeners; t; t = t->tp_nextlisten)
764 if ((t->tp_lsuffixlen == 0 ||
765 (lsufxlen == t->tp_lsuffixlen &&
766 bcmp(lsufxloc, t->tp_lsuffix, lsufxlen) == 0)) &&
767 ((t->tp_flags & TPF_GENERAL_ADDR) ||
768 (laddr->sa_family == t->tp_domain &&
769 (*t->tp_nlproto->nlp_cmpnetaddr)
770 (t->tp_npcb, laddr, TP_LOCAL))))
771 break;
772
773 CHECK(t == 0, E_TP_NO_SESSION, ts_inv_sufx, respond,
774 (1 + 2 + (caddr_t) & hdr->_tpduf - (caddr_t) hdr))
775 /*
776 * _tpduf is the fixed part; add 2 to get the dref
777 * bits of the fixed part (can't take the address of
778 * a bit field)
779 */
780 #ifdef ARGO_DEBUG
781 if (argo_debug[D_TPINPUT]) {
782 printf("checking if dup CR\n");
783 }
784 #endif
785 tpcb = t;
786 for (t = tpcb->tp_next; t != tpcb; t = t->tp_next) {
787 if (sref != t->tp_fref)
788 continue;
789 if ((*tpcb->tp_nlproto->nlp_cmpnetaddr) (
790 t->tp_npcb, faddr, TP_FOREIGN)) {
791 #ifdef ARGO_DEBUG
792 if (argo_debug[D_TPINPUT]) {
793 printf("duplicate CR discarded\n");
794 }
795 #endif
796 goto discard;
797 }
798 }
799 #ifdef TPPT
800 if (tp_traceflags[D_TPINPUT]) {
801 tptrace(TPPTmisc, "tp_input: tpcb *lsufxloc tpstate",
802 tpcb, *lsufxloc, tpcb->tp_state, 0);
803 }
804 #endif
805 }
806
807 /*
808 * WE HAVE A TPCB already know that the classes in the CR
809 * match at least one class implemented, but we don't know
810 * yet if they include any classes permitted by this server.
811 */
812
813 #ifdef ARGO_DEBUG
814 if (argo_debug[D_TPINPUT]) {
815 printf("HAVE A TPCB 1: %p\n", tpcb);
816 }
817 #endif
818 #ifdef ARGO_DEBUG
819 if (argo_debug[D_CONN]) {
820 printf(
821 "CR: bef CHKS: flags 0x%x class_to_use 0x%x alt 0x%x opt 0x%x tp_class 0x%x\n",
822 tpcb->tp_flags, class_to_use, alt_classes, opt, tpcb->tp_class);
823 }
824 #endif
825 /* tpcb->tp_class doesn't include any classes not implemented */
826 class_to_use = (preferred_class & tpcb->tp_class);
827 if ((class_to_use = preferred_class & tpcb->tp_class) == 0)
828 class_to_use = alt_classes & tpcb->tp_class;
829
830 class_to_use = 1 << tp_mask_to_num(class_to_use);
831
832 {
833 tpp = tpcb->_tp_param;
834 tpp.p_class = class_to_use;
835 tpp.p_tpdusize = dusize;
836 tpp.p_ptpdusize = pdusize;
837 tpp.p_xtd_format = (opt & TPO_XTD_FMT) == TPO_XTD_FMT;
838 tpp.p_xpd_service = (addlopt & TPAO_USE_TXPD) == TPAO_USE_TXPD;
839 tpp.p_use_checksum = (tpp.p_class == TP_CLASS_0) ? 0 :
840 (addlopt & TPAO_NO_CSUM) == 0;
841 tpp.p_version = version;
842 #ifdef notdef
843 tpp.p_use_efc = (opt & TPO_USE_EFC) == TPO_USE_EFC;
844 tpp.p_use_nxpd = (addlopt & TPAO_USE_NXPD) == TPAO_USE_NXPD;
845 tpp.p_use_rcc = (addlopt & TPAO_USE_RCC) == TPAO_USE_RCC;
846 #endif /* notdef */
847
848 CHECK(
849 tp_consistency(tpcb, 0 /* not force or strict */ , &tpp) != 0,
850 E_TP_NEGOT_FAILED, ts_negotfailed, clear_parent_tcb,
851 (1 + 2 + (caddr_t) & hdr->_tpdufr.CRCC - (caddr_t) hdr)
852 /* ^ more or less the location of class */
853 )
854 }
855 #ifdef TPPT
856 if (tp_traceflags[D_CONN]) {
857 tptrace(TPPTmisc,
858 "after 1 consist class_to_use class, out, tpconsout",
859 class_to_use,
860 tpcb->tp_class, dgout_routine, tpcons_output
861 );
862 }
863 #endif
864 CHECK(((class_to_use == TP_CLASS_0) &&
865 (dgout_routine != tpcons_output)),
866 E_TP_NEGOT_FAILED, ts_negotfailed, clear_parent_tcb,
867 (1 + 2 + (caddr_t) & hdr->_tpdufr.CRCC - (caddr_t) hdr)
868 /* ^ more or less the location of class */
869 )
870 #ifdef ARGO_DEBUG
871 if (argo_debug[D_CONN]) {
872 printf("CR: after CRCCCHECKS: tpcb %p, flags 0x%x\n",
873 tpcb, tpcb->tp_flags);
874 }
875 #endif
876 takes_data = TRUE;
877 e.TPDU_ATTR(CR).e_cdt = hdr->tpdu_CRcdt;
878 e.ev_number = CR_TPDU;
879
880 so = tpcb->tp_sock;
881 if (so->so_options & SO_ACCEPTCONN) {
882 struct tp_pcb *parent_tpcb = tpcb;
883 /*
884 * Create a socket, tpcb, ll pcb, etc. for this
885 * newborn connection, and fill in all the values.
886 */
887 #ifdef ARGO_DEBUG
888 if (argo_debug[D_CONN]) {
889 printf("abt to call tp_newsocket(%p, %p, %p, %p)\n",
890 so, laddr, faddr, cons_channel);
891 }
892 #endif
893 if ((so =
894 tp_newsocket(so, faddr, cons_channel,
895 class_to_use,
896 ((tpcb->tp_netservice == IN_CLNS) ? IN_CLNS :
897 (dgout_routine == tpcons_output) ? ISO_CONS : ISO_CLNS))
898 ) == (struct socket *) 0) {
899 /*
900 * note - even if netservice is IN_CLNS, as
901 * far as the tp entity is concerned, the
902 * only differences are CO vs CL
903 */
904 #ifdef ARGO_DEBUG
905 if (argo_debug[D_CONN]) {
906 printf("tp_newsocket returns 0\n");
907 }
908 #endif
909 goto discard;
910 clear_parent_tcb:
911 tpcb = 0;
912 goto respond;
913 }
914 tpcb = sototpcb(so);
915 insque(tpcb, parent_tpcb);
916
917 /*
918 * Stash the addresses in the net level pcb
919 * kind of like a pcbconnect() but don't need
920 * or want all those checks.
921 */
922 (tpcb->tp_nlproto->nlp_putnetaddr) (tpcb->tp_npcb, faddr, TP_FOREIGN);
923 (tpcb->tp_nlproto->nlp_putnetaddr) (tpcb->tp_npcb, laddr, TP_LOCAL);
924
925 /* stash the f suffix in the new tpcb */
926 if ((tpcb->tp_fsuffixlen = fsufxlen) != 0) {
927 bcopy(fsufxloc, tpcb->tp_fsuffix, fsufxlen);
928 (tpcb->tp_nlproto->nlp_putsufx)
929 (tpcb->tp_npcb, fsufxloc, fsufxlen, TP_FOREIGN);
930 }
931 /* stash the l suffix in the new tpcb */
932 tpcb->tp_lsuffixlen = lsufxlen;
933 bcopy(lsufxloc, tpcb->tp_lsuffix, lsufxlen);
934 (tpcb->tp_nlproto->nlp_putsufx)
935 (tpcb->tp_npcb, lsufxloc, lsufxlen, TP_LOCAL);
936 #ifdef TP_PERF_MEAS
937 if (tpcb->tp_perf_on = perf_meas) { /* assignment */
938 /*
939 * ok, let's create an mbuf for stashing the
940 * statistics if one doesn't already exist
941 */
942 (void) tp_setup_perf(tpcb);
943 }
944 #endif /* TP_PERF_MEAS */
945 tpcb->tp_fref = sref;
946
947 /*
948 * We've already checked for consistency with the
949 * options set in tpp, but we couldn't set them
950 * earlier because we didn't want to change options
951 * in the LISTENING tpcb. Now we set the options in
952 * the new socket's tpcb.
953 */
954 (void) tp_consistency(tpcb, TP_FORCE, &tpp);
955
956 if (!tpcb->tp_use_checksum)
957 IncStat(ts_csum_off);
958 if (tpcb->tp_xpd_service)
959 IncStat(ts_use_txpd);
960 if (tpcb->tp_xtd_format)
961 IncStat(ts_xtd_fmt);
962
963 tpcb->tp_peer_acktime = acktime;
964
965 /*
966 * The following kludge is used to test
967 * retransmissions and timeout during connection
968 * establishment.
969 */
970 #ifdef ARGO_DEBUG
971 if (argo_debug[D_ZDREF]) {
972 IncStat(ts_zdebug);
973 /* tpcb->tp_fref = 0; */
974 }
975 #endif
976 }
977 LOCAL_CREDIT(tpcb);
978 IncStat(ts_CR_rcvd);
979 if (!tpcb->tp_cebit_off) {
980 tpcb->tp_win_recv = tp_start_win << 8;
981 tpcb->tp_cong_sample.cs_size = 0;
982 CONG_INIT_SAMPLE(tpcb);
983 CONG_UPDATE_SAMPLE(tpcb, ce_bit);
984 }
985 } else if (dutype == ER_TPDU_type) {
986 /*
987 * ER TPDUs have to be recognized separately because they
988 * don't necessarily have a tpcb with them and we don't want
989 * err out looking for such a beast. We could put a bunch of
990 * little kludges in the next section of code so it would
991 * avoid references to tpcb if dutype == ER_TPDU_type but we
992 * don't want code for ERs to mess up code for data transfer.
993 */
994 IncStat(ts_ER_rcvd);
995 e.ev_number = ER_TPDU;
996 e.TPDU_ATTR(ER).e_reason = (u_char) hdr->tpdu_ERreason;
997 CHECK(((int) dref <= 0 || dref >= tp_refinfo.tpr_size ||
998 (tpcb = tp_ref[dref].tpr_pcb) == (struct tp_pcb *) 0 ||
999 tpcb->tp_refstate == REF_FREE ||
1000 tpcb->tp_refstate == REF_FROZEN),
1001 E_TP_MISM_REFS, ts_inv_dref, discard, 0)
1002 } else {
1003 /* tpdu type is CC, XPD, XAK, GR, AK, DR, DC, or DT */
1004
1005 /*
1006 * In the next 4 checks, _tpduf is the fixed part; add 2 to
1007 * get the dref bits of the fixed part (can't take the
1008 * address of a bit field)
1009 */
1010 #ifdef TPCONS
1011 if (cons_channel && dutype == DT_TPDU_type) {
1012 struct isopcb *isop = ((struct isopcb *)
1013 ((struct pklcd *) cons_channel)->lcd_upnext);
1014 if (isop && isop->isop_refcnt == 1 && isop->isop_socket &&
1015 (tpcb = sototpcb(isop->isop_socket)) &&
1016 (tpcb->tp_class == TP_CLASS_0 /* || == CLASS_1 */ )) {
1017 #ifdef ARGO_DEBUG
1018 if (argo_debug[D_TPINPUT]) {
1019 printf("tpinput_dt: class 0 short circuit\n");
1020 }
1021 #endif
1022 dref = tpcb->tp_lref;
1023 sref = tpcb->tp_fref;
1024 CHECK((tpcb->tp_refstate == REF_FREE),
1025 E_TP_MISM_REFS, ts_inv_dref, nonx_dref,
1026 (1 + 2 + (caddr_t) & hdr->_tpduf - (caddr_t) hdr))
1027 goto tp0_data;
1028 }
1029 }
1030 #endif
1031 {
1032
1033 CHECK(((int) dref <= 0 || dref >= tp_refinfo.tpr_size),
1034 E_TP_MISM_REFS, ts_inv_dref, nonx_dref,
1035 (1 + 2 + (caddr_t) & hdr->_tpduf - (caddr_t) hdr))
1036 CHECK(((tpcb = tp_ref[dref].tpr_pcb) == (struct tp_pcb *) 0),
1037 E_TP_MISM_REFS, ts_inv_dref, nonx_dref,
1038 (1 + 2 + (caddr_t) & hdr->_tpduf - (caddr_t) hdr))
1039 CHECK((tpcb->tp_refstate == REF_FREE),
1040 E_TP_MISM_REFS, ts_inv_dref, nonx_dref,
1041 (1 + 2 + (caddr_t) & hdr->_tpduf - (caddr_t) hdr))
1042 }
1043
1044 #ifdef ARGO_DEBUG
1045 if (argo_debug[D_TPINPUT]) {
1046 printf("HAVE A TPCB 2: %p\n", tpcb);
1047 }
1048 #endif
1049
1050 /* causes a DR to be sent for CC; ER for all else */
1051 CHECK((tpcb->tp_refstate == REF_FROZEN),
1052 (dutype == CC_TPDU_type ? E_TP_NO_SESSION : E_TP_MISM_REFS),
1053 ts_inv_dref, respond,
1054 (1 + 2 + (caddr_t) & hdr->_tpduf - (caddr_t) hdr))
1055 #ifdef ARGO_DEBUG
1056 if (argo_debug[D_TPINPUT]) {
1057 printf("state of dref %d ok, tpcb %p\n", dref, tpcb);
1058 }
1059 #endif
1060 /*
1061 * At this point the state of the dref could be FROZEN:
1062 * tpr_pcb == NULL, has ( reference only) timers for
1063 * example, DC may arrive after the close() has detached the
1064 * tpcb (e.g., if user turned off SO_LISTEN option) OPENING :
1065 * a tpcb exists but no timers yet OPEN : tpcb exists &
1066 * timers are outstanding
1067 */
1068
1069 if (!tpcb->tp_cebit_off)
1070 CONG_UPDATE_SAMPLE(tpcb, ce_bit);
1071
1072 dusize = tpcb->tp_tpdusize;
1073 pdusize = tpcb->tp_ptpdusize;
1074
1075 dutype = hdr->tpdu_type << 8; /* for the switch below */
1076
1077 WHILE_OPTIONS(P, hdr, tpcb->tp_xtd_format) /* { */
1078 #define caseof(x,y) case (((x)<<8)+(y))
1079 switch (dutype | vbptr(P)->tpv_code) {
1080
1081 caseof(CC_TPDU_type, TPP_addl_opt):
1082 /* not in class 0; 1 octet */
1083 vb_getval(P, u_char, addlopt);
1084 break;
1085 caseof(CC_TPDU_type, TPP_tpdu_size):
1086 {
1087 u_char odusize = dusize;
1088 vb_getval(P, u_char, dusize);
1089 CHECK((dusize < TP_MIN_TPDUSIZE ||
1090 dusize > TP_MAX_TPDUSIZE || dusize > odusize),
1091 E_TP_INV_PVAL, ts_inv_pval, respond,
1092 (1 + (caddr_t) & vbptr(P)->tpv_val - (caddr_t) hdr))
1093 #ifdef ARGO_DEBUG
1094 if (argo_debug[D_TPINPUT]) {
1095 printf("CC dusize 0x%x\n", dusize);
1096 }
1097 #endif
1098 }
1099 break;
1100 caseof(CC_TPDU_type, TPP_ptpdu_size):
1101 {
1102 u_short opdusize = pdusize;
1103 switch (vbptr(P)->tpv_len) {
1104 case 1:
1105 pdusize = vbval(P, u_char);
1106 break;
1107 case 2:
1108 pdusize = ntohs(vbval(P, u_short));
1109 break;
1110 default:;
1111 #ifdef ARGO_DEBUG
1112 if (argo_debug[D_TPINPUT]) {
1113 printf("malformed prefered TPDU option\n");
1114 }
1115 #endif
1116 }
1117 CHECK((pdusize == 0 ||
1118 (opdusize && (pdusize > opdusize))),
1119 E_TP_INV_PVAL, ts_inv_pval, respond,
1120 (1 + (caddr_t) & vbptr(P)->tpv_val - (caddr_t) hdr))
1121 }
1122 break;
1123 caseof(CC_TPDU_type, TPP_calling_sufx):
1124 #ifdef ARGO_DEBUG
1125 if (argo_debug[D_TPINPUT]) {
1126 printf("CC calling (local) sufxlen 0x%x\n", lsufxlen);
1127 }
1128 #endif
1129 lsufxloc = (caddr_t) & vbptr(P)->tpv_val;
1130 lsufxlen = vbptr(P)->tpv_len;
1131 break;
1132 caseof(CC_TPDU_type, TPP_acktime):
1133 /* class 4 only, 2 octets */
1134 vb_getval(P, u_short, acktime);
1135 acktime = ntohs(acktime);
1136 acktime = acktime / 500; /* convert to slowtimo
1137 * ticks */
1138 if ((short) acktime <= 0)
1139 acktime = 2;
1140 break;
1141 caseof(CC_TPDU_type, TPP_called_sufx):
1142 fsufxloc = (caddr_t) & vbptr(P)->tpv_val;
1143 fsufxlen = vbptr(P)->tpv_len;
1144 #ifdef ARGO_DEBUG
1145 if (argo_debug[D_TPINPUT]) {
1146 printf("CC called (foreign) sufx len %d\n", fsufxlen);
1147 }
1148 #endif
1149 break;
1150
1151 caseof(CC_TPDU_type, TPP_checksum):
1152 caseof(DR_TPDU_type, TPP_checksum):
1153 caseof(DT_TPDU_type, TPP_checksum):
1154 caseof(XPD_TPDU_type, TPP_checksum):
1155 if (tpcb->tp_use_checksum) {
1156 CHECK(iso_check_csum(m, tpdu_len),
1157 E_TP_INV_PVAL, ts_bad_csum, discard, 0)
1158 }
1159 break;
1160
1161 /*
1162 * this is different from the above because in the
1163 * context of concat/ sep tpdu_len might not be the
1164 * same as hdr len
1165 */
1166 caseof(AK_TPDU_type, TPP_checksum):
1167 caseof(XAK_TPDU_type, TPP_checksum):
1168 caseof(DC_TPDU_type, TPP_checksum):
1169 if (tpcb->tp_use_checksum) {
1170 CHECK(iso_check_csum(m, (int) hdr->tpdu_li + 1),
1171 E_TP_INV_PVAL, ts_bad_csum, discard, 0)
1172 }
1173 break;
1174 #ifdef notdef
1175 caseof(DR_TPDU_type, TPP_addl_info):
1176 /*
1177 * ignore - its length and meaning are user defined
1178 * and there's no way to pass this info to the user
1179 * anyway
1180 */
1181 break;
1182 #endif /* notdef */
1183
1184 caseof(AK_TPDU_type, TPP_subseq):
1185 /* used after reduction of window */
1186 vb_getval(P, u_short, subseq);
1187 subseq = ntohs(subseq);
1188 #ifdef ARGO_DEBUG
1189 if (argo_debug[D_ACKRECV]) {
1190 printf("AK dref 0x%x Subseq 0x%x\n", dref, subseq);
1191 }
1192 #endif
1193 break;
1194
1195 caseof(AK_TPDU_type, TPP_flow_cntl_conf):
1196 {
1197 u_int ylwe;
1198 u_short ysubseq, ycredit;
1199
1200 fcc_present = TRUE;
1201 vb_getval(P, u_int, ylwe);
1202 vb_getval(P, u_short, ysubseq);
1203 vb_getval(P, u_short, ycredit);
1204 ylwe = ntohl(ylwe);
1205 ysubseq = ntohs(ysubseq);
1206 ycredit = ntohs(ycredit);
1207 #ifdef ARGO_DEBUG
1208 if (argo_debug[D_ACKRECV]) {
1209 printf("%s%x, subseq 0x%x, cdt 0x%x dref 0x%x\n",
1210 "AK FCC lwe 0x", ylwe, ysubseq, ycredit, dref);
1211 }
1212 #endif
1213 }
1214 break;
1215
1216 default:
1217 #ifdef ARGO_DEBUG
1218 if (argo_debug[D_TPINPUT]) {
1219 printf("param ignored dutype 0x%x, code 0x%x\n",
1220 dutype, vbptr(P)->tpv_code);
1221 }
1222 #endif
1223 #ifdef TPPT
1224 if (tp_traceflags[D_TPINPUT]) {
1225 tptrace(TPPTmisc, "param ignored dutype code ",
1226 dutype, vbptr(P)->tpv_code, 0, 0);
1227 }
1228 #endif
1229 IncStat(ts_param_ignored);
1230 break;
1231 #undef caseof
1232 }
1233 /* } */ END_WHILE_OPTIONS(P)
1234 /* NOTE: the variable dutype has been shifted left! */
1235
1236 switch (hdr->tpdu_type) {
1237 case CC_TPDU_type:
1238 /*
1239 * If CC comes back with an unacceptable class
1240 * respond with a DR or ER
1241 */
1242
1243 opt = hdr->tpdu_CCoptions; /* 1 byte */
1244
1245 {
1246 tpp = tpcb->_tp_param;
1247 tpp.p_class = (1 << hdr->tpdu_CCclass);
1248 tpp.p_tpdusize = dusize;
1249 tpp.p_ptpdusize = pdusize;
1250 tpp.p_dont_change_params = 0;
1251 tpp.p_xtd_format = (opt & TPO_XTD_FMT) == TPO_XTD_FMT;
1252 tpp.p_xpd_service = (addlopt & TPAO_USE_TXPD) == TPAO_USE_TXPD;
1253 tpp.p_use_checksum = (addlopt & TPAO_NO_CSUM) == 0;
1254 #ifdef notdef
1255 tpp.p_use_efc = (opt & TPO_USE_EFC) == TPO_USE_EFC;
1256 tpp.p_use_nxpd = (addlopt & TPAO_USE_NXPD) == TPAO_USE_NXPD;
1257 tpp.p_use_rcc = (addlopt & TPAO_USE_RCC) == TPAO_USE_RCC;
1258 #endif /* notdef */
1259
1260 CHECK(
1261 tp_consistency(tpcb, TP_FORCE, &tpp) != 0,
1262 E_TP_NEGOT_FAILED, ts_negotfailed, respond,
1263 (1 + 2 + (caddr_t) & hdr->_tpdufr.CRCC - (caddr_t) hdr)
1264 /* ^ more or less the location of class */
1265 )
1266 #ifdef TPPT
1267 if (tp_traceflags[D_CONN]) {
1268 tptrace(TPPTmisc,
1269 "after 1 consist class, out, tpconsout",
1270 tpcb->tp_class, dgout_routine, tpcons_output, 0
1271 );
1272 }
1273 #endif
1274 CHECK(
1275 ((class_to_use == TP_CLASS_0) &&
1276 (dgout_routine != tpcons_output)),
1277 E_TP_NEGOT_FAILED, ts_negotfailed, respond,
1278 (1 + 2 + (caddr_t) & hdr->_tpdufr.CRCC - (caddr_t) hdr)
1279 /* ^ more or less the location of class */
1280 )
1281 #ifdef TPCONS
1282 if (tpcb->tp_netservice == ISO_CONS &&
1283 class_to_use == TP_CLASS_0) {
1284 struct isopcb *isop = (struct isopcb *) tpcb->tp_npcb;
1285 struct pklcd *lcp = (struct pklcd *) isop->isop_chan;
1286 lcp->lcd_flags &= ~X25_DG_CIRCUIT;
1287 }
1288 #endif
1289 }
1290 if (!tpcb->tp_use_checksum)
1291 IncStat(ts_csum_off);
1292 if (tpcb->tp_xpd_service)
1293 IncStat(ts_use_txpd);
1294 if (tpcb->tp_xtd_format)
1295 IncStat(ts_xtd_fmt);
1296
1297 #ifdef TPPT
1298 if (tp_traceflags[D_CONN]) {
1299 tptrace(TPPTmisc, "after CC class flags dusize CCclass",
1300 tpcb->tp_class, tpcb->tp_flags, tpcb->tp_tpdusize,
1301 hdr->tpdu_CCclass);
1302 }
1303 #endif
1304
1305 /*
1306 * if called or calling suffixes appeared on the CC,
1307 * they'd better jive with what's in the pcb
1308 */
1309 if (fsufxlen) {
1310 CHECK(((tpcb->tp_fsuffixlen != fsufxlen) ||
1311 bcmp(fsufxloc, tpcb->tp_fsuffix, fsufxlen)),
1312 E_TP_INV_PVAL, ts_inv_sufx, respond,
1313 (1 + fsufxloc - (caddr_t) hdr))
1314 }
1315 if (lsufxlen) {
1316 CHECK(((tpcb->tp_lsuffixlen != lsufxlen) ||
1317 bcmp(lsufxloc, tpcb->tp_lsuffix, lsufxlen)),
1318 E_TP_INV_PVAL, ts_inv_sufx, respond,
1319 (1 + lsufxloc - (caddr_t) hdr))
1320 }
1321 e.TPDU_ATTR(CC).e_sref = sref;
1322 e.TPDU_ATTR(CC).e_cdt = hdr->tpdu_CCcdt;
1323 takes_data = TRUE;
1324 e.ev_number = CC_TPDU;
1325 IncStat(ts_CC_rcvd);
1326 break;
1327
1328 case DC_TPDU_type:
1329 if (sref != tpcb->tp_fref)
1330 printf("INPUT: inv sufx DCsref 0x%x, tp_fref 0x%x\n",
1331 sref, tpcb->tp_fref);
1332
1333 CHECK((sref != tpcb->tp_fref),
1334 E_TP_MISM_REFS, ts_inv_sufx, discard,
1335 (1 + (caddr_t) & hdr->tpdu_DCsref - (caddr_t) hdr))
1336 e.ev_number = DC_TPDU;
1337 IncStat(ts_DC_rcvd);
1338 break;
1339
1340 case DR_TPDU_type:
1341 #ifdef TPPT
1342 if (tp_traceflags[D_TPINPUT]) {
1343 tptrace(TPPTmisc, "DR recvd", hdr->tpdu_DRreason, 0, 0, 0);
1344 }
1345 #endif
1346 if (sref != tpcb->tp_fref) {
1347 printf("INPUT: inv sufx DRsref 0x%x tp_fref 0x%x\n",
1348 sref, tpcb->tp_fref);
1349 }
1350 CHECK((sref != 0 && sref != tpcb->tp_fref &&
1351 tpcb->tp_state != TP_CRSENT),
1352 (TP_ERROR_SNDC | E_TP_MISM_REFS), ts_inv_sufx, respond,
1353 (1 + (caddr_t) & hdr->tpdu_DRsref - (caddr_t) hdr))
1354 e.TPDU_ATTR(DR).e_reason = hdr->tpdu_DRreason;
1355 e.TPDU_ATTR(DR).e_sref = (u_short) sref;
1356 takes_data = TRUE;
1357 e.ev_number = DR_TPDU;
1358 IncStat(ts_DR_rcvd);
1359 break;
1360
1361 case ER_TPDU_type:
1362 #ifdef TPPT
1363 if (tp_traceflags[D_TPINPUT]) {
1364 tptrace(TPPTmisc, "ER recvd", hdr->tpdu_ERreason, 0, 0, 0);
1365 }
1366 #endif
1367 e.ev_number = ER_TPDU;
1368 e.TPDU_ATTR(ER).e_reason = hdr->tpdu_ERreason;
1369 IncStat(ts_ER_rcvd);
1370 break;
1371
1372 case AK_TPDU_type:
1373
1374 e.TPDU_ATTR(AK).e_subseq = subseq;
1375 e.TPDU_ATTR(AK).e_fcc_present = fcc_present;
1376
1377 if (tpcb->tp_xtd_format) {
1378 #ifdef BYTE_ORDER
1379 union seq_type seqeotX;
1380
1381 seqeotX.s_seqeot = ntohl(hdr->tpdu_seqeotX);
1382 e.TPDU_ATTR(AK).e_seq = seqeotX.s_seq;
1383 e.TPDU_ATTR(AK).e_cdt = ntohs(hdr->tpdu_AKcdtX);
1384 #else
1385 e.TPDU_ATTR(AK).e_cdt = hdr->tpdu_AKcdtX;
1386 e.TPDU_ATTR(AK).e_seq = hdr->tpdu_AKseqX;
1387 #endif /* BYTE_ORDER */
1388 } else {
1389 e.TPDU_ATTR(AK).e_cdt = hdr->tpdu_AKcdt;
1390 e.TPDU_ATTR(AK).e_seq = hdr->tpdu_AKseq;
1391 }
1392 #ifdef TPPT
1393 if (tp_traceflags[D_TPINPUT]) {
1394 tptrace(TPPTmisc, "AK recvd seq cdt subseq fcc_pres",
1395 e.TPDU_ATTR(AK).e_seq, e.TPDU_ATTR(AK).e_cdt,
1396 subseq, fcc_present);
1397 }
1398 #endif
1399
1400 e.ev_number = AK_TPDU;
1401 IncStat(ts_AK_rcvd);
1402 IncPStat(tpcb, tps_AK_rcvd);
1403 break;
1404
1405 case XAK_TPDU_type:
1406 if (tpcb->tp_xtd_format) {
1407 #ifdef BYTE_ORDER
1408 union seq_type seqeotX;
1409
1410 seqeotX.s_seqeot = ntohl(hdr->tpdu_seqeotX);
1411 e.TPDU_ATTR(XAK).e_seq = seqeotX.s_seq;
1412 #else
1413 e.TPDU_ATTR(XAK).e_seq = hdr->tpdu_XAKseqX;
1414 #endif /* BYTE_ORDER */
1415 } else {
1416 e.TPDU_ATTR(XAK).e_seq = hdr->tpdu_XAKseq;
1417 }
1418 e.ev_number = XAK_TPDU;
1419 IncStat(ts_XAK_rcvd);
1420 IncPStat(tpcb, tps_XAK_rcvd);
1421 break;
1422
1423 case XPD_TPDU_type:
1424 if (tpcb->tp_xtd_format) {
1425 #ifdef BYTE_ORDER
1426 union seq_type seqeotX;
1427
1428 seqeotX.s_seqeot = ntohl(hdr->tpdu_seqeotX);
1429 e.TPDU_ATTR(XPD).e_seq = seqeotX.s_seq;
1430 #else
1431 e.TPDU_ATTR(XPD).e_seq = hdr->tpdu_XPDseqX;
1432 #endif /* BYTE_ORDER */
1433 } else {
1434 e.TPDU_ATTR(XPD).e_seq = hdr->tpdu_XPDseq;
1435 }
1436 takes_data = TRUE;
1437 e.ev_number = XPD_TPDU;
1438 IncStat(ts_XPD_rcvd);
1439 IncPStat(tpcb, tps_XPD_rcvd);
1440 break;
1441
1442 case DT_TPDU_type:
1443 /*
1444 * the y option will cause occasional packets
1445 * to be dropped. A little crude but it
1446 * works.
1447 */
1448
1449 #ifdef ARGO_DEBUG
1450 if (argo_debug[D_DROP]) {
1451 if (time.tv_usec & 0x4 &&
1452 hdr->tpdu_DTseq & 0x1) {
1453 IncStat(ts_ydebug);
1454 goto discard;
1455 }
1456 }
1457 #endif
1458 if (tpcb->tp_class == TP_CLASS_0) {
1459 #ifdef TPCONS
1460 tp0_data:
1461 #endif
1462 e.TPDU_ATTR(DT).e_seq = 0; /* actually don't care */
1463 e.TPDU_ATTR(DT).e_eot = (((struct tp0du *) hdr)->tp0du_eot);
1464 } else if (tpcb->tp_xtd_format) {
1465 #ifdef BYTE_ORDER
1466 union seq_type seqeotX;
1467
1468 seqeotX.s_seqeot = ntohl(hdr->tpdu_seqeotX);
1469 e.TPDU_ATTR(DT).e_seq = seqeotX.s_seq;
1470 e.TPDU_ATTR(DT).e_eot = seqeotX.s_eot;
1471 #else
1472 e.TPDU_ATTR(DT).e_seq = hdr->tpdu_DTseqX;
1473 e.TPDU_ATTR(DT).e_eot = hdr->tpdu_DTeotX;
1474 #endif /* BYTE_ORDER */
1475 } else {
1476 e.TPDU_ATTR(DT).e_seq = hdr->tpdu_DTseq;
1477 e.TPDU_ATTR(DT).e_eot = hdr->tpdu_DTeot;
1478 }
1479 if (e.TPDU_ATTR(DT).e_eot)
1480 IncStat(ts_eot_input);
1481 takes_data = TRUE;
1482 e.ev_number = DT_TPDU;
1483 IncStat(ts_DT_rcvd);
1484 IncPStat(tpcb, tps_DT_rcvd);
1485 break;
1486
1487 case GR_TPDU_type:
1488 tp_indicate(T_DISCONNECT, tpcb, ECONNABORTED);
1489 /* drop through */
1490 default:
1491 /*
1492 * this should NEVER happen because there is a check
1493 * for dutype well above here
1494 */
1495 error = E_TP_INV_TPDU; /* causes an ER */
1496 #ifdef ARGO_DEBUG
1497 if (argo_debug[D_TPINPUT]) {
1498 printf("INVALID dutype 0x%x\n", hdr->tpdu_type);
1499 }
1500 #endif
1501 IncStat(ts_inv_dutype);
1502 goto respond;
1503 }
1504 }
1505 /*
1506 * peel off the tp header; remember that the du_li doesn't count
1507 * itself. This may leave us w/ an empty mbuf at the front of a
1508 * chain. We can't just throw away the empty mbuf because hdr still
1509 * points into the mbuf's data area and we're still using hdr (the
1510 * tpdu header)
1511 */
1512 m->m_len -= ((int) hdr->tpdu_li + 1);
1513 m->m_data += ((int) hdr->tpdu_li + 1);
1514
1515 if (takes_data) {
1516 int max = tpdu_info[hdr->tpdu_type][TP_MAX_DATA_INDEX];
1517 int datalen = tpdu_len - hdr->tpdu_li - 1, mbtype = MT_DATA;
1518 struct {
1519 struct tp_disc_reason dr;
1520 struct cmsghdr x_hdr;
1521 } x;
1522 #define c_hdr x.x_hdr
1523 struct mbuf *n;
1524
1525 CHECK((max && datalen > max), E_TP_LENGTH_INVAL,
1526 ts_inv_length, respond, (max + hdr->tpdu_li + 1));
1527 switch (hdr->tpdu_type) {
1528
1529 case CR_TPDU_type:
1530 c_hdr.cmsg_type = TPOPT_CONN_DATA;
1531 goto make_control_msg;
1532
1533 case CC_TPDU_type:
1534 c_hdr.cmsg_type = TPOPT_CFRM_DATA;
1535 goto make_control_msg;
1536
1537 case DR_TPDU_type:
1538 x.dr.dr_hdr.cmsg_len = sizeof(x) - sizeof(c_hdr);
1539 x.dr.dr_hdr.cmsg_type = TPOPT_DISC_REASON;
1540 x.dr.dr_hdr.cmsg_level = SOL_TRANSPORT;
1541 x.dr.dr_reason = hdr->tpdu_DRreason;
1542 c_hdr.cmsg_type = TPOPT_DISC_DATA;
1543 make_control_msg:
1544 datalen += sizeof(c_hdr);
1545 c_hdr.cmsg_len = datalen;
1546 c_hdr.cmsg_level = SOL_TRANSPORT;
1547 mbtype = MT_CONTROL;
1548 MGET(n, M_DONTWAIT, MT_DATA);
1549 if (n == 0) {
1550 m_freem(m);
1551 m = 0;
1552 datalen = 0;
1553 goto invoke;
1554 }
1555 if (hdr->tpdu_type == DR_TPDU_type) {
1556 datalen += sizeof(x) - sizeof(c_hdr);
1557 bcopy((caddr_t) & x, mtod(n, caddr_t), n->m_len = sizeof(x));
1558 } else
1559 bcopy((caddr_t) & c_hdr, mtod(n, caddr_t),
1560 n->m_len = sizeof(c_hdr));
1561 n->m_next = m;
1562 m = n;
1563 /* FALLTHROUGH */
1564
1565 case XPD_TPDU_type:
1566 if (mbtype != MT_CONTROL)
1567 mbtype = MT_OOBDATA;
1568 m->m_flags |= M_EOR;
1569 /* FALLTHROUGH */
1570
1571 case DT_TPDU_type:
1572 for (n = m; n; n = n->m_next) {
1573 MCHTYPE(n, mbtype);
1574 }
1575 invoke:
1576 e.TPDU_ATTR(DT).e_datalen = datalen;
1577 e.TPDU_ATTR(DT).e_data = m;
1578 break;
1579
1580 default:
1581 printf(
1582 "ERROR in tp_input! hdr->tpdu_type 0x%x takes_data 0x%x m %p\n",
1583 hdr->tpdu_type, takes_data, m);
1584 break;
1585 }
1586 /*
1587 * prevent m_freem() after tp_driver() from throwing it all
1588 * away
1589 */
1590 m = NULL;
1591 }
1592 IncStat(ts_tpdu_rcvd);
1593
1594 #ifdef ARGO_DEBUG
1595 if (argo_debug[D_TPINPUT]) {
1596 printf("tp_input: before driver, state 0x%x event 0x%x m %p",
1597 tpcb->tp_state, e.ev_number, m);
1598 printf(" e.e_data %p\n", e.TPDU_ATTR(DT).e_data);
1599 printf("takes_data 0x%x m_len 0x%x, tpdu_len 0x%x\n",
1600 takes_data, (m == NULL) ? 0 : m->m_len, tpdu_len);
1601 }
1602 #endif
1603
1604 error = tp_driver(tpcb, &e);
1605
1606 ASSERT(tpcb != (struct tp_pcb *) 0);
1607 ASSERT(tpcb->tp_sock != (struct socket *) 0);
1608 if (tpcb->tp_sock->so_error == 0)
1609 tpcb->tp_sock->so_error = error;
1610
1611 /*
1612 * Kludge to keep the state tables under control (adding data on
1613 * connect & disconnect & freeing the mbuf containing the data would
1614 * have exploded the tables and made a big mess ).
1615 */
1616 switch (e.ev_number) {
1617 case CC_TPDU:
1618 case DR_TPDU:
1619 case CR_TPDU:
1620 m = e.TPDU_ATTR(CC).e_data; /* same field for all three
1621 * dutypes */
1622 #ifdef ARGO_DEBUG
1623 if (argo_debug[D_TPINPUT]) {
1624 printf("after driver, restoring m to %p, takes_data 0x%x\n",
1625 m, takes_data);
1626 }
1627 #endif
1628 break;
1629 default:
1630 break;
1631 }
1632 /*
1633 * Concatenated sequences are terminated by any tpdu that carries
1634 * data: CR, CC, DT, XPD, DR. All other tpdu types may be
1635 * concatenated: AK, XAK, DC, ER.
1636 */
1637
1638 if (takes_data == 0) {
1639 ASSERT(m != NULL);
1640 /*
1641 * we already peeled off the prev. tp header so we can just
1642 * pull up some more and repeat
1643 */
1644
1645 if ((m = tp_inputprep(m)) != NULL) {
1646 #ifdef ARGO_DEBUG
1647 if (argo_debug[D_TPINPUT]) {
1648 hdr = mtod(m, struct tpdu *);
1649 printf("tp_input @ separate: hdr %p size %d m %p\n",
1650 hdr, (int) hdr->tpdu_li + 1, m);
1651 dump_mbuf(m, "tp_input after driver, at separate");
1652 }
1653 #endif
1654
1655 IncStat(ts_concat_rcvd);
1656 goto again;
1657 }
1658 }
1659 if (m != NULL) {
1660 #ifdef ARGO_DEBUG
1661 if (argo_debug[D_TPINPUT]) {
1662 printf("tp_input : m_freem(%p)\n", m);
1663 }
1664 #endif
1665 m_freem(m);
1666 #ifdef ARGO_DEBUG
1667 if (argo_debug[D_TPINPUT]) {
1668 printf("tp_input : after m_freem %p\n", m);
1669 }
1670 #endif
1671 }
1672 return;
1673
1674 discard:
1675 /* class 4: drop the tpdu */
1676 /*
1677 * class 2,0: Should drop the net connection, if you can figure out
1678 * to which connection it applies
1679 */
1680 #ifdef ARGO_DEBUG
1681 if (argo_debug[D_TPINPUT]) {
1682 printf("tp_input DISCARD\n");
1683 }
1684 #endif
1685 #ifdef TPPT
1686 if (tp_traceflags[D_TPINPUT]) {
1687 tptrace(TPPTmisc, "tp_input DISCARD m", m, 0, 0, 0);
1688 }
1689 #endif
1690 m_freem(m);
1691 IncStat(ts_recv_drop);
1692 return;
1693
1694 nonx_dref:
1695 switch (dutype) {
1696 default:
1697 goto discard;
1698 case CC_TPDU_type:
1699 /* error = E_TP_MISM_REFS; */
1700 break;
1701 case DR_TPDU_type:
1702 error |= TP_ERROR_SNDC;
1703 }
1704 respond:
1705 #ifdef ARGO_DEBUG
1706 if (argo_debug[D_TPINPUT]) {
1707 printf("RESPOND: error 0x%x, errlen 0x%x\n", error, errlen);
1708 }
1709 #endif
1710 #ifdef TPPT
1711 if (tp_traceflags[D_TPINPUT]) {
1712 tptrace(TPPTmisc, "tp_input RESPOND m error sref", m, error, sref, 0);
1713 }
1714 #endif
1715 if (sref == 0)
1716 goto discard;
1717 (void) tp_error_emit(error, (u_long) sref, satosiso(faddr),
1718 satosiso(laddr), m, errlen, tpcb,
1719 cons_channel, dgout_routine);
1720 #ifdef ARGO_DEBUG
1721 if (argo_debug[D_ERROR_EMIT]) {
1722 printf("tp_input after error_emit\n");
1723 }
1724 #endif
1725
1726 #ifdef lint
1727 printf("", sref, opt);
1728 #endif /* lint */
1729 IncStat(ts_recv_drop);
1730 }
1731
1732
1733 /*
1734 * NAME: tp_headersize()
1735 *
1736 * CALLED FROM:
1737 * tp_emit() and tp_sbsend()
1738 * TP needs to know the header size so it can figure out how
1739 * much data to put in each tpdu.
1740 *
1741 * FUNCTION, ARGUMENTS, and RETURN VALUE:
1742 * For a given connection, represented by (tpcb), and
1743 * tpdu type (dutype), return the size of a tp header.
1744 *
1745 * RETURNS: the expected size of the heade in bytesr
1746 *
1747 * SIDE EFFECTS:
1748 *
1749 * NOTES: It would be nice if it got the network header size as well.
1750 */
1751 int
1752 tp_headersize(dutype, tpcb)
1753 int dutype;
1754 struct tp_pcb *tpcb;
1755 {
1756 int size = 0;
1757
1758 #ifdef TPPT
1759 if (tp_traceflags[D_CONN]) {
1760 tptrace(TPPTmisc, "tp_headersize dutype class xtd_format",
1761 dutype, tpcb->tp_class, tpcb->tp_xtd_format, 0);
1762 }
1763 #endif
1764 if (!((tpcb->tp_class == TP_CLASS_0) ||
1765 (tpcb->tp_class == TP_CLASS_4) ||
1766 (dutype == DR_TPDU_type) ||
1767 (dutype == CR_TPDU_type))) {
1768 printf("tp_headersize:dutype 0x%x, class 0x%x",
1769 dutype, tpcb->tp_class);
1770 /* TODO: identify this and GET RID OF IT */
1771 }
1772 ASSERT((tpcb->tp_class == TP_CLASS_0) ||
1773 (tpcb->tp_class == TP_CLASS_4) ||
1774 (dutype == DR_TPDU_type) ||
1775 (dutype == CR_TPDU_type));
1776
1777 if (tpcb->tp_class == TP_CLASS_0) {
1778 size = tpdu_info[dutype][TP_LEN_CLASS_0_INDEX];
1779 } else {
1780 size = tpdu_info[dutype][tpcb->tp_xtd_format];
1781 }
1782 return size;
1783 /* caller must get network level header size separately */
1784 }
Cache object: 9309fc7bf0e5aeebac47ff889d28fdbd
|