1 /* $NetBSD: tp_output.c,v 1.35 2008/08/06 15:01:23 plunky 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.35 2008/08/06 15:01:23 plunky 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 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.
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_ctloutput1()
354 *
355 * CALLED FROM:
356 * [sg]etsockopt(), via so[sg]etopt().
357 * via tp_ctloutput() below
358 *
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.
366 *
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
384 *
385 * SIDE EFFECTS:
386 *
387 * NOTES:
388 */
389 static int
390 tp_ctloutput1(int cmd, struct socket *so, int level, int optname,
391 struct mbuf **mp)
392 {
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;
399
400 #ifdef TPPT
401 if (tp_traceflags[D_REQUEST]) {
402 tptrace(TPPTmisc, "tp_ctloutput cmd so optname mp",
403 cmd, so, optname, mp);
404 }
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);
411 }
412 #endif
413 if (tpcb == (struct tp_pcb *) 0) {
414 error = ENOTSOCK;
415 goto done;
416 }
417 if (mp && *mp == NULL) {
418 struct mbuf *m;
419
420 MGET(m, M_DONTWAIT, TPMT_SONAME); /* does off, type, next */
421 if (m == NULL) {
422 splx(s);
423 return ENOBUFS;
424 }
425 m->m_len = 0;
426 m->m_nextpkt = 0;
427 *mp = m;
428 }
429 /*
430 * Hook so one can set network options via a tp socket.
431 */
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;
451 }
452 goto done;
453 } else if (level != SOL_TRANSPORT) {
454 error = EOPNOTSUPP;
455 goto done;
456 }
457 if (cmd != PRCO_GETOPT && cmd != PRCO_SETOPT) {
458 error = EOPNOTSUPP;
459 goto done;
460 }
461 if (so->so_error) {
462 error = so->so_error;
463 goto done;
464 }
465 /*
466 * The only options allowed after connection is established are GET
467 * (anything) and SET DISC DATA and SET PERF MEAS
468 */
469 if (((so->so_state & SS_ISCONNECTING) || (so->so_state & SS_ISCONNECTED))
470 &&
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;
478 }
479 /*
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
483 */
484 if ((so->so_state & (SS_ISCONNECTED | SS_ISCONFIRMING)) == 0) {
485 if (so->so_pcb == 0) {
486 error = ENOTCONN;
487 goto done;
488 }
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;
493 }
494 }
495 value = mtod(*mp, void *); /* it's aligned, don't worry, but
496 * lint complains about it */
497 val_len = (*mp)->m_len;
498
499 switch (optname) {
500
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)
504
505 if (l == 0 || (error = kauth_authorize_generic(l->l_cred,
506 KAUTH_GENERIC_ISSUSER, NULL))) {
507 error = EPERM;
508 } else if (cmd != PRCO_SETOPT || tpcb->tp_state != TP_CLOSED ||
509 (tpcb->tp_flags & TPF_GENERAL_ADDR) ||
510 tpcb->tp_next == 0)
511 error = EINVAL;
512 else {
513 struct tp_pcb *t;
514 error = EADDRINUSE;
515 for (t = tp_listeners; t; t = t->tp_nextlisten)
516 if ((t->tp_flags & TPF_GENERAL_ADDR) == 0 &&
517 t->tp_domain == tpcb->tp_domain)
518 switch (tpcb->tp_domain) {
519 default:
520 goto done;
521 #ifdef INET
522 case AF_INET:
523 if (INA(t) == INA(tpcb))
524 goto done;
525 continue;
526 #endif
527 #ifdef ISO
528 case AF_ISO:
529 if (bcmp(ISOA(t).isoa_genaddr, ISOA(tpcb).isoa_genaddr,
530 ISOA(t).isoa_len) == 0)
531 goto done;
532 continue;
533 #endif
534 }
535 tpcb->tp_lsuffixlen = 0;
536 tpcb->tp_state = TP_LISTENING;
537 error = 0;
538 iso_remque(tpcb);
539 tpcb->tp_next = tpcb->tp_prev = tpcb;
540 tpcb->tp_nextlisten = tp_listeners;
541 tp_listeners = tpcb;
542 }
543 break;
544
545 case TPOPT_MY_TSEL:
546 if (cmd == PRCO_GETOPT) {
547 ASSERT(tpcb->tp_lsuffixlen <= MAX_TSAP_SEL_LEN);
548 bcopy((void *) tpcb->tp_lsuffix, value, tpcb->tp_lsuffixlen);
549 (*mp)->m_len = tpcb->tp_lsuffixlen;
550 } else { /* cmd == PRCO_SETOPT */
551 if ((val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0)) {
552 printf("val_len 0x%x (*mp)->m_len %p\n",
553 val_len, (*mp));
554 error = EINVAL;
555 } else {
556 bcopy(value, (void *) tpcb->tp_lsuffix, val_len);
557 tpcb->tp_lsuffixlen = val_len;
558 }
559 }
560 break;
561
562 case TPOPT_PEER_TSEL:
563 if (cmd == PRCO_GETOPT) {
564 ASSERT(tpcb->tp_fsuffixlen <= MAX_TSAP_SEL_LEN);
565 bcopy((void *) tpcb->tp_fsuffix, value, tpcb->tp_fsuffixlen);
566 (*mp)->m_len = tpcb->tp_fsuffixlen;
567 } else { /* cmd == PRCO_SETOPT */
568 if ((val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0)) {
569 printf("val_len 0x%x (*mp)->m_len %p\n",
570 val_len, (*mp));
571 error = EINVAL;
572 } else {
573 bcopy(value, (void *) tpcb->tp_fsuffix, val_len);
574 tpcb->tp_fsuffixlen = val_len;
575 }
576 }
577 break;
578
579 case TPOPT_FLAGS:
580 #ifdef ARGO_DEBUG
581 if (argo_debug[D_REQUEST]) {
582 printf("%s TPOPT_FLAGS value %p *value 0x%x, flags 0x%x \n",
583 cmd == PRCO_GETOPT ? "GET" : "SET",
584 value,
585 *(unsigned char *)value,
586 tpcb->tp_flags);
587 }
588 #endif
589
590 if (cmd == PRCO_GETOPT) {
591 *(int *) value = (int) tpcb->tp_flags;
592 (*mp)->m_len = sizeof(u_int);
593 } else { /* cmd == PRCO_SETOPT */
594 error = EINVAL;
595 goto done;
596 }
597 break;
598
599 case TPOPT_PARAMS:
600 /*
601 * This handles: timer values, class, use of transport
602 * expedited data, max tpdu size, checksum, xtd format and
603 * disconnect indications, and may get rid of connect/disc
604 * data
605 */
606 #ifdef ARGO_DEBUG
607 if (argo_debug[D_SETPARAMS]) {
608 printf("TPOPT_PARAMS value %p, cmd %s \n", value,
609 cmd == PRCO_GETOPT ? "GET" : "SET");
610 }
611 #endif
612 #ifdef ARGO_DEBUG
613 if (argo_debug[D_REQUEST]) {
614 printf("TPOPT_PARAMS value %p, cmd %s \n", value,
615 cmd == PRCO_GETOPT ? "GET" : "SET");
616 }
617 #endif
618
619 if (cmd == PRCO_GETOPT) {
620 *(struct tp_conn_param *) value = tpcb->_tp_param;
621 (*mp)->m_len = sizeof(tpcb->_tp_param);
622 } else { /* cmd == PRCO_SETOPT */
623 if ((error =
624 tp_consistency(tpcb, TP_STRICT | TP_FORCE,
625 (struct tp_conn_param *) value)) == 0) {
626 /*
627 * tp_consistency doesn't copy the whole set
628 * of params
629 */
630 tpcb->_tp_param = *(struct tp_conn_param *) value;
631 (*mp)->m_len = sizeof(tpcb->_tp_param);
632 }
633 }
634 break;
635
636 case TPOPT_PSTATISTICS:
637 #ifdef TP_PERF_MEAS
638 if (cmd == PRCO_SETOPT) {
639 error = EINVAL;
640 goto done;
641 }
642 if (tpcb->tp_perf_on) {
643 m_clget(*mp, M_WAIT);
644 if (((*mp)->m_flags & M_EXT) == 0) {
645 error = ENOBUFS; goto done;
646 }
647 (*mp)->m_len = sizeof(struct tp_pmeas);
648 bcopy(tpcb->tp_p_meas, mtod(*mp), sizeof(struct tp_pmeas));
649 }
650 else {
651 error = EINVAL;
652 goto done;
653 }
654 break;
655 #else
656 error = EOPNOTSUPP;
657 goto done;
658 #endif /* TP_PERF_MEAS */
659
660 case TPOPT_CDDATA_CLEAR:
661 if (cmd == PRCO_GETOPT) {
662 error = EINVAL;
663 } else {
664 if (tpcb->tp_ucddata) {
665 m_freem(tpcb->tp_ucddata);
666 tpcb->tp_ucddata = 0;
667 }
668 }
669 break;
670
671 case TPOPT_CFRM_DATA:
672 case TPOPT_DISC_DATA:
673 case TPOPT_CONN_DATA:
674 if (tpcb->tp_class == TP_CLASS_0) {
675 error = EOPNOTSUPP;
676 break;
677 }
678 #ifdef ARGO_DEBUG
679 if (argo_debug[D_REQUEST]) {
680 printf("%s\n", optname == TPOPT_DISC_DATA ? "DISC data" : "CONN data");
681 printf("m_len 0x%x, vallen 0x%x so_snd.cc 0x%lx\n",
682 (*mp)->m_len, val_len, so->so_snd.sb_cc);
683 dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput: sosnd ");
684 }
685 #endif
686 if (cmd == PRCO_SETOPT) {
687 int len = tpcb->tp_ucddata ? tpcb->tp_ucddata->m_len : 0;
688 /* can append connect data in several calls */
689 if (len + val_len >
690 (optname == TPOPT_CONN_DATA ? TP_MAX_CR_DATA : TP_MAX_DR_DATA)) {
691 error = EMSGSIZE;
692 goto done;
693 }
694 (*mp)->m_next = NULL;
695 (*mp)->m_nextpkt = 0;
696 if (tpcb->tp_ucddata)
697 m_cat(tpcb->tp_ucddata, *mp);
698 else
699 tpcb->tp_ucddata = *mp;
700 #ifdef ARGO_DEBUG
701 if (argo_debug[D_REQUEST]) {
702 dump_mbuf(tpcb->tp_ucddata, "tp_ctloutput after CONN_DATA");
703 }
704 #endif
705 #ifdef TPPT
706 if (tp_traceflags[D_REQUEST]) {
707 tptrace(TPPTmisc, "C/D DATA: flags snd.sbcc val_len",
708 tpcb->tp_flags, so->so_snd.sb_cc, val_len, 0);
709 }
710 #endif
711 *mp = NULL;
712 if (optname == TPOPT_CFRM_DATA && (so->so_state & SS_ISCONFIRMING))
713 (void) tp_confirm(tpcb);
714 }
715 break;
716
717 case TPOPT_PERF_MEAS:
718 #ifdef TP_PERF_MEAS
719 if (cmd == PRCO_GETOPT) {
720 *value = (u_int) tpcb->tp_perf_on;
721 (*mp)->m_len = sizeof(u_int);
722 } else if (cmd == PRCO_SETOPT) {
723 (*mp)->m_len = 0;
724 if ((*value) != 0 && (*value) != 1)
725 error = EINVAL;
726 else
727 tpcb->tp_perf_on = (*value);
728 }
729 if (tpcb->tp_perf_on)
730 error = tp_setup_perf(tpcb);
731 #else /* TP_PERF_MEAS */
732 error = EOPNOTSUPP;
733 #endif /* TP_PERF_MEAS */
734 break;
735
736 default:
737 error = EOPNOTSUPP;
738 }
739
740 done:
741 #ifdef ARGO_DEBUG
742 if (argo_debug[D_REQUEST]) {
743 dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput sosnd at end");
744 dump_mbuf(*mp, "tp_ctloutput *mp");
745 }
746 #endif
747 /*
748 * sigh: getsockopt looks only at m_len : all output data must reside
749 * in the first mbuf
750 */
751 if (*mp) {
752 if (cmd == PRCO_SETOPT) {
753 m_freem(*mp);
754 *mp = NULL;
755 } else {
756 ASSERT(m_compress(*mp, mp) <= MLEN);
757 if (error)
758 (*mp)->m_len = 0;
759 #ifdef ARGO_DEBUG
760 if (argo_debug[D_REQUEST]) {
761 dump_mbuf(*mp, "tp_ctloutput *mp after compress");
762 }
763 #endif
764 }
765 }
766 splx(s);
767 return error;
768 }
769
770 /*
771 * temporary sockopt wrapper, the above needs to be worked through
772 */
773 int
774 tp_ctloutput(int cmd, struct socket *so, struct sockopt *sopt)
775 {
776 struct mbuf *m;
777 int err;
778
779 switch(cmd) {
780 case PRCO_SETOPT:
781 m = sockopt_getmbuf(sopt);
782 if (m == NULL) {
783 err = ENOMEM;
784 break;
785 }
786
787 err = tp_ctloutput1(cmd, so, sopt->sopt_level, sopt->sopt_name, &m);
788 break;
789
790 case PRCO_GETOPT:
791 m = NULL;
792 err = tp_ctloutput1(cmd, so, sopt->sopt_level, sopt->sopt_name, &m);
793 if (err)
794 break;
795
796 err = sockopt_setmbuf(sopt, m);
797 break;
798
799 default:
800 err = ENOPROTOOPT;
801 break;
802 }
803
804 return err;
805 }
Cache object: f8a0da9a3325b75a30f96e3276ddd9d5
|