get rid of obsolete structures and allow timer-setting routines to be macros
[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 *
26717a37 7 * @(#)tp_output.c 7.13 (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 }
152 IFDEBUG(D_SETPARAMS)
153 printf("winsize 0x%x\n", param->p_winsize );
154 ENDDEBUG
155 if( (param->p_winsize < 128 ) ||
156 (param->p_winsize < param->p_tpdusize ) ||
157 (param->p_winsize > ((1+SB_MAX)>>2 /* 1/4 of the max */)) ) {
158 error = EINVAL; goto done;
159 } else {
160 if( tpcb->tp_state == TP_CLOSED )
a50e2bc0
KS
161 soreserve(tpcb->tp_sock, (u_long)param->p_winsize,
162 (u_long)param->p_winsize);
5f183145
KS
163 }
164 IFDEBUG(D_SETPARAMS)
165 printf("use_csum 0x%x\n", param->p_use_checksum );
166 printf("xtd_format 0x%x\n", param->p_xtd_format );
167 printf("xpd_service 0x%x\n", param->p_xpd_service );
168 printf("tpdusize 0x%x\n", param->p_tpdusize );
169 printf("tpcb->flags 0x%x\n", tpcb->tp_flags );
170 ENDDEBUG
171 switch( class_to_use ) {
172
173 case 0:
174 /* do not use checksums, xtd format, or XPD */
175
176 if( param->p_use_checksum | param->p_xtd_format | param->p_xpd_service ) {
177 if(cmd & TP_STRICT) {
178 error = EINVAL;
179 } else {
180 param->p_use_checksum = 0;
181 param->p_xtd_format = 0;
182 param->p_xpd_service = 0;
183 }
184 break;
185 }
186
187 if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
188 if(cmd & TP_STRICT) {
189 error = EINVAL;
190 } else {
191 param->p_tpdusize = TP_MIN_TPDUSIZE;
192 }
193 break;
194 }
195 if (param->p_tpdusize > TP0_TPDUSIZE) {
196 if (cmd & TP_STRICT) {
197 error = EINVAL;
198 } else {
199 param->p_tpdusize = TP0_TPDUSIZE;
200 }
201 break;
202 }
203
204 /* connect/disc data not allowed for class 0 */
a50e2bc0 205 if (tpcb->tp_ucddata) {
5f183145
KS
206 if(cmd & TP_STRICT) {
207 error = EINVAL;
208 } else if(cmd & TP_FORCE) {
a50e2bc0
KS
209 m_freem(tpcb->tp_ucddata);
210 tpcb->tp_ucddata = 0;
5f183145
KS
211 }
212 }
213 break;
214
215 case 4:
216 IFDEBUG(D_SETPARAMS)
217 printf("dt_ticks 0x%x\n", param->p_dt_ticks );
218 printf("x_ticks 0x%x\n", param->p_x_ticks );
219 printf("dr_ticks 0x%x\n", param->p_dr_ticks );
220 printf("keepalive 0x%x\n", param->p_keepalive_ticks );
221 printf("sendack 0x%x\n", param->p_sendack_ticks );
222 printf("inact 0x%x\n", param->p_inact_ticks );
223 printf("ref 0x%x\n", param->p_ref_ticks );
224 ENDDEBUG
225 if( (param->p_class & TP_CLASS_4 ) && (
226 (param->p_dt_ticks < 1) || (param->p_dr_ticks < 1) ||
227 (param->p_x_ticks < 1) || (param->p_keepalive_ticks < 1) ||
228 (param->p_sendack_ticks < 1) || (param->p_ref_ticks < 1) ||
229 (param->p_inact_ticks < 1) ) ) {
230 error = EINVAL;
231 break;
232 }
233 IFDEBUG(D_SETPARAMS)
234 printf("rx_strat 0x%x\n", param->p_rx_strat );
235 ENDDEBUG
236 if(param->p_rx_strat >
237 ( TPRX_USE_CW | TPRX_EACH | TPRX_FASTSTART) ) {
238 if(cmd & TP_STRICT) {
239 error = EINVAL;
240 } else {
241 param->p_rx_strat = TPRX_USE_CW;
242 }
243 break;
244 }
245 IFDEBUG(D_SETPARAMS)
246 printf("ack_strat 0x%x\n", param->p_ack_strat );
247 ENDDEBUG
248 if((param->p_ack_strat != 0) && (param->p_ack_strat != 1)) {
249 if(cmd & TP_STRICT) {
250 error = EINVAL;
251 } else {
252 param->p_ack_strat = TPACK_WINDOW;
253 }
254 break;
255 }
256 if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
257 if(cmd & TP_STRICT) {
258 error = EINVAL;
259 } else {
260 param->p_tpdusize = TP_MIN_TPDUSIZE;
261 }
262 break;
263 }
264 if (param->p_tpdusize > TP_TPDUSIZE) {
265 if(cmd & TP_STRICT) {
266 error = EINVAL;
267 } else {
268 param->p_tpdusize = TP_TPDUSIZE;
269 }
270 break;
271 }
272 break;
273 }
274
275 if ((error==0) && (cmd & TP_FORCE)) {
186f0000
KS
276 /* Enforce Negotation rules below */
277 if (tpcb->tp_tpdusize > param->p_tpdusize)
278 tpcb->tp_tpdusize = param->p_tpdusize;
5f183145 279 tpcb->tp_class = param->p_class;
186f0000
KS
280 if (tpcb->tp_use_checksum || param->p_use_checksum)
281 tpcb->tp_use_checksum = 1;
282 if (!tpcb->tp_xpd_service || !param->p_xpd_service)
283 tpcb->tp_xpd_service = 0;
284 if (!tpcb->tp_xtd_format || !param->p_xtd_format)
285 tpcb->tp_xtd_format = 0;
5f183145
KS
286 }
287
288done:
289
290 IFTRACE(D_CONN)
291 tptrace(TPPTmisc, "tp_consist returns class xtdfmt cmd",
292 error, tpcb->tp_class, tpcb->tp_xtd_format, cmd);
293 ENDTRACE
294 IFDEBUG(D_CONN)
295 printf(
296 "tp_consist rtns 0x%x class 0x%x xtd_fmt 0x%x cmd 0x%x\n",
297 error, tpcb->tp_class, tpcb->tp_xtd_format, cmd);
298 ENDDEBUG
299 return error;
300}
301
302/*
303 * NAME: tp_ctloutput()
304 *
305 * CALLED FROM:
306 * [sg]etsockopt(), via so[sg]etopt().
307 *
308 * FUNCTION and ARGUMENTS:
309 * Implements the socket options at transport level.
e663c139 310 * (cmd) is either PRCO_SETOPT or PRCO_GETOPT (see ../sys/protosw.h).
5f183145 311 * (so) is the socket.
e663c139 312 * (level) is SOL_TRANSPORT (see ../sys/socket.h)
5f183145
KS
313 * (optname) is the particular command or option to be set.
314 * (**mp) is an mbuf structure.
315 *
316 * RETURN VALUE:
317 * ENOTSOCK if the socket hasn't got an associated tpcb
318 * EINVAL if
319 * trying to set window too big
320 * trying to set illegal max tpdu size
321 * trying to set illegal credit fraction
322 * trying to use unknown or unimplemented class of TP
323 * structure passed to set timer values is wrong size
324 * illegal combination of command/GET-SET option,
325 * e.g., GET w/ TPOPT_CDDATA_CLEAR:
326 * EOPNOTSUPP if the level isn't transport, or command is neither GET nor SET
327 * or if the transport-specific command is not implemented
328 * EISCONN if trying a command that isn't allowed after a connection
329 * is established
330 * ENOTCONN if trying a command that is allowed only if a connection is
331 * established
332 * EMSGSIZE if trying to give too much data on connect/disconnect
333 *
334 * SIDE EFFECTS:
335 *
336 * NOTES:
337 */
338ProtoHook
339tp_ctloutput(cmd, so, level, optname, mp)
340 int cmd, level, optname;
341 struct socket *so;
342 struct mbuf **mp;
343{
344 struct tp_pcb *tpcb = sototpcb(so);
345 int s = splnet();
a50e2bc0
KS
346 caddr_t value;
347 unsigned val_len;
5f183145
KS
348 int error = 0;
349
350 IFTRACE(D_REQUEST)
351 tptrace(TPPTmisc, "tp_ctloutput cmd so optname mp",
352 cmd, so, optname, mp);
353 ENDTRACE
354 IFDEBUG(D_REQUEST)
355 printf(
356 "tp_ctloutput so 0x%x cmd 0x%x optname 0x%x, mp 0x%x *mp 0x%x tpcb 0x%x\n",
357 so, cmd, optname, mp, mp?*mp:0, tpcb);
358 ENDDEBUG
359 if( tpcb == (struct tp_pcb *)0 ) {
360 error = ENOTSOCK; goto done;
361 }
362 if(*mp == MNULL) {
363 register struct mbuf *m;
364
365 MGET(m, M_DONTWAIT, TPMT_SONAME); /* does off, type, next */
366 if (m == NULL) {
367 splx(s);
368 return ENOBUFS;
369 }
370 m->m_len = 0;
371 m->m_act = 0;
372 *mp = m;
373 }
374
375 /*
376 * Hook so one can set network options via a tp socket.
377 */
378 if ( level == SOL_NETWORK ) {
379 if ((tpcb->tp_nlproto == NULL) || (tpcb->tp_npcb == NULL))
380 error = ENOTSOCK;
381 else if (tpcb->tp_nlproto->nlp_ctloutput == NULL)
382 error = EOPNOTSUPP;
383 else
26717a37
KS
384 return ((tpcb->tp_nlproto->nlp_ctloutput)(cmd, optname,
385 tpcb->tp_npcb, *mp));
386 goto done;
387 } else if ( level == SOL_SOCKET) {
388 if (optname == SO_RCVBUF && cmd == PRCO_SETOPT) {
389 tp_rsyset(tpcb);
390 }
5f183145
KS
391 goto done;
392 } else if ( level != SOL_TRANSPORT ) {
393 error = EOPNOTSUPP; goto done;
394 }
395 if (cmd != PRCO_GETOPT && cmd != PRCO_SETOPT) {
396 error = EOPNOTSUPP; goto done;
397 }
398 if ( so->so_error ) {
399 error = so->so_error; goto done;
400 }
401
402 /* The only options allowed after connection is established
403 * are GET (anything) and SET DISC DATA and SET PERF MEAS
404 */
405 if ( ((so->so_state & SS_ISCONNECTING)||(so->so_state & SS_ISCONNECTED))
406 &&
407 (cmd == PRCO_SETOPT &&
408 optname != TPOPT_DISC_DATA &&
44f52ea5 409 optname != TPOPT_CFRM_DATA &&
5f183145
KS
410 optname != TPOPT_PERF_MEAS &&
411 optname != TPOPT_CDDATA_CLEAR ) ) {
412 error = EISCONN; goto done;
413 }
414 /* The only options allowed after disconnection are GET DISC DATA,
415 * and TPOPT_PSTATISTICS
416 * and they're not allowed if the ref timer has gone off, because
417 * the tpcb is gone
418 */
44f52ea5 419 if ((so->so_state & (SS_ISCONNECTED | SS_ISCONFIRMING)) == 0) {
f15f73d2 420 if ( so->so_pcb == (caddr_t)0 ) {
5f183145
KS
421 error = ENOTCONN; goto done;
422 }
423 if ( (tpcb->tp_state == TP_REFWAIT || tpcb->tp_state == TP_CLOSING) &&
424 (optname != TPOPT_DISC_DATA && optname != TPOPT_PSTATISTICS)) {
425 error = ENOTCONN; goto done;
426 }
427 }
428
a50e2bc0
KS
429 value = mtod(*mp, caddr_t); /* it's aligned, don't worry,
430 * but lint complains about it
431 */
5f183145
KS
432 val_len = (*mp)->m_len;
433
434 switch (optname) {
435
63f88aec 436 case TPOPT_INTERCEPT:
01acbfa1
KS
437#define INA(t) (((struct inpcb *)(t->tp_npcb))->inp_laddr.s_addr)
438#define ISOA(t) (((struct isopcb *)(t->tp_npcb))->isop_laddr->siso_addr)
439
67857e5a
KS
440 if ((so->so_state & SS_PRIV) == 0) {
441 error = EPERM;
01acbfa1
KS
442 } else if (cmd != PRCO_SETOPT || tpcb->tp_state != TP_CLOSED ||
443 (tpcb->tp_flags & TPF_GENERAL_ADDR) ||
444 tpcb->tp_next == 0)
63f88aec
KS
445 error = EINVAL;
446 else {
01acbfa1
KS
447 register struct tp_pcb *t;
448 error = EADDRINUSE;
449 for (t = tp_listeners; t; t = t->tp_nextlisten)
450 if ((t->tp_flags & TPF_GENERAL_ADDR) == 0 &&
451 t->tp_domain == tpcb->tp_domain)
452 switch (tpcb->tp_domain) {
453 default:
454 goto done;
455#ifdef INET
456 case AF_INET:
457 if (INA(t) == INA(tpcb))
458 goto done;
459 continue;
460#endif
461#ifdef ISO
462 case AF_ISO:
463 if (bcmp(ISOA(t).isoa_genaddr, ISOA(tpcb).isoa_genaddr,
464 ISOA(t).isoa_len) == 0)
465 goto done;
466 continue;
467#endif
468 }
469 tpcb->tp_lsuffixlen = 0;
470 tpcb->tp_state = TP_LISTENING;
471 error = 0;
472 remque(tpcb);
473 tpcb->tp_next = tpcb->tp_prev = tpcb;
474 tpcb->tp_nextlisten = tp_listeners;
475 tp_listeners = tpcb;
63f88aec 476 }
63f88aec
KS
477 break;
478
5f183145
KS
479 case TPOPT_MY_TSEL:
480 if ( cmd == PRCO_GETOPT ) {
481 ASSERT( tpcb->tp_lsuffixlen <= MAX_TSAP_SEL_LEN );
a50e2bc0 482 bcopy((caddr_t)tpcb->tp_lsuffix, value, tpcb->tp_lsuffixlen);
5f183145
KS
483 (*mp)->m_len = tpcb->tp_lsuffixlen;
484 } else /* cmd == PRCO_SETOPT */ {
485 if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) {
486 printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp));
487 error = EINVAL;
488 } else {
a50e2bc0 489 bcopy(value, (caddr_t)tpcb->tp_lsuffix, val_len);
5f183145
KS
490 tpcb->tp_lsuffixlen = val_len;
491 }
492 }
493 break;
494
495 case TPOPT_PEER_TSEL:
496 if ( cmd == PRCO_GETOPT ) {
497 ASSERT( tpcb->tp_fsuffixlen <= MAX_TSAP_SEL_LEN );
a50e2bc0 498 bcopy((caddr_t)tpcb->tp_fsuffix, value, tpcb->tp_fsuffixlen);
5f183145
KS
499 (*mp)->m_len = tpcb->tp_fsuffixlen;
500 } else /* cmd == PRCO_SETOPT */ {
501 if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) {
502 printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp));
503 error = EINVAL;
504 } else {
a50e2bc0 505 bcopy(value, (caddr_t)tpcb->tp_fsuffix, val_len);
5f183145
KS
506 tpcb->tp_fsuffixlen = val_len;
507 }
508 }
509 break;
510
511 case TPOPT_FLAGS:
512 IFDEBUG(D_REQUEST)
513 printf("%s TPOPT_FLAGS value 0x%x *value 0x%x, flags 0x%x \n",
514 cmd==PRCO_GETOPT?"GET":"SET",
515 value,
516 *value,
517 tpcb->tp_flags);
518 ENDDEBUG
519
520 if ( cmd == PRCO_GETOPT ) {
521 *(int *)value = (int)tpcb->tp_flags;
522 (*mp)->m_len = sizeof(u_int);
523 } else /* cmd == PRCO_SETOPT */ {
524 error = EINVAL; goto done;
525 }
526 break;
527
528 case TPOPT_PARAMS:
529 /* This handles:
530 * timer values,
531 * class, use of transport expedited data,
532 * max tpdu size, checksum, xtd format and
533 * disconnect indications, and may get rid of connect/disc data
534 */
535 IFDEBUG(D_SETPARAMS)
536 printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value,
537 cmd==PRCO_GETOPT?"GET":"SET");
538 ENDDEBUG
539 IFDEBUG(D_REQUEST)
540 printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value,
541 cmd==PRCO_GETOPT?"GET":"SET");
542 ENDDEBUG
543
544 if ( cmd == PRCO_GETOPT ) {
545 *(struct tp_conn_param *)value = tpcb->_tp_param;
546 (*mp)->m_len = sizeof(tpcb->_tp_param);
547 } else /* cmd == PRCO_SETOPT */ {
548 if( (error =
549 tp_consistency(tpcb, TP_STRICT | TP_FORCE,
550 (struct tp_conn_param *)value))==0) {
551 /*
552 * tp_consistency doesn't copy the whole set of params
553 */
554 tpcb->_tp_param = *(struct tp_conn_param *)value;
555 (*mp)->m_len = sizeof(tpcb->_tp_param);
556 }
557 }
558 break;
559
560 case TPOPT_PSTATISTICS:
561#ifdef TP_PERF_MEAS
562 if (cmd == PRCO_SETOPT) {
563 error = EINVAL; goto done;
564 }
565 IFPERF(tpcb)
a50e2bc0
KS
566 if (*mp) {
567 struct mbuf * n;
568 do {
569 MFREE(*mp, n);
570 *mp = n;
571 } while (n);
572 }
573 *mp = m_copym(tpcb->tp_p_mbuf, (int)M_COPYALL, M_WAITOK);
5f183145
KS
574 ENDPERF
575 else {
576 error = EINVAL; goto done;
577 }
578 break;
579#else
580 error = EOPNOTSUPP;
581 goto done;
582#endif TP_PERF_MEAS
583
584 case TPOPT_CDDATA_CLEAR:
585 if (cmd == PRCO_GETOPT) {
586 error = EINVAL;
587 } else {
a50e2bc0 588 if (tpcb->tp_ucddata) {
44f52ea5
KS
589 m_freem(tpcb->tp_ucddata);
590 tpcb->tp_ucddata = 0;
5f183145
KS
591 }
592 }
593 break;
594
44f52ea5 595 case TPOPT_CFRM_DATA:
5f183145 596 case TPOPT_DISC_DATA:
5f183145
KS
597 case TPOPT_CONN_DATA:
598 if( tpcb->tp_class == TP_CLASS_0 ) {
599 error = EOPNOTSUPP;
600 break;
601 }
602 IFDEBUG(D_REQUEST)
603 printf("%s\n", optname==TPOPT_DISC_DATA?"DISC data":"CONN data");
604 printf("m_len 0x%x, vallen 0x%x so_snd.cc 0x%x\n",
605 (*mp)->m_len, val_len, so->so_snd.sb_cc);
606 dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput: sosnd ");
5f183145
KS
607 ENDDEBUG
608 if (cmd == PRCO_SETOPT) {
a50e2bc0 609 int len = tpcb->tp_ucddata ? tpcb->tp_ucddata->m_len : 0;
5f183145 610 /* can append connect data in several calls */
a50e2bc0 611 if (len + val_len >
5f183145
KS
612 (optname==TPOPT_CONN_DATA?TP_MAX_CR_DATA:TP_MAX_DR_DATA) ) {
613 error = EMSGSIZE; goto done;
614 }
5f183145
KS
615 (*mp)->m_next = MNULL;
616 (*mp)->m_act = 0;
a50e2bc0
KS
617 if (tpcb->tp_ucddata)
618 m_cat(tpcb->tp_ucddata, *mp);
619 else
620 tpcb->tp_ucddata = *mp;
39f04878
KS
621 IFDEBUG(D_REQUEST)
622 dump_mbuf(tpcb->tp_ucddata, "tp_ctloutput after CONN_DATA");
623 ENDDEBUG
5f183145
KS
624 IFTRACE(D_REQUEST)
625 tptrace(TPPTmisc,"C/D DATA: flags snd.sbcc val_len",
626 tpcb->tp_flags, so->so_snd.sb_cc,val_len,0);
627 ENDTRACE
26717a37 628 *mp = MNULL;
39f04878
KS
629 if (optname == TPOPT_CFRM_DATA && (so->so_state & SS_ISCONFIRMING))
630 (void) tp_confirm(tpcb);
5f183145
KS
631 }
632 break;
633
634 case TPOPT_PERF_MEAS:
635#ifdef TP_PERF_MEAS
636 if (cmd == PRCO_GETOPT) {
637 *value = (u_int)tpcb->tp_perf_on;
638 (*mp)->m_len = sizeof(u_int);
639 } else if (cmd == PRCO_SETOPT) {
640 (*mp)->m_len = 0;
641 if ((*value) != 0 && (*value) != 1 )
642 error = EINVAL;
643 else tpcb->tp_perf_on = (*value);
644 }
645 if( tpcb->tp_perf_on )
646 error = tp_setup_perf(tpcb);
647#else TP_PERF_MEAS
648 error = EOPNOTSUPP;
649#endif TP_PERF_MEAS
650 break;
651
652 default:
653 error = EOPNOTSUPP;
654 }
655
656done:
657 IFDEBUG(D_REQUEST)
658 dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput sosnd at end");
659 dump_mbuf(*mp, "tp_ctloutput *mp");
660 ENDDEBUG
661 /*
662 * sigh: getsockopt looks only at m_len : all output data must
663 * reside in the first mbuf
664 */
26717a37
KS
665 if (*mp) {
666 if (cmd == PRCO_SETOPT)
667 m_freem(*mp);
668 else {
669 ASSERT ( m_compress(*mp, mp) <= MLEN );
670 if (error)
671 (*mp)->m_len = 0;
672 IFDEBUG(D_REQUEST)
673 dump_mbuf(*mp, "tp_ctloutput *mp after compress");
674 ENDDEBUG
675 }
5f183145 676 }
5f183145
KS
677 splx(s);
678 return error;
679}