Commit | Line | Data |
---|---|---|
15637ed4 RG |
1 | /* |
2 | * Copyright (c) University of British Columbia, 1984 | |
3 | * Copyright (c) 1990 The Regents of the University of California. | |
4 | * All rights reserved. | |
5 | * | |
6 | * This code is derived from software contributed to Berkeley by | |
7 | * the Laboratory for Computation Vision and the Computer Science Department | |
8 | * of the University of British Columbia. | |
9 | * | |
10 | * Redistribution and use in source and binary forms, with or without | |
11 | * modification, are permitted provided that the following conditions | |
12 | * are met: | |
13 | * 1. Redistributions of source code must retain the above copyright | |
14 | * notice, this list of conditions and the following disclaimer. | |
15 | * 2. Redistributions in binary form must reproduce the above copyright | |
16 | * notice, this list of conditions and the following disclaimer in the | |
17 | * documentation and/or other materials provided with the distribution. | |
18 | * 3. All advertising materials mentioning features or use of this software | |
19 | * must display the following acknowledgement: | |
20 | * This product includes software developed by the University of | |
21 | * California, Berkeley and its contributors. | |
22 | * 4. Neither the name of the University nor the names of its contributors | |
23 | * may be used to endorse or promote products derived from this software | |
24 | * without specific prior written permission. | |
25 | * | |
26 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
27 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
28 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
29 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
30 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
31 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
32 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
33 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
34 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
35 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
36 | * SUCH DAMAGE. | |
37 | * | |
38 | * @(#)pk_subr.c 7.16 (Berkeley) 6/6/91 | |
39 | */ | |
40 | ||
41 | #include "param.h" | |
42 | #include "systm.h" | |
43 | #include "mbuf.h" | |
44 | #include "socket.h" | |
45 | #include "protosw.h" | |
46 | #include "socketvar.h" | |
47 | #include "errno.h" | |
48 | #include "time.h" | |
49 | #include "kernel.h" | |
50 | ||
51 | #include "../net/if.h" | |
52 | ||
53 | #include "x25.h" | |
54 | #include "pk.h" | |
55 | #include "pk_var.h" | |
56 | #include "x25err.h" | |
57 | ||
58 | int pk_sendspace = 1024 * 2 + 8; | |
59 | int pk_recvspace = 1024 * 2 + 8; | |
60 | ||
61 | struct pklcd_q pklcd_q = {&pklcd_q, &pklcd_q}; | |
62 | ||
63 | /* | |
64 | * Attach X.25 protocol to socket, allocate logical channel descripter | |
65 | * and buffer space, and enter LISTEN state if we are to accept | |
66 | * IN-COMMING CALL packets. | |
67 | * | |
68 | */ | |
69 | ||
70 | struct pklcd * | |
71 | pk_attach (so) | |
72 | struct socket *so; | |
73 | { | |
74 | register struct pklcd *lcp; | |
75 | register int error = ENOBUFS; | |
76 | int pk_output(); | |
77 | ||
78 | MALLOC(lcp, struct pklcd *, sizeof (*lcp), M_PCB, M_NOWAIT); | |
79 | if (lcp) { | |
80 | bzero ((caddr_t)lcp, sizeof (*lcp)); | |
81 | insque (&lcp -> lcd_q, &pklcd_q); | |
82 | lcp -> lcd_state = READY; | |
83 | lcp -> lcd_send = pk_output; | |
84 | if (so) { | |
85 | error = soreserve (so, pk_sendspace, pk_recvspace); | |
86 | lcp -> lcd_so = so; | |
87 | if (so -> so_options & SO_ACCEPTCONN) | |
88 | lcp -> lcd_state = LISTEN; | |
89 | } else | |
90 | sbreserve (&lcp -> lcd_sb, pk_sendspace); | |
91 | } | |
92 | if (so) { | |
93 | so -> so_pcb = (caddr_t) lcp; | |
94 | so -> so_error = error; | |
95 | } | |
96 | return (lcp); | |
97 | } | |
98 | ||
99 | /* | |
100 | * Disconnect X.25 protocol from socket. | |
101 | */ | |
102 | ||
103 | pk_disconnect (lcp) | |
104 | register struct pklcd *lcp; | |
105 | { | |
106 | register struct socket *so = lcp -> lcd_so; | |
107 | register struct pklcd *l, *p; | |
108 | ||
109 | switch (lcp -> lcd_state) { | |
110 | case LISTEN: | |
111 | for (p = 0, l = pk_listenhead; l && l != lcp; p = l, l = l -> lcd_listen); | |
112 | if (p == 0) { | |
113 | if (l != 0) | |
114 | pk_listenhead = l -> lcd_listen; | |
115 | } | |
116 | else | |
117 | if (l != 0) | |
118 | p -> lcd_listen = l -> lcd_listen; | |
119 | pk_close (lcp); | |
120 | break; | |
121 | ||
122 | case READY: | |
123 | pk_acct (lcp); | |
124 | pk_close (lcp); | |
125 | break; | |
126 | ||
127 | case SENT_CLEAR: | |
128 | case RECEIVED_CLEAR: | |
129 | break; | |
130 | ||
131 | default: | |
132 | pk_acct (lcp); | |
133 | if (so) { | |
134 | soisdisconnecting (so); | |
135 | sbflush (&so -> so_rcv); | |
136 | } | |
137 | pk_clear (lcp, 241, 0); /* Normal Disconnect */ | |
138 | ||
139 | } | |
140 | } | |
141 | ||
142 | /* | |
143 | * Close an X.25 Logical Channel. Discard all space held by the | |
144 | * connection and internal descriptors. Wake up any sleepers. | |
145 | */ | |
146 | ||
147 | pk_close (lcp) | |
148 | struct pklcd *lcp; | |
149 | { | |
150 | register struct socket *so = lcp -> lcd_so; | |
151 | ||
152 | pk_freelcd (lcp); | |
153 | ||
154 | if (so == NULL) | |
155 | return; | |
156 | ||
157 | so -> so_pcb = 0; | |
158 | soisdisconnected (so); | |
159 | /* sofree (so); /* gak!!! you can't do that here */ | |
160 | } | |
161 | ||
162 | /* | |
163 | * Create a template to be used to send X.25 packets on a logical | |
164 | * channel. It allocates an mbuf and fills in a skeletal packet | |
165 | * depending on its type. This packet is passed to pk_output where | |
166 | * the remainer of the packet is filled in. | |
167 | */ | |
168 | ||
169 | struct mbuf * | |
170 | pk_template (lcn, type) | |
171 | int lcn, type; | |
172 | { | |
173 | register struct mbuf *m; | |
174 | register struct x25_packet *xp; | |
175 | ||
176 | MGETHDR (m, M_DONTWAIT, MT_HEADER); | |
177 | if (m == 0) | |
178 | panic ("pk_template"); | |
179 | m -> m_act = 0; | |
180 | ||
181 | /* | |
182 | * Efficiency hack: leave a four byte gap at the beginning | |
183 | * of the packet level header with the hope that this will | |
184 | * be enough room for the link level to insert its header. | |
185 | */ | |
186 | m -> m_data += max_linkhdr; | |
187 | m -> m_pkthdr.len = m -> m_len = PKHEADERLN; | |
188 | ||
189 | xp = mtod (m, struct x25_packet *); | |
190 | *(long *)xp = 0; /* ugly, but fast */ | |
191 | /* xp -> q_bit = 0;*/ | |
192 | xp -> fmt_identifier = 1; | |
193 | /* xp -> lc_group_number = 0;*/ | |
194 | ||
195 | SET_LCN(xp, lcn); | |
196 | xp -> packet_type = type; | |
197 | ||
198 | return (m); | |
199 | } | |
200 | ||
201 | /* | |
202 | * This routine restarts all the virtual circuits. Actually, | |
203 | * the virtual circuits are not "restarted" as such. Instead, | |
204 | * any active switched circuit is simply returned to READY | |
205 | * state. | |
206 | */ | |
207 | ||
208 | pk_restart (pkp, restart_cause) | |
209 | register struct pkcb *pkp; | |
210 | int restart_cause; | |
211 | { | |
212 | register struct mbuf *m; | |
213 | register struct pklcd *lcp; | |
214 | register int i; | |
215 | ||
216 | /* Restart all logical channels. */ | |
217 | if (pkp -> pk_chan == 0) | |
218 | return; | |
219 | for (i = 1; i <= pkp -> pk_maxlcn; ++i) | |
220 | if ((lcp = pkp -> pk_chan[i]) != NULL) { | |
221 | if (lcp -> lcd_so) { | |
222 | lcp -> lcd_so -> so_error = ENETRESET; | |
223 | pk_close (lcp); | |
224 | } else { | |
225 | pk_flush (lcp); | |
226 | lcp -> lcd_state = READY; | |
227 | if (lcp -> lcd_upper) | |
228 | lcp -> lcd_upper (lcp, 0); | |
229 | } | |
230 | } | |
231 | ||
232 | if (restart_cause < 0) | |
233 | return; | |
234 | ||
235 | pkp -> pk_state = DTE_SENT_RESTART; | |
236 | lcp = pkp -> pk_chan[0]; | |
237 | m = lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_RESTART); | |
238 | m -> m_pkthdr.len = m -> m_len += 2; | |
239 | mtod (m, struct x25_packet *) -> packet_data = 0; /* DTE only */ | |
240 | mtod (m, octet *)[4] = restart_cause; | |
241 | pk_output (lcp); | |
242 | } | |
243 | ||
244 | ||
245 | /* | |
246 | * This procedure frees up the Logical Channel Descripter. | |
247 | */ | |
248 | ||
249 | pk_freelcd (lcp) | |
250 | register struct pklcd *lcp; | |
251 | { | |
252 | if (lcp == NULL) | |
253 | return; | |
254 | ||
255 | if (lcp -> lcd_lcn > 0) | |
256 | lcp -> lcd_pkp -> pk_chan[lcp -> lcd_lcn] = NULL; | |
257 | ||
258 | pk_flush (lcp); | |
259 | remque (&lcp -> lcd_q); | |
260 | free ((caddr_t)lcp, M_PCB); | |
261 | } | |
262 | ||
263 | ||
264 | /* | |
265 | * Bind a address and protocol value to a socket. The important | |
266 | * part is the protocol value - the first four characters of the | |
267 | * Call User Data field. | |
268 | */ | |
269 | ||
270 | pk_bind (lcp, nam) | |
271 | struct pklcd *lcp; | |
272 | struct mbuf *nam; | |
273 | { | |
274 | register struct pkcb *pkp; | |
275 | register struct pklcd *pp; | |
276 | register struct sockaddr_x25 *sa; | |
277 | ||
278 | if (nam == NULL) | |
279 | return (EADDRNOTAVAIL); | |
280 | if (lcp -> lcd_ceaddr) /* XXX */ | |
281 | return (EADDRINUSE); | |
282 | if (pk_checksockaddr (nam)) | |
283 | return (EINVAL); | |
284 | sa = mtod (nam, struct sockaddr_x25 *); | |
285 | ||
286 | /* | |
287 | * If the user wishes to accept calls only from a particular | |
288 | * net (net != 0), make sure the net is known | |
289 | */ | |
290 | ||
291 | if (sa -> x25_net) | |
292 | for (pkp = pkcbhead; ; pkp = pkp -> pk_next) { | |
293 | if (pkp == 0) | |
294 | return (ENETUNREACH); | |
295 | if (pkp -> pk_xcp -> xc_addr.x25_net == sa -> x25_net) | |
296 | break; | |
297 | } | |
298 | ||
299 | /* | |
300 | * For ISO's sake permit default listeners, but only one such . . . | |
301 | */ | |
302 | for (pp = pk_listenhead; pp; pp = pp -> lcd_listen) { | |
303 | register struct sockaddr_x25 *sa2 = pp -> lcd_ceaddr; | |
304 | if ((sa2 -> x25_udlen == sa -> x25_udlen) && | |
305 | (sa2 -> x25_udlen == 0 || | |
306 | (bcmp (sa2 -> x25_udata, sa -> x25_udata, | |
307 | min (sa2 -> x25_udlen, sa -> x25_udlen)) == 0))) | |
308 | return (EADDRINUSE); | |
309 | } | |
310 | lcp -> lcd_laddr = *sa; | |
311 | lcp -> lcd_ceaddr = &lcp -> lcd_laddr; | |
312 | return (0); | |
313 | } | |
314 | ||
315 | /* | |
316 | * Include a bound control block in the list of listeners. | |
317 | */ | |
318 | pk_listen (lcp) | |
319 | register struct pklcd *lcp; | |
320 | { | |
321 | register struct pklcd **pp; | |
322 | ||
323 | if (lcp -> lcd_ceaddr == 0) | |
324 | return (EDESTADDRREQ); | |
325 | ||
326 | lcp -> lcd_state = LISTEN; | |
327 | /* | |
328 | * Add default listener at end, any others at start. | |
329 | */ | |
330 | if (lcp -> lcd_ceaddr -> x25_udlen == 0) { | |
331 | for (pp = &pk_listenhead; *pp; ) | |
332 | pp = &((*pp) -> lcd_listen); | |
333 | *pp = lcp; | |
334 | } else { | |
335 | lcp -> lcd_listen = pk_listenhead; | |
336 | pk_listenhead = lcp; | |
337 | } | |
338 | return (0); | |
339 | } | |
340 | /* | |
341 | * Include a listening control block for the benefit of other protocols. | |
342 | */ | |
343 | pk_protolisten (spi, spilen, callee) | |
344 | int (*callee) (); | |
345 | { | |
346 | register struct pklcd *lcp = pk_attach ((struct socket *)0); | |
347 | register struct mbuf *nam; | |
348 | register struct sockaddr_x25 *sa; | |
349 | int error = ENOBUFS; | |
350 | ||
351 | if (lcp) { | |
352 | if (nam = m_getclr (MT_SONAME, M_DONTWAIT)) { | |
353 | sa = mtod (nam, struct sockaddr_x25 *); | |
354 | sa -> x25_family = AF_CCITT; | |
355 | sa -> x25_len = nam -> m_len = sizeof (*sa); | |
356 | sa -> x25_udlen = spilen; | |
357 | sa -> x25_udata[0] = spi; | |
358 | lcp -> lcd_upper = callee; | |
359 | lcp -> lcd_flags = X25_MBS_HOLD; | |
360 | if ((error = pk_bind (lcp, nam)) == 0) | |
361 | error = pk_listen (lcp); | |
362 | (void) m_free (nam); | |
363 | } | |
364 | if (error) | |
365 | pk_freelcd (lcp); | |
366 | } | |
367 | return error; /* Hopefully Zero !*/ | |
368 | } | |
369 | ||
370 | /* | |
371 | * Associate a logical channel descriptor with a network. | |
372 | * Fill in the default network specific parameters and then | |
373 | * set any parameters explicitly specified by the user or | |
374 | * by the remote DTE. | |
375 | */ | |
376 | ||
377 | pk_assoc (pkp, lcp, sa) | |
378 | register struct pkcb *pkp; | |
379 | register struct pklcd *lcp; | |
380 | register struct sockaddr_x25 *sa; | |
381 | { | |
382 | ||
383 | lcp -> lcd_pkp = pkp; | |
384 | lcp -> lcd_packetsize = pkp -> pk_xcp -> xc_psize; | |
385 | lcp -> lcd_windowsize = pkp -> pk_xcp -> xc_pwsize; | |
386 | lcp -> lcd_rsn = MODULUS - 1; | |
387 | pkp -> pk_chan[lcp -> lcd_lcn] = lcp; | |
388 | ||
389 | if (sa -> x25_opts.op_psize) | |
390 | lcp -> lcd_packetsize = sa -> x25_opts.op_psize; | |
391 | else | |
392 | sa -> x25_opts.op_psize = lcp -> lcd_packetsize; | |
393 | if (sa -> x25_opts.op_wsize) | |
394 | lcp -> lcd_windowsize = sa -> x25_opts.op_wsize; | |
395 | else | |
396 | sa -> x25_opts.op_wsize = lcp -> lcd_windowsize; | |
397 | sa -> x25_net = pkp -> pk_xcp -> xc_addr.x25_net; | |
398 | lcp -> lcd_flags |= sa -> x25_opts.op_flags; | |
399 | lcp -> lcd_stime = time.tv_sec; | |
400 | } | |
401 | ||
402 | pk_connect (lcp, sa) | |
403 | register struct pklcd *lcp; | |
404 | register struct sockaddr_x25 *sa; | |
405 | { | |
406 | register struct pkcb *pkp; | |
407 | ||
408 | if (sa -> x25_addr[0] == '\0') | |
409 | return (EDESTADDRREQ); | |
410 | if (lcp -> lcd_pkp == 0) | |
411 | for (pkp = pkcbhead; ; pkp = pkp -> pk_next) { | |
412 | if (pkp == 0) | |
413 | return (ENETUNREACH); | |
414 | /* | |
415 | * use first net configured (last in list | |
416 | * headed by pkcbhead) if net is zero | |
417 | * | |
418 | * This is clearly bogus for many llc2's sharing | |
419 | * the same xcp; we will replace this with a | |
420 | * routing lookup. | |
421 | */ | |
422 | if (sa -> x25_net == 0 && pkp -> pk_next == 0) | |
423 | break; | |
424 | if (sa -> x25_net == pkp -> pk_xcp -> xc_addr.x25_net) | |
425 | break; | |
426 | } | |
427 | ||
428 | if (pkp -> pk_state != DTE_READY) | |
429 | return (ENETDOWN); | |
430 | if ((lcp -> lcd_lcn = pk_getlcn (pkp)) == 0) | |
431 | return (EMFILE); | |
432 | lcp -> lcd_faddr = *sa; | |
433 | lcp -> lcd_ceaddr = & lcp -> lcd_faddr; | |
434 | pk_assoc (pkp, lcp, lcp -> lcd_ceaddr); | |
435 | if (lcp -> lcd_so) | |
436 | soisconnecting (lcp -> lcd_so); | |
437 | lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_CALL); | |
438 | pk_callrequest (lcp, lcp -> lcd_ceaddr, pkp -> pk_xcp); | |
439 | return (*pkp -> pk_ia -> ia_start) (lcp); | |
440 | } | |
441 | ||
442 | struct bcdinfo { | |
443 | octet *cp; | |
444 | unsigned posn; | |
445 | }; | |
446 | /* | |
447 | * Build the rest of the CALL REQUEST packet. Fill in calling | |
448 | * address, facilities fields and the user data field. | |
449 | */ | |
450 | ||
451 | pk_callrequest (lcp, sa, xcp) | |
452 | struct pklcd *lcp; | |
453 | register struct sockaddr_x25 *sa; | |
454 | register struct x25config *xcp; | |
455 | { | |
456 | register struct x25_calladdr *a; | |
457 | register struct mbuf *m = lcp -> lcd_template; | |
458 | register struct x25_packet *xp = mtod (m, struct x25_packet *); | |
459 | struct bcdinfo b; | |
460 | ||
461 | if (lcp -> lcd_flags & X25_DBIT) | |
462 | xp -> d_bit = 1; | |
463 | a = (struct x25_calladdr *) &xp -> packet_data; | |
464 | b.cp = (octet *) a -> address_field; | |
465 | b.posn = 0; | |
466 | a -> called_addrlen = to_bcd (&b, sa, xcp); | |
467 | a -> calling_addrlen = to_bcd (&b, &xcp -> xc_addr, xcp); | |
468 | if (b.posn & 0x01) | |
469 | *b.cp++ &= 0xf0; | |
470 | m -> m_pkthdr.len = m -> m_len += b.cp - (octet *) a; | |
471 | ||
472 | if (lcp -> lcd_facilities) { | |
473 | m -> m_pkthdr.len += | |
474 | (m -> m_next = lcp -> lcd_facilities) -> m_pkthdr.len; | |
475 | lcp -> lcd_facilities = 0; | |
476 | } else | |
477 | pk_build_facilities (m, sa, (int)xcp -> xc_type); | |
478 | ||
479 | m_copyback (m, m -> m_pkthdr.len, sa -> x25_udlen, sa -> x25_udata); | |
480 | } | |
481 | ||
482 | pk_build_facilities (m, sa, type) | |
483 | register struct mbuf *m; | |
484 | struct sockaddr_x25 *sa; | |
485 | { | |
486 | register octet *cp; | |
487 | register octet *fcp; | |
488 | register int revcharge; | |
489 | ||
490 | cp = mtod (m, octet *) + m -> m_len; | |
491 | fcp = cp + 1; | |
492 | revcharge = sa -> x25_opts.op_flags & X25_REVERSE_CHARGE ? 1 : 0; | |
493 | /* | |
494 | * This is specific to Datapac X.25(1976) DTEs. International | |
495 | * calls must have the "hi priority" bit on. | |
496 | */ | |
497 | if (type == X25_1976 && sa -> x25_opts.op_psize == X25_PS128) | |
498 | revcharge |= 02; | |
499 | if (revcharge) { | |
500 | *fcp++ = FACILITIES_REVERSE_CHARGE; | |
501 | *fcp++ = revcharge; | |
502 | } | |
503 | switch (type) { | |
504 | case X25_1980: | |
505 | case X25_1984: | |
506 | *fcp++ = FACILITIES_PACKETSIZE; | |
507 | *fcp++ = sa -> x25_opts.op_psize; | |
508 | *fcp++ = sa -> x25_opts.op_psize; | |
509 | ||
510 | *fcp++ = FACILITIES_WINDOWSIZE; | |
511 | *fcp++ = sa -> x25_opts.op_wsize; | |
512 | *fcp++ = sa -> x25_opts.op_wsize; | |
513 | } | |
514 | *cp = fcp - cp - 1; | |
515 | m -> m_pkthdr.len = (m -> m_len += *cp + 1); | |
516 | } | |
517 | ||
518 | to_bcd (b, sa, xcp) | |
519 | register struct bcdinfo *b; | |
520 | struct sockaddr_x25 *sa; | |
521 | register struct x25config *xcp; | |
522 | { | |
523 | register char *x = sa -> x25_addr; | |
524 | unsigned start = b -> posn; | |
525 | /* | |
526 | * The nodnic and prepnd0 stuff looks tedious, | |
527 | * but it does allow full X.121 addresses to be used, | |
528 | * which is handy for routing info (& OSI type 37 addresses). | |
529 | */ | |
530 | if (xcp -> xc_addr.x25_net && (xcp -> xc_nodnic || xcp -> xc_prepnd0)) { | |
531 | char dnicname[sizeof(long) * NBBY/3 + 2]; | |
532 | register char *p = dnicname; | |
533 | ||
534 | sprintf (p, "%d", xcp -> xc_addr.x25_net & 0x7fff); | |
535 | for (; *p; p++) /* *p == 0 means dnic matched */ | |
536 | if ((*p ^ *x++) & 0x0f) | |
537 | break; | |
538 | if (*p || xcp -> xc_nodnic == 0) | |
539 | x = sa -> x25_addr; | |
540 | if (*p && xcp -> xc_prepnd0) { | |
541 | if ((b -> posn)++ & 0x01) | |
542 | *(b -> cp)++; | |
543 | else | |
544 | *(b -> cp) = 0; | |
545 | } | |
546 | } | |
547 | while (*x) | |
548 | if ((b -> posn)++ & 0x01) | |
549 | *(b -> cp)++ |= *x++ & 0x0F; | |
550 | else | |
551 | *(b -> cp) = *x++ << 4; | |
552 | return ((b -> posn) - start); | |
553 | } | |
554 | ||
555 | /* | |
556 | * This routine gets the first available logical channel number. The | |
557 | * search is from the highest number to lowest number (DTE). | |
558 | */ | |
559 | ||
560 | pk_getlcn (pkp) | |
561 | register struct pkcb *pkp; | |
562 | { | |
563 | register int i; | |
564 | ||
565 | if (pkp -> pk_chan == 0) | |
566 | return (0); | |
567 | for (i = pkp -> pk_maxlcn; i > 0; --i) | |
568 | if (pkp -> pk_chan[i] == NULL) | |
569 | break; | |
570 | return (i); | |
571 | ||
572 | } | |
573 | ||
574 | /* | |
575 | * This procedure sends a CLEAR request packet. The lc state is | |
576 | * set to "SENT_CLEAR". | |
577 | */ | |
578 | ||
579 | pk_clear (lcp, diagnostic, abortive) | |
580 | register struct pklcd *lcp; | |
581 | { | |
582 | register struct mbuf *m = pk_template (lcp -> lcd_lcn, X25_CLEAR); | |
583 | ||
584 | m -> m_len += 2; | |
585 | mtod (m, struct x25_packet *) -> packet_data = 0; | |
586 | mtod (m, octet *)[4] = diagnostic; | |
587 | if (lcp -> lcd_facilities) { | |
588 | m -> m_next = lcp -> lcd_facilities; | |
589 | m -> m_pkthdr.len += m -> m_next -> m_len; | |
590 | lcp -> lcd_facilities = 0; | |
591 | } | |
592 | if (abortive) | |
593 | lcp -> lcd_template = m; | |
594 | else { | |
595 | struct socket *so = lcp -> lcd_so; | |
596 | struct sockbuf *sb = so ? & so -> so_snd : & lcp -> lcd_sb; | |
597 | sbappendrecord (sb, m); | |
598 | } | |
599 | pk_output (lcp); | |
600 | ||
601 | } | |
602 | ||
603 | /* | |
604 | * This procedure generates RNR's or RR's to inhibit or enable | |
605 | * inward data flow, if the current state changes (blocked ==> open or | |
606 | * vice versa), or if forced to generate one. One forces RNR's to ack data. | |
607 | */ | |
608 | pk_flowcontrol (lcp, inhibit, forced) | |
609 | register struct pklcd *lcp; | |
610 | { | |
611 | inhibit = (inhibit != 0); | |
612 | if (lcp == 0 || lcp -> lcd_state != DATA_TRANSFER || | |
613 | (forced == 0 && lcp -> lcd_rxrnr_condition == inhibit)) | |
614 | return; | |
615 | lcp -> lcd_rxrnr_condition = inhibit; | |
616 | lcp -> lcd_template = | |
617 | pk_template (lcp -> lcd_lcn, inhibit ? X25_RNR : X25_RR); | |
618 | pk_output (lcp); | |
619 | } | |
620 | ||
621 | /* | |
622 | * This procedure sends a RESET request packet. It re-intializes | |
623 | * virtual circuit. | |
624 | */ | |
625 | ||
626 | static | |
627 | pk_reset (lcp, diagnostic) | |
628 | register struct pklcd *lcp; | |
629 | { | |
630 | register struct mbuf *m; | |
631 | register struct socket *so = lcp -> lcd_so; | |
632 | ||
633 | if (lcp -> lcd_state != DATA_TRANSFER) | |
634 | return; | |
635 | ||
636 | if (so) | |
637 | so -> so_error = ECONNRESET; | |
638 | lcp -> lcd_reset_condition = TRUE; | |
639 | ||
640 | /* Reset all the control variables for the channel. */ | |
641 | pk_flush (lcp); | |
642 | lcp -> lcd_window_condition = lcp -> lcd_rnr_condition = | |
643 | lcp -> lcd_intrconf_pending = FALSE; | |
644 | lcp -> lcd_rsn = MODULUS - 1; | |
645 | lcp -> lcd_ssn = 0; | |
646 | lcp -> lcd_output_window = lcp -> lcd_input_window = | |
647 | lcp -> lcd_last_transmitted_pr = 0; | |
648 | m = lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_RESET); | |
649 | m -> m_pkthdr.len = m -> m_len += 2; | |
650 | mtod (m, struct x25_packet *) -> packet_data = 0; | |
651 | mtod (m, octet *)[4] = diagnostic; | |
652 | pk_output (lcp); | |
653 | ||
654 | } | |
655 | ||
656 | /* | |
657 | * This procedure frees all data queued for output or delivery on a | |
658 | * virtual circuit. | |
659 | */ | |
660 | ||
661 | pk_flush (lcp) | |
662 | register struct pklcd *lcp; | |
663 | { | |
664 | register struct socket *so; | |
665 | ||
666 | if (lcp -> lcd_template) | |
667 | m_freem (lcp -> lcd_template); | |
668 | ||
669 | if (lcp -> lcd_cps) { | |
670 | m_freem (lcp -> lcd_cps); | |
671 | lcp -> lcd_cps = 0; | |
672 | } | |
673 | if (lcp -> lcd_facilities) { | |
674 | m_freem (lcp -> lcd_facilities); | |
675 | lcp -> lcd_facilities = 0; | |
676 | } | |
677 | if (so = lcp -> lcd_so) { | |
678 | sbflush (&so -> so_rcv); | |
679 | sbflush (&so -> so_snd); | |
680 | } else | |
681 | sbflush (&lcp -> lcd_sb); | |
682 | } | |
683 | ||
684 | /* | |
685 | * This procedure handles all local protocol procedure errors. | |
686 | */ | |
687 | ||
688 | pk_procerror (error, lcp, errstr, diagnostic) | |
689 | register struct pklcd *lcp; | |
690 | char *errstr; | |
691 | { | |
692 | ||
693 | pk_message (lcp -> lcd_lcn, lcp -> lcd_pkp -> pk_xcp, errstr); | |
694 | ||
695 | switch (error) { | |
696 | case CLEAR: | |
697 | if (lcp -> lcd_so) { | |
698 | lcp -> lcd_so -> so_error = ECONNABORTED; | |
699 | soisdisconnecting (lcp -> lcd_so); | |
700 | } | |
701 | pk_clear (lcp, diagnostic, 1); | |
702 | break; | |
703 | ||
704 | case RESET: | |
705 | pk_reset (lcp, diagnostic); | |
706 | } | |
707 | } | |
708 | ||
709 | /* | |
710 | * This procedure is called during the DATA TRANSFER state to check | |
711 | * and process the P(R) values received in the DATA, RR OR RNR | |
712 | * packets. | |
713 | */ | |
714 | ||
715 | pk_ack (lcp, pr) | |
716 | struct pklcd *lcp; | |
717 | unsigned pr; | |
718 | { | |
719 | register struct socket *so = lcp -> lcd_so; | |
720 | ||
721 | if (lcp -> lcd_output_window == pr) | |
722 | return (PACKET_OK); | |
723 | if (lcp -> lcd_output_window < lcp -> lcd_ssn) { | |
724 | if (pr < lcp -> lcd_output_window || pr > lcp -> lcd_ssn) { | |
725 | pk_procerror (RESET, lcp, | |
726 | "p(r) flow control error", 2); | |
727 | return (ERROR_PACKET); | |
728 | } | |
729 | } | |
730 | else { | |
731 | if (pr < lcp -> lcd_output_window && pr > lcp -> lcd_ssn) { | |
732 | pk_procerror (RESET, lcp, | |
733 | "p(r) flow control error #2", 2); | |
734 | return (ERROR_PACKET); | |
735 | } | |
736 | } | |
737 | ||
738 | lcp -> lcd_output_window = pr; /* Rotate window. */ | |
739 | if (lcp -> lcd_window_condition == TRUE) | |
740 | lcp -> lcd_window_condition = FALSE; | |
741 | ||
742 | if (so && ((so -> so_snd.sb_flags & SB_WAIT) || so -> so_snd.sb_sel)) | |
743 | sowwakeup (so); | |
744 | ||
745 | return (PACKET_OK); | |
746 | } | |
747 | ||
748 | /* | |
749 | * This procedure decodes the X.25 level 3 packet returning a | |
750 | * code to be used in switchs or arrays. | |
751 | */ | |
752 | ||
753 | pk_decode (xp) | |
754 | register struct x25_packet *xp; | |
755 | { | |
756 | register int type; | |
757 | ||
758 | if (xp -> fmt_identifier != 1) | |
759 | return (INVALID_PACKET); | |
760 | #ifdef ancient_history | |
761 | /* | |
762 | * Make sure that the logical channel group number is 0. | |
763 | * This restriction may be removed at some later date. | |
764 | */ | |
765 | if (xp -> lc_group_number != 0) | |
766 | return (INVALID_PACKET); | |
767 | #endif | |
768 | /* | |
769 | * Test for data packet first. | |
770 | */ | |
771 | if (!(xp -> packet_type & DATA_PACKET_DESIGNATOR)) | |
772 | return (DATA); | |
773 | ||
774 | /* | |
775 | * Test if flow control packet (RR or RNR). | |
776 | */ | |
777 | if (!(xp -> packet_type & RR_OR_RNR_PACKET_DESIGNATOR)) | |
778 | switch (xp -> packet_type & 0x1f) { | |
779 | case X25_RR: | |
780 | return (RR); | |
781 | case X25_RNR: | |
782 | return (RNR); | |
783 | case X25_REJECT: | |
784 | return (REJECT); | |
785 | } | |
786 | ||
787 | /* | |
788 | * Determine the rest of the packet types. | |
789 | */ | |
790 | switch (xp -> packet_type) { | |
791 | case X25_CALL: | |
792 | type = CALL; | |
793 | break; | |
794 | ||
795 | case X25_CALL_ACCEPTED: | |
796 | type = CALL_ACCEPTED; | |
797 | break; | |
798 | ||
799 | case X25_CLEAR: | |
800 | type = CLEAR; | |
801 | break; | |
802 | ||
803 | case X25_CLEAR_CONFIRM: | |
804 | type = CLEAR_CONF; | |
805 | break; | |
806 | ||
807 | case X25_INTERRUPT: | |
808 | type = INTERRUPT; | |
809 | break; | |
810 | ||
811 | case X25_INTERRUPT_CONFIRM: | |
812 | type = INTERRUPT_CONF; | |
813 | break; | |
814 | ||
815 | case X25_RESET: | |
816 | type = RESET; | |
817 | break; | |
818 | ||
819 | case X25_RESET_CONFIRM: | |
820 | type = RESET_CONF; | |
821 | break; | |
822 | ||
823 | case X25_RESTART: | |
824 | type = RESTART; | |
825 | break; | |
826 | ||
827 | case X25_RESTART_CONFIRM: | |
828 | type = RESTART_CONF; | |
829 | break; | |
830 | ||
831 | case X25_DIAGNOSTIC: | |
832 | type = DIAG_TYPE; | |
833 | break; | |
834 | ||
835 | default: | |
836 | type = INVALID_PACKET; | |
837 | } | |
838 | return (type); | |
839 | } | |
840 | ||
841 | /* | |
842 | * A restart packet has been received. Print out the reason | |
843 | * for the restart. | |
844 | */ | |
845 | ||
846 | pk_restartcause (pkp, xp) | |
847 | struct pkcb *pkp; | |
848 | register struct x25_packet *xp; | |
849 | { | |
850 | register struct x25config *xcp = pkp -> pk_xcp; | |
851 | register int lcn = LCN(xp); | |
852 | ||
853 | switch (xp -> packet_data) { | |
854 | case X25_RESTART_LOCAL_PROCEDURE_ERROR: | |
855 | pk_message (lcn, xcp, "restart: local procedure error"); | |
856 | break; | |
857 | ||
858 | case X25_RESTART_NETWORK_CONGESTION: | |
859 | pk_message (lcn, xcp, "restart: network congestion"); | |
860 | break; | |
861 | ||
862 | case X25_RESTART_NETWORK_OPERATIONAL: | |
863 | pk_message (lcn, xcp, "restart: network operational"); | |
864 | break; | |
865 | ||
866 | default: | |
867 | pk_message (lcn, xcp, "restart: unknown cause"); | |
868 | } | |
869 | } | |
870 | ||
871 | #define MAXRESETCAUSE 7 | |
872 | ||
873 | int Reset_cause[] = { | |
874 | EXRESET, EXROUT, 0, EXRRPE, 0, EXRLPE, 0, EXRNCG | |
875 | }; | |
876 | ||
877 | /* | |
878 | * A reset packet has arrived. Return the cause to the user. | |
879 | */ | |
880 | ||
881 | pk_resetcause (pkp, xp) | |
882 | struct pkcb *pkp; | |
883 | register struct x25_packet *xp; | |
884 | { | |
885 | register struct pklcd *lcp = | |
886 | pkp -> pk_chan[LCN(xp)]; | |
887 | register int code = xp -> packet_data; | |
888 | ||
889 | if (code > MAXRESETCAUSE) | |
890 | code = 7; /* EXRNCG */ | |
891 | ||
892 | pk_message(LCN(xp), lcp -> lcd_pkp, "reset code 0x%x, diagnostic 0x%x", | |
893 | xp -> packet_data, 4[(u_char *)xp]); | |
894 | ||
895 | if (lcp -> lcd_so) | |
896 | lcp -> lcd_so -> so_error = Reset_cause[code]; | |
897 | } | |
898 | ||
899 | #define MAXCLEARCAUSE 25 | |
900 | ||
901 | int Clear_cause[] = { | |
902 | EXCLEAR, EXCBUSY, 0, EXCINV, 0, EXCNCG, 0, | |
903 | 0, 0, EXCOUT, 0, EXCAB, 0, EXCNOB, 0, 0, 0, EXCRPE, | |
904 | 0, EXCLPE, 0, 0, 0, 0, 0, EXCRRC | |
905 | }; | |
906 | ||
907 | /* | |
908 | * A clear packet has arrived. Return the cause to the user. | |
909 | */ | |
910 | ||
911 | pk_clearcause (pkp, xp) | |
912 | struct pkcb *pkp; | |
913 | register struct x25_packet *xp; | |
914 | { | |
915 | register struct pklcd *lcp = | |
916 | pkp -> pk_chan[LCN(xp)]; | |
917 | register int code = xp -> packet_data; | |
918 | ||
919 | if (code > MAXCLEARCAUSE) | |
920 | code = 5; /* EXRNCG */ | |
921 | if (lcp -> lcd_so) | |
922 | lcp -> lcd_so -> so_error = Clear_cause[code]; | |
923 | } | |
924 | ||
925 | char * | |
926 | format_ntn (xcp) | |
927 | register struct x25config *xcp; | |
928 | { | |
929 | ||
930 | return (xcp -> xc_addr.x25_addr); | |
931 | } | |
932 | ||
933 | /* VARARGS1 */ | |
934 | pk_message (lcn, xcp, fmt, a1, a2, a3, a4, a5, a6) | |
935 | struct x25config *xcp; | |
936 | char *fmt; | |
937 | { | |
938 | ||
939 | if (lcn) | |
940 | if (pkcbhead -> pk_next) | |
941 | printf ("X.25(%s): lcn %d: ", format_ntn (xcp), lcn); | |
942 | else | |
943 | printf ("X.25: lcn %d: ", lcn); | |
944 | else | |
945 | if (pkcbhead -> pk_next) | |
946 | printf ("X.25(%s): ", format_ntn (xcp)); | |
947 | else | |
948 | printf ("X.25: "); | |
949 | ||
950 | printf (fmt, a1, a2, a3, a4, a5, a6); | |
951 | printf ("\n"); | |
952 | } | |
953 | ||
954 | pk_fragment (lcp, m0, qbit, mbit, wait) | |
955 | struct mbuf *m0; | |
956 | register struct pklcd *lcp; | |
957 | { | |
958 | register struct mbuf *m = m0; | |
959 | register struct x25_packet *xp; | |
960 | register struct sockbuf *sb; | |
961 | struct mbuf *head = 0, *next, **mp = &head, *m_split (); | |
962 | int totlen, psize = 1 << (lcp -> lcd_packetsize); | |
963 | ||
964 | if (m == 0) | |
965 | return 0; | |
966 | if (m -> m_flags & M_PKTHDR == 0) | |
967 | panic ("pk_fragment"); | |
968 | totlen = m -> m_pkthdr.len; | |
969 | m -> m_act = 0; | |
970 | sb = lcp -> lcd_so ? &lcp -> lcd_so -> so_snd : & lcp -> lcd_sb; | |
971 | do { | |
972 | if (totlen > psize) { | |
973 | if ((next = m_split (m, psize, wait)) == 0) | |
974 | goto abort; | |
975 | totlen -= psize; | |
976 | } else | |
977 | next = 0; | |
978 | M_PREPEND(m, PKHEADERLN, wait); | |
979 | if (m == 0) | |
980 | goto abort; | |
981 | *mp = m; | |
982 | mp = & m -> m_act; | |
983 | *mp = 0; | |
984 | xp = mtod (m, struct x25_packet *); | |
985 | 0[(char *)xp] = 0; | |
986 | if (qbit) | |
987 | xp -> q_bit = 1; | |
988 | if (lcp -> lcd_flags & X25_DBIT) | |
989 | xp -> d_bit = 1; | |
990 | xp -> fmt_identifier = 1; | |
991 | xp -> packet_type = X25_DATA; | |
992 | SET_LCN(xp, lcp -> lcd_lcn); | |
993 | if (next || (mbit && (totlen == psize || | |
994 | (lcp -> lcd_flags & X25_DBIT)))) | |
995 | MBIT(xp) = 1; | |
996 | } while (m = next); | |
997 | for (m = head; m; m = next) { | |
998 | next = m -> m_act; | |
999 | m -> m_act = 0; | |
1000 | sbappendrecord (sb, m); | |
1001 | } | |
1002 | return 0; | |
1003 | abort: | |
1004 | if (wait) | |
1005 | panic ("pk_fragment null mbuf after wait"); | |
1006 | if (next) | |
1007 | m_freem (next); | |
1008 | for (m = head; m; m = next) { | |
1009 | next = m -> m_act; | |
1010 | m_freem (m); | |
1011 | } | |
1012 | return ENOBUFS; | |
1013 | } | |
1014 | ||
1015 | struct mbuf * | |
1016 | m_split (m0, len0, wait) | |
1017 | register struct mbuf *m0; | |
1018 | int len0; | |
1019 | { | |
1020 | register struct mbuf *m, *n; | |
1021 | unsigned len = len0, remain; | |
1022 | ||
1023 | for (m = m0; m && len > m -> m_len; m = m -> m_next) | |
1024 | len -= m -> m_len; | |
1025 | if (m == 0) | |
1026 | return (0); | |
1027 | remain = m -> m_len - len; | |
1028 | if (m0 -> m_flags & M_PKTHDR) { | |
1029 | MGETHDR(n, wait, m0 -> m_type); | |
1030 | if (n == 0) | |
1031 | return (0); | |
1032 | n -> m_pkthdr.rcvif = m0 -> m_pkthdr.rcvif; | |
1033 | n -> m_pkthdr.len = m0 -> m_pkthdr.len - len0; | |
1034 | m0 -> m_pkthdr.len = len0; | |
1035 | if (m -> m_flags & M_EXT) | |
1036 | goto extpacket; | |
1037 | if (remain > MHLEN) { | |
1038 | /* m can't be the lead packet */ | |
1039 | MH_ALIGN(n, 0); | |
1040 | n -> m_next = m_split (m, len, wait); | |
1041 | if (n -> m_next == 0) { | |
1042 | (void) m_free (n); | |
1043 | return (0); | |
1044 | } else | |
1045 | return (n); | |
1046 | } else | |
1047 | MH_ALIGN(n, remain); | |
1048 | } else if (remain == 0) { | |
1049 | n = m -> m_next; | |
1050 | m -> m_next = 0; | |
1051 | return (n); | |
1052 | } else { | |
1053 | MGET(n, wait, m -> m_type); | |
1054 | if (n == 0) | |
1055 | return (0); | |
1056 | M_ALIGN(n, remain); | |
1057 | } | |
1058 | extpacket: | |
1059 | if (m -> m_flags & M_EXT) { | |
1060 | n -> m_flags |= M_EXT; | |
1061 | n -> m_ext = m -> m_ext; | |
1062 | mclrefcnt[mtocl (m -> m_ext.ext_buf)]++; | |
1063 | n -> m_data = m -> m_data + len; | |
1064 | } else { | |
1065 | bcopy (mtod (m, caddr_t) + len, mtod (n, caddr_t), remain); | |
1066 | } | |
1067 | n -> m_len = remain; | |
1068 | m -> m_len = len; | |
1069 | n -> m_next = m -> m_next; | |
1070 | m -> m_next = 0; | |
1071 | return (n); | |
1072 | } |