Commit | Line | Data |
---|---|---|
7bcd1bb8 KB |
1 | /*- |
2 | * Copyright (c) 1991 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * %sccs.include.redist.c% | |
6 | * | |
4d8170e5 | 7 | * @(#)tp_output.c 7.18 (Berkeley) %G% |
7bcd1bb8 KB |
8 | */ |
9 | ||
5f183145 KS |
10 | /*********************************************************** |
11 | Copyright IBM Corporation 1987 | |
12 | ||
13 | All Rights Reserved | |
14 | ||
15 | Permission to use, copy, modify, and distribute this software and its | |
16 | documentation for any purpose and without fee is hereby granted, | |
17 | provided that the above copyright notice appear in all copies and that | |
18 | both that copyright notice and this permission notice appear in | |
19 | supporting documentation, and that the name of IBM not be | |
20 | used in advertising or publicity pertaining to distribution of the | |
21 | software without specific, written prior permission. | |
22 | ||
23 | IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING | |
24 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL | |
25 | IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR | |
26 | ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, | |
27 | WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, | |
28 | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS | |
29 | SOFTWARE. | |
30 | ||
31 | ******************************************************************/ | |
32 | ||
33 | /* | |
34 | * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison | |
35 | */ | |
36 | /* | |
37 | * ARGO TP | |
38 | * | |
39 | * $Header: tp_output.c,v 5.4 88/11/18 17:28:08 nhall Exp $ | |
40 | * $Source: /usr/argo/sys/netiso/RCS/tp_output.c,v $ | |
41 | * | |
42 | * In here is tp_ctloutput(), the guy called by [sg]etsockopt(), | |
43 | */ | |
44 | ||
5548a02f KB |
45 | #include <sys/param.h> |
46 | #include <sys/mbuf.h> | |
47 | #include <sys/systm.h> | |
48 | #include <sys/socket.h> | |
49 | #include <sys/socketvar.h> | |
50 | #include <sys/protosw.h> | |
51 | #include <sys/errno.h> | |
52 | #include <sys/time.h> | |
53 | #include <sys/kernel.h> | |
54 | ||
55 | #include <netiso/tp_param.h> | |
56 | #include <netiso/tp_user.h> | |
57 | #include <netiso/tp_stat.h> | |
58 | #include <netiso/tp_ip.h> | |
59 | #include <netiso/tp_clnp.h> | |
60 | #include <netiso/tp_timer.h> | |
61 | #include <netiso/argo_debug.h> | |
62 | #include <netiso/tp_pcb.h> | |
63 | #include <netiso/tp_trace.h> | |
5f183145 | 64 | |
5f183145 KS |
65 | #define TPDUSIZESHIFT 24 |
66 | #define CLASSHIFT 16 | |
67 | ||
68 | /* | |
69 | * NAME: tp_consistency() | |
70 | * | |
71 | * CALLED FROM: | |
72 | * tp_ctloutput(), tp_input() | |
73 | * | |
74 | * FUNCTION and ARGUMENTS: | |
75 | * Checks the consistency of options and tpdusize with class, | |
76 | * using the parameters passed in via (param). | |
77 | * (cmd) may be TP_STRICT or TP_FORCE or both. | |
78 | * Force means it will set all the values in (tpcb) to those in | |
79 | * the input arguements iff no errors were encountered. | |
80 | * Strict means that no inconsistency will be tolerated. If it's | |
81 | * not used, checksum and tpdusize inconsistencies will be tolerated. | |
82 | * The reason for this is that in some cases, when we're negotiating down | |
83 | * from class 4, these options should be changed but should not | |
84 | * cause negotiation to fail. | |
85 | * | |
86 | * RETURNS | |
87 | * E* or EOK | |
88 | * E* if the various parms aren't ok for a given class | |
89 | * EOK if they are ok for a given class | |
90 | */ | |
91 | ||
92 | int | |
93 | tp_consistency( tpcb, cmd, param ) | |
94 | u_int cmd; | |
95 | struct tp_conn_param *param; | |
96 | struct tp_pcb *tpcb; | |
97 | { | |
98 | register int error = EOK; | |
99 | int class_to_use = tp_mask_to_num(param->p_class); | |
100 | ||
101 | IFTRACE(D_SETPARAMS) | |
102 | tptrace(TPPTmisc, | |
103 | "tp_consist enter class_to_use dontchange param.class cmd", | |
104 | class_to_use, param->p_dont_change_params, param->p_class, cmd); | |
105 | ENDTRACE | |
106 | IFDEBUG(D_SETPARAMS) | |
107 | printf("tp_consistency %s %s\n", | |
108 | cmd& TP_FORCE? "TP_FORCE": "", | |
109 | cmd& TP_STRICT? "TP_STRICT":""); | |
110 | ENDDEBUG | |
111 | if ((cmd & TP_FORCE) && (param->p_dont_change_params)) { | |
112 | cmd &= ~TP_FORCE; | |
113 | } | |
114 | /* can switch net services within a domain, but | |
115 | * cannot switch domains | |
116 | */ | |
117 | switch( param->p_netservice) { | |
118 | case ISO_CONS: | |
119 | case ISO_CLNS: | |
120 | case ISO_COSNS: | |
121 | /* param->p_netservice in ISO DOMAIN */ | |
122 | if(tpcb->tp_domain != AF_ISO ) { | |
123 | error = EINVAL; goto done; | |
124 | } | |
125 | break; | |
126 | case IN_CLNS: | |
127 | /* param->p_netservice in INET DOMAIN */ | |
128 | if( tpcb->tp_domain != AF_INET ) { | |
129 | error = EINVAL; goto done; | |
130 | } | |
131 | break; | |
132 | /* no others not possible-> netservice is a 2-bit field! */ | |
133 | } | |
134 | ||
135 | IFDEBUG(D_SETPARAMS) | |
136 | printf("p_class 0x%x, class_to_use 0x%x\n", param->p_class, | |
137 | class_to_use); | |
138 | ENDDEBUG | |
139 | if((param->p_netservice < 0) || (param->p_netservice > TP_MAX_NETSERVICES)){ | |
140 | error = EINVAL; goto done; | |
141 | } | |
142 | if( (param->p_class & TP_CLASSES_IMPLEMENTED) == 0 ) { | |
143 | error = EINVAL; goto done; | |
144 | } | |
145 | IFDEBUG(D_SETPARAMS) | |
146 | printf("Nretrans 0x%x\n", param->p_Nretrans ); | |
147 | ENDDEBUG | |
148 | if( ( param->p_Nretrans < 1 ) || | |
149 | (param->p_cr_ticks < 1) || (param->p_cc_ticks < 1) ) { | |
150 | /* bad for any class because negot has to be done a la class 4 */ | |
151 | error = EINVAL; goto done; | |
152 | } | |
5f183145 KS |
153 | IFDEBUG(D_SETPARAMS) |
154 | printf("use_csum 0x%x\n", param->p_use_checksum ); | |
155 | printf("xtd_format 0x%x\n", param->p_xtd_format ); | |
156 | printf("xpd_service 0x%x\n", param->p_xpd_service ); | |
157 | printf("tpdusize 0x%x\n", param->p_tpdusize ); | |
158 | printf("tpcb->flags 0x%x\n", tpcb->tp_flags ); | |
159 | ENDDEBUG | |
160 | switch( class_to_use ) { | |
161 | ||
162 | case 0: | |
163 | /* do not use checksums, xtd format, or XPD */ | |
164 | ||
165 | if( param->p_use_checksum | param->p_xtd_format | param->p_xpd_service ) { | |
166 | if(cmd & TP_STRICT) { | |
167 | error = EINVAL; | |
168 | } else { | |
169 | param->p_use_checksum = 0; | |
170 | param->p_xtd_format = 0; | |
171 | param->p_xpd_service = 0; | |
172 | } | |
173 | break; | |
174 | } | |
175 | ||
176 | if (param->p_tpdusize < TP_MIN_TPDUSIZE) { | |
177 | if(cmd & TP_STRICT) { | |
178 | error = EINVAL; | |
179 | } else { | |
180 | param->p_tpdusize = TP_MIN_TPDUSIZE; | |
181 | } | |
182 | break; | |
183 | } | |
184 | if (param->p_tpdusize > TP0_TPDUSIZE) { | |
185 | if (cmd & TP_STRICT) { | |
186 | error = EINVAL; | |
187 | } else { | |
188 | param->p_tpdusize = TP0_TPDUSIZE; | |
189 | } | |
190 | break; | |
191 | } | |
192 | ||
193 | /* connect/disc data not allowed for class 0 */ | |
a50e2bc0 | 194 | if (tpcb->tp_ucddata) { |
5f183145 KS |
195 | if(cmd & TP_STRICT) { |
196 | error = EINVAL; | |
197 | } else if(cmd & TP_FORCE) { | |
a50e2bc0 KS |
198 | m_freem(tpcb->tp_ucddata); |
199 | tpcb->tp_ucddata = 0; | |
5f183145 KS |
200 | } |
201 | } | |
202 | break; | |
203 | ||
204 | case 4: | |
205 | IFDEBUG(D_SETPARAMS) | |
206 | printf("dt_ticks 0x%x\n", param->p_dt_ticks ); | |
207 | printf("x_ticks 0x%x\n", param->p_x_ticks ); | |
208 | printf("dr_ticks 0x%x\n", param->p_dr_ticks ); | |
209 | printf("keepalive 0x%x\n", param->p_keepalive_ticks ); | |
210 | printf("sendack 0x%x\n", param->p_sendack_ticks ); | |
211 | printf("inact 0x%x\n", param->p_inact_ticks ); | |
212 | printf("ref 0x%x\n", param->p_ref_ticks ); | |
213 | ENDDEBUG | |
214 | if( (param->p_class & TP_CLASS_4 ) && ( | |
215 | (param->p_dt_ticks < 1) || (param->p_dr_ticks < 1) || | |
216 | (param->p_x_ticks < 1) || (param->p_keepalive_ticks < 1) || | |
217 | (param->p_sendack_ticks < 1) || (param->p_ref_ticks < 1) || | |
218 | (param->p_inact_ticks < 1) ) ) { | |
219 | error = EINVAL; | |
220 | break; | |
221 | } | |
222 | IFDEBUG(D_SETPARAMS) | |
223 | printf("rx_strat 0x%x\n", param->p_rx_strat ); | |
224 | ENDDEBUG | |
225 | if(param->p_rx_strat > | |
226 | ( TPRX_USE_CW | TPRX_EACH | TPRX_FASTSTART) ) { | |
227 | if(cmd & TP_STRICT) { | |
228 | error = EINVAL; | |
229 | } else { | |
230 | param->p_rx_strat = TPRX_USE_CW; | |
231 | } | |
232 | break; | |
233 | } | |
234 | IFDEBUG(D_SETPARAMS) | |
235 | printf("ack_strat 0x%x\n", param->p_ack_strat ); | |
236 | ENDDEBUG | |
237 | if((param->p_ack_strat != 0) && (param->p_ack_strat != 1)) { | |
238 | if(cmd & TP_STRICT) { | |
239 | error = EINVAL; | |
240 | } else { | |
241 | param->p_ack_strat = TPACK_WINDOW; | |
242 | } | |
243 | break; | |
244 | } | |
245 | if (param->p_tpdusize < TP_MIN_TPDUSIZE) { | |
246 | if(cmd & TP_STRICT) { | |
247 | error = EINVAL; | |
248 | } else { | |
249 | param->p_tpdusize = TP_MIN_TPDUSIZE; | |
250 | } | |
251 | break; | |
252 | } | |
253 | if (param->p_tpdusize > TP_TPDUSIZE) { | |
254 | if(cmd & TP_STRICT) { | |
255 | error = EINVAL; | |
256 | } else { | |
257 | param->p_tpdusize = TP_TPDUSIZE; | |
258 | } | |
259 | break; | |
260 | } | |
261 | break; | |
262 | } | |
263 | ||
264 | if ((error==0) && (cmd & TP_FORCE)) { | |
d119092b | 265 | long dusize = ((long)param->p_ptpdusize) << 7; |
186f0000 | 266 | /* Enforce Negotation rules below */ |
5f183145 | 267 | tpcb->tp_class = param->p_class; |
186f0000 KS |
268 | if (tpcb->tp_use_checksum || param->p_use_checksum) |
269 | tpcb->tp_use_checksum = 1; | |
270 | if (!tpcb->tp_xpd_service || !param->p_xpd_service) | |
271 | tpcb->tp_xpd_service = 0; | |
272 | if (!tpcb->tp_xtd_format || !param->p_xtd_format) | |
273 | tpcb->tp_xtd_format = 0; | |
d119092b KS |
274 | if (dusize) { |
275 | if (tpcb->tp_l_tpdusize > dusize) | |
276 | tpcb->tp_l_tpdusize = dusize; | |
277 | if (tpcb->tp_ptpdusize == 0 || | |
278 | tpcb->tp_ptpdusize > param->p_ptpdusize) | |
279 | tpcb->tp_ptpdusize = param->p_ptpdusize; | |
280 | } else { | |
281 | if (param->p_tpdusize != 0 && | |
282 | tpcb->tp_tpdusize > param->p_tpdusize) | |
283 | tpcb->tp_tpdusize = param->p_tpdusize; | |
284 | tpcb->tp_l_tpdusize = 1 << tpcb->tp_tpdusize; | |
285 | } | |
5f183145 | 286 | } |
5f183145 KS |
287 | done: |
288 | ||
289 | IFTRACE(D_CONN) | |
290 | tptrace(TPPTmisc, "tp_consist returns class xtdfmt cmd", | |
291 | error, tpcb->tp_class, tpcb->tp_xtd_format, cmd); | |
292 | ENDTRACE | |
293 | IFDEBUG(D_CONN) | |
294 | printf( | |
295 | "tp_consist rtns 0x%x class 0x%x xtd_fmt 0x%x cmd 0x%x\n", | |
296 | error, tpcb->tp_class, tpcb->tp_xtd_format, cmd); | |
297 | ENDDEBUG | |
298 | return error; | |
299 | } | |
300 | ||
301 | /* | |
302 | * NAME: tp_ctloutput() | |
303 | * | |
304 | * CALLED FROM: | |
305 | * [sg]etsockopt(), via so[sg]etopt(). | |
306 | * | |
307 | * FUNCTION and ARGUMENTS: | |
308 | * Implements the socket options at transport level. | |
e663c139 | 309 | * (cmd) is either PRCO_SETOPT or PRCO_GETOPT (see ../sys/protosw.h). |
5f183145 | 310 | * (so) is the socket. |
e663c139 | 311 | * (level) is SOL_TRANSPORT (see ../sys/socket.h) |
5f183145 KS |
312 | * (optname) is the particular command or option to be set. |
313 | * (**mp) is an mbuf structure. | |
314 | * | |
315 | * RETURN VALUE: | |
316 | * ENOTSOCK if the socket hasn't got an associated tpcb | |
317 | * EINVAL if | |
318 | * trying to set window too big | |
319 | * trying to set illegal max tpdu size | |
320 | * trying to set illegal credit fraction | |
321 | * trying to use unknown or unimplemented class of TP | |
322 | * structure passed to set timer values is wrong size | |
323 | * illegal combination of command/GET-SET option, | |
324 | * e.g., GET w/ TPOPT_CDDATA_CLEAR: | |
325 | * EOPNOTSUPP if the level isn't transport, or command is neither GET nor SET | |
326 | * or if the transport-specific command is not implemented | |
327 | * EISCONN if trying a command that isn't allowed after a connection | |
328 | * is established | |
329 | * ENOTCONN if trying a command that is allowed only if a connection is | |
330 | * established | |
331 | * EMSGSIZE if trying to give too much data on connect/disconnect | |
332 | * | |
333 | * SIDE EFFECTS: | |
334 | * | |
335 | * NOTES: | |
336 | */ | |
337 | ProtoHook | |
338 | tp_ctloutput(cmd, so, level, optname, mp) | |
339 | int cmd, level, optname; | |
340 | struct socket *so; | |
341 | struct mbuf **mp; | |
342 | { | |
343 | struct tp_pcb *tpcb = sototpcb(so); | |
344 | int s = splnet(); | |
a50e2bc0 KS |
345 | caddr_t value; |
346 | unsigned val_len; | |
5f183145 KS |
347 | int error = 0; |
348 | ||
349 | IFTRACE(D_REQUEST) | |
350 | tptrace(TPPTmisc, "tp_ctloutput cmd so optname mp", | |
351 | cmd, so, optname, mp); | |
352 | ENDTRACE | |
353 | IFDEBUG(D_REQUEST) | |
354 | printf( | |
355 | "tp_ctloutput so 0x%x cmd 0x%x optname 0x%x, mp 0x%x *mp 0x%x tpcb 0x%x\n", | |
356 | so, cmd, optname, mp, mp?*mp:0, tpcb); | |
357 | ENDDEBUG | |
358 | if( tpcb == (struct tp_pcb *)0 ) { | |
359 | error = ENOTSOCK; goto done; | |
360 | } | |
361 | if(*mp == MNULL) { | |
362 | register struct mbuf *m; | |
363 | ||
364 | MGET(m, M_DONTWAIT, TPMT_SONAME); /* does off, type, next */ | |
365 | if (m == NULL) { | |
366 | splx(s); | |
367 | return ENOBUFS; | |
368 | } | |
369 | m->m_len = 0; | |
370 | m->m_act = 0; | |
371 | *mp = m; | |
372 | } | |
373 | ||
374 | /* | |
375 | * Hook so one can set network options via a tp socket. | |
376 | */ | |
377 | if ( level == SOL_NETWORK ) { | |
378 | if ((tpcb->tp_nlproto == NULL) || (tpcb->tp_npcb == NULL)) | |
379 | error = ENOTSOCK; | |
380 | else if (tpcb->tp_nlproto->nlp_ctloutput == NULL) | |
381 | error = EOPNOTSUPP; | |
382 | else | |
26717a37 KS |
383 | return ((tpcb->tp_nlproto->nlp_ctloutput)(cmd, optname, |
384 | tpcb->tp_npcb, *mp)); | |
385 | goto done; | |
386 | } else if ( level == SOL_SOCKET) { | |
387 | if (optname == SO_RCVBUF && cmd == PRCO_SETOPT) { | |
b9ae8854 | 388 | u_long old_credit = tpcb->tp_maxlcredit; |
26717a37 | 389 | tp_rsyset(tpcb); |
b9ae8854 KS |
390 | if (tpcb->tp_rhiwat != so->so_rcv.sb_hiwat && |
391 | tpcb->tp_state == TP_OPEN && | |
392 | (old_credit < tpcb->tp_maxlcredit)) | |
393 | tp_emit(AK_TPDU_type, tpcb, | |
394 | tpcb->tp_rcvnxt, 0, MNULL); | |
395 | tpcb->tp_rhiwat = so->so_rcv.sb_hiwat; | |
26717a37 | 396 | } |
5f183145 KS |
397 | goto done; |
398 | } else if ( level != SOL_TRANSPORT ) { | |
399 | error = EOPNOTSUPP; goto done; | |
400 | } | |
401 | if (cmd != PRCO_GETOPT && cmd != PRCO_SETOPT) { | |
402 | error = EOPNOTSUPP; goto done; | |
403 | } | |
404 | if ( so->so_error ) { | |
405 | error = so->so_error; goto done; | |
406 | } | |
407 | ||
408 | /* The only options allowed after connection is established | |
409 | * are GET (anything) and SET DISC DATA and SET PERF MEAS | |
410 | */ | |
411 | if ( ((so->so_state & SS_ISCONNECTING)||(so->so_state & SS_ISCONNECTED)) | |
412 | && | |
413 | (cmd == PRCO_SETOPT && | |
414 | optname != TPOPT_DISC_DATA && | |
44f52ea5 | 415 | optname != TPOPT_CFRM_DATA && |
5f183145 KS |
416 | optname != TPOPT_PERF_MEAS && |
417 | optname != TPOPT_CDDATA_CLEAR ) ) { | |
418 | error = EISCONN; goto done; | |
419 | } | |
420 | /* The only options allowed after disconnection are GET DISC DATA, | |
421 | * and TPOPT_PSTATISTICS | |
422 | * and they're not allowed if the ref timer has gone off, because | |
423 | * the tpcb is gone | |
424 | */ | |
44f52ea5 | 425 | if ((so->so_state & (SS_ISCONNECTED | SS_ISCONFIRMING)) == 0) { |
f15f73d2 | 426 | if ( so->so_pcb == (caddr_t)0 ) { |
5f183145 KS |
427 | error = ENOTCONN; goto done; |
428 | } | |
429 | if ( (tpcb->tp_state == TP_REFWAIT || tpcb->tp_state == TP_CLOSING) && | |
430 | (optname != TPOPT_DISC_DATA && optname != TPOPT_PSTATISTICS)) { | |
431 | error = ENOTCONN; goto done; | |
432 | } | |
433 | } | |
434 | ||
a50e2bc0 KS |
435 | value = mtod(*mp, caddr_t); /* it's aligned, don't worry, |
436 | * but lint complains about it | |
437 | */ | |
5f183145 KS |
438 | val_len = (*mp)->m_len; |
439 | ||
440 | switch (optname) { | |
441 | ||
63f88aec | 442 | case TPOPT_INTERCEPT: |
01acbfa1 KS |
443 | #define INA(t) (((struct inpcb *)(t->tp_npcb))->inp_laddr.s_addr) |
444 | #define ISOA(t) (((struct isopcb *)(t->tp_npcb))->isop_laddr->siso_addr) | |
445 | ||
67857e5a KS |
446 | if ((so->so_state & SS_PRIV) == 0) { |
447 | error = EPERM; | |
01acbfa1 KS |
448 | } else if (cmd != PRCO_SETOPT || tpcb->tp_state != TP_CLOSED || |
449 | (tpcb->tp_flags & TPF_GENERAL_ADDR) || | |
450 | tpcb->tp_next == 0) | |
63f88aec KS |
451 | error = EINVAL; |
452 | else { | |
01acbfa1 KS |
453 | register struct tp_pcb *t; |
454 | error = EADDRINUSE; | |
455 | for (t = tp_listeners; t; t = t->tp_nextlisten) | |
456 | if ((t->tp_flags & TPF_GENERAL_ADDR) == 0 && | |
457 | t->tp_domain == tpcb->tp_domain) | |
458 | switch (tpcb->tp_domain) { | |
459 | default: | |
460 | goto done; | |
461 | #ifdef INET | |
462 | case AF_INET: | |
463 | if (INA(t) == INA(tpcb)) | |
464 | goto done; | |
465 | continue; | |
466 | #endif | |
467 | #ifdef ISO | |
468 | case AF_ISO: | |
469 | if (bcmp(ISOA(t).isoa_genaddr, ISOA(tpcb).isoa_genaddr, | |
470 | ISOA(t).isoa_len) == 0) | |
471 | goto done; | |
472 | continue; | |
473 | #endif | |
474 | } | |
475 | tpcb->tp_lsuffixlen = 0; | |
476 | tpcb->tp_state = TP_LISTENING; | |
477 | error = 0; | |
478 | remque(tpcb); | |
479 | tpcb->tp_next = tpcb->tp_prev = tpcb; | |
480 | tpcb->tp_nextlisten = tp_listeners; | |
481 | tp_listeners = tpcb; | |
63f88aec | 482 | } |
63f88aec KS |
483 | break; |
484 | ||
5f183145 KS |
485 | case TPOPT_MY_TSEL: |
486 | if ( cmd == PRCO_GETOPT ) { | |
487 | ASSERT( tpcb->tp_lsuffixlen <= MAX_TSAP_SEL_LEN ); | |
a50e2bc0 | 488 | bcopy((caddr_t)tpcb->tp_lsuffix, value, tpcb->tp_lsuffixlen); |
5f183145 KS |
489 | (*mp)->m_len = tpcb->tp_lsuffixlen; |
490 | } else /* cmd == PRCO_SETOPT */ { | |
491 | if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) { | |
492 | printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp)); | |
493 | error = EINVAL; | |
494 | } else { | |
a50e2bc0 | 495 | bcopy(value, (caddr_t)tpcb->tp_lsuffix, val_len); |
5f183145 KS |
496 | tpcb->tp_lsuffixlen = val_len; |
497 | } | |
498 | } | |
499 | break; | |
500 | ||
501 | case TPOPT_PEER_TSEL: | |
502 | if ( cmd == PRCO_GETOPT ) { | |
503 | ASSERT( tpcb->tp_fsuffixlen <= MAX_TSAP_SEL_LEN ); | |
a50e2bc0 | 504 | bcopy((caddr_t)tpcb->tp_fsuffix, value, tpcb->tp_fsuffixlen); |
5f183145 KS |
505 | (*mp)->m_len = tpcb->tp_fsuffixlen; |
506 | } else /* cmd == PRCO_SETOPT */ { | |
507 | if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) { | |
508 | printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp)); | |
509 | error = EINVAL; | |
510 | } else { | |
a50e2bc0 | 511 | bcopy(value, (caddr_t)tpcb->tp_fsuffix, val_len); |
5f183145 KS |
512 | tpcb->tp_fsuffixlen = val_len; |
513 | } | |
514 | } | |
515 | break; | |
516 | ||
517 | case TPOPT_FLAGS: | |
518 | IFDEBUG(D_REQUEST) | |
519 | printf("%s TPOPT_FLAGS value 0x%x *value 0x%x, flags 0x%x \n", | |
520 | cmd==PRCO_GETOPT?"GET":"SET", | |
521 | value, | |
522 | *value, | |
523 | tpcb->tp_flags); | |
524 | ENDDEBUG | |
525 | ||
526 | if ( cmd == PRCO_GETOPT ) { | |
527 | *(int *)value = (int)tpcb->tp_flags; | |
528 | (*mp)->m_len = sizeof(u_int); | |
529 | } else /* cmd == PRCO_SETOPT */ { | |
530 | error = EINVAL; goto done; | |
531 | } | |
532 | break; | |
533 | ||
534 | case TPOPT_PARAMS: | |
535 | /* This handles: | |
536 | * timer values, | |
537 | * class, use of transport expedited data, | |
538 | * max tpdu size, checksum, xtd format and | |
539 | * disconnect indications, and may get rid of connect/disc data | |
540 | */ | |
541 | IFDEBUG(D_SETPARAMS) | |
542 | printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value, | |
543 | cmd==PRCO_GETOPT?"GET":"SET"); | |
544 | ENDDEBUG | |
545 | IFDEBUG(D_REQUEST) | |
546 | printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value, | |
547 | cmd==PRCO_GETOPT?"GET":"SET"); | |
548 | ENDDEBUG | |
549 | ||
550 | if ( cmd == PRCO_GETOPT ) { | |
551 | *(struct tp_conn_param *)value = tpcb->_tp_param; | |
552 | (*mp)->m_len = sizeof(tpcb->_tp_param); | |
553 | } else /* cmd == PRCO_SETOPT */ { | |
554 | if( (error = | |
555 | tp_consistency(tpcb, TP_STRICT | TP_FORCE, | |
556 | (struct tp_conn_param *)value))==0) { | |
557 | /* | |
558 | * tp_consistency doesn't copy the whole set of params | |
559 | */ | |
560 | tpcb->_tp_param = *(struct tp_conn_param *)value; | |
561 | (*mp)->m_len = sizeof(tpcb->_tp_param); | |
562 | } | |
563 | } | |
564 | break; | |
565 | ||
566 | case TPOPT_PSTATISTICS: | |
567 | #ifdef TP_PERF_MEAS | |
568 | if (cmd == PRCO_SETOPT) { | |
569 | error = EINVAL; goto done; | |
570 | } | |
571 | IFPERF(tpcb) | |
a50e2bc0 KS |
572 | if (*mp) { |
573 | struct mbuf * n; | |
574 | do { | |
575 | MFREE(*mp, n); | |
576 | *mp = n; | |
577 | } while (n); | |
578 | } | |
579 | *mp = m_copym(tpcb->tp_p_mbuf, (int)M_COPYALL, M_WAITOK); | |
5f183145 KS |
580 | ENDPERF |
581 | else { | |
582 | error = EINVAL; goto done; | |
583 | } | |
584 | break; | |
585 | #else | |
586 | error = EOPNOTSUPP; | |
587 | goto done; | |
4d8170e5 | 588 | #endif /* TP_PERF_MEAS */ |
5f183145 KS |
589 | |
590 | case TPOPT_CDDATA_CLEAR: | |
591 | if (cmd == PRCO_GETOPT) { | |
592 | error = EINVAL; | |
593 | } else { | |
a50e2bc0 | 594 | if (tpcb->tp_ucddata) { |
44f52ea5 KS |
595 | m_freem(tpcb->tp_ucddata); |
596 | tpcb->tp_ucddata = 0; | |
5f183145 KS |
597 | } |
598 | } | |
599 | break; | |
600 | ||
44f52ea5 | 601 | case TPOPT_CFRM_DATA: |
5f183145 | 602 | case TPOPT_DISC_DATA: |
5f183145 KS |
603 | case TPOPT_CONN_DATA: |
604 | if( tpcb->tp_class == TP_CLASS_0 ) { | |
605 | error = EOPNOTSUPP; | |
606 | break; | |
607 | } | |
608 | IFDEBUG(D_REQUEST) | |
609 | printf("%s\n", optname==TPOPT_DISC_DATA?"DISC data":"CONN data"); | |
610 | printf("m_len 0x%x, vallen 0x%x so_snd.cc 0x%x\n", | |
611 | (*mp)->m_len, val_len, so->so_snd.sb_cc); | |
612 | dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput: sosnd "); | |
5f183145 KS |
613 | ENDDEBUG |
614 | if (cmd == PRCO_SETOPT) { | |
a50e2bc0 | 615 | int len = tpcb->tp_ucddata ? tpcb->tp_ucddata->m_len : 0; |
5f183145 | 616 | /* can append connect data in several calls */ |
a50e2bc0 | 617 | if (len + val_len > |
5f183145 KS |
618 | (optname==TPOPT_CONN_DATA?TP_MAX_CR_DATA:TP_MAX_DR_DATA) ) { |
619 | error = EMSGSIZE; goto done; | |
620 | } | |
5f183145 KS |
621 | (*mp)->m_next = MNULL; |
622 | (*mp)->m_act = 0; | |
a50e2bc0 KS |
623 | if (tpcb->tp_ucddata) |
624 | m_cat(tpcb->tp_ucddata, *mp); | |
625 | else | |
626 | tpcb->tp_ucddata = *mp; | |
39f04878 KS |
627 | IFDEBUG(D_REQUEST) |
628 | dump_mbuf(tpcb->tp_ucddata, "tp_ctloutput after CONN_DATA"); | |
629 | ENDDEBUG | |
5f183145 KS |
630 | IFTRACE(D_REQUEST) |
631 | tptrace(TPPTmisc,"C/D DATA: flags snd.sbcc val_len", | |
632 | tpcb->tp_flags, so->so_snd.sb_cc,val_len,0); | |
633 | ENDTRACE | |
26717a37 | 634 | *mp = MNULL; |
39f04878 KS |
635 | if (optname == TPOPT_CFRM_DATA && (so->so_state & SS_ISCONFIRMING)) |
636 | (void) tp_confirm(tpcb); | |
5f183145 KS |
637 | } |
638 | break; | |
639 | ||
640 | case TPOPT_PERF_MEAS: | |
641 | #ifdef TP_PERF_MEAS | |
642 | if (cmd == PRCO_GETOPT) { | |
643 | *value = (u_int)tpcb->tp_perf_on; | |
644 | (*mp)->m_len = sizeof(u_int); | |
645 | } else if (cmd == PRCO_SETOPT) { | |
646 | (*mp)->m_len = 0; | |
647 | if ((*value) != 0 && (*value) != 1 ) | |
648 | error = EINVAL; | |
649 | else tpcb->tp_perf_on = (*value); | |
650 | } | |
651 | if( tpcb->tp_perf_on ) | |
652 | error = tp_setup_perf(tpcb); | |
4d8170e5 | 653 | #else /* TP_PERF_MEAS */ |
5f183145 | 654 | error = EOPNOTSUPP; |
4d8170e5 | 655 | #endif /* TP_PERF_MEAS */ |
5f183145 KS |
656 | break; |
657 | ||
658 | default: | |
659 | error = EOPNOTSUPP; | |
660 | } | |
661 | ||
662 | done: | |
663 | IFDEBUG(D_REQUEST) | |
664 | dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput sosnd at end"); | |
665 | dump_mbuf(*mp, "tp_ctloutput *mp"); | |
666 | ENDDEBUG | |
667 | /* | |
668 | * sigh: getsockopt looks only at m_len : all output data must | |
669 | * reside in the first mbuf | |
670 | */ | |
26717a37 | 671 | if (*mp) { |
b9ae8854 | 672 | if (cmd == PRCO_SETOPT) { |
26717a37 | 673 | m_freem(*mp); |
b9ae8854 KS |
674 | *mp = MNULL; |
675 | } else { | |
26717a37 KS |
676 | ASSERT ( m_compress(*mp, mp) <= MLEN ); |
677 | if (error) | |
678 | (*mp)->m_len = 0; | |
679 | IFDEBUG(D_REQUEST) | |
680 | dump_mbuf(*mp, "tp_ctloutput *mp after compress"); | |
681 | ENDDEBUG | |
682 | } | |
5f183145 | 683 | } |
5f183145 KS |
684 | splx(s); |
685 | return error; | |
686 | } |