1 /* $NetBSD: tp_output.c,v 1.30 2006/07/23 22:06:14 ad Exp $ */
2
3 /*-
4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
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.
18 *
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.
30 *
31 * @(#)tp_output.c 8.2 (Berkeley) 2/9/95
32 */
33
34 /***********************************************************
35 Copyright IBM Corporation 1987
36
37 All Rights Reserved
38
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.
46
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.
54
55 ******************************************************************/
56
57 /*
58 * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
59 */
60 /*
61 * In here is tp_ctloutput(), the guy called by [sg]etsockopt(),
62 */
63
64 #include <sys/cdefs.h>
65 __KERNEL_RCSID(0, "$NetBSD: tp_output.c,v 1.30 2006/07/23 22:06:14 ad Exp $");
66
67 #include "opt_inet.h"
68 #include "opt_iso.h"
69
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>
81
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>
92
93 #define TPDUSIZESHIFT 24
94 #define CLASSHIFT 16
95
96 /*
97 * NAME: tp_consistency()
98 *
99 * CALLED FROM:
100 * tp_ctloutput(), tp_input()
101 *
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 arguements 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.
113 *
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
118 */
119
120 int
121 tp_consistency(struct tp_pcb *tpcb, u_int cmd, struct tp_conn_param *param)
122 {
123 int error = EOK;
124 int class_to_use = tp_mask_to_num(param->p_class);
125
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);
131 }
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" : "");
138 }
139 #endif
140 if ((cmd & TP_FORCE) && (param->p_dont_change_params)) {
141 cmd &= ~TP_FORCE;
142 }
143 /*
144 * can switch net services within a domain, but cannot switch domains
145 */
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;
154 }
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;
161 }
162 break;
163 /* no others not possible-> netservice is a 2-bit field! */
164 }
165
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);
170 }
171 #endif
172 if ((param->p_netservice > TP_MAX_NETSERVICES)) {
173 error = EINVAL;
174 goto done;
175 }
176 if ((param->p_class & TP_CLASSES_IMPLEMENTED) == 0) {
177 error = EINVAL;
178 goto done;
179 }
180 #ifdef ARGO_DEBUG
181 if (argo_debug[D_SETPARAMS]) {
182 printf("Nretrans 0x%x\n", param->p_Nretrans);
183 }
184 #endif
185 if ((param->p_Nretrans < 1) ||
186 (param->p_cr_ticks < 1) || (param->p_cc_ticks < 1)) {
187 /*
188 * bad for any class because negot has to be done a la class
189 * 4
190 */
191 error = EINVAL;
192 goto done;
193 }
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);
201 }
202 #endif
203 switch (class_to_use) {
204
205 case 0:
206 /* do not use checksums, xtd format, or XPD */
207
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;
215 }
216 break;
217 }
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;
223 }
224 break;
225 }
226 if (param->p_tpdusize > TP0_TPDUSIZE) {
227 if (cmd & TP_STRICT) {
228 error = EINVAL;
229 } else {
230 param->p_tpdusize = TP0_TPDUSIZE;
231 }
232 break;
233 }
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;
241 }
242 }
243 break;
244
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);
255 }
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;
264 }
265 #ifdef ARGO_DEBUG
266 if (argo_debug[D_SETPARAMS]) {
267 printf("rx_strat 0x%x\n", param->p_rx_strat);
268 }
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;
276 }
277 break;
278 }
279 #ifdef ARGO_DEBUG
280 if (argo_debug[D_SETPARAMS]) {
281 printf("ack_strat 0x%x\n", param->p_ack_strat);
282 }
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;
289 }
290 break;
291 }
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;
297 }
298 break;
299 }
300 if (param->p_tpdusize > TP_TPDUSIZE) {
301 if (cmd & TP_STRICT) {
302 error = EINVAL;
303 } else {
304 param->p_tpdusize = TP_TPDUSIZE;
305 }
306 break;
307 }
308 break;
309 }
310
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;
332 }
333 }
334 done:
335
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);
340 }
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);
347 }
348 #endif
349 return error;
350 }
351
352 /*
353 * NAME: tp_ctloutput()
354 *
355 * CALLED FROM:
356 * [sg]etsockopt(), via so[sg]etopt().
357 *
358 * FUNCTION and ARGUMENTS:
359 * Implements the socket options at transport level.
360 * (cmd) is either PRCO_SETOPT or PRCO_GETOPT (see ../sys/protosw.h).
361 * (so) is the socket.
362 * (level) is SOL_TRANSPORT (see ../sys/socket.h)
363 * (optname) is the particular command or option to be set.
364 * (**mp) is an mbuf structure.
365 *
366 * RETURN VALUE:
367 * ENOTSOCK if the socket hasn't got an associated tpcb
368 * EINVAL if
369 * trying to set window too big
370 * trying to set illegal max tpdu size
371 * trying to set illegal credit fraction
372 * trying to use unknown or unimplemented class of TP
373 * structure passed to set timer values is wrong size
374 * illegal combination of command/GET-SET option,
375 * e.g., GET w/ TPOPT_CDDATA_CLEAR:
376 * EOPNOTSUPP if the level isn't transport, or command is neither GET nor SET
377 * or if the transport-specific command is not implemented
378 * EISCONN if trying a command that isn't allowed after a connection
379 * is established
380 * ENOTCONN if trying a command that is allowed only if a connection is
381 * established
382 * EMSGSIZE if trying to give too much data on connect/disconnect
383 *
384 * SIDE EFFECTS:
385 *
386 * NOTES:
387 */
388 int
389 tp_ctloutput(int cmd, struct socket *so, int level, int optname,
390 struct mbuf **mp)
391 {
392 struct lwp *l = curlwp; /* XXX */
393 struct tp_pcb *tpcb = sototpcb(so);
394 int s = splsoftnet();
395 caddr_t value;
396 unsigned val_len;
397 int error = 0;
398
399 #ifdef TPPT
400 if (tp_traceflags[D_REQUEST]) {
401 tptrace(TPPTmisc, "tp_ctloutput cmd so optname mp",
402 cmd, so, optname, mp);
403 }
404 #endif
405 #ifdef ARGO_DEBUG
406 if (argo_debug[D_REQUEST]) {
407 printf(
408 "tp_ctloutput so %p cmd 0x%x optname 0x%x, mp %p *mp %p tpcb %p\n",
409 so, cmd, optname, mp, mp ? *mp : 0, tpcb);
410 }
411 #endif
412 if (tpcb == (struct tp_pcb *) 0) {
413 error = ENOTSOCK;
414 goto done;
415 }
416 if (mp && *mp == NULL) {
417 struct mbuf *m;
418
419 MGET(m, M_DONTWAIT, TPMT_SONAME); /* does off, type, next */
420 if (m == NULL) {
421 splx(s);
422 return ENOBUFS;
423 }
424 m->m_len = 0;
425 m->m_nextpkt = 0;
426 *mp = m;
427 }
428 /*
429 * Hook so one can set network options via a tp socket.
430 */
431 if (level == SOL_NETWORK) {
432 if ((tpcb->tp_nlproto == NULL) || (tpcb->tp_npcb == NULL))
433 error = ENOTSOCK;
434 else if (tpcb->tp_nlproto->nlp_ctloutput == NULL)
435 error = EOPNOTSUPP;
436 else
437 return ((tpcb->tp_nlproto->nlp_ctloutput) (cmd, optname,
438 tpcb->tp_npcb, *mp));
439 goto done;
440 } else if (level == SOL_SOCKET) {
441 if (optname == SO_RCVBUF && cmd == PRCO_SETOPT) {
442 u_long old_credit = tpcb->tp_maxlcredit;
443 tp_rsyset(tpcb);
444 if (tpcb->tp_rhiwat != so->so_rcv.sb_hiwat &&
445 tpcb->tp_state == TP_OPEN &&
446 (old_credit < tpcb->tp_maxlcredit))
447 tp_emit(AK_TPDU_type, tpcb,
448 tpcb->tp_rcvnxt, 0, NULL);
449 tpcb->tp_rhiwat = so->so_rcv.sb_hiwat;
450 }
451 goto done;
452 } else if (level != SOL_TRANSPORT) {
453 error = EOPNOTSUPP;
454 goto done;
455 }
456 if (cmd != PRCO_GETOPT && cmd != PRCO_SETOPT) {
457 error = EOPNOTSUPP;
458 goto done;
459 }
460 if (so->so_error) {
461 error = so->so_error;
462 goto done;
463 }
464 /*
465 * The only options allowed after connection is established are GET
466 * (anything) and SET DISC DATA and SET PERF MEAS
467 */
468 if (((so->so_state & SS_ISCONNECTING) || (so->so_state & SS_ISCONNECTED))
469 &&
470 (cmd == PRCO_SETOPT &&
471 optname != TPOPT_DISC_DATA &&
472 optname != TPOPT_CFRM_DATA &&
473 optname != TPOPT_PERF_MEAS &&
474 optname != TPOPT_CDDATA_CLEAR)) {
475 error = EISCONN;
476 goto done;
477 }
478 /*
479 * The only options allowed after disconnection are GET DISC DATA,
480 * and TPOPT_PSTATISTICS and they're not allowed if the ref timer has
481 * gone off, because the tpcb is gone
482 */
483 if ((so->so_state & (SS_ISCONNECTED | SS_ISCONFIRMING)) == 0) {
484 if (so->so_pcb == 0) {
485 error = ENOTCONN;
486 goto done;
487 }
488 if ((tpcb->tp_state == TP_REFWAIT || tpcb->tp_state == TP_CLOSING) &&
489 (optname != TPOPT_DISC_DATA && optname != TPOPT_PSTATISTICS)) {
490 error = ENOTCONN;
491 goto done;
492 }
493 }
494 value = mtod(*mp, caddr_t); /* it's aligned, don't worry, but
495 * lint complains about it */
496 val_len = (*mp)->m_len;
497
498 switch (optname) {
499
500 case TPOPT_INTERCEPT:
501 #define INA(t) (((struct inpcb *)(t->tp_npcb))->inp_laddr.s_addr)
502 #define ISOA(t) (((struct isopcb *)(t->tp_npcb))->isop_laddr->siso_addr)
503
504 if (l == 0 || (error = kauth_authorize_generic(l->l_cred,
505 KAUTH_GENERIC_ISSUSER, &l->l_acflag))) {
506 error = EPERM;
507 } else if (cmd != PRCO_SETOPT || tpcb->tp_state != TP_CLOSED ||
508 (tpcb->tp_flags & TPF_GENERAL_ADDR) ||
509 tpcb->tp_next == 0)
510 error = EINVAL;
511 else {
512 struct tp_pcb *t;
513 error = EADDRINUSE;
514 for (t = tp_listeners; t; t = t->tp_nextlisten)
515 if ((t->tp_flags & TPF_GENERAL_ADDR) == 0 &&
516 t->tp_domain == tpcb->tp_domain)
517 switch (tpcb->tp_domain) {
518 default:
519 goto done;
520 #ifdef INET
521 case AF_INET:
522 if (INA(t) == INA(tpcb))
523 goto done;
524 continue;
525 #endif
526 #ifdef ISO
527 case AF_ISO:
528 if (bcmp(ISOA(t).isoa_genaddr, ISOA(tpcb).isoa_genaddr,
529 ISOA(t).isoa_len) == 0)
530 goto done;
531 continue;
532 #endif
533 }
534 tpcb->tp_lsuffixlen = 0;
535 tpcb->tp_state = TP_LISTENING;
536 error = 0;
537 remque(tpcb);
538 tpcb->tp_next = tpcb->tp_prev = tpcb;
539 tpcb->tp_nextlisten = tp_listeners;
540 tp_listeners = tpcb;
541 }
542 break;
543
544 case TPOPT_MY_TSEL:
545 if (cmd == PRCO_GETOPT) {
546 ASSERT(tpcb->tp_lsuffixlen <= MAX_TSAP_SEL_LEN);
547 bcopy((caddr_t) tpcb->tp_lsuffix, value, tpcb->tp_lsuffixlen);
548 (*mp)->m_len = tpcb->tp_lsuffixlen;
549 } else { /* cmd == PRCO_SETOPT */
550 if ((val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0)) {
551 printf("val_len 0x%x (*mp)->m_len %p\n",
552 val_len, (*mp));
553 error = EINVAL;
554 } else {
555 bcopy(value, (caddr_t) tpcb->tp_lsuffix, val_len);
556 tpcb->tp_lsuffixlen = val_len;
557 }
558 }
559 break;
560
561 case TPOPT_PEER_TSEL:
562 if (cmd == PRCO_GETOPT) {
563 ASSERT(tpcb->tp_fsuffixlen <= MAX_TSAP_SEL_LEN);
564 bcopy((caddr_t) tpcb->tp_fsuffix, value, tpcb->tp_fsuffixlen);
565 (*mp)->m_len = tpcb->tp_fsuffixlen;
566 } else { /* cmd == PRCO_SETOPT */
567 if ((val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0)) {
568 printf("val_len 0x%x (*mp)->m_len %p\n",
569 val_len, (*mp));
570 error = EINVAL;
571 } else {
572 bcopy(value, (caddr_t) tpcb->tp_fsuffix, val_len);
573 tpcb->tp_fsuffixlen = val_len;
574 }
575 }
576 break;
577
578 case TPOPT_FLAGS:
579 #ifdef ARGO_DEBUG
580 if (argo_debug[D_REQUEST]) {
581 printf("%s TPOPT_FLAGS value %p *value 0x%x, flags 0x%x \n",
582 cmd == PRCO_GETOPT ? "GET" : "SET",
583 value,
584 *value,
585 tpcb->tp_flags);
586 }
587 #endif
588
589 if (cmd == PRCO_GETOPT) {
590 *(int *) value = (int) tpcb->tp_flags;
591 (*mp)->m_len = sizeof(u_int);
592 } else { /* cmd == PRCO_SETOPT */
593 error = EINVAL;
594 goto done;
595 }
596 break;
597
598 case TPOPT_PARAMS:
599 /*
600 * This handles: timer values, class, use of transport
601 * expedited data, max tpdu size, checksum, xtd format and
602 * disconnect indications, and may get rid of connect/disc
603 * data
604 */
605 #ifdef ARGO_DEBUG
606 if (argo_debug[D_SETPARAMS]) {
607 printf("TPOPT_PARAMS value %p, cmd %s \n", value,
608 cmd == PRCO_GETOPT ? "GET" : "SET");
609 }
610 #endif
611 #ifdef ARGO_DEBUG
612 if (argo_debug[D_REQUEST]) {
613 printf("TPOPT_PARAMS value %p, cmd %s \n", value,
614 cmd == PRCO_GETOPT ? "GET" : "SET");
615 }
616 #endif
617
618 if (cmd == PRCO_GETOPT) {
619 *(struct tp_conn_param *) value = tpcb->_tp_param;
620 (*mp)->m_len = sizeof(tpcb->_tp_param);
621 } else { /* cmd == PRCO_SETOPT */
622 if ((error =
623 tp_consistency(tpcb, TP_STRICT | TP_FORCE,
624 (struct tp_conn_param *) value)) == 0) {
625 /*
626 * tp_consistency doesn't copy the whole set
627 * of params
628 */
629 tpcb->_tp_param = *(struct tp_conn_param *) value;
630 (*mp)->m_len = sizeof(tpcb->_tp_param);
631 }
632 }
633 break;
634
635 case TPOPT_PSTATISTICS:
636 #ifdef TP_PERF_MEAS
637 if (cmd == PRCO_SETOPT) {
638 error = EINVAL;
639 goto done;
640 }
641 if (tpcb->tp_perf_on) {
642 m_clget(*mp, M_WAIT);
643 if (((*mp)->m_flags & M_EXT) == 0) {
644 error = ENOBUFS; goto done;
645 }
646 (*mp)->m_len = sizeof(struct tp_pmeas);
647 bcopy(tpcb->tp_p_meas, mtod(*mp), sizeof(struct tp_pmeas));
648 }
649 else {
650 error = EINVAL;
651 goto done;
652 }
653 break;
654 #else
655 error = EOPNOTSUPP;
656 goto done;
657 #endif /* TP_PERF_MEAS */
658
659 case TPOPT_CDDATA_CLEAR:
660 if (cmd == PRCO_GETOPT) {
661 error = EINVAL;
662 } else {
663 if (tpcb->tp_ucddata) {
664 m_freem(tpcb->tp_ucddata);
665 tpcb->tp_ucddata = 0;
666 }
667 }
668 break;
669
670 case TPOPT_CFRM_DATA:
671 case TPOPT_DISC_DATA:
672 case TPOPT_CONN_DATA:
673 if (tpcb->tp_class == TP_CLASS_0) {
674 error = EOPNOTSUPP;
675 break;
676 }
677 #ifdef ARGO_DEBUG
678 if (argo_debug[D_REQUEST]) {
679 printf("%s\n", optname == TPOPT_DISC_DATA ? "DISC data" : "CONN data");
680 printf("m_len 0x%x, vallen 0x%x so_snd.cc 0x%lx\n",
681 (*mp)->m_len, val_len, so->so_snd.sb_cc);
682 dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput: sosnd ");
683 }
684 #endif
685 if (cmd == PRCO_SETOPT) {
686 int len = tpcb->tp_ucddata ? tpcb->tp_ucddata->m_len : 0;
687 /* can append connect data in several calls */
688 if (len + val_len >
689 (optname == TPOPT_CONN_DATA ? TP_MAX_CR_DATA : TP_MAX_DR_DATA)) {
690 error = EMSGSIZE;
691 goto done;
692 }
693 (*mp)->m_next = NULL;
694 (*mp)->m_nextpkt = 0;
695 if (tpcb->tp_ucddata)
696 m_cat(tpcb->tp_ucddata, *mp);
697 else
698 tpcb->tp_ucddata = *mp;
699 #ifdef ARGO_DEBUG
700 if (argo_debug[D_REQUEST]) {
701 dump_mbuf(tpcb->tp_ucddata, "tp_ctloutput after CONN_DATA");
702 }
703 #endif
704 #ifdef TPPT
705 if (tp_traceflags[D_REQUEST]) {
706 tptrace(TPPTmisc, "C/D DATA: flags snd.sbcc val_len",
707 tpcb->tp_flags, so->so_snd.sb_cc, val_len, 0);
708 }
709 #endif
710 *mp = NULL;
711 if (optname == TPOPT_CFRM_DATA && (so->so_state & SS_ISCONFIRMING))
712 (void) tp_confirm(tpcb);
713 }
714 break;
715
716 case TPOPT_PERF_MEAS:
717 #ifdef TP_PERF_MEAS
718 if (cmd == PRCO_GETOPT) {
719 *value = (u_int) tpcb->tp_perf_on;
720 (*mp)->m_len = sizeof(u_int);
721 } else if (cmd == PRCO_SETOPT) {
722 (*mp)->m_len = 0;
723 if ((*value) != 0 && (*value) != 1)
724 error = EINVAL;
725 else
726 tpcb->tp_perf_on = (*value);
727 }
728 if (tpcb->tp_perf_on)
729 error = tp_setup_perf(tpcb);
730 #else /* TP_PERF_MEAS */
731 error = EOPNOTSUPP;
732 #endif /* TP_PERF_MEAS */
733 break;
734
735 default:
736 error = EOPNOTSUPP;
737 }
738
739 done:
740 #ifdef ARGO_DEBUG
741 if (argo_debug[D_REQUEST]) {
742 dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput sosnd at end");
743 dump_mbuf(*mp, "tp_ctloutput *mp");
744 }
745 #endif
746 /*
747 * sigh: getsockopt looks only at m_len : all output data must reside
748 * in the first mbuf
749 */
750 if (*mp) {
751 if (cmd == PRCO_SETOPT) {
752 m_freem(*mp);
753 *mp = NULL;
754 } else {
755 ASSERT(m_compress(*mp, mp) <= MLEN);
756 if (error)
757 (*mp)->m_len = 0;
758 #ifdef ARGO_DEBUG
759 if (argo_debug[D_REQUEST]) {
760 dump_mbuf(*mp, "tp_ctloutput *mp after compress");
761 }
762 #endif
763 }
764 }
765 splx(s);
766 return error;
767 }
Cache object: 91703a2dc0d7d91cb089a49bdaf3c318
|