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