1 /* $NetBSD: pk_usrreq.c,v 1.26 2003/08/07 16:33:05 agc Exp $ */
2
3 /*
4 * Copyright (c) 1991, 1992, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by the
8 * Laboratory for Computation Vision and the Computer Science Department
9 * of the University of British Columbia and the Computer Science
10 * Department (IV) of the University of Erlangen-Nuremberg, Germany.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * @(#)pk_usrreq.c 8.2 (Berkeley) 1/9/95
37 */
38
39 /*
40 * Copyright (c) 1984 University of British Columbia.
41 * Copyright (c) 1992 Computer Science Department IV,
42 * University of Erlangen-Nuremberg, Germany.
43 *
44 * This code is derived from software contributed to Berkeley by the
45 * Laboratory for Computation Vision and the Computer Science Department
46 * of the University of British Columbia and the Computer Science
47 * Department (IV) of the University of Erlangen-Nuremberg, Germany.
48 *
49 * Redistribution and use in source and binary forms, with or without
50 * modification, are permitted provided that the following conditions
51 * are met:
52 * 1. Redistributions of source code must retain the above copyright
53 * notice, this list of conditions and the following disclaimer.
54 * 2. Redistributions in binary form must reproduce the above copyright
55 * notice, this list of conditions and the following disclaimer in the
56 * documentation and/or other materials provided with the distribution.
57 * 3. All advertising materials mentioning features or use of this software
58 * must display the following acknowledgement:
59 * This product includes software developed by the University of
60 * California, Berkeley and its contributors.
61 * 4. Neither the name of the University nor the names of its contributors
62 * may be used to endorse or promote products derived from this software
63 * without specific prior written permission.
64 *
65 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
66 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
67 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
68 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
69 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
70 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
71 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
72 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
73 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
74 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
75 * SUCH DAMAGE.
76 *
77 * @(#)pk_usrreq.c 8.2 (Berkeley) 1/9/95
78 */
79
80 #include <sys/cdefs.h>
81 __KERNEL_RCSID(0, "$NetBSD: pk_usrreq.c,v 1.26 2003/08/07 16:33:05 agc Exp $");
82
83 #include <sys/param.h>
84 #include <sys/systm.h>
85 #include <sys/mbuf.h>
86 #include <sys/socket.h>
87 #include <sys/socketvar.h>
88 #include <sys/protosw.h>
89 #include <sys/errno.h>
90 #include <sys/ioctl.h>
91 #include <sys/stat.h>
92 #include <sys/proc.h>
93
94 #include <net/if.h>
95 #include <net/if_types.h>
96 #include <net/route.h>
97
98 #include <netccitt/x25.h>
99 #include <netccitt/pk.h>
100 #include <netccitt/pk_var.h>
101 #include <netccitt/pk_extern.h>
102
103 static void pk_setsockaddr __P((struct pklcd *, struct mbuf *));
104 static void pk_setpeeraddr __P((struct pklcd *, struct mbuf *));
105 static void old_to_new __P((struct mbuf *));
106 static void new_to_old __P((struct mbuf *));
107
108 static void
109 pk_setsockaddr(lcp, nam)
110 struct pklcd *lcp;
111 struct mbuf *nam;
112 {
113
114 nam->m_len = sizeof(struct sockaddr_x25);
115 bcopy(lcp->lcd_ceaddr, mtod(nam, caddr_t), (size_t)nam->m_len);
116 if (lcp->lcd_flags & X25_OLDSOCKADDR)
117 new_to_old(nam);
118 }
119
120 static void
121 pk_setpeeraddr(lcp, nam)
122 struct pklcd *lcp;
123 struct mbuf *nam;
124 {
125
126 nam->m_len = sizeof(struct sockaddr_x25);
127 bcopy(lcp->lcd_craddr, mtod(nam, caddr_t), (size_t)nam->m_len);
128 if (lcp->lcd_flags & X25_OLDSOCKADDR)
129 new_to_old(nam);
130 }
131
132 /*
133 *
134 * X.25 Packet level protocol interface to socket abstraction.
135 *
136 * Process an X.25 user request on a logical channel. If this is a send
137 * request then m is the mbuf chain of the send data. If this is a timer
138 * expiration (called from the software clock routine) them timertype is
139 * the particular timer.
140 *
141 */
142 int
143 pk_usrreq(so, req, m, nam, control, p)
144 struct socket *so;
145 int req;
146 struct mbuf *m, *nam, *control;
147 struct proc *p;
148 {
149 struct pklcd *lcp;
150 int s;
151 int error = 0;
152
153 if (req == PRU_CONTROL)
154 return (pk_control(so, (long)m, (caddr_t)nam,
155 (struct ifnet *)control, p));
156
157 s = splsoftnet();
158 lcp = (struct pklcd *)so->so_pcb;
159 #ifdef DIAGNOSTIC
160 if (req != PRU_SEND && req != PRU_SENDOOB && control)
161 panic("pk_usrreq: unexpected control mbuf");
162 #endif
163 if (lcp == 0 && req != PRU_ATTACH) {
164 error = EINVAL;
165 goto release;
166 }
167
168 /*
169 pk_trace (pkcbhead, TR_USER, (struct pklcd *)0,
170 req, (struct x25_packet *)0);
171 */
172 switch (req) {
173
174 /*
175 * X.25 attaches to socket via PRU_ATTACH and allocates a
176 * logical channel descriptor. If the socket is to receive
177 * connections, then the LISTEN state is entered.
178 */
179 case PRU_ATTACH:
180 if (lcp != 0) {
181 error = EISCONN;
182 break;
183 }
184 lcp = pk_attach(so);
185 if (lcp == 0)
186 error = ENOBUFS;
187 break;
188
189 /*
190 * Detach a logical channel from the socket. If the state of
191 * the channel is embryonic, simply discard it. Otherwise we
192 * have to initiate a PRU_DISCONNECT which will finish later.
193 */
194 case PRU_DETACH:
195 pk_disconnect(lcp);
196 break;
197
198 /*
199 * Give the socket an address.
200 */
201 case PRU_BIND:
202 if (nam->m_len == sizeof(struct x25_sockaddr))
203 old_to_new(nam);
204 error = pk_bind(lcp, nam);
205 break;
206
207 /*
208 * Prepare to accept connections.
209 */
210 case PRU_LISTEN:
211 error = pk_listen(lcp);
212 break;
213
214 /*
215 * Initiate a CALL REQUEST to peer entity. Enter state
216 * SENT_CALL and mark the socket as connecting. Set timer
217 * waiting for CALL ACCEPT or CLEAR.
218 */
219 case PRU_CONNECT:
220 if (nam->m_len == sizeof(struct x25_sockaddr))
221 old_to_new(nam);
222 if (pk_checksockaddr(nam)) {
223 error = EINVAL;
224 break;
225 }
226 error = pk_connect(lcp, mtod(nam, struct sockaddr_x25 *));
227 break;
228
229 case PRU_CONNECT2:
230 error = EOPNOTSUPP;
231 break;
232
233 /*
234 * Initiate a disconnect to peer entity via a CLEAR REQUEST
235 * packet. The socket will be disconnected when we receive a
236 * confirmation or a clear collision.
237 */
238 case PRU_DISCONNECT:
239 pk_disconnect(lcp);
240 break;
241
242 /*
243 * Accept an INCOMING CALL. Most of the work has already been
244 * done by pk_input. Just return the callers address to the
245 * user.
246 */
247 case PRU_ACCEPT:
248 if (lcp->lcd_craddr == NULL)
249 break;
250 pk_setpeeraddr(lcp, nam);
251 break;
252
253 case PRU_SHUTDOWN:
254 socantsendmore(so);
255 break;
256
257 /*
258 * After a receive, we should send a RR.
259 */
260 case PRU_RCVD:
261 pk_flowcontrol(lcp, /* sbspace (&so -> so_rcv) <= */ 0, 1);
262 break;
263
264 /*
265 * Do send by placing data on the socket output queue.
266 */
267 case PRU_SEND:
268 if (control) {
269 struct cmsghdr *ch = mtod(m, struct cmsghdr *);
270 control->m_len -= sizeof(*ch);
271 control->m_data += sizeof(*ch);
272 error = pk_ctloutput(PRCO_SETOPT, so, ch->cmsg_level,
273 ch->cmsg_type, &control);
274 if (error)
275 break;
276 }
277 if (m)
278 error = pk_send(m, lcp);
279 break;
280
281 /*
282 * Abort a virtual circuit. For example all completed calls
283 * waiting acceptance.
284 */
285 case PRU_ABORT:
286 pk_disconnect(lcp);
287 break;
288
289 case PRU_SENSE:
290 /*
291 * stat: don't bother with a blocksize.
292 */
293 splx(s);
294 return (0);
295
296 /*
297 * Receive INTERRUPT packet.
298 */
299 case PRU_RCVOOB:
300 if (so->so_options & SO_OOBINLINE) {
301 struct mbuf *n = so->so_rcv.sb_mb;
302 if (n && n->m_type == MT_OOBDATA) {
303 unsigned len = n->m_pkthdr.len;
304 so->so_rcv.sb_mb = n->m_nextpkt;
305 SB_EMPTY_FIXUP(&so->so_rcv);
306 if (len != n->m_len &&
307 (n = m_pullup(n, len)) == 0)
308 break;
309 m->m_len = len;
310 bcopy(mtod(m, caddr_t), mtod(n, caddr_t), len);
311 m_freem(n);
312 }
313 break;
314 }
315 m->m_len = 1;
316 *mtod(m, char *) = lcp->lcd_intrdata;
317 break;
318
319 /*
320 * Send INTERRUPT packet.
321 */
322 case PRU_SENDOOB:
323 if (control) {
324 struct cmsghdr *ch = mtod(m, struct cmsghdr *);
325 control->m_len -= sizeof(*ch);
326 control->m_data += sizeof(*ch);
327 error = pk_ctloutput(PRCO_SETOPT, so, ch->cmsg_level,
328 ch->cmsg_type, &control);
329 if (error)
330 break;
331 }
332 if (m) {
333 MCHTYPE(m, MT_OOBDATA);
334 error = pk_send(m, lcp);
335 }
336 break;
337
338 case PRU_SOCKADDR:
339 if (lcp->lcd_ceaddr == 0) {
340 error = EADDRNOTAVAIL;
341 break;
342 }
343 pk_setsockaddr(lcp, nam);
344 break;
345
346 case PRU_PEERADDR:
347 if (lcp->lcd_state != DATA_TRANSFER) {
348 error = ENOTCONN;
349 break;
350 }
351 pk_setpeeraddr(lcp, nam);
352 break;
353
354 default:
355 panic("pk_usrreq");
356 }
357
358 release:
359 splx(s);
360 return (error);
361 }
362
363 /*
364 * If you want to use UBC X.25 level 3 in conjunction with some other X.25
365 * level 2 driver, have the ifp -> if_ioctl routine assign pk_start to ia ->
366 * ia_start when called with SIOCSIFCONF_X25.
367 */
368 /* ARGSUSED */
369 int
370 pk_start(lcp)
371 struct pklcd *lcp;
372 {
373 pk_output(lcp);
374 return (0); /* XXX pk_output should return a value */
375 }
376
377 struct sockaddr_x25 pk_sockmask = {
378 offsetof(struct sockaddr_x25, x25_addr[0]), /* x25_len */
379 0, /* x25_family */
380 -1, /* x25_net id */
381 };
382
383 /* ARGSUSED */
384 int
385 pk_control(so, cmd, data, ifp, p)
386 struct socket *so;
387 u_long cmd;
388 caddr_t data;
389 struct ifnet *ifp;
390 struct proc *p;
391 {
392 struct ifreq_x25 *ifr = (struct ifreq_x25 *) data;
393 struct ifaddr *ifa = 0;
394 struct x25_ifaddr *ia = 0;
395 int error = 0, s, old_maxlcn;
396
397 /*
398 * Find address for this interface, if it exists.
399 */
400 if (ifp)
401 for (ifa = ifp->if_addrlist.tqh_first; ifa != 0;
402 ifa = ifa->ifa_list.tqe_next)
403 if (ifa->ifa_addr->sa_family == AF_CCITT)
404 break;
405
406 ia = (struct x25_ifaddr *) ifa;
407 switch (cmd) {
408 case SIOCGIFCONF_X25:
409 if (ifa == 0)
410 return (EADDRNOTAVAIL);
411 ifr->ifr_xc = ia->ia_xc;
412 return (0);
413
414 case SIOCSIFCONF_X25:
415 if (p == 0 || (error = suser(p->p_ucred, &p->p_acflag)))
416 return (EPERM);
417 if (ifp == 0)
418 panic("pk_control");
419 if (ifa == (struct ifaddr *) 0) {
420 MALLOC(ia, struct x25_ifaddr *, sizeof(*ia),
421 M_IFADDR, M_WAITOK);
422 if (ia == 0)
423 return (ENOBUFS);
424 bzero((caddr_t) ia, sizeof(*ia));
425 TAILQ_INSERT_TAIL(&ifp->if_addrlist, &ia->ia_ifa,
426 ifa_list);
427 ifa = &ia->ia_ifa;
428 IFAREF(ifa);
429 ifa->ifa_netmask = (struct sockaddr *) & pk_sockmask;
430 ifa->ifa_addr = (struct sockaddr *) & ia->ia_xc.xc_addr;
431 ifa->ifa_dstaddr = (struct sockaddr *) & ia->ia_dstaddr; /* XXX */
432 ia->ia_ifp = ifp;
433 ia->ia_dstaddr.x25_family = AF_CCITT;
434 ia->ia_dstaddr.x25_len = pk_sockmask.x25_len;
435 } else if (ISISO8802(ifp) == 0) {
436 rtinit(ifa, (int) RTM_DELETE, 0);
437 }
438 old_maxlcn = ia->ia_maxlcn;
439 ia->ia_xc = ifr->ifr_xc;
440 ia->ia_dstaddr.x25_net = ia->ia_xc.xc_addr.x25_net;
441 if (ia->ia_maxlcn != old_maxlcn && old_maxlcn != 0) {
442 /* VERY messy XXX */
443 struct pkcb *pkp;
444 FOR_ALL_PKCBS(pkp)
445 if (pkp->pk_ia == ia)
446 pk_resize(pkp);
447 }
448 /*
449 * Give the interface a chance to initialize if this
450 p * is its first address, and to validate the address.
451 */
452 ia->ia_start = pk_start;
453 s = splnet();
454 if (ifp->if_ioctl)
455 error = (*ifp->if_ioctl) (ifp, SIOCSIFCONF_X25,
456 (caddr_t) ifa);
457 if (error)
458 ifp->if_flags &= ~IFF_UP;
459 else if (ISISO8802(ifp) == 0)
460 error = rtinit(ifa, (int) RTM_ADD, RTF_UP);
461 splx(s);
462 return (error);
463
464 default:
465 if (ifp == 0 || ifp->if_ioctl == 0)
466 return (EOPNOTSUPP);
467 return ((*ifp->if_ioctl) (ifp, cmd, data));
468 }
469 }
470
471 int
472 pk_ctloutput(cmd, so, level, optname, mp)
473 struct socket *so;
474 struct mbuf **mp;
475 int cmd, level, optname;
476 {
477 struct proc *p = curproc; /* XXX */
478 struct mbuf *m = *mp;
479 struct pklcd *lcp = (struct pklcd *) so->so_pcb;
480 int error = EOPNOTSUPP;
481
482 if (m == 0)
483 return (EINVAL);
484 if (cmd == PRCO_SETOPT)
485 switch (optname) {
486 case PK_FACILITIES:
487 if (m == 0)
488 return (EINVAL);
489 lcp->lcd_facilities = m;
490 *mp = 0;
491 return (0);
492
493 case PK_ACCTFILE:
494 if (p == 0 || (error = suser(p->p_ucred, &p->p_acflag)))
495 error = EPERM;
496 else if (m->m_len)
497 error = pk_accton(mtod(m, char *));
498 else
499 error = pk_accton((char *) 0);
500 break;
501
502 case PK_RTATTACH:
503 error = pk_rtattach(so, m);
504 break;
505
506 case PK_PRLISTEN:
507 error = pk_user_protolisten(mtod(m, u_char *));
508 }
509 if (*mp) {
510 (void) m_freem(*mp);
511 *mp = 0;
512 }
513 return (error);
514
515 }
516
517
518 /*
519 * Do an in-place conversion of an "old style"
520 * socket address to the new style
521 */
522
523 static void
524 old_to_new(m)
525 struct mbuf *m;
526 {
527 struct x25_sockaddr *oldp;
528 struct sockaddr_x25 *newp;
529 char *ocp, *ncp;
530 struct sockaddr_x25 new;
531
532 oldp = mtod(m, struct x25_sockaddr *);
533 newp = &new;
534 bzero((caddr_t) newp, sizeof(*newp));
535
536 newp->x25_family = AF_CCITT;
537 newp->x25_len = sizeof(*newp);
538 newp->x25_opts.op_flags = (oldp->xaddr_facilities & X25_REVERSE_CHARGE)
539 | X25_MQBIT | X25_OLDSOCKADDR;
540 if (oldp->xaddr_facilities & XS_HIPRIO) /* Datapac specific */
541 newp->x25_opts.op_psize = X25_PS128;
542 bcopy((caddr_t) oldp->xaddr_addr, newp->x25_addr,
543 (unsigned) min(oldp->xaddr_len, sizeof(newp->x25_addr) - 1));
544 if (bcmp((caddr_t) oldp->xaddr_proto, newp->x25_udata, 4) != 0) {
545 bcopy((caddr_t) oldp->xaddr_proto, newp->x25_udata, 4);
546 newp->x25_udlen = 4;
547 }
548 ocp = (caddr_t) oldp->xaddr_userdata;
549 ncp = newp->x25_udata + 4;
550 while (*ocp && ocp < (caddr_t) oldp->xaddr_userdata + 12) {
551 if (newp->x25_udlen == 0)
552 newp->x25_udlen = 4;
553 *ncp++ = *ocp++;
554 newp->x25_udlen++;
555 }
556 bcopy((caddr_t) newp, mtod(m, char *), sizeof(*newp));
557 m->m_len = sizeof(*newp);
558 }
559
560 /*
561 * Do an in-place conversion of a new style
562 * socket address to the old style
563 */
564
565 static void
566 new_to_old(m)
567 struct mbuf *m;
568 {
569 struct x25_sockaddr *oldp;
570 struct sockaddr_x25 *newp;
571 char *ocp, *ncp;
572 struct x25_sockaddr old;
573
574 oldp = &old;
575 newp = mtod(m, struct sockaddr_x25 *);
576 bzero((caddr_t) oldp, sizeof(*oldp));
577
578 oldp->xaddr_facilities = newp->x25_opts.op_flags & X25_REVERSE_CHARGE;
579 if (newp->x25_opts.op_psize == X25_PS128)
580 oldp->xaddr_facilities |= XS_HIPRIO; /* Datapac specific */
581 ocp = (char *) oldp->xaddr_addr;
582 ncp = newp->x25_addr;
583 while (*ncp) {
584 *ocp++ = *ncp++;
585 oldp->xaddr_len++;
586 }
587
588 bcopy(newp->x25_udata, (caddr_t) oldp->xaddr_proto, 4);
589 if (newp->x25_udlen > 4)
590 bcopy(newp->x25_udata + 4, (caddr_t) oldp->xaddr_userdata,
591 (unsigned) (newp->x25_udlen - 4));
592
593 bcopy((caddr_t) oldp, mtod(m, char *), sizeof(*oldp));
594 m->m_len = sizeof(*oldp);
595 }
596
597
598 int
599 pk_checksockaddr(m)
600 struct mbuf *m;
601 {
602 struct sockaddr_x25 *sa = mtod(m, struct sockaddr_x25 *);
603 char *cp;
604
605 if (m->m_len != sizeof(struct sockaddr_x25))
606 return (1);
607 if (sa->x25_family != AF_CCITT ||
608 sa->x25_udlen > sizeof(sa->x25_udata))
609 return (1);
610 for (cp = sa->x25_addr; *cp; cp++) {
611 if (*cp < '' || *cp > '9' ||
612 cp >= &sa->x25_addr[sizeof(sa->x25_addr) - 1])
613 return (1);
614 }
615 return (0);
616 }
617
618 int
619 pk_send(m, v)
620 struct mbuf *m;
621 void *v;
622 {
623 struct pklcd *lcp = v;
624 int mqbit = 0, error = 0;
625 struct x25_packet *xp;
626 struct socket *so;
627
628 if (m->m_type == MT_OOBDATA) {
629 if (lcp->lcd_intrconf_pending)
630 error = ETOOMANYREFS;
631 if (m->m_pkthdr.len > 32)
632 error = EMSGSIZE;
633 M_PREPEND(m, PKHEADERLN, M_WAITOK);
634 if (m == 0 || error)
635 goto bad;
636 *(mtod(m, octet *)) = 0;
637 xp = mtod(m, struct x25_packet *);
638 X25SBITS(xp->bits, fmt_identifier, 1);
639 xp->packet_type = X25_INTERRUPT;
640 SET_LCN(xp, lcp->lcd_lcn);
641 sbinsertoob((so = lcp->lcd_so) ?
642 &so->so_snd : &lcp->lcd_sb, m);
643 goto send;
644 }
645 /*
646 * Application has elected (at call setup time) to prepend
647 * a control byte to each packet written indicating m-bit
648 * and q-bit status. Examine and then discard this byte.
649 */
650 if (lcp->lcd_flags & X25_MQBIT) {
651 if (m->m_len < 1) {
652 m_freem(m);
653 return (EMSGSIZE);
654 }
655 mqbit = *(mtod(m, u_char *));
656 m->m_len--;
657 m->m_data++;
658 m->m_pkthdr.len--;
659 }
660 error = pk_fragment(lcp, m, mqbit & 0x80, mqbit & 0x40, 1);
661 send:
662 if (error == 0 && lcp->lcd_state == DATA_TRANSFER)
663 lcp->lcd_send(lcp); /* XXXXXXXXX fix pk_output!!! */
664 return (error);
665 bad:
666 if (m)
667 m_freem(m);
668 return (error);
669 }
Cache object: 61cb7d2a52909428bb9dd40b392d2434
|