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