checkpoint for ``alpha tape''; tp is still a little broken.
[unix-history] / usr / src / sys / netiso / tp_output.c
CommitLineData
5f183145
KS
1/***********************************************************
2 Copyright IBM Corporation 1987
3
4 All Rights Reserved
5
6Permission to use, copy, modify, and distribute this software and its
7documentation for any purpose and without fee is hereby granted,
8provided that the above copyright notice appear in all copies and that
9both that copyright notice and this permission notice appear in
10supporting documentation, and that the name of IBM not be
11used in advertising or publicity pertaining to distribution of the
12software without specific, written prior permission.
13
14IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
15ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
16IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
17ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
18WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
19ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20SOFTWARE.
21
22******************************************************************/
23
24/*
25 * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
26 */
27/*
28 * ARGO TP
29 *
30 * $Header: tp_output.c,v 5.4 88/11/18 17:28:08 nhall Exp $
31 * $Source: /usr/argo/sys/netiso/RCS/tp_output.c,v $
44f52ea5 32 * @(#)tp_output.c 7.4 (Berkeley) %G% *
5f183145
KS
33 *
34 * In here is tp_ctloutput(), the guy called by [sg]etsockopt(),
35 */
36
37#ifndef lint
38static char *rcsid = "$Header: tp_output.c,v 5.4 88/11/18 17:28:08 nhall Exp $";
39#endif lint
40
41#include "param.h"
42#include "systm.h"
43#include "mbuf.h"
44#include "protosw.h"
45#include "socket.h"
46#include "socketvar.h"
47#include "errno.h"
48#include "types.h"
49#include "time.h"
a50e2bc0
KS
50#include "tp_param.h"
51#include "tp_user.h"
52#include "tp_stat.h"
53#include "tp_ip.h"
54#include "tp_timer.h"
55#include "argo_debug.h"
56#include "tp_pcb.h"
57#include "tp_trace.h"
5f183145
KS
58
59#define USERFLAGSMASK_G 0x0f00643b
60#define USERFLAGSMASK_S 0x0f000432
61#define TPDUSIZESHIFT 24
62#define CLASSHIFT 16
63
64/*
65 * NAME: tp_consistency()
66 *
67 * CALLED FROM:
68 * tp_ctloutput(), tp_input()
69 *
70 * FUNCTION and ARGUMENTS:
71 * Checks the consistency of options and tpdusize with class,
72 * using the parameters passed in via (param).
73 * (cmd) may be TP_STRICT or TP_FORCE or both.
74 * Force means it will set all the values in (tpcb) to those in
75 * the input arguements iff no errors were encountered.
76 * Strict means that no inconsistency will be tolerated. If it's
77 * not used, checksum and tpdusize inconsistencies will be tolerated.
78 * The reason for this is that in some cases, when we're negotiating down
79 * from class 4, these options should be changed but should not
80 * cause negotiation to fail.
81 *
82 * RETURNS
83 * E* or EOK
84 * E* if the various parms aren't ok for a given class
85 * EOK if they are ok for a given class
86 */
87
88int
89tp_consistency( tpcb, cmd, param )
90 u_int cmd;
91 struct tp_conn_param *param;
92 struct tp_pcb *tpcb;
93{
94 register int error = EOK;
95 int class_to_use = tp_mask_to_num(param->p_class);
96
97 IFTRACE(D_SETPARAMS)
98 tptrace(TPPTmisc,
99 "tp_consist enter class_to_use dontchange param.class cmd",
100 class_to_use, param->p_dont_change_params, param->p_class, cmd);
101 ENDTRACE
102 IFDEBUG(D_SETPARAMS)
103 printf("tp_consistency %s %s\n",
104 cmd& TP_FORCE? "TP_FORCE": "",
105 cmd& TP_STRICT? "TP_STRICT":"");
106 ENDDEBUG
107 if ((cmd & TP_FORCE) && (param->p_dont_change_params)) {
108 cmd &= ~TP_FORCE;
109 }
110 /* can switch net services within a domain, but
111 * cannot switch domains
112 */
113 switch( param->p_netservice) {
114 case ISO_CONS:
115 case ISO_CLNS:
116 case ISO_COSNS:
117 /* param->p_netservice in ISO DOMAIN */
118 if(tpcb->tp_domain != AF_ISO ) {
119 error = EINVAL; goto done;
120 }
121 break;
122 case IN_CLNS:
123 /* param->p_netservice in INET DOMAIN */
124 if( tpcb->tp_domain != AF_INET ) {
125 error = EINVAL; goto done;
126 }
127 break;
128 /* no others not possible-> netservice is a 2-bit field! */
129 }
130
131 IFDEBUG(D_SETPARAMS)
132 printf("p_class 0x%x, class_to_use 0x%x\n", param->p_class,
133 class_to_use);
134 ENDDEBUG
135 if((param->p_netservice < 0) || (param->p_netservice > TP_MAX_NETSERVICES)){
136 error = EINVAL; goto done;
137 }
138 if( (param->p_class & TP_CLASSES_IMPLEMENTED) == 0 ) {
139 error = EINVAL; goto done;
140 }
141 IFDEBUG(D_SETPARAMS)
142 printf("Nretrans 0x%x\n", param->p_Nretrans );
143 ENDDEBUG
144 if( ( param->p_Nretrans < 1 ) ||
145 (param->p_cr_ticks < 1) || (param->p_cc_ticks < 1) ) {
146 /* bad for any class because negot has to be done a la class 4 */
147 error = EINVAL; goto done;
148 }
149 IFDEBUG(D_SETPARAMS)
150 printf("winsize 0x%x\n", param->p_winsize );
151 ENDDEBUG
152 if( (param->p_winsize < 128 ) ||
153 (param->p_winsize < param->p_tpdusize ) ||
154 (param->p_winsize > ((1+SB_MAX)>>2 /* 1/4 of the max */)) ) {
155 error = EINVAL; goto done;
156 } else {
157 if( tpcb->tp_state == TP_CLOSED )
a50e2bc0
KS
158 soreserve(tpcb->tp_sock, (u_long)param->p_winsize,
159 (u_long)param->p_winsize);
5f183145
KS
160 }
161 IFDEBUG(D_SETPARAMS)
162 printf("use_csum 0x%x\n", param->p_use_checksum );
163 printf("xtd_format 0x%x\n", param->p_xtd_format );
164 printf("xpd_service 0x%x\n", param->p_xpd_service );
165 printf("tpdusize 0x%x\n", param->p_tpdusize );
166 printf("tpcb->flags 0x%x\n", tpcb->tp_flags );
167 ENDDEBUG
168 switch( class_to_use ) {
169
170 case 0:
171 /* do not use checksums, xtd format, or XPD */
172
173 if( param->p_use_checksum | param->p_xtd_format | param->p_xpd_service ) {
174 if(cmd & TP_STRICT) {
175 error = EINVAL;
176 } else {
177 param->p_use_checksum = 0;
178 param->p_xtd_format = 0;
179 param->p_xpd_service = 0;
180 }
181 break;
182 }
183
184 if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
185 if(cmd & TP_STRICT) {
186 error = EINVAL;
187 } else {
188 param->p_tpdusize = TP_MIN_TPDUSIZE;
189 }
190 break;
191 }
192 if (param->p_tpdusize > TP0_TPDUSIZE) {
193 if (cmd & TP_STRICT) {
194 error = EINVAL;
195 } else {
196 param->p_tpdusize = TP0_TPDUSIZE;
197 }
198 break;
199 }
200
201 /* connect/disc data not allowed for class 0 */
a50e2bc0 202 if (tpcb->tp_ucddata) {
5f183145
KS
203 if(cmd & TP_STRICT) {
204 error = EINVAL;
205 } else if(cmd & TP_FORCE) {
a50e2bc0
KS
206 m_freem(tpcb->tp_ucddata);
207 tpcb->tp_ucddata = 0;
5f183145
KS
208 }
209 }
210 break;
211
212 case 4:
213 IFDEBUG(D_SETPARAMS)
214 printf("dt_ticks 0x%x\n", param->p_dt_ticks );
215 printf("x_ticks 0x%x\n", param->p_x_ticks );
216 printf("dr_ticks 0x%x\n", param->p_dr_ticks );
217 printf("keepalive 0x%x\n", param->p_keepalive_ticks );
218 printf("sendack 0x%x\n", param->p_sendack_ticks );
219 printf("inact 0x%x\n", param->p_inact_ticks );
220 printf("ref 0x%x\n", param->p_ref_ticks );
221 ENDDEBUG
222 if( (param->p_class & TP_CLASS_4 ) && (
223 (param->p_dt_ticks < 1) || (param->p_dr_ticks < 1) ||
224 (param->p_x_ticks < 1) || (param->p_keepalive_ticks < 1) ||
225 (param->p_sendack_ticks < 1) || (param->p_ref_ticks < 1) ||
226 (param->p_inact_ticks < 1) ) ) {
227 error = EINVAL;
228 break;
229 }
230 IFDEBUG(D_SETPARAMS)
231 printf("rx_strat 0x%x\n", param->p_rx_strat );
232 ENDDEBUG
233 if(param->p_rx_strat >
234 ( TPRX_USE_CW | TPRX_EACH | TPRX_FASTSTART) ) {
235 if(cmd & TP_STRICT) {
236 error = EINVAL;
237 } else {
238 param->p_rx_strat = TPRX_USE_CW;
239 }
240 break;
241 }
242 IFDEBUG(D_SETPARAMS)
243 printf("ack_strat 0x%x\n", param->p_ack_strat );
244 ENDDEBUG
245 if((param->p_ack_strat != 0) && (param->p_ack_strat != 1)) {
246 if(cmd & TP_STRICT) {
247 error = EINVAL;
248 } else {
249 param->p_ack_strat = TPACK_WINDOW;
250 }
251 break;
252 }
253 if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
254 if(cmd & TP_STRICT) {
255 error = EINVAL;
256 } else {
257 param->p_tpdusize = TP_MIN_TPDUSIZE;
258 }
259 break;
260 }
261 if (param->p_tpdusize > TP_TPDUSIZE) {
262 if(cmd & TP_STRICT) {
263 error = EINVAL;
264 } else {
265 param->p_tpdusize = TP_TPDUSIZE;
266 }
267 break;
268 }
269 break;
270 }
271
272 if ((error==0) && (cmd & TP_FORCE)) {
273 tpcb->tp_tpdusize = param->p_tpdusize;
274 tpcb->tp_class = param->p_class;
275 tpcb->tp_use_checksum = param->p_use_checksum;
276 tpcb->tp_xpd_service = param->p_xpd_service;
277 tpcb->tp_xtd_format = param->p_xtd_format;
278 }
279
280done:
281
282 IFTRACE(D_CONN)
283 tptrace(TPPTmisc, "tp_consist returns class xtdfmt cmd",
284 error, tpcb->tp_class, tpcb->tp_xtd_format, cmd);
285 ENDTRACE
286 IFDEBUG(D_CONN)
287 printf(
288 "tp_consist rtns 0x%x class 0x%x xtd_fmt 0x%x cmd 0x%x\n",
289 error, tpcb->tp_class, tpcb->tp_xtd_format, cmd);
290 ENDDEBUG
291 return error;
292}
293
294/*
295 * NAME: tp_ctloutput()
296 *
297 * CALLED FROM:
298 * [sg]etsockopt(), via so[sg]etopt().
299 *
300 * FUNCTION and ARGUMENTS:
301 * Implements the socket options at transport level.
e663c139 302 * (cmd) is either PRCO_SETOPT or PRCO_GETOPT (see ../sys/protosw.h).
5f183145 303 * (so) is the socket.
e663c139 304 * (level) is SOL_TRANSPORT (see ../sys/socket.h)
5f183145
KS
305 * (optname) is the particular command or option to be set.
306 * (**mp) is an mbuf structure.
307 *
308 * RETURN VALUE:
309 * ENOTSOCK if the socket hasn't got an associated tpcb
310 * EINVAL if
311 * trying to set window too big
312 * trying to set illegal max tpdu size
313 * trying to set illegal credit fraction
314 * trying to use unknown or unimplemented class of TP
315 * structure passed to set timer values is wrong size
316 * illegal combination of command/GET-SET option,
317 * e.g., GET w/ TPOPT_CDDATA_CLEAR:
318 * EOPNOTSUPP if the level isn't transport, or command is neither GET nor SET
319 * or if the transport-specific command is not implemented
320 * EISCONN if trying a command that isn't allowed after a connection
321 * is established
322 * ENOTCONN if trying a command that is allowed only if a connection is
323 * established
324 * EMSGSIZE if trying to give too much data on connect/disconnect
325 *
326 * SIDE EFFECTS:
327 *
328 * NOTES:
329 */
330ProtoHook
331tp_ctloutput(cmd, so, level, optname, mp)
332 int cmd, level, optname;
333 struct socket *so;
334 struct mbuf **mp;
335{
336 struct tp_pcb *tpcb = sototpcb(so);
337 int s = splnet();
a50e2bc0
KS
338 caddr_t value;
339 unsigned val_len;
5f183145
KS
340 int error = 0;
341
342 IFTRACE(D_REQUEST)
343 tptrace(TPPTmisc, "tp_ctloutput cmd so optname mp",
344 cmd, so, optname, mp);
345 ENDTRACE
346 IFDEBUG(D_REQUEST)
347 printf(
348 "tp_ctloutput so 0x%x cmd 0x%x optname 0x%x, mp 0x%x *mp 0x%x tpcb 0x%x\n",
349 so, cmd, optname, mp, mp?*mp:0, tpcb);
350 ENDDEBUG
351 if( tpcb == (struct tp_pcb *)0 ) {
352 error = ENOTSOCK; goto done;
353 }
354 if(*mp == MNULL) {
355 register struct mbuf *m;
356
357 MGET(m, M_DONTWAIT, TPMT_SONAME); /* does off, type, next */
358 if (m == NULL) {
359 splx(s);
360 return ENOBUFS;
361 }
362 m->m_len = 0;
363 m->m_act = 0;
364 *mp = m;
365 }
366
367 /*
368 * Hook so one can set network options via a tp socket.
369 */
370 if ( level == SOL_NETWORK ) {
371 if ((tpcb->tp_nlproto == NULL) || (tpcb->tp_npcb == NULL))
372 error = ENOTSOCK;
373 else if (tpcb->tp_nlproto->nlp_ctloutput == NULL)
374 error = EOPNOTSUPP;
375 else
376 error = (tpcb->tp_nlproto->nlp_ctloutput)(cmd, optname,
377 tpcb->tp_npcb, *mp);
378 goto done;
379 } else if ( level != SOL_TRANSPORT ) {
380 error = EOPNOTSUPP; goto done;
381 }
382 if (cmd != PRCO_GETOPT && cmd != PRCO_SETOPT) {
383 error = EOPNOTSUPP; goto done;
384 }
385 if ( so->so_error ) {
386 error = so->so_error; goto done;
387 }
388
389 /* The only options allowed after connection is established
390 * are GET (anything) and SET DISC DATA and SET PERF MEAS
391 */
392 if ( ((so->so_state & SS_ISCONNECTING)||(so->so_state & SS_ISCONNECTED))
393 &&
394 (cmd == PRCO_SETOPT &&
395 optname != TPOPT_DISC_DATA &&
44f52ea5 396 optname != TPOPT_CFRM_DATA &&
5f183145
KS
397 optname != TPOPT_PERF_MEAS &&
398 optname != TPOPT_CDDATA_CLEAR ) ) {
399 error = EISCONN; goto done;
400 }
401 /* The only options allowed after disconnection are GET DISC DATA,
402 * and TPOPT_PSTATISTICS
403 * and they're not allowed if the ref timer has gone off, because
404 * the tpcb is gone
405 */
44f52ea5 406 if ((so->so_state & (SS_ISCONNECTED | SS_ISCONFIRMING)) == 0) {
5f183145
KS
407 if ( so->so_tpcb == (caddr_t)0 ) {
408 error = ENOTCONN; goto done;
409 }
410 if ( (tpcb->tp_state == TP_REFWAIT || tpcb->tp_state == TP_CLOSING) &&
411 (optname != TPOPT_DISC_DATA && optname != TPOPT_PSTATISTICS)) {
412 error = ENOTCONN; goto done;
413 }
414 }
415
a50e2bc0
KS
416 value = mtod(*mp, caddr_t); /* it's aligned, don't worry,
417 * but lint complains about it
418 */
5f183145
KS
419 val_len = (*mp)->m_len;
420
421 switch (optname) {
422
423 case TPOPT_MY_TSEL:
424 if ( cmd == PRCO_GETOPT ) {
425 ASSERT( tpcb->tp_lsuffixlen <= MAX_TSAP_SEL_LEN );
a50e2bc0 426 bcopy((caddr_t)tpcb->tp_lsuffix, value, tpcb->tp_lsuffixlen);
5f183145
KS
427 (*mp)->m_len = tpcb->tp_lsuffixlen;
428 } else /* cmd == PRCO_SETOPT */ {
429 if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) {
430 printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp));
431 error = EINVAL;
432 } else {
a50e2bc0 433 bcopy(value, (caddr_t)tpcb->tp_lsuffix, val_len);
5f183145
KS
434 tpcb->tp_lsuffixlen = val_len;
435 }
436 }
437 break;
438
439 case TPOPT_PEER_TSEL:
440 if ( cmd == PRCO_GETOPT ) {
441 ASSERT( tpcb->tp_fsuffixlen <= MAX_TSAP_SEL_LEN );
a50e2bc0 442 bcopy((caddr_t)tpcb->tp_fsuffix, value, tpcb->tp_fsuffixlen);
5f183145
KS
443 (*mp)->m_len = tpcb->tp_fsuffixlen;
444 } else /* cmd == PRCO_SETOPT */ {
445 if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) {
446 printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp));
447 error = EINVAL;
448 } else {
a50e2bc0 449 bcopy(value, (caddr_t)tpcb->tp_fsuffix, val_len);
5f183145
KS
450 tpcb->tp_fsuffixlen = val_len;
451 }
452 }
453 break;
454
455 case TPOPT_FLAGS:
456 IFDEBUG(D_REQUEST)
457 printf("%s TPOPT_FLAGS value 0x%x *value 0x%x, flags 0x%x \n",
458 cmd==PRCO_GETOPT?"GET":"SET",
459 value,
460 *value,
461 tpcb->tp_flags);
462 ENDDEBUG
463
464 if ( cmd == PRCO_GETOPT ) {
465 *(int *)value = (int)tpcb->tp_flags;
466 (*mp)->m_len = sizeof(u_int);
467 } else /* cmd == PRCO_SETOPT */ {
468 error = EINVAL; goto done;
469 }
470 break;
471
472 case TPOPT_PARAMS:
473 /* This handles:
474 * timer values,
475 * class, use of transport expedited data,
476 * max tpdu size, checksum, xtd format and
477 * disconnect indications, and may get rid of connect/disc data
478 */
479 IFDEBUG(D_SETPARAMS)
480 printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value,
481 cmd==PRCO_GETOPT?"GET":"SET");
482 ENDDEBUG
483 IFDEBUG(D_REQUEST)
484 printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value,
485 cmd==PRCO_GETOPT?"GET":"SET");
486 ENDDEBUG
487
488 if ( cmd == PRCO_GETOPT ) {
489 *(struct tp_conn_param *)value = tpcb->_tp_param;
490 (*mp)->m_len = sizeof(tpcb->_tp_param);
491 } else /* cmd == PRCO_SETOPT */ {
492 if( (error =
493 tp_consistency(tpcb, TP_STRICT | TP_FORCE,
494 (struct tp_conn_param *)value))==0) {
495 /*
496 * tp_consistency doesn't copy the whole set of params
497 */
498 tpcb->_tp_param = *(struct tp_conn_param *)value;
499 (*mp)->m_len = sizeof(tpcb->_tp_param);
500 }
501 }
502 break;
503
504 case TPOPT_PSTATISTICS:
505#ifdef TP_PERF_MEAS
506 if (cmd == PRCO_SETOPT) {
507 error = EINVAL; goto done;
508 }
509 IFPERF(tpcb)
a50e2bc0
KS
510 if (*mp) {
511 struct mbuf * n;
512 do {
513 MFREE(*mp, n);
514 *mp = n;
515 } while (n);
516 }
517 *mp = m_copym(tpcb->tp_p_mbuf, (int)M_COPYALL, M_WAITOK);
5f183145
KS
518 ENDPERF
519 else {
520 error = EINVAL; goto done;
521 }
522 break;
523#else
524 error = EOPNOTSUPP;
525 goto done;
526#endif TP_PERF_MEAS
527
528 case TPOPT_CDDATA_CLEAR:
529 if (cmd == PRCO_GETOPT) {
530 error = EINVAL;
531 } else {
a50e2bc0 532 if (tpcb->tp_ucddata) {
44f52ea5
KS
533 m_freem(tpcb->tp_ucddata);
534 tpcb->tp_ucddata = 0;
5f183145
KS
535 }
536 }
537 break;
538
44f52ea5
KS
539#ifdef TPOPT_NGC8_ACCEPT
540 case TPOPT_NGC8_ACCEPT:
541 if ( cmd == PRCO_GETOPT ) {
542 *(int *)value = (int)tpcb->tp_flags & TPFLAG_NGC8_ACCEPT;
543 (*mp)->m_len = sizeof(u_int);
544 } else {
545 if (*(int *)value)
546 tpcb->tp_flags |= TPFLAG_NGC8_ACCEPT;
547 else
548 tpcb->tp_flags &= ~TPFLAG_NGC8_ACCEPT;
549 }
550 break;
551#endif
552
553 case TPOPT_CFRM_DATA:
554 if (cmd == PRCO_SETOPT && (so->so_state & SS_ISCONFIRMING))
555 (void) tp_confirm(tpcb);
556 /* drop through */
5f183145
KS
557 case TPOPT_DISC_DATA:
558 /* drop through */
559 /* sending is for debugging purposes only -- we don't pretend
560 * to support * data on connect or disconnect fully. It's a
561 * kludge at best.
562 * This data-on-connect is only for the active side. It's sort of
563 * meaningless on the passive side (because
564 * you can't reject a connect request based on the data
565 * arriving w/ the CR, this, and because you'd have to
566 * do this setsockopt system call for each accept).
567 * but you can use it if you want.
568 */
569 case TPOPT_CONN_DATA:
570 if( tpcb->tp_class == TP_CLASS_0 ) {
571 error = EOPNOTSUPP;
572 break;
573 }
574 IFDEBUG(D_REQUEST)
575 printf("%s\n", optname==TPOPT_DISC_DATA?"DISC data":"CONN data");
576 printf("m_len 0x%x, vallen 0x%x so_snd.cc 0x%x\n",
577 (*mp)->m_len, val_len, so->so_snd.sb_cc);
578 dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput: sosnd ");
5f183145
KS
579 ENDDEBUG
580 if (cmd == PRCO_SETOPT) {
a50e2bc0 581 int len = tpcb->tp_ucddata ? tpcb->tp_ucddata->m_len : 0;
5f183145 582 /* can append connect data in several calls */
a50e2bc0 583 if (len + val_len >
5f183145
KS
584 (optname==TPOPT_CONN_DATA?TP_MAX_CR_DATA:TP_MAX_DR_DATA) ) {
585 error = EMSGSIZE; goto done;
586 }
5f183145
KS
587 (*mp)->m_next = MNULL;
588 (*mp)->m_act = 0;
a50e2bc0
KS
589 if (tpcb->tp_ucddata)
590 m_cat(tpcb->tp_ucddata, *mp);
591 else
592 tpcb->tp_ucddata = *mp;
5f183145 593 IFDEBUG(D_REQUEST)
a50e2bc0 594 dump_mbuf(tpcb->tp_ucddata, "tp_ctloutput after CONN_DATA");
5f183145
KS
595 ENDDEBUG
596 IFTRACE(D_REQUEST)
597 tptrace(TPPTmisc,"C/D DATA: flags snd.sbcc val_len",
598 tpcb->tp_flags, so->so_snd.sb_cc,val_len,0);
599 ENDTRACE
600 *mp = MNULL; /* prevent sosetopt from freeing it! */
5f183145
KS
601 }
602 break;
603
604 case TPOPT_PERF_MEAS:
605#ifdef TP_PERF_MEAS
606 if (cmd == PRCO_GETOPT) {
607 *value = (u_int)tpcb->tp_perf_on;
608 (*mp)->m_len = sizeof(u_int);
609 } else if (cmd == PRCO_SETOPT) {
610 (*mp)->m_len = 0;
611 if ((*value) != 0 && (*value) != 1 )
612 error = EINVAL;
613 else tpcb->tp_perf_on = (*value);
614 }
615 if( tpcb->tp_perf_on )
616 error = tp_setup_perf(tpcb);
617#else TP_PERF_MEAS
618 error = EOPNOTSUPP;
619#endif TP_PERF_MEAS
620 break;
621
622 default:
623 error = EOPNOTSUPP;
624 }
625
626done:
627 IFDEBUG(D_REQUEST)
628 dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput sosnd at end");
629 dump_mbuf(*mp, "tp_ctloutput *mp");
630 ENDDEBUG
631 /*
632 * sigh: getsockopt looks only at m_len : all output data must
633 * reside in the first mbuf
634 */
635 if ( error && (*mp) != MNULL )
636 (*mp)->m_len = 0;
637 if( (*mp) != MNULL ) {
638 ASSERT ( m_compress(*mp, mp) <= MLEN );
639 IFDEBUG(D_REQUEST)
640 dump_mbuf(*mp, "tp_ctloutput *mp after compress");
641 ENDDEBUG
642 }
643
644 splx(s);
645 return error;
646}