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