No empty .Rs/.Re
[netbsd-mini2440.git] / sys / netiso / tp_output.c
blob0fa88845926235411f9ea2d10efbe0f2d62a545c
1 /* $NetBSD: tp_output.c,v 1.38 2009/04/16 22:22:06 elad Exp $ */
3 /*-
4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
31 * @(#)tp_output.c 8.2 (Berkeley) 2/9/95
34 /***********************************************************
35 Copyright IBM Corporation 1987
37 All Rights Reserved
39 Permission to use, copy, modify, and distribute this software and its
40 documentation for any purpose and without fee is hereby granted,
41 provided that the above copyright notice appear in all copies and that
42 both that copyright notice and this permission notice appear in
43 supporting documentation, and that the name of IBM not be
44 used in advertising or publicity pertaining to distribution of the
45 software without specific, written prior permission.
47 IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
48 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
49 IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
50 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
51 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
52 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
53 SOFTWARE.
55 ******************************************************************/
58 * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
61 * In here is tp_ctloutput(), the guy called by [sg]etsockopt(),
64 #include <sys/cdefs.h>
65 __KERNEL_RCSID(0, "$NetBSD: tp_output.c,v 1.38 2009/04/16 22:22:06 elad Exp $");
67 #include "opt_inet.h"
68 #include "opt_iso.h"
70 #include <sys/param.h>
71 #include <sys/mbuf.h>
72 #include <sys/systm.h>
73 #include <sys/socket.h>
74 #include <sys/socketvar.h>
75 #include <sys/protosw.h>
76 #include <sys/errno.h>
77 #include <sys/time.h>
78 #include <sys/kernel.h>
79 #include <sys/proc.h>
80 #include <sys/kauth.h>
82 #include <netiso/tp_param.h>
83 #include <netiso/tp_var.h>
84 #include <netiso/tp_user.h>
85 #include <netiso/tp_stat.h>
86 #include <netiso/tp_ip.h>
87 #include <netiso/tp_clnp.h>
88 #include <netiso/tp_timer.h>
89 #include <netiso/argo_debug.h>
90 #include <netiso/tp_pcb.h>
91 #include <netiso/tp_trace.h>
93 #define TPDUSIZESHIFT 24
94 #define CLASSHIFT 16
97 * NAME: tp_consistency()
99 * CALLED FROM:
100 * tp_ctloutput(), tp_input()
102 * FUNCTION and ARGUMENTS:
103 * Checks the consistency of options and tpdusize with class,
104 * using the parameters passed in via (param).
105 * (cmd) may be TP_STRICT or TP_FORCE or both.
106 * Force means it will set all the values in (tpcb) to those in
107 * the input arguments iff no errors were encountered.
108 * Strict means that no inconsistency will be tolerated. If it's
109 * not used, checksum and tpdusize inconsistencies will be tolerated.
110 * The reason for this is that in some cases, when we're negotiating down
111 * from class 4, these options should be changed but should not
112 * cause negotiation to fail.
114 * RETURNS
115 * E* or EOK
116 * E* if the various parms aren't ok for a given class
117 * EOK if they are ok for a given class
121 tp_consistency(struct tp_pcb *tpcb, u_int cmd, struct tp_conn_param *param)
123 int error = EOK;
124 int class_to_use = tp_mask_to_num(param->p_class);
126 #ifdef TPPT
127 if (tp_traceflags[D_SETPARAMS]) {
128 tptrace(TPPTmisc,
129 "tp_consist enter class_to_use dontchange param.class cmd",
130 class_to_use, param->p_dont_change_params, param->p_class, cmd);
132 #endif
133 #ifdef ARGO_DEBUG
134 if (argo_debug[D_SETPARAMS]) {
135 printf("tp_consistency %s %s\n",
136 cmd & TP_FORCE ? "TP_FORCE" : "",
137 cmd & TP_STRICT ? "TP_STRICT" : "");
139 #endif
140 if ((cmd & TP_FORCE) && (param->p_dont_change_params)) {
141 cmd &= ~TP_FORCE;
144 * can switch net services within a domain, but cannot switch domains
146 switch (param->p_netservice) {
147 case ISO_CONS:
148 case ISO_CLNS:
149 case ISO_COSNS:
150 /* param->p_netservice in ISO DOMAIN */
151 if (tpcb->tp_domain != AF_ISO) {
152 error = EINVAL;
153 goto done;
155 break;
156 case IN_CLNS:
157 /* param->p_netservice in INET DOMAIN */
158 if (tpcb->tp_domain != AF_INET) {
159 error = EINVAL;
160 goto done;
162 break;
163 /* no others not possible-> netservice is a 2-bit field! */
166 #ifdef ARGO_DEBUG
167 if (argo_debug[D_SETPARAMS]) {
168 printf("p_class 0x%x, class_to_use 0x%x\n", param->p_class,
169 class_to_use);
171 #endif
172 if ((param->p_netservice > TP_MAX_NETSERVICES)) {
173 error = EINVAL;
174 goto done;
176 if ((param->p_class & TP_CLASSES_IMPLEMENTED) == 0) {
177 error = EINVAL;
178 goto done;
180 #ifdef ARGO_DEBUG
181 if (argo_debug[D_SETPARAMS]) {
182 printf("Nretrans 0x%x\n", param->p_Nretrans);
184 #endif
185 if ((param->p_Nretrans < 1) ||
186 (param->p_cr_ticks < 1) || (param->p_cc_ticks < 1)) {
188 * bad for any class because negot has to be done a la class
191 error = EINVAL;
192 goto done;
194 #ifdef ARGO_DEBUG
195 if (argo_debug[D_SETPARAMS]) {
196 printf("use_csum 0x%x\n", param->p_use_checksum);
197 printf("xtd_format 0x%x\n", param->p_xtd_format);
198 printf("xpd_service 0x%x\n", param->p_xpd_service);
199 printf("tpdusize 0x%x\n", param->p_tpdusize);
200 printf("tpcb->flags 0x%x\n", tpcb->tp_flags);
202 #endif
203 switch (class_to_use) {
205 case 0:
206 /* do not use checksums, xtd format, or XPD */
208 if (param->p_use_checksum | param->p_xtd_format | param->p_xpd_service) {
209 if (cmd & TP_STRICT) {
210 error = EINVAL;
211 } else {
212 param->p_use_checksum = 0;
213 param->p_xtd_format = 0;
214 param->p_xpd_service = 0;
216 break;
218 if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
219 if (cmd & TP_STRICT) {
220 error = EINVAL;
221 } else {
222 param->p_tpdusize = TP_MIN_TPDUSIZE;
224 break;
226 if (param->p_tpdusize > TP0_TPDUSIZE) {
227 if (cmd & TP_STRICT) {
228 error = EINVAL;
229 } else {
230 param->p_tpdusize = TP0_TPDUSIZE;
232 break;
234 /* connect/disc data not allowed for class 0 */
235 if (tpcb->tp_ucddata) {
236 if (cmd & TP_STRICT) {
237 error = EINVAL;
238 } else if (cmd & TP_FORCE) {
239 m_freem(tpcb->tp_ucddata);
240 tpcb->tp_ucddata = 0;
243 break;
245 case 4:
246 #ifdef ARGO_DEBUG
247 if (argo_debug[D_SETPARAMS]) {
248 printf("dt_ticks 0x%x\n", param->p_dt_ticks);
249 printf("x_ticks 0x%x\n", param->p_x_ticks);
250 printf("dr_ticks 0x%x\n", param->p_dr_ticks);
251 printf("keepalive 0x%x\n", param->p_keepalive_ticks);
252 printf("sendack 0x%x\n", param->p_sendack_ticks);
253 printf("inact 0x%x\n", param->p_inact_ticks);
254 printf("ref 0x%x\n", param->p_ref_ticks);
256 #endif
257 if ((param->p_class & TP_CLASS_4) && (
258 (param->p_dt_ticks < 1) || (param->p_dr_ticks < 1) ||
259 (param->p_x_ticks < 1) || (param->p_keepalive_ticks < 1) ||
260 (param->p_sendack_ticks < 1) || (param->p_ref_ticks < 1) ||
261 (param->p_inact_ticks < 1))) {
262 error = EINVAL;
263 break;
265 #ifdef ARGO_DEBUG
266 if (argo_debug[D_SETPARAMS]) {
267 printf("rx_strat 0x%x\n", param->p_rx_strat);
269 #endif
270 if (param->p_rx_strat >
271 (TPRX_USE_CW | TPRX_EACH | TPRX_FASTSTART)) {
272 if (cmd & TP_STRICT) {
273 error = EINVAL;
274 } else {
275 param->p_rx_strat = TPRX_USE_CW;
277 break;
279 #ifdef ARGO_DEBUG
280 if (argo_debug[D_SETPARAMS]) {
281 printf("ack_strat 0x%x\n", param->p_ack_strat);
283 #endif
284 if ((param->p_ack_strat != 0) && (param->p_ack_strat != 1)) {
285 if (cmd & TP_STRICT) {
286 error = EINVAL;
287 } else {
288 param->p_ack_strat = TPACK_WINDOW;
290 break;
292 if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
293 if (cmd & TP_STRICT) {
294 error = EINVAL;
295 } else {
296 param->p_tpdusize = TP_MIN_TPDUSIZE;
298 break;
300 if (param->p_tpdusize > TP_TPDUSIZE) {
301 if (cmd & TP_STRICT) {
302 error = EINVAL;
303 } else {
304 param->p_tpdusize = TP_TPDUSIZE;
306 break;
308 break;
311 if ((error == 0) && (cmd & TP_FORCE)) {
312 long dusize = ((long) param->p_ptpdusize) << 7;
313 /* Enforce Negotation rules below */
314 tpcb->tp_class = param->p_class;
315 if (tpcb->tp_use_checksum || param->p_use_checksum)
316 tpcb->tp_use_checksum = 1;
317 if (!tpcb->tp_xpd_service || !param->p_xpd_service)
318 tpcb->tp_xpd_service = 0;
319 if (!tpcb->tp_xtd_format || !param->p_xtd_format)
320 tpcb->tp_xtd_format = 0;
321 if (dusize) {
322 if (tpcb->tp_l_tpdusize > dusize)
323 tpcb->tp_l_tpdusize = dusize;
324 if (tpcb->tp_ptpdusize == 0 ||
325 tpcb->tp_ptpdusize > param->p_ptpdusize)
326 tpcb->tp_ptpdusize = param->p_ptpdusize;
327 } else {
328 if (param->p_tpdusize != 0 &&
329 tpcb->tp_tpdusize > param->p_tpdusize)
330 tpcb->tp_tpdusize = param->p_tpdusize;
331 tpcb->tp_l_tpdusize = 1 << tpcb->tp_tpdusize;
334 done:
336 #ifdef TPPT
337 if (tp_traceflags[D_CONN]) {
338 tptrace(TPPTmisc, "tp_consist returns class xtdfmt cmd",
339 error, tpcb->tp_class, tpcb->tp_xtd_format, cmd);
341 #endif
342 #ifdef ARGO_DEBUG
343 if (argo_debug[D_CONN]) {
344 printf(
345 "tp_consist rtns 0x%x class 0x%x xtd_fmt 0x%x cmd 0x%x\n",
346 error, tpcb->tp_class, tpcb->tp_xtd_format, cmd);
348 #endif
349 return error;
353 * NAME: tp_ctloutput1()
355 * CALLED FROM:
356 * [sg]etsockopt(), via so[sg]etopt().
357 * via tp_ctloutput() below
359 * FUNCTION and ARGUMENTS:
360 * Implements the socket options at transport level.
361 * (cmd) is either PRCO_SETOPT or PRCO_GETOPT (see ../sys/protosw.h).
362 * (so) is the socket.
363 * (level) is SOL_TRANSPORT (see ../sys/socket.h)
364 * (optname) is the particular command or option to be set.
365 * (**mp) is an mbuf structure.
367 * RETURN VALUE:
368 * ENOTSOCK if the socket hasn't got an associated tpcb
369 * EINVAL if
370 * trying to set window too big
371 * trying to set illegal max tpdu size
372 * trying to set illegal credit fraction
373 * trying to use unknown or unimplemented class of TP
374 * structure passed to set timer values is wrong size
375 * illegal combination of command/GET-SET option,
376 * e.g., GET w/ TPOPT_CDDATA_CLEAR:
377 * EOPNOTSUPP if the level isn't transport, or command is neither GET nor SET
378 * or if the transport-specific command is not implemented
379 * EISCONN if trying a command that isn't allowed after a connection
380 * is established
381 * ENOTCONN if trying a command that is allowed only if a connection is
382 * established
383 * EMSGSIZE if trying to give too much data on connect/disconnect
385 * SIDE EFFECTS:
387 * NOTES:
389 static int
390 tp_ctloutput1(int cmd, struct socket *so, int level, int optname,
391 struct mbuf **mp)
393 struct lwp *l = curlwp; /* XXX */
394 struct tp_pcb *tpcb = sototpcb(so);
395 int s = splsoftnet();
396 void * value;
397 unsigned val_len;
398 int error = 0;
400 #ifdef TPPT
401 if (tp_traceflags[D_REQUEST]) {
402 tptrace(TPPTmisc, "tp_ctloutput cmd so optname mp",
403 cmd, so, optname, mp);
405 #endif
406 #ifdef ARGO_DEBUG
407 if (argo_debug[D_REQUEST]) {
408 printf(
409 "tp_ctloutput so %p cmd 0x%x optname 0x%x, mp %p *mp %p tpcb %p\n",
410 so, cmd, optname, mp, mp ? *mp : 0, tpcb);
412 #endif
413 if (tpcb == (struct tp_pcb *) 0) {
414 error = ENOTSOCK;
415 goto done;
417 if (mp && *mp == NULL) {
418 struct mbuf *m;
420 MGET(m, M_DONTWAIT, TPMT_SONAME); /* does off, type, next */
421 if (m == NULL) {
422 splx(s);
423 return ENOBUFS;
425 m->m_len = 0;
426 m->m_nextpkt = 0;
427 *mp = m;
430 * Hook so one can set network options via a tp socket.
432 if (level == SOL_NETWORK) {
433 if ((tpcb->tp_nlproto == NULL) || (tpcb->tp_npcb == NULL))
434 error = ENOTSOCK;
435 else if (tpcb->tp_nlproto->nlp_ctloutput == NULL)
436 error = EOPNOTSUPP;
437 else
438 return ((tpcb->tp_nlproto->nlp_ctloutput) (cmd, optname,
439 tpcb->tp_npcb, *mp));
440 goto done;
441 } else if (level == SOL_SOCKET) {
442 if (optname == SO_RCVBUF && cmd == PRCO_SETOPT) {
443 u_long old_credit = tpcb->tp_maxlcredit;
444 tp_rsyset(tpcb);
445 if (tpcb->tp_rhiwat != so->so_rcv.sb_hiwat &&
446 tpcb->tp_state == TP_OPEN &&
447 (old_credit < tpcb->tp_maxlcredit))
448 tp_emit(AK_TPDU_type, tpcb,
449 tpcb->tp_rcvnxt, 0, NULL);
450 tpcb->tp_rhiwat = so->so_rcv.sb_hiwat;
452 goto done;
453 } else if (level != SOL_TRANSPORT) {
454 error = EOPNOTSUPP;
455 goto done;
457 if (cmd != PRCO_GETOPT && cmd != PRCO_SETOPT) {
458 error = EOPNOTSUPP;
459 goto done;
461 if (so->so_error) {
462 error = so->so_error;
463 goto done;
466 * The only options allowed after connection is established are GET
467 * (anything) and SET DISC DATA and SET PERF MEAS
469 if (((so->so_state & SS_ISCONNECTING) || (so->so_state & SS_ISCONNECTED))
471 (cmd == PRCO_SETOPT &&
472 optname != TPOPT_DISC_DATA &&
473 optname != TPOPT_CFRM_DATA &&
474 optname != TPOPT_PERF_MEAS &&
475 optname != TPOPT_CDDATA_CLEAR)) {
476 error = EISCONN;
477 goto done;
480 * The only options allowed after disconnection are GET DISC DATA,
481 * and TPOPT_PSTATISTICS and they're not allowed if the ref timer has
482 * gone off, because the tpcb is gone
484 if ((so->so_state & (SS_ISCONNECTED | SS_ISCONFIRMING)) == 0) {
485 if (so->so_pcb == 0) {
486 error = ENOTCONN;
487 goto done;
489 if ((tpcb->tp_state == TP_REFWAIT || tpcb->tp_state == TP_CLOSING) &&
490 (optname != TPOPT_DISC_DATA && optname != TPOPT_PSTATISTICS)) {
491 error = ENOTCONN;
492 goto done;
495 value = mtod(*mp, void *); /* it's aligned, don't worry, but
496 * lint complains about it */
497 val_len = (*mp)->m_len;
499 switch (optname) {
501 case TPOPT_INTERCEPT:
502 #define INA(t) (((struct inpcb *)(t->tp_npcb))->inp_laddr.s_addr)
503 #define ISOA(t) (((struct isopcb *)(t->tp_npcb))->isop_laddr->siso_addr)
505 if (l == NULL || (error = kauth_authorize_network(l->l_cred,
506 KAUTH_NETWORK_SOCKET, KAUTH_REQ_NETWORK_SOCKET_SETPRIV,
507 KAUTH_ARG(optname), NULL, NULL))) {
508 error = EPERM;
509 } else if (cmd != PRCO_SETOPT || tpcb->tp_state != TP_CLOSED ||
510 (tpcb->tp_flags & TPF_GENERAL_ADDR) ||
511 tpcb->tp_next == 0)
512 error = EINVAL;
513 else {
514 struct tp_pcb *t;
515 error = EADDRINUSE;
516 for (t = tp_listeners; t; t = t->tp_nextlisten)
517 if ((t->tp_flags & TPF_GENERAL_ADDR) == 0 &&
518 t->tp_domain == tpcb->tp_domain)
519 switch (tpcb->tp_domain) {
520 default:
521 goto done;
522 #ifdef INET
523 case AF_INET:
524 if (INA(t) == INA(tpcb))
525 goto done;
526 continue;
527 #endif
528 #ifdef ISO
529 case AF_ISO:
530 if (memcmp(ISOA(t).isoa_genaddr, ISOA(tpcb).isoa_genaddr,
531 ISOA(t).isoa_len) == 0)
532 goto done;
533 continue;
534 #endif
536 tpcb->tp_lsuffixlen = 0;
537 tpcb->tp_state = TP_LISTENING;
538 error = 0;
539 iso_remque(tpcb);
540 tpcb->tp_next = tpcb->tp_prev = tpcb;
541 tpcb->tp_nextlisten = tp_listeners;
542 tp_listeners = tpcb;
544 break;
546 case TPOPT_MY_TSEL:
547 if (cmd == PRCO_GETOPT) {
548 ASSERT(tpcb->tp_lsuffixlen <= MAX_TSAP_SEL_LEN);
549 memcpy(value, (void *) tpcb->tp_lsuffix, tpcb->tp_lsuffixlen);
550 (*mp)->m_len = tpcb->tp_lsuffixlen;
551 } else { /* cmd == PRCO_SETOPT */
552 if ((val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0)) {
553 printf("val_len 0x%x (*mp)->m_len %p\n",
554 val_len, (*mp));
555 error = EINVAL;
556 } else {
557 memcpy((void *) tpcb->tp_lsuffix, value, val_len);
558 tpcb->tp_lsuffixlen = val_len;
561 break;
563 case TPOPT_PEER_TSEL:
564 if (cmd == PRCO_GETOPT) {
565 ASSERT(tpcb->tp_fsuffixlen <= MAX_TSAP_SEL_LEN);
566 memcpy(value, (void *) tpcb->tp_fsuffix, tpcb->tp_fsuffixlen);
567 (*mp)->m_len = tpcb->tp_fsuffixlen;
568 } else { /* cmd == PRCO_SETOPT */
569 if ((val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0)) {
570 printf("val_len 0x%x (*mp)->m_len %p\n",
571 val_len, (*mp));
572 error = EINVAL;
573 } else {
574 memcpy((void *) tpcb->tp_fsuffix, value, val_len);
575 tpcb->tp_fsuffixlen = val_len;
578 break;
580 case TPOPT_FLAGS:
581 #ifdef ARGO_DEBUG
582 if (argo_debug[D_REQUEST]) {
583 printf("%s TPOPT_FLAGS value %p *value 0x%x, flags 0x%x \n",
584 cmd == PRCO_GETOPT ? "GET" : "SET",
585 value,
586 *(unsigned char *)value,
587 tpcb->tp_flags);
589 #endif
591 if (cmd == PRCO_GETOPT) {
592 *(int *) value = (int) tpcb->tp_flags;
593 (*mp)->m_len = sizeof(u_int);
594 } else { /* cmd == PRCO_SETOPT */
595 error = EINVAL;
596 goto done;
598 break;
600 case TPOPT_PARAMS:
602 * This handles: timer values, class, use of transport
603 * expedited data, max tpdu size, checksum, xtd format and
604 * disconnect indications, and may get rid of connect/disc
605 * data
607 #ifdef ARGO_DEBUG
608 if (argo_debug[D_SETPARAMS]) {
609 printf("TPOPT_PARAMS value %p, cmd %s \n", value,
610 cmd == PRCO_GETOPT ? "GET" : "SET");
612 #endif
613 #ifdef ARGO_DEBUG
614 if (argo_debug[D_REQUEST]) {
615 printf("TPOPT_PARAMS value %p, cmd %s \n", value,
616 cmd == PRCO_GETOPT ? "GET" : "SET");
618 #endif
620 if (cmd == PRCO_GETOPT) {
621 *(struct tp_conn_param *) value = tpcb->_tp_param;
622 (*mp)->m_len = sizeof(tpcb->_tp_param);
623 } else { /* cmd == PRCO_SETOPT */
624 if ((error =
625 tp_consistency(tpcb, TP_STRICT | TP_FORCE,
626 (struct tp_conn_param *) value)) == 0) {
628 * tp_consistency doesn't copy the whole set
629 * of params
631 tpcb->_tp_param = *(struct tp_conn_param *) value;
632 (*mp)->m_len = sizeof(tpcb->_tp_param);
635 break;
637 case TPOPT_PSTATISTICS:
638 #ifdef TP_PERF_MEAS
639 if (cmd == PRCO_SETOPT) {
640 error = EINVAL;
641 goto done;
643 if (tpcb->tp_perf_on) {
644 m_clget(*mp, M_WAIT);
645 if (((*mp)->m_flags & M_EXT) == 0) {
646 error = ENOBUFS; goto done;
648 (*mp)->m_len = sizeof(struct tp_pmeas);
649 memcpy(mtod(*mp), tpcb->tp_p_meas, sizeof(struct tp_pmeas));
651 else {
652 error = EINVAL;
653 goto done;
655 break;
656 #else
657 error = EOPNOTSUPP;
658 goto done;
659 #endif /* TP_PERF_MEAS */
661 case TPOPT_CDDATA_CLEAR:
662 if (cmd == PRCO_GETOPT) {
663 error = EINVAL;
664 } else {
665 if (tpcb->tp_ucddata) {
666 m_freem(tpcb->tp_ucddata);
667 tpcb->tp_ucddata = 0;
670 break;
672 case TPOPT_CFRM_DATA:
673 case TPOPT_DISC_DATA:
674 case TPOPT_CONN_DATA:
675 if (tpcb->tp_class == TP_CLASS_0) {
676 error = EOPNOTSUPP;
677 break;
679 #ifdef ARGO_DEBUG
680 if (argo_debug[D_REQUEST]) {
681 printf("%s\n", optname == TPOPT_DISC_DATA ? "DISC data" : "CONN data");
682 printf("m_len 0x%x, vallen 0x%x so_snd.cc 0x%lx\n",
683 (*mp)->m_len, val_len, so->so_snd.sb_cc);
684 dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput: sosnd ");
686 #endif
687 if (cmd == PRCO_SETOPT) {
688 int len = tpcb->tp_ucddata ? tpcb->tp_ucddata->m_len : 0;
689 /* can append connect data in several calls */
690 if (len + val_len >
691 (optname == TPOPT_CONN_DATA ? TP_MAX_CR_DATA : TP_MAX_DR_DATA)) {
692 error = EMSGSIZE;
693 goto done;
695 (*mp)->m_next = NULL;
696 (*mp)->m_nextpkt = 0;
697 if (tpcb->tp_ucddata)
698 m_cat(tpcb->tp_ucddata, *mp);
699 else
700 tpcb->tp_ucddata = *mp;
701 #ifdef ARGO_DEBUG
702 if (argo_debug[D_REQUEST]) {
703 dump_mbuf(tpcb->tp_ucddata, "tp_ctloutput after CONN_DATA");
705 #endif
706 #ifdef TPPT
707 if (tp_traceflags[D_REQUEST]) {
708 tptrace(TPPTmisc, "C/D DATA: flags snd.sbcc val_len",
709 tpcb->tp_flags, so->so_snd.sb_cc, val_len, 0);
711 #endif
712 *mp = NULL;
713 if (optname == TPOPT_CFRM_DATA && (so->so_state & SS_ISCONFIRMING))
714 (void) tp_confirm(tpcb);
716 break;
718 case TPOPT_PERF_MEAS:
719 #ifdef TP_PERF_MEAS
720 if (cmd == PRCO_GETOPT) {
721 *value = (u_int) tpcb->tp_perf_on;
722 (*mp)->m_len = sizeof(u_int);
723 } else if (cmd == PRCO_SETOPT) {
724 (*mp)->m_len = 0;
725 if ((*value) != 0 && (*value) != 1)
726 error = EINVAL;
727 else
728 tpcb->tp_perf_on = (*value);
730 if (tpcb->tp_perf_on)
731 error = tp_setup_perf(tpcb);
732 #else /* TP_PERF_MEAS */
733 error = EOPNOTSUPP;
734 #endif /* TP_PERF_MEAS */
735 break;
737 default:
738 error = EOPNOTSUPP;
741 done:
742 #ifdef ARGO_DEBUG
743 if (argo_debug[D_REQUEST]) {
744 dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput sosnd at end");
745 dump_mbuf(*mp, "tp_ctloutput *mp");
747 #endif
749 * sigh: getsockopt looks only at m_len : all output data must reside
750 * in the first mbuf
752 if (*mp) {
753 if (cmd == PRCO_SETOPT) {
754 m_freem(*mp);
755 *mp = NULL;
756 } else {
757 ASSERT(m_compress(*mp, mp) <= MLEN);
758 if (error)
759 (*mp)->m_len = 0;
760 #ifdef ARGO_DEBUG
761 if (argo_debug[D_REQUEST]) {
762 dump_mbuf(*mp, "tp_ctloutput *mp after compress");
764 #endif
767 splx(s);
768 return error;
772 * temporary sockopt wrapper, the above needs to be worked through
775 tp_ctloutput(int cmd, struct socket *so, struct sockopt *sopt)
777 struct mbuf *m;
778 int err;
780 switch(cmd) {
781 case PRCO_SETOPT:
782 m = sockopt_getmbuf(sopt);
783 if (m == NULL) {
784 err = ENOMEM;
785 break;
788 err = tp_ctloutput1(cmd, so, sopt->sopt_level, sopt->sopt_name, &m);
789 break;
791 case PRCO_GETOPT:
792 m = NULL;
793 err = tp_ctloutput1(cmd, so, sopt->sopt_level, sopt->sopt_name, &m);
794 if (err)
795 break;
797 err = sockopt_setmbuf(sopt, m);
798 break;
800 default:
801 err = ENOPROTOOPT;
802 break;
805 return err;