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