1 /* $NetBSD: tp_usrreq.c,v 1.39 2009/03/18 17:06:53 cegger Exp $ */
4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
31 * @(#)tp_usrreq.c 8.1 (Berkeley) 6/10/93
34 /***********************************************************
35 Copyright IBM Corporation 1987
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.
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
55 ******************************************************************/
58 * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
61 * tp_usrreq(), the fellow that gets called from most of the socket code.
62 * Pretty straighforward. THe only really awful stuff here is the OOB
63 * processing, which is done wholly here. tp_rcvoob() and tp_sendoob() are
64 * contained here and called by tp_usrreq().
67 #include <sys/cdefs.h>
68 __KERNEL_RCSID(0, "$NetBSD: tp_usrreq.c,v 1.39 2009/03/18 17:06:53 cegger Exp $");
70 #include <sys/param.h>
71 #include <sys/systm.h>
73 #include <sys/socket.h>
74 #include <sys/socketvar.h>
75 #include <sys/domain.h>
76 #include <sys/protosw.h>
77 #include <sys/errno.h>
81 #include <netiso/tp_param.h>
82 #include <netiso/tp_var.h>
83 #include <netiso/tp_timer.h>
84 #include <netiso/tp_seq.h>
85 #include <netiso/tp_stat.h>
86 #include <netiso/tp_ip.h>
87 #include <netiso/tp_pcb.h>
88 #include <netiso/argo_debug.h>
89 #include <netiso/tp_trace.h>
90 #include <netiso/tp_meas.h>
91 #include <netiso/iso.h>
92 #include <netiso/iso_errno.h>
93 #include <netiso/iso_var.h>
96 int TPNagle1
, TPNagle2
;
97 struct tp_pcb
*tp_listeners
, *tp_intercepts
;
102 * anywhere you want to debug...
103 * FUNCTION and ARGUMENTS:
104 * print (str) followed by the control info in the mbufs of an mbuf chain (n)
107 dump_mbuf(struct mbuf
*n
, const char *str
)
109 struct mbuf
*nextrecord
;
111 printf("dump %s\n", str
);
118 nextrecord
= n
->m_nextpkt
;
121 printf("%p : Len %x Data %p A %p Nx %p Tp %x\n",
122 n
, n
->m_len
, n
->m_data
, n
->m_nextpkt
, n
->m_next
, n
->m_type
);
125 char *p
= mtod(n
, char *);
129 for (i
= 0; i
< n
->m_len
; i
++) {
132 printf("0x%x ", *(p
+ i
));
137 if (n
->m_next
== n
) {
148 #endif /* ARGO_DEBUG */
152 * tp_usrreq(), PRU_RCVOOB
153 * FUNCTION and ARGUMENTS:
154 * Copy data from the expedited data socket buffer into
155 * the pre-allocated mbuf m.
156 * There is an isomorphism between XPD TPDUs and expedited data TSDUs.
157 * XPD tpdus are limited to 16 bytes of data so they fit in one mbuf.
159 * EINVAL if debugging is on and a disaster has occurred
160 * ENOTCONN if the socket isn't connected
161 * EWOULDBLOCK if the socket is in non-blocking mode and there's no
162 * xpd data in the buffer
163 * E* whatever is returned from the fsm.
166 tp_rcvoob(struct tp_pcb
*tpcb
, struct socket
*so
, struct mbuf
*m
,
167 int *outflags
, int inflags
)
170 struct sockbuf
*sb
= &so
->so_rcv
;
176 if (argo_debug
[D_XPD
]) {
177 printf("PRU_RCVOOB, sostate 0x%x\n", so
->so_state
);
181 /* if you use soreceive */
186 if ((((so
->so_state
& SS_ISCONNECTED
) == 0)
187 || (so
->so_state
& SS_ISDISCONNECTING
) != 0) &&
188 (so
->so_proto
->pr_flags
& PR_CONNREQUIRED
)) {
192 * Take the first mbuf off the chain. Each XPD TPDU gives you a
193 * complete TSDU so the chains don't get coalesced, but one TSDU may
194 * span several mbufs. Nevertheless, since n should have a most 16
195 * bytes, it will fit into m. (size was checked in tp_input() )
199 * Code for excision of OOB data should be added to
200 * uipc_socket2.c (like sbappend).
203 sblock(sb
, M_WAITOK
);
204 for (nn
= &sb
->sb_mb
; (n
= *nn
) != NULL
; nn
= &n
->m_nextpkt
)
205 if (n
->m_type
== MT_OOBDATA
)
210 if (argo_debug
[D_XPD
]) {
211 printf("RCVOOB: empty queue!\n");
223 /* Assuming at most one xpd tpdu is in the buffer at once */
225 m
->m_len
+= n
->m_len
;
226 memcpy(mtod(m
, void *), mtod(n
, void *), (unsigned) n
->m_len
);
227 m
->m_data
+= n
->m_len
; /* so mtod() in bcopy() above gives
231 m
->m_data
= m
->m_dat
;
235 if (argo_debug
[D_XPD
]) {
236 printf("tp_rcvoob: xpdlen 0x%x\n", m
->m_len
);
237 dump_mbuf(so
->so_rcv
.sb_mb
, "RCVOOB: Rcv socketbuf");
238 dump_mbuf(sb
->sb_mb
, "RCVOOB: Xrcv socketbuf");
242 if ((inflags
& MSG_PEEK
) == 0) {
245 for (; n
; n
= m_free(n
))
251 if (tp_traceflags
[D_XPD
]) {
252 tptraceTPCB(TPPTmisc
, "PRU_RCVOOB @ release sb_cc m_len",
253 tpcb
->tp_Xrcv
.sb_cc
, m
->m_len
, 0, 0);
257 error
= DoEvent(T_USR_Xrcvd
);
263 * tp_usrreq(), PRU_SENDOOB
264 * FUNCTION and ARGUMENTS:
265 * Send what's in the mbuf chain (m) as an XPD TPDU.
266 * The mbuf may not contain more than 16 bytes of data.
267 * XPD TSDUs aren't segmented, so they translate into
268 * exactly one XPD TPDU, with EOT bit set.
270 * EWOULDBLOCK if socket is in non-blocking mode and the previous
271 * xpd data haven't been acked yet.
272 * EMSGSIZE if trying to send > max-xpd bytes (16)
273 * ENOBUFS if ran out of mbufs
276 tp_sendoob(struct tp_pcb
*tpcb
, struct socket
*so
, struct mbuf
*xdata
,
280 * Each mbuf chain represents a sequence # in the XPD seq space.
281 * The first one in the queue has sequence # tp_Xuna.
282 * When we add to the XPD queue, we stuff a zero-length
283 * mbuf (mark) into the DATA queue, with its sequence number in m_next
284 * to be assigned to this XPD tpdu, so data xfer can stop
285 * when it reaches the zero-length mbuf if this XPD TPDU hasn't
286 * yet been acknowledged.
288 struct sockbuf
*sb
= &(tpcb
->tp_Xsnd
);
294 if (argo_debug
[D_XPD
]) {
295 printf("tp_sendoob:");
297 printf("xdata len 0x%x\n", xdata
->m_len
);
301 * DO NOT LOCK the Xsnd buffer!!!! You can have at MOST one socket
302 * buf locked at any time!!! (otherwise you might sleep() in sblock()
303 * w/ a signal pending and cause the system call to be aborted w/ a
304 * locked socketbuf, which is a problem. So the so_snd buffer lock
305 * (done in sosend()) serves as the lock for Xpd.
307 if (sb
->sb_mb
) { /* Anything already in eXpedited data
313 sbunlock(&so
->so_snd
); /* already locked by sosend */
315 sblock(&so
->so_snd
, M_WAITOK
); /* sosend will unlock on
319 if (xdata
== (struct mbuf
*) 0) {
320 /* empty xpd packet */
321 xdata
= m_gethdr(M_WAIT
, MT_OOBDATA
);
323 xdata
->m_pkthdr
.len
= 0;
326 if (argo_debug
[D_XPD
]) {
327 printf("tp_sendoob 1:");
329 printf("xdata len 0x%x\n", xdata
->m_len
);
332 xmark
= xdata
; /* temporary use of variable xmark */
335 xmark
= xmark
->m_next
;
337 if (len
> TP_MAX_XPD_DATA
) {
341 if (argo_debug
[D_XPD
]) {
342 printf("tp_sendoob 2:");
344 printf("xdata len 0x%x\n", len
);
350 if (tp_traceflags
[D_XPD
]) {
351 tptraceTPCB(TPPTmisc
, "XPD mark m_next ", xdata
->m_next
, 0, 0, 0);
355 sbappendrecord(sb
, xdata
);
358 if (argo_debug
[D_XPD
]) {
359 printf("tp_sendoob len 0x%x\n", len
);
360 dump_mbuf(so
->so_snd
.sb_mb
, "XPD request Regular sndbuf:");
361 dump_mbuf(tpcb
->tp_Xsnd
.sb_mb
, "XPD request Xsndbuf:");
364 return DoEvent(T_XPD_req
);
369 * the socket routines
370 * FUNCTION and ARGUMENTS:
371 * Handles all "user requests" except the [gs]ockopts() requests.
372 * The argument (req) is the request type (PRU*),
373 * (m) is an mbuf chain, generally used for send and
374 * receive type requests only.
375 * (nam) is used for addresses usually, in particular for the bind request.
380 tp_usrreq(struct socket
*so
, int req
, struct mbuf
*m
, struct mbuf
*nam
,
381 struct mbuf
*control
, struct lwp
*l
)
385 int flags
, *outflags
= &flags
;
390 if (argo_debug
[D_REQUEST
]) {
391 printf("usrreq(%p,%d,%p,%p,%p)\n", so
, req
, m
, nam
, outflags
);
393 printf("WARNING!!! so->so_error is 0x%x\n", so
->so_error
);
397 if (tp_traceflags
[D_REQUEST
]) {
398 tptraceTPCB(TPPTusrreq
, "req so m state [", req
, so
, m
,
399 tpcb
? tpcb
->tp_state
: 0);
403 if (req
== PRU_CONTROL
)
407 if (tpcb
== 0 && req
!= PRU_ATTACH
) {
409 if (tp_traceflags
[D_REQUEST
]) {
410 tptraceTPCB(TPPTusrreq
, "req failed NO TPCB[", 0, 0, 0, 0);
425 error
= tp_attach(so
, (int)(long)nam
);
431 case PRU_DETACH
: /* called from close() */
432 /* called only after disconnect was called */
433 error
= DoEvent(T_DETACH
);
434 if (tpcb
->tp_state
== TP_CLOSED
) {
435 if (tpcb
->tp_notdetached
) {
437 if (argo_debug
[D_CONN
]) {
438 printf("PRU_DETACH: not detached\n");
443 free((void *) tpcb
, M_PCB
);
449 error
= tp_pcbbind(tpcb
, nam
, l
);
453 if (tpcb
->tp_state
!= TP_CLOSED
|| tpcb
->tp_lsuffixlen
== 0 ||
459 tpcb
->tp_next
= tpcb
->tp_prev
= tpcb
;
460 for (tt
= &tp_listeners
; *tt
; tt
= &((*tt
)->tp_nextlisten
))
461 if ((*tt
)->tp_lsuffixlen
)
463 tpcb
->tp_nextlisten
= *tt
;
465 error
= DoEvent(T_LISTEN_req
);
471 if (tp_traceflags
[D_CONN
]) {
472 tptraceTPCB(TPPTmisc
,
473 "PRU_CONNECT: so 0x%x *SHORT_LSUFXP(tpcb) 0x%x lsuflen 0x%x, class 0x%x",
474 tpcb
->tp_sock
, *SHORT_LSUFXP(tpcb
), tpcb
->tp_lsuffixlen
,
479 if (argo_debug
[D_CONN
]) {
480 printf("PRU_CONNECT: so %p *SHORT_LSUFXP(tpcb) 0x%x lsuflen 0x%x, class 0x%x",
481 tpcb
->tp_sock
, *SHORT_LSUFXP(tpcb
), tpcb
->tp_lsuffixlen
,
485 if (tpcb
->tp_lsuffixlen
== 0) {
486 error
= tp_pcbbind(tpcb
, (struct mbuf
*)0,
490 if (argo_debug
[D_CONN
]) {
491 printf("pcbbind returns error 0x%x\n", error
);
498 if (argo_debug
[D_CONN
]) {
499 printf("isop %p isop->isop_socket offset 12 :\n", tpcb
->tp_npcb
);
500 dump_buf(tpcb
->tp_npcb
, 16);
503 if ((error
= tp_route_to(nam
, tpcb
, /* channel */ 0)) != 0)
506 if (argo_debug
[D_CONN
]) {
508 "PRU_CONNECT after tpcb %p so %p npcb %p flags 0x%x\n",
509 tpcb
, so
, tpcb
->tp_npcb
, tpcb
->tp_flags
);
510 printf("isop %p isop->isop_socket offset 12 :\n", tpcb
->tp_npcb
);
511 dump_buf(tpcb
->tp_npcb
, 16);
514 if (tpcb
->tp_fsuffixlen
== 0) {
515 /* didn't set peer extended suffix */
516 (tpcb
->tp_nlproto
->nlp_getsufx
) (tpcb
->tp_npcb
,
517 &tpcb
->tp_fsuffixlen
,
518 tpcb
->tp_fsuffix
, TP_FOREIGN
);
520 if (tpcb
->tp_state
== TP_CLOSED
) {
522 error
= DoEvent(T_CONN_req
);
524 (tpcb
->tp_nlproto
->nlp_pcbdisc
) (tpcb
->tp_npcb
);
532 lsufx
= *(u_short
*) (tpcb
->tp_lsuffix
);
533 fsufx
= *(u_short
*) (tpcb
->tp_fsuffix
);
536 tpmeas(tpcb
->tp_lref
,
537 TPtime_open
| (tpcb
->tp_xtd_format
<< 4),
538 &now
, lsufx
, fsufx
, tpcb
->tp_fref
);
544 error
= EOPNOTSUPP
; /* for unix domain sockets */
548 E
.TPDU_ATTR(REQ
).e_reason
= E_TP_NORMAL_DISC
^ TP_ERROR_MASK
;
549 error
= DoEvent(T_DISC_req
);
553 (tpcb
->tp_nlproto
->nlp_getnetaddr
) (tpcb
->tp_npcb
, nam
, TP_FOREIGN
);
555 if (argo_debug
[D_REQUEST
]) {
556 printf("ACCEPT PEERADDDR:");
557 dump_buf(mtod(nam
, char *), nam
->m_len
);
565 lsufx
= *(u_short
*) (tpcb
->tp_lsuffix
);
566 fsufx
= *(u_short
*) (tpcb
->tp_fsuffix
);
569 tpmeas(tpcb
->tp_lref
, TPtime_open
,
570 &time
, lsufx
, fsufx
, tpcb
->tp_fref
);
577 * recv end may have been released; local credit might be
580 E
.TPDU_ATTR(REQ
).e_reason
= E_TP_NORMAL_DISC
^ TP_ERROR_MASK
;
581 error
= DoEvent(T_DISC_req
);
585 if (so
->so_state
& SS_ISCONFIRMING
) {
586 if (tpcb
->tp_state
== TP_CONFIRMING
)
587 error
= tp_confirm(tpcb
);
591 if (tp_traceflags
[D_DATA
]) {
592 tptraceTPCB(TPPTmisc
,
593 "RCVD BF: lcredit sent_lcdt cc hiwat \n",
594 tpcb
->tp_lcredit
, tpcb
->tp_sent_lcdt
,
595 so
->so_rcv
.sb_cc
, so
->so_rcv
.sb_hiwat
);
597 tptraceTPCB(TPPTmisc
,
598 "PRU_RCVD AF sbspace lcredit hiwat cc",
599 sbspace(&so
->so_rcv
), tpcb
->tp_lcredit
,
600 so
->so_rcv
.sb_cc
, so
->so_rcv
.sb_hiwat
);
604 if (argo_debug
[D_REQUEST
]) {
605 printf("RCVD: cc %ld space %ld hiwat %ld\n",
606 so
->so_rcv
.sb_cc
, sbspace(&so
->so_rcv
),
607 so
->so_rcv
.sb_hiwat
);
610 if (((long) nam
) & MSG_OOB
)
611 error
= DoEvent(T_USR_Xrcvd
);
613 error
= DoEvent(T_USR_rcvd
);
617 if ((so
->so_state
& SS_ISCONNECTED
) == 0) {
621 if (!tpcb
->tp_xpd_service
) {
625 /* kludge - nam is really flags here */
626 error
= tp_rcvoob(tpcb
, so
, m
, outflags
, (long) nam
);
632 error
= tp_snd_control(control
, so
, &m
);
637 if ((so
->so_state
& SS_ISCONFIRMING
) &&
638 (tpcb
->tp_state
== TP_CONFIRMING
) &&
639 (error
= tp_confirm(tpcb
)))
641 if (req
== PRU_SENDOOB
) {
642 error
= (tpcb
->tp_xpd_service
== 0) ?
643 EOPNOTSUPP
: tp_sendoob(tpcb
, so
, m
, outflags
);
648 if (m
->m_flags
& M_EOR
) {
650 m
->m_flags
&= ~M_EOR
;
652 if (eotsdu
== 0 && m
->m_pkthdr
.len
== 0)
654 if (tpcb
->tp_state
!= TP_AKWAIT
&& tpcb
->tp_state
!= TP_OPEN
) {
659 * The protocol machine copies mbuf chains,
660 * prepends headers, assigns seq numbers, and
661 * puts the packets on the device.
662 * When they are acked they are removed from the socket buf.
664 * sosend calls this up until sbspace goes negative.
665 * Sbspace may be made negative by appending this mbuf chain,
666 * possibly by a whole cluster.
670 * Could have eotsdu and no data.(presently MUST have
671 * an mbuf though, even if its length == 0)
673 int totlen
= m
->m_pkthdr
.len
;
674 struct sockbuf
*sb
= &so
->so_snd
;
677 PStat(tpcb
, Nb_from_sess
) += totlen
;
678 tpmeas(tpcb
->tp_lref
, TPtime_from_session
, 0, 0,
679 PStat(tpcb
, Nb_from_sess
), totlen
);
683 if (argo_debug
[D_SYSCALL
]) {
685 "PRU_SEND: eot %ld before sbappend %p len 0x%x to sb @ %p\n",
686 eotsdu
, m
, totlen
, sb
);
687 dump_mbuf(sb
->sb_mb
, "so_snd.sb_mb");
688 dump_mbuf(m
, "m : to be added");
691 tp_packetize(tpcb
, m
, eotsdu
);
693 if (argo_debug
[D_SYSCALL
]) {
694 printf("PRU_SEND: eot %ld after sbappend %p\n", eotsdu
, m
);
695 dump_mbuf(sb
->sb_mb
, "so_snd.sb_mb");
698 if (tpcb
->tp_state
== TP_OPEN
)
699 error
= DoEvent(T_DATA_req
);
701 if (argo_debug
[D_SYSCALL
]) {
702 printf("PRU_SEND: after driver error 0x%x \n", error
);
703 printf("so_snd %p cc 0t%ld mbcnt 0t%ld\n",
704 sb
, sb
->sb_cc
, sb
->sb_mbcnt
);
705 dump_mbuf(sb
->sb_mb
, "so_snd.sb_mb after driver");
711 case PRU_ABORT
: /* called from close() */
713 * called for each incoming connect queued on the parent
716 if (tpcb
->tp_state
== TP_OPEN
|| tpcb
->tp_state
== TP_CONFIRMING
) {
717 E
.TPDU_ATTR(REQ
).e_reason
= E_TP_NO_SESSION
^ TP_ERROR_MASK
;
718 error
= DoEvent(T_DISC_req
); /* pretend it was a
725 * stat: don't bother with a blocksize.
730 (tpcb
->tp_nlproto
->nlp_getnetaddr
) (tpcb
->tp_npcb
, nam
, TP_LOCAL
);
734 (tpcb
->tp_nlproto
->nlp_getnetaddr
) (tpcb
->tp_npcb
, nam
, TP_FOREIGN
);
739 printf("tp_usrreq UNKNOWN PRU %d\n", req
);
740 #endif /* ARGO_DEBUG */
745 if (argo_debug
[D_REQUEST
]) {
746 printf("%s, so %p, tpcb %p, error %d, state %d\n",
747 "returning from tp_usrreq", so
, tpcb
, error
,
748 tpcb
? tpcb
->tp_state
: 0);
752 if (tp_traceflags
[D_REQUEST
]) {
753 tptraceTPCB(TPPTusrreq
, "END req so m state [", req
, so
, m
,
754 tpcb
? tpcb
->tp_state
: 0);
762 tp_ltrace(struct socket
*so
, struct uio
*uio
)
765 if (tp_traceflags
[D_DATA
]) {
766 struct tp_pcb
*tpcb
= sototpcb(so
);
768 tptraceTPCB(TPPTmisc
, "sosend so resid iovcnt", so
,
769 uio
->uio_resid
, uio
->uio_iovcnt
, 0);
776 tp_confirm(struct tp_pcb
*tpcb
)
779 if (tpcb
->tp_state
== TP_CONFIRMING
)
780 return DoEvent(T_ACPT_req
);
781 printf("Tp confirm called when not confirming; tpcb %p, state 0x%x\n",
782 tpcb
, tpcb
->tp_state
);
787 * Process control data sent with sendmsg()
790 tp_snd_control(struct mbuf
*m
, struct socket
*so
, struct mbuf
**data
)
797 ch
= mtod(m
, struct cmsghdr
*);
798 m
->m_len
-= sizeof(*ch
);
799 m
->m_data
+= sizeof(*ch
);
801 sockopt_init(&sopt
, ch
->cmsg_level
, ch
->cmsg_type
, 0);
802 error
= sockopt_setmbuf(&sopt
, m
);
804 error
= tp_ctloutput(PRCO_SETOPT
, so
, &sopt
);
805 sockopt_destroy(&sopt
);
808 if (ch
->cmsg_type
== TPOPT_DISC_DATA
) {
813 error
= tp_usrreq(so
, PRU_DISCONNECT
, (struct mbuf
*)0,
814 (struct mbuf
*)0, (struct mbuf
*)0,