mark state before allowing dynamic pkcb's
[unix-history] / usr / src / sys / netccitt / if_x25subr.c
CommitLineData
16e7cbfd 1/*
94b7ce3b 2 * Copyright (c) 1990 The Regents of the University of California.
16e7cbfd
KS
3 * All rights reserved.
4 *
94b7ce3b 5 * %sccs.include.redist.c%
16e7cbfd 6 *
97565545 7 * @(#)if_x25subr.c 7.11 (Berkeley) %G%
16e7cbfd
KS
8 */
9
10#include "param.h"
11#include "systm.h"
12#include "malloc.h"
13#include "mbuf.h"
14#include "protosw.h"
15#include "socket.h"
4507dea2 16#include "socketvar.h"
16e7cbfd
KS
17#include "ioctl.h"
18#include "errno.h"
19#include "syslog.h"
20
039be508 21#include "../net/if.h"
4507dea2 22#include "../net/if_types.h"
039be508
KS
23#include "../net/netisr.h"
24#include "../net/route.h"
25
26#include "x25.h"
4507dea2
KS
27#include "x25err.h"
28#include "pk.h"
039be508 29#include "pk_var.h"
16e7cbfd 30
16e7cbfd
KS
31#include "machine/mtpr.h"
32
33#ifdef INET
34#include "../netinet/in.h"
35#include "../netinet/in_var.h"
36#endif
37
38#ifdef NS
39#include "../netns/ns.h"
40#include "../netns/ns_if.h"
41#endif
42
43#ifdef ISO
44#include "../netiso/argo_debug.h"
45#include "../netiso/iso.h"
46#include "../netiso/iso_var.h"
47#endif
48
49extern struct ifnet loif;
461586fd
KS
50struct llinfo_x25 llinfo_x25 = {&llinfo_x25, &llinfo_x25};
51int x25_autoconnect = 0;
16e7cbfd
KS
52
53#define senderr(x) {error = x; goto bad;}
461586fd
KS
54/*
55 * Ancillary routines
56 */
57static struct llinfo_x25 *
58x25_lxalloc(rt)
59register struct rtentry *rt;
60{
61 register struct llinfo_x25 *lx;
62 register struct sockaddr *dst = rt_key(rt);
63 register struct ifaddr *ifa;
64
65 MALLOC(lx, struct llinfo_x25 *, sizeof (*lx), M_PCB, M_NOWAIT);
66 if (lx == 0)
67 return lx;
68 Bzero(lx, sizeof(*lx));
69 lx->lx_rt = rt;
70 lx->lx_family = dst->sa_family;
71 rt->rt_refcnt++;
72 if (rt->rt_llinfo)
73 insque(lx, (struct llinfo_x25 *)rt->rt_llinfo);
74 else {
75 rt->rt_llinfo = (caddr_t)lx;
76 insque(lx, &llinfo_x25);
77 }
78 for (ifa = rt->rt_ifp->if_addrlist; ifa; ifa = ifa->ifa_next) {
79 if (ifa->ifa_addr->sa_family == AF_CCITT)
80 lx->lx_ia = (struct x25_ifaddr *)ifa;
81 }
82 return lx;
83}
84x25_lxfree(lx)
85register struct llinfo_x25 *lx;
86{
87 register struct rtentry *rt = lx->lx_rt;
88 register struct pklcd *lcp = lx->lx_lcd;
89
90 if (lcp) {
91 lcp->lcd_upper = 0;
92 pk_disconnect(lcp);
93 }
94 if ((rt->rt_llinfo == (caddr_t)lx) && (lx->lx_next->lx_rt == rt))
95 rt->rt_llinfo = (caddr_t)lx->lx_next;
96 else
97 rt->rt_llinfo = 0;
98 RTFREE(rt);
99 remque(lx);
100 FREE(lx, M_PCB);
101}
102/*
103 * Process a x25 packet as datagram;
104 */
105x25_ifinput(lcp, m)
106struct pklcd *lcp;
107register struct mbuf *m;
108{
109 struct llinfo_x25 *lx = (struct llinfo_x25 *)lcp->lcd_upnext;
110 register struct ifnet *ifp;
111 struct ifqueue *inq;
112 extern struct timeval time;
113 int s, len, isr;
114
115 if (m == 0 || lcp->lcd_state != DATA_TRANSFER) {
116 x25_connect_callback(lcp, 0);
117 return;
118 }
119 ifp = m->m_pkthdr.rcvif;
120 ifp->if_lastchange = time;
121 switch (m->m_type) {
122 case MT_OOBDATA:
123 if (m)
124 m_freem(m);
125 default:
126 return;
127
128 case MT_DATA:
129 /* FALLTHROUGH */;
130 }
131 switch (lx->lx_family) {
132#ifdef INET
133 case AF_INET:
134 isr = NETISR_IP;
135 inq = &ipintrq;
136 break;
137
138#endif
139#ifdef NS
140 case AF_NS:
141 isr = NETISR_NS;
142 inq = &nsintrq;
143 break;
144
145#endif
146#ifdef ISO
147 case AF_ISO:
148 isr = NETISR_ISO;
149 inq = &clnlintrq;
150 break;
151#endif
152 default:
153 m_freem(m);
154 ifp->if_noproto++;
155 return;
156 }
157 s = splimp();
158 schednetisr(isr);
159 if (IF_QFULL(inq)) {
160 IF_DROP(inq);
161 m_freem(m);
162 } else {
163 IF_ENQUEUE(inq, m);
164 ifp->if_ibytes += m->m_pkthdr.len;
165 }
166 splx(s);
167}
168x25_connect_callback(lcp, m)
169register struct pklcd *lcp;
170register struct mbuf *m;
171{
172 register struct llinfo_x25 *lx = (struct llinfo_x25 *)lcp->lcd_upnext;
173 if (m == 0)
174 goto refused;
175 if (m->m_type != MT_CONTROL) {
176 printf("x25_connect_callback: should panic\n");
177 goto refused;
178 }
179 switch (pk_decode(mtod(m, struct x25_packet *))) {
180 case CALL_ACCEPTED:
181 lcp->lcd_upper = x25_ifinput;
182 if (lcp->lcd_sb.sb_mb)
183 lcp->lcd_send(lcp); /* XXX start queued packets */
184 return;
185 default:
186 refused:
187 lcp->lcd_upper = 0;
188 lx->lx_lcd = 0;
189 pk_disconnect(lcp);
190 return;
191 }
192}
16e7cbfd
KS
193/*
194 * X.25 output routine.
195 */
039be508
KS
196x25_ifoutput(ifp, m0, dst, rt)
197struct ifnet *ifp;
16e7cbfd
KS
198struct mbuf *m0;
199struct sockaddr *dst;
200register struct rtentry *rt;
201{
461586fd
KS
202 register struct mbuf *m;
203 register struct llinfo_x25 *lx;
039be508 204 struct pklcd *lcp;
461586fd 205 int s, error = 0;
16e7cbfd 206
039be508 207 if ((ifp->if_flags & IFF_UP) == 0)
461586fd 208 senderr(ENETDOWN);
97565545
KS
209 while (rt == 0 || (rt->rt_flags & RTF_GATEWAY)) {
210 if (rt) {
211 if (rt->rt_llinfo) {
212 rt = (struct rtentry *)rt->rt_llinfo;
213 continue;
214 }
215 dst = rt->rt_gateway;
216 }
16e7cbfd 217 if ((rt = rtalloc1(dst, 1)) == 0)
461586fd
KS
218 senderr(EHOSTUNREACH);
219 rt->rt_refcnt--;
16e7cbfd
KS
220 }
221 /*
222 * Sanity checks.
223 */
039be508 224 if ((rt->rt_ifp != ifp) ||
16e7cbfd 225 (rt->rt_flags & (RTF_CLONING | RTF_GATEWAY)) ||
4507dea2 226 ((lx = (struct llinfo_x25 *)rt->rt_llinfo) == 0)) {
16e7cbfd
KS
227 senderr(ENETUNREACH);
228 }
461586fd
KS
229next_circuit:
230 lcp = lx->lx_lcd;
231 if (lcp == 0) {
232 lx->lx_lcd = lcp = pk_attach((struct socket *)0);
233 if (lcp == 0)
234 senderr(ENOBUFS);
235 lcp->lcd_upper = x25_connect_callback;
236 lcp->lcd_upnext = (caddr_t)lx;
237 lcp->lcd_packetsize = lx->lx_ia->ia_xc.xc_psize;
039be508 238 }
461586fd
KS
239 switch (lcp->lcd_state) {
240 case READY:
97565545
KS
241 if (dst->sa_family == AF_INET &&
242 ifp->if_type == IFT_X25DDN &&
243 rt->rt_gateway->sa_family != AF_CCITT)
244 x25_ddnip_to_ccitt(dst, rt);
16e7cbfd 245 if (rt->rt_gateway->sa_family != AF_CCITT) {
16e7cbfd 246 if ((rt->rt_flags & RTF_XRESOLVE) == 0)
461586fd
KS
247 senderr(EHOSTUNREACH);
248 } else if (x25_autoconnect)
249 error = pk_connect(lcp,
250 (struct sockaddr_x25 *)rt->rt_gateway);
251 if (error)
252 senderr(error);
16e7cbfd 253 /* FALLTHROUGH */
461586fd
KS
254 case SENT_CALL:
255 case DATA_TRANSFER:
256 if (sbspace(&lcp->lcd_sb) < 0) {
257 lx = lx->lx_next;
258 if (lx->lx_rt != rt)
259 senderr(ENOSPC);
260 goto next_circuit;
261 }
262 if (lx->lx_ia)
263 lcp->lcd_dg_timer =
264 lx->lx_ia->ia_xc.xc_dg_idletimo;
6c58e9b2 265 pk_send(lcp, m);
039be508 266 break;
16e7cbfd
KS
267 default:
268 /*
269 * We count on the timer routine to close idle
270 * connections, if there are not enough circuits to go
271 * around.
272 *
273 * So throw away data for now.
274 * After we get it all working, we'll rewrite to handle
275 * actively closing connections (other than by timers),
276 * when circuits get tight.
277 *
278 * In the DDN case, the imp itself closes connections
279 * under heavy load.
280 */
281 error = ENOBUFS;
282 bad:
283 if (m)
284 m_freem(m);
285 }
16e7cbfd
KS
286 return (error);
287}
288
289/*
039be508 290 * Simpleminded timer routine.
16e7cbfd 291 */
039be508
KS
292x25_iftimeout(ifp)
293struct ifnet *ifp;
16e7cbfd 294{
039be508
KS
295 register struct pkcb *pkcb = 0;
296 register struct ifaddr *ifa;
297 register struct pklcd **lcpp, *lcp;
16e7cbfd
KS
298 int s = splimp();
299
461586fd 300 for (ifa = ifp->if_addrlist; ifa && !pkcb; ifa = ifa->ifa_next) {
039be508 301 if (ifa->ifa_addr->sa_family == AF_CCITT)
461586fd 302 pkcb = &((struct x25_ifaddr *)ifa)->ia_pkcb;
039be508 303 }
039be508
KS
304 if (pkcb)
305 for (lcpp = pkcb->pk_chan + pkcb->pk_maxlcn;
c4b47c42 306 --lcpp > pkcb->pk_chan;)
039be508
KS
307 if ((lcp = *lcpp) &&
308 lcp->lcd_state == DATA_TRANSFER &&
4507dea2 309 (lcp->lcd_flags & X25_DG_CIRCUIT) &&
c4b47c42 310 (lcp->lcd_dg_timer && --lcp->lcd_dg_timer == 0)) {
461586fd 311 lcp->lcd_upper(lcp, 0);
c4b47c42 312 }
16e7cbfd
KS
313 splx(s);
314}
16e7cbfd 315/*
461586fd 316 * This routine gets called when validating additions of new routes
97565545 317 * or deletions of old ones.
c4b47c42
KS
318 */
319x25_ifrtchange(cmd, rt, dst)
320register struct rtentry *rt;
321struct sockaddr *dst;
322{
323 register struct llinfo_x25 *lx = (struct llinfo_x25 *)rt->rt_llinfo;
324 register struct sockaddr_x25 *sa =(struct sockaddr_x25 *)rt->rt_gateway;
325 register struct pklcd *lcp;
c4b47c42
KS
326#define SA(p) ((struct sockaddr *)(p))
327
461586fd
KS
328 if (rt->rt_flags & RTF_GATEWAY) {
329 if (rt->rt_llinfo)
330 RTFREE((struct rtentry *)rt->rt_llinfo);
331 rt->rt_llinfo = (cmd == RTM_ADD) ?
332 (caddr_t)rtalloc1(rt->rt_gateway, 1) : 0;
6c58e9b2
KS
333 return;
334 }
97565545
KS
335 if ((rt->rt_flags & RTF_HOST) == 0)
336 return;
461586fd
KS
337 if (cmd == RTM_DELETE) {
338 while (rt->rt_llinfo)
339 x25_lxfree((struct llinfo *)rt->rt_llinfo);
340 x25_rtinvert(RTM_DELETE, rt->rt_gateway, rt);
c4b47c42
KS
341 return;
342 }
97565545
KS
343 if (lx == 0 && (lx = x25_lxalloc(rt)) == 0)
344 return;
461586fd
KS
345 if ((lcp = lx->lx_lcd) && lcp->lcd_state != READY) {
346 /*
347 * This can only happen on a RTM_CHANGE operation
348 * though cmd will be RTM_ADD.
349 */
350 if (lcp->lcd_ceaddr &&
351 Bcmp(rt->rt_gateway, lcp->lcd_ceaddr,
352 lcp->lcd_ceaddr->x25_len) != 0) {
353 x25_rtinvert(RTM_DELETE, lcp->lcd_ceaddr, rt);
354 lcp->lcd_upper = 0;
355 pk_disconnect(lcp);
356 }
c4b47c42
KS
357 lcp = 0;
358 }
461586fd
KS
359 x25_rtinvert(RTM_ADD, rt->rt_gateway, rt);
360}
361
97565545
KS
362int x25_dont_rtinvert = 1;
363
461586fd
KS
364x25_rtinvert(cmd, sa, rt)
365register struct sockaddr *sa;
366register struct rtentry *rt;
367{
368 struct rtentry *rt2 = 0;
369 /*
370 * rt_gateway contains PID indicating which proto
371 * family on the other end, so will be different
372 * from general host route via X.25.
373 */
97565545
KS
374 if (x25_dont_rtinvert)
375 return;
c4b47c42
KS
376 if (rt->rt_ifp->if_type == IFT_X25DDN)
377 return;
461586fd
KS
378 if (sa->sa_family != AF_CCITT)
379 return;
380 if (cmd == RTM_ADD) {
381 rtrequest(RTM_ADD, sa, rt_key(rt), SA(0),
382 RTF_HOST|RTF_PROTO1, &rt2);
383 if (rt2) {
384 rt2->rt_llinfo = (caddr_t) rt;
385 rt->rt_refcnt++;
c4b47c42 386 }
461586fd 387 return;
c4b47c42 388 }
461586fd
KS
389 rt2 = rt;
390 if ((rt = rtalloc1(sa, 0)) == 0 ||
391 (rt->rt_flags & RTF_PROTO1) == 0 ||
392 rt->rt_llinfo != (caddr_t)rt) {
393 printf("x25_rtchange: inverse route screwup\n");
394 return;
395 } else
396 rt2->rt_refcnt--;
397 rtrequest(RTM_DELETE, rt->rt_gateway, rt_key(rt),
398 SA(0), 0, (struct rtentry **) 0);
c4b47c42
KS
399}
400
16e7cbfd
KS
401static struct sockaddr_x25 blank_x25 = {sizeof blank_x25, AF_CCITT};
402/*
403 * IP to X25 address routine copyright ACC, used by permission.
404 */
c4b47c42
KS
405union imp_addr {
406 struct in_addr ip;
407 struct imp {
408 u_char s_net;
409 u_char s_host;
410 u_char s_lh;
411 u_char s_impno;
412 } imp;
413};
414
461586fd
KS
415/*
416 * The following is totally bogus and here only to preserve
417 * the IP to X.25 translation.
418 */
419x25_ddnip_to_ccitt(src, rt)
16e7cbfd 420struct sockaddr_in *src;
461586fd 421register struct rtentry *rt;
16e7cbfd 422{
461586fd 423 register struct sockaddr_x25 *dst = (struct sockaddr_x25 *)rt->rt_gateway;
16e7cbfd 424 union imp_addr imp_addr;
4507dea2
KS
425 int imp_no, imp_port, temp;
426 char *x25addr = dst->x25_addr;
16e7cbfd
KS
427
428
4507dea2 429 imp_addr.ip = src->sin_addr;
16e7cbfd
KS
430 *dst = blank_x25;
431 if ((imp_addr.imp.s_net & 0x80) == 0x00) { /* class A */
432 imp_no = imp_addr.imp.s_impno;
433 imp_port = imp_addr.imp.s_host;
434 } else if ((imp_addr.imp.s_net & 0xc0) == 0x80) { /* class B */
435 imp_no = imp_addr.imp.s_impno;
436 imp_port = imp_addr.imp.s_lh;
437 } else { /* class C */
438 imp_no = imp_addr.imp.s_impno / 32;
439 imp_port = imp_addr.imp.s_impno % 32;
440 }
441
442 x25addr[0] = 12; /* length */
443 /* DNIC is cleared by struct copy above */
444
445 if (imp_port < 64) { /* Physical: 0000 0 IIIHH00 [SS] *//* s_impno
446 * -> III, s_host -> HH */
447 x25addr[5] = 0; /* set flag bit */
448 x25addr[6] = imp_no / 100;
449 x25addr[7] = (imp_no % 100) / 10;
450 x25addr[8] = imp_no % 10;
451 x25addr[9] = imp_port / 10;
452 x25addr[10] = imp_port % 10;
453 } else { /* Logical: 0000 1 RRRRR00 [SS] *//* s
454 * _host * 256 + s_impno -> RRRRR */
455 temp = (imp_port << 8) + imp_no;
456 x25addr[5] = 1;
457 x25addr[6] = temp / 10000;
458 x25addr[7] = (temp % 10000) / 1000;
459 x25addr[8] = (temp % 1000) / 100;
460 x25addr[9] = (temp % 100) / 10;
461 x25addr[10] = temp % 10;
462 }
463}
464
16e7cbfd 465/*
c4b47c42
KS
466 * This routine is a sketch and is not to be believed!!!!!
467 *
16e7cbfd
KS
468 * This is a utility routine to be called by x25 devices when a
469 * call request is honored with the intent of starting datagram forwarding.
470 */
039be508 471x25_dg_rtinit(dst, ia, af)
16e7cbfd 472struct sockaddr_x25 *dst;
4507dea2 473register struct x25_ifaddr *ia;
16e7cbfd
KS
474{
475 struct sockaddr *sa = 0;
4507dea2
KS
476 struct rtentry *rt;
477 struct in_addr my_addr;
461586fd 478 static struct sockaddr_in sin = {sizeof(sin), AF_INET};
4507dea2
KS
479
480 if (ia->ia_ifp->if_type == IFT_X25DDN && af == AF_INET) {
16e7cbfd 481 /*
4507dea2 482 * Inverse X25 to IP mapping copyright and courtesy ACC.
16e7cbfd
KS
483 */
484 int imp_no, imp_port, temp;
485 union imp_addr imp_addr;
486 {
487 /*
488 * First determine our IP addr for network
489 */
4507dea2 490 register struct in_ifaddr *ina;
16e7cbfd 491 extern struct in_ifaddr *in_ifaddr;
4507dea2
KS
492
493 for (ina = in_ifaddr; ina; ina = ina->ia_next)
494 if (ina->ia_ifp == ia->ia_ifp) {
495 my_addr = ina->ia_addr.sin_addr;
16e7cbfd
KS
496 break;
497 }
498 }
499 {
500
501 register char *x25addr = dst->x25_addr;
502
503 switch (x25addr[5] & 0x0f) {
504 case 0: /* Physical: 0000 0 IIIHH00 [SS] */
505 imp_no =
506 ((int) (x25addr[6] & 0x0f) * 100) +
507 ((int) (x25addr[7] & 0x0f) * 10) +
508 ((int) (x25addr[8] & 0x0f));
509
510
511 imp_port =
512 ((int) (x25addr[9] & 0x0f) * 10) +
513 ((int) (x25addr[10] & 0x0f));
514 break;
515 case 1: /* Logical: 0000 1 RRRRR00 [SS] */
516 temp = ((int) (x25addr[6] & 0x0f) * 10000)
517 + ((int) (x25addr[7] & 0x0f) * 1000)
518 + ((int) (x25addr[8] & 0x0f) * 100)
519 + ((int) (x25addr[9] & 0x0f) * 10)
520 + ((int) (x25addr[10] & 0x0f));
521
522 imp_port = temp >> 8;
523 imp_no = temp & 0xff;
524 break;
525 default:
526 return (0L);
527 }
4507dea2 528 imp_addr.ip = my_addr;
16e7cbfd
KS
529 if ((imp_addr.imp.s_net & 0x80) == 0x00) {
530 /* class A */
531 imp_addr.imp.s_host = imp_port;
532 imp_addr.imp.s_impno = imp_no;
533 imp_addr.imp.s_lh = 0;
534 } else if ((imp_addr.imp.s_net & 0xc0) == 0x80) {
535 /* class B */
536 imp_addr.imp.s_lh = imp_port;
537 imp_addr.imp.s_impno = imp_no;
538 } else {
539 /* class C */
540 imp_addr.imp.s_impno = (imp_no << 5) + imp_port;
541 }
542 }
543 sin.sin_addr = imp_addr.ip;
544 sa = (struct sockaddr *)&sin;
545 } else {
546 /*
547 * This uses the X25 routing table to do inverse
548 * lookup of x25 address to sockaddr.
549 */
16e7cbfd
KS
550 if (rt = rtalloc1(dst, 0)) {
551 sa = rt->rt_gateway;
552 rt->rt_refcnt--;
553 }
16e7cbfd
KS
554 }
555 /*
556 * Call to rtalloc1 will create rtentry for reverse path
557 * to callee by virtue of cloning magic and will allocate
558 * space for local control block.
559 */
4507dea2 560 if (sa && (rt = rtalloc1(sa, 1)))
16e7cbfd
KS
561 rt->rt_refcnt--;
562}
b84e7ca8
KS
563
564struct radix_tree_head *x25_rnhead;
565
566pk_init()
567{
568 /*
569 * warning, sizeof (struct sockaddr_x25) > 32,
570 * but contains no data of interest beyond 32
571 */
6c58e9b2 572 rn_inithead(&x25_rnhead, 32, AF_CCITT);
b84e7ca8 573}
461586fd
KS
574/*
575 * This routine steals a virtual circuit from a socket,
576 * and glues it to a routing entry. It wouldn't be hard
577 * to extend this to a routine that stole back the vc from
578 * rtentry.
579 */
580pk_rtattach(so, m0)
581register struct socket *so;
582struct mbuf *m0;
583{
584 register struct pklcd *lcp = (struct pklcd *)so->so_pcb;
585 register struct mbuf *m = m0;
586 struct sockaddr *dst = mtod(m, struct sockaddr *);
587 register struct rtentry *rt = rtalloc1(dst, 0);
588 register struct llinfo_x25 *lx;
589 caddr_t cp;
590#define ROUNDUP(a) \
591 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
592#define transfer_sockbuf(s, f, l) \
593 while (m = (s)->sb_mb) {(s)->sb_mb = m->m_act; sbfree((s), m); f(l, m);}
594
595 if (rt)
596 rt->rt_refcnt--;
597 cp = (dst->sa_len < m->m_len) ? ROUNDUP(dst->sa_len) + (caddr_t)dst : 0;
598 while (rt &&
599 ((cp == 0 && rt_mask(rt) != 0) ||
600 (cp != 0 && (rt_mask(rt) == 0 ||
601 Bcmp(cp, rt_mask(rt), rt_mask(rt)->sa_len)) != 0)))
602 rt = (struct rtentry *)rt->rt_nodes->rn_dupedkey;
603 if (rt == 0 || (rt->rt_flags & RTF_GATEWAY) ||
604 (lx = (struct llinfo_x25 *)rt->rt_llinfo) == 0)
605 return ESRCH;
606 if (lcp == 0 || lcp->lcd_state != DATA_TRANSFER)
607 return ENOTCONN;
608 if (lcp = lx->lx_lcd) { /* adding an additional VC */
609 if (lcp->lcd_state == READY) {
610 transfer_sockbuf(&lcp->lcd_sb, pk_output,
611 (struct pklcd *)so->so_pcb);
612 lcp->lcd_upper = 0;
613 pk_close(lcp);
614 } else {
615 lx = x25_lxalloc(rt);
616 if (lx == 0)
617 return ENOBUFS;
618 }
619 }
620 lx->lx_lcd = lcp = (struct pklcd *)so->so_pcb;
621 lcp->lcd_so = 0;
622 lcp->lcd_sb = so->so_snd; /* structure copy */
623 lcp->lcd_upper = x25_ifinput;
624 lcp->lcd_upnext = (caddr_t)lx;
625 transfer_sockbuf(&so->so_rcv, x25_ifinput, lcp);
626 so->so_pcb = 0;
627 bzero((caddr_t)&so->so_snd, sizeof(so->so_snd)); /* XXXXXX */
628 soisdisconnected(so);
629 return (0);
630}