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