BSD 4_3_Net_2 release
[unix-history] / usr / src / sys / netccitt / pk_usrreq.c
index aa92376..7bfcd0a 100644 (file)
@@ -7,30 +7,54 @@
  * the Laboratory for Computation Vision and the Computer Science Department
  * of the University of British Columbia.
  *
  * the Laboratory for Computation Vision and the Computer Science Department
  * of the University of British Columbia.
  *
- * %sccs.include.redist.c%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
  *
  *
- *     @(#)pk_usrreq.c 7.7 (Berkeley) %G%
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)pk_usrreq.c 7.16 (Berkeley) 6/27/91
  */
 
 #include "param.h"
 #include "systm.h"
 #include "mbuf.h"
 #include "socket.h"
  */
 
 #include "param.h"
 #include "systm.h"
 #include "mbuf.h"
 #include "socket.h"
-#include "protosw.h"
 #include "socketvar.h"
 #include "socketvar.h"
+#include "protosw.h"
 #include "errno.h"
 #include "ioctl.h"
 #include "errno.h"
 #include "ioctl.h"
-#include "user.h"
 #include "stat.h"
 
 #include "../net/if.h"
 #include "stat.h"
 
 #include "../net/if.h"
+#include "../net/route.h"
 
 #include "x25.h"
 #include "pk.h"
 #include "pk_var.h"
 
 
 #include "x25.h"
 #include "pk.h"
 #include "pk_var.h"
 
-struct x25_packet *pk_template ();
-
 /*
  * 
  *  X.25 Packet level protocol interface to socket abstraction.
 /*
  * 
  *  X.25 Packet level protocol interface to socket abstraction.
@@ -49,13 +73,12 @@ register struct mbuf *m, *nam;
 struct mbuf *control;
 {
        register struct pklcd *lcp = (struct pklcd *) so -> so_pcb;
 struct mbuf *control;
 {
        register struct pklcd *lcp = (struct pklcd *) so -> so_pcb;
-       register struct x25_packet *xp;
        register int error = 0;
 
        if (req == PRU_CONTROL)
        register int error = 0;
 
        if (req == PRU_CONTROL)
-               return (pk_control(so, (int)m, (caddr_t)nam,
+               return (pk_control (so, (int)m, (caddr_t)nam,
                        (struct ifnet *)control));
                        (struct ifnet *)control));
-       if (control && control->m_len) {
+       if (control && control -> m_len) {
                error = EINVAL;
                goto release;
        }
                error = EINVAL;
                goto release;
        }
@@ -108,13 +131,7 @@ struct mbuf *control;
         *  Prepare to accept connections.
         */
        case PRU_LISTEN: 
         *  Prepare to accept connections.
         */
        case PRU_LISTEN: 
-               if (lcp -> lcd_ceaddr == 0) {
-                       error = EDESTADDRREQ;
-                       break;
-               }
-               lcp -> lcd_state = LISTEN;
-               lcp -> lcd_listen = pk_listenhead;
-               pk_listenhead = lcp;
+               error = pk_listen (lcp);
                break;
 
        /* 
                break;
 
        /* 
@@ -125,7 +142,9 @@ struct mbuf *control;
        case PRU_CONNECT: 
                if (nam -> m_len == sizeof (struct x25_sockaddr))
                        old_to_new (nam);
        case PRU_CONNECT: 
                if (nam -> m_len == sizeof (struct x25_sockaddr))
                        old_to_new (nam);
-               error = pk_connect (lcp, nam, (struct sockaddr_25 *)0);
+               if (pk_checksockaddr (nam))
+                       return (EINVAL);
+               error = pk_connect (lcp, mtod (nam, struct sockaddr_x25 *));
                break;
 
        /* 
                break;
 
        /* 
@@ -155,17 +174,39 @@ struct mbuf *control;
         *  After a receive, we should send a RR.
         */
        case PRU_RCVD: 
         *  After a receive, we should send a RR.
         */
        case PRU_RCVD: 
-               lcp -> lcd_rxcnt++;
-               lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_RR);
-               pk_output (lcp);
+               pk_flowcontrol (lcp, /*sbspace (&so -> so_rcv) <= */ 0, 1);
                break;
 
                break;
 
+       /* 
+        *  Send INTERRUPT packet.
+        */
+       case PRU_SENDOOB: 
+               if (m == 0) {
+                       MGETHDR(m, M_WAITOK, MT_OOBDATA);
+                       m -> m_pkthdr.len = m -> m_len = 1;
+                       *mtod (m, octet *) = 0;
+               }
+               if (m -> m_pkthdr.len > 32) {
+                       m_freem (m);
+                       error = EMSGSIZE;
+                       break;
+               }
+               MCHTYPE(m, MT_OOBDATA);
+               /* FALLTHROUGH */
+
        /* 
         *  Do send by placing data on the socket output queue.
        /* 
         *  Do send by placing data on the socket output queue.
-        *  SHOULD WE USE m_cat HERE.
         */
        case PRU_SEND: 
         */
        case PRU_SEND: 
-               error = pk_send (lcp, m);
+               if (control) {
+                       register struct cmsghdr *ch = mtod (m, struct cmsghdr *);
+                       control -> m_len -= sizeof (*ch);
+                       control -> m_data += sizeof (*ch);
+                       error = pk_ctloutput (PRCO_SETOPT, so, ch -> cmsg_level,
+                                       ch -> cmsg_type, &control);
+               }
+               if (error == 0 && m)
+                       error = pk_send (lcp, m);
                break;
 
        /* 
                break;
 
        /* 
@@ -221,24 +262,22 @@ struct mbuf *control;
         *  Receive INTERRUPT packet.
         */
        case PRU_RCVOOB: 
         *  Receive INTERRUPT packet.
         */
        case PRU_RCVOOB: 
-               m -> m_len = 1;
-               *mtod (m, char *) = lcp -> lcd_intrdata;
-               break;
-
-       /* 
-        *  Send INTERRUPT packet.
-        */
-       case PRU_SENDOOB: 
-               m_freem (m);
-               if (lcp -> lcd_intrconf_pending) {
-                       error = ETOOMANYREFS;
+               if (so -> so_options & SO_OOBINLINE) {
+                       register struct mbuf *n  = so -> so_rcv.sb_mb;
+                       if (n && n -> m_type == MT_OOBDATA) {
+                               unsigned len =  n -> m_pkthdr.len;
+                               so -> so_rcv.sb_mb = n -> m_nextpkt;
+                               if (len !=  n -> m_len &&
+                                   (n = m_pullup (n, len)) == 0)
+                                       break;
+                               m -> m_len = len;
+                               bcopy (mtod (m, caddr_t), mtod (n, caddr_t), len);
+                               m_freem (n);
+                       }
                        break;
                }
                        break;
                }
-               lcp -> lcd_intrcnt++;
-               xp = lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_INTERRUPT);
-               xp -> packet_data = 0;
-               (dtom (xp)) -> m_len++;
-               pk_output (lcp);
+               m -> m_len = 1;
+               *mtod (m, char *) = lcp -> lcd_intrdata;
                break;
 
        default: 
                break;
 
        default: 
@@ -246,27 +285,30 @@ struct mbuf *control;
        }
 release:
        if (control != NULL)
        }
 release:
        if (control != NULL)
-               m_freem(control);
-       if (m != NULL)
-               m_freem(m);
+               m_freem (control);
        return (error);
 }
 
 /* 
  * If you want to use UBC X.25 level 3 in conjunction with some
        return (error);
 }
 
 /* 
  * If you want to use UBC X.25 level 3 in conjunction with some
- * other X.25 level 2 driver, have the ifp->if_ioctl routine
- * assign pk_start to pkp -> pk_start when called with SIOCSIFCONF_X25.
+ * other X.25 level 2 driver, have the ifp -> if_ioctl routine
+ * assign pk_start to ia -> ia_start when called with SIOCSIFCONF_X25.
  */
 /* ARGSUSED */
 pk_start (lcp)
 register struct pklcd *lcp;
 {
  */
 /* ARGSUSED */
 pk_start (lcp)
 register struct pklcd *lcp;
 {
-       extern int pk_send();
-
-       lcp -> lcd_send = pk_send;
-       return (pk_output(lcp));
+       pk_output (lcp);
+       return (0); /* XXX pk_output should return a value */
 }
 
 }
 
+#ifndef _offsetof
+#define _offsetof(t, m) ((int)((caddr_t)&((t *)0)->m))
+#endif
+struct sockaddr_x25 pk_sockmask = {
+_offsetof(struct sockaddr_x25, x25_addr[0]),
+0, -1};
+
 /*ARGSUSED*/
 pk_control (so, cmd, data, ifp)
 struct socket *so;
 /*ARGSUSED*/
 pk_control (so, cmd, data, ifp)
 struct socket *so;
@@ -278,15 +320,15 @@ register struct ifnet *ifp;
        register struct ifaddr *ifa = 0;
        register struct x25_ifaddr *ia = 0;
        struct pklcd *dev_lcp = 0;
        register struct ifaddr *ifa = 0;
        register struct x25_ifaddr *ia = 0;
        struct pklcd *dev_lcp = 0;
-       int error, s;
+       int error, s, old_maxlcn;
        unsigned n;
 
        /*
         * Find address for this interface, if it exists.
         */
        if (ifp)
        unsigned n;
 
        /*
         * Find address for this interface, if it exists.
         */
        if (ifp)
-               for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
-                       if (ifa->ifa_addr->sa_family == AF_CCITT)
+               for (ifa = ifp -> if_addrlist; ifa; ifa = ifa -> ifa_next)
+                       if (ifa -> ifa_addr -> sa_family == AF_CCITT)
                                break;
 
        ia = (struct x25_ifaddr *)ifa;
                                break;
 
        ia = (struct x25_ifaddr *)ifa;
@@ -294,14 +336,14 @@ register struct ifnet *ifp;
        case SIOCGIFCONF_X25:
                if (ifa == 0)
                        return (EADDRNOTAVAIL);
        case SIOCGIFCONF_X25:
                if (ifa == 0)
                        return (EADDRNOTAVAIL);
-               ifr->ifr_xc = ia->ia_xc;
+               ifr -> ifr_xc = ia -> ia_xc;
                return (0);
 
        case SIOCSIFCONF_X25:
                return (0);
 
        case SIOCSIFCONF_X25:
-               if (error = suser(u.u_cred, &u.u_acflag))
-                       return (error);
+               if ((so->so_state & SS_PRIV) == 0)
+                       return (EPERM);
                if (ifp == 0)
                if (ifp == 0)
-                       panic("pk_control");
+                       panic ("pk_control");
                if (ifa == (struct ifaddr *)0) {
                        register struct mbuf *m;
 
                if (ifa == (struct ifaddr *)0) {
                        register struct mbuf *m;
 
@@ -309,91 +351,99 @@ register struct ifnet *ifp;
                                M_IFADDR, M_WAITOK);
                        if (ia == 0)
                                return (ENOBUFS);
                                M_IFADDR, M_WAITOK);
                        if (ia == 0)
                                return (ENOBUFS);
-                       if (ifa = ifp->if_addrlist) {
-                               for ( ; ifa->ifa_next; ifa = ifa->ifa_next)
+                       bzero ((caddr_t)ia, sizeof (*ia));
+                       if (ifa = ifp -> if_addrlist) {
+                               for ( ; ifa -> ifa_next; ifa = ifa -> ifa_next)
                                        ;
                                        ;
-                               ifa->ifa_next = &ia->ia_ifa;
+                               ifa -> ifa_next = &ia -> ia_ifa;
                        } else
                        } else
-                               ifp->if_addrlist = &ia->ia_ifa;
-                       ifa = &ia->ia_ifa;
-                       ifa->ifa_netmask = (struct sockaddr *)&ia->ia_sockmask;
-                       ifa->ifa_addr = (struct sockaddr *)&ia->ia_xc.xc_addr;
-                       ia->ia_ifp = ifp;
-                       ia->ia_pkcb.pk_ia = ia;
-                       ia->ia_pkcb.pk_next = pkcbhead;
-                       pkcbhead = &ia->ia_pkcb;
+                               ifp -> if_addrlist = &ia -> ia_ifa;
+                       ifa = &ia -> ia_ifa;
+                       ifa -> ifa_netmask = (struct sockaddr *)&pk_sockmask;
+                       ifa -> ifa_addr = (struct sockaddr *)&ia -> ia_xc.xc_addr;
+                       ifa -> ifa_dstaddr = (struct sockaddr *)&ia -> ia_dstaddr; /* XXX */
+                       ia -> ia_ifp = ifp;
+                       ia -> ia_dstaddr.x25_family = AF_CCITT;
+                       ia -> ia_dstaddr.x25_len = pk_sockmask.x25_len;
+               } else {
+                       rtinit (ifa, (int)RTM_DELETE, 0);
                }
                }
-               ia->ia_xcp = &(ifr->ifr_xc);
-               if (ia->ia_chan && (ia->ia_maxlcn != ia->ia_xcp->xc_maxlcn)) {
-                       pk_restart(&ia->ia_pkcb, X25_RESTART_NETWORK_CONGESTION);
-                       dev_lcp = ia->ia_chan[0];
-                       free((caddr_t)ia->ia_chan, M_IFADDR);
-                       ia->ia_chan = 0;
-               }
-               if (ia->ia_chan == 0) {
-                       n = ia->ia_maxlcn * sizeof(struct pklcd *);
-                       ia->ia_chan = (struct pklcd **) malloc(n, M_IFADDR);
-                       if (ia->ia_chan) {
-                               bzero((caddr_t)ia->ia_chan, n);
-                               if (dev_lcp == 0)
-                                       dev_lcp = pk_attach((struct socket *)0);
-                               ia->ia_chan[0] = dev_lcp;
-                       } else {
-                               if (dev_lcp)
-                                       pk_close(dev_lcp);
-                               ia->ia_xcp = &ia->ia_xc;
-                               return (ENOBUFS);
-                       }
+               old_maxlcn = ia -> ia_maxlcn;
+               ia -> ia_xc = ifr -> ifr_xc;
+               ia -> ia_dstaddr.x25_net = ia -> ia_xc.xc_addr.x25_net;
+               if (ia -> ia_maxlcn != old_maxlcn && old_maxlcn != 0) {
+                       /* VERY messy XXX */
+                       register struct pkcb *pkp;
+                       for (pkp = pkcbhead; pkp; pkp = pkp -> pk_next)
+                               if (pkp -> pk_ia == ia)
+                                       pk_resize (pkp);
                }
                /*
                 * Give the interface a chance to initialize if this
                 * is its first address, and to validate the address.
                 */
                }
                /*
                 * Give the interface a chance to initialize if this
                 * is its first address, and to validate the address.
                 */
+               ia -> ia_start = pk_start;
                s = splimp();
                s = splimp();
-               if (ifp->if_ioctl)
-                       error = (*ifp->if_ioctl)(ifp, SIOCSIFCONF_X25, ifa);
-               splx(s);
-               if (error == 0) {
-                       ia->ia_xc = *ia->ia_xcp;
-               }
-               ia->ia_xcp = &ia->ia_xc;
+               if (ifp -> if_ioctl)
+                       error = (*ifp -> if_ioctl)(ifp, SIOCSIFCONF_X25, ifa);
+               if (error)
+                       ifp -> if_flags &= ~IFF_UP;
+               else
+                       error = rtinit (ifa, (int)RTM_ADD, RTF_UP);
+               splx (s);
                return (error);
 
        default:
                return (error);
 
        default:
-               if (ifp == 0 || ifp->if_ioctl == 0)
+               if (ifp == 0 || ifp -> if_ioctl == 0)
                        return (EOPNOTSUPP);
                        return (EOPNOTSUPP);
-               return ((*ifp->if_ioctl)(ifp, cmd, data));
+               return ((*ifp -> if_ioctl)(ifp, cmd, data));
        }
 }
 
        }
 }
 
-pk_ctloutput(cmd, so, level, optname, mp)
+pk_ctloutput (cmd, so, level, optname, mp)
 struct socket *so;
 struct mbuf **mp;
 int cmd, level, optname;
 {
        register struct mbuf *m = *mp;
 struct socket *so;
 struct mbuf **mp;
 int cmd, level, optname;
 {
        register struct mbuf *m = *mp;
-       int error;
+       register struct pklcd *lcp = (struct pklcd *) so -> so_pcb;
+       int error = EOPNOTSUPP;
 
 
+       if (m == 0)
+               return (EINVAL);
        if (cmd == PRCO_SETOPT) switch (optname) {
        if (cmd == PRCO_SETOPT) switch (optname) {
-       case PK_ACCTFILE:
+       case PK_FACILITIES:
                if (m == 0)
                        return (EINVAL);
                if (m == 0)
                        return (EINVAL);
-               if (m->m_len)
-                       error = pk_accton(mtod(m, char *));
-               else
-                       error = pk_accton((char *)0);
-               (void) m_freem(m);
+               lcp -> lcd_facilities = m;
                *mp = 0;
                *mp = 0;
-               return (error);
+               return (0);
+
+       case PK_ACCTFILE:
+               if ((so->so_state & SS_PRIV) == 0)
+                       error = EPERM;
+               else if (m -> m_len)
+                       error = pk_accton (mtod (m, char *));
+               else
+                       error = pk_accton ((char *)0);
+               break;
+
+       case PK_RTATTACH:
+               error = pk_rtattach (so, m);
+               break;
+           
+       case PK_PRLISTEN:
+               error = pk_user_protolisten (mtod (m, u_char *));
        }
        if (*mp) {
        }
        if (*mp) {
-               (void) m_freem(*mp);
+               (void) m_freem (*mp);
                *mp = 0;
        }
                *mp = 0;
        }
-       return (EOPNOTSUPP);
+       return (error);
 
 }
 
 
 }
 
+
 /*
  * Do an in-place conversion of an "old style"
  * socket address to the new style
 /*
  * Do an in-place conversion of an "old style"
  * socket address to the new style
@@ -413,24 +463,27 @@ register struct mbuf *m;
        bzero ((caddr_t)newp, sizeof (*newp));
 
        newp -> x25_family = AF_CCITT;
        bzero ((caddr_t)newp, sizeof (*newp));
 
        newp -> x25_family = AF_CCITT;
-       newp->x25_opts.op_flags = (oldp->xaddr_facilities & X25_REVERSE_CHARGE)
+       newp -> x25_len = sizeof(*newp);
+       newp -> x25_opts.op_flags = (oldp -> xaddr_facilities & X25_REVERSE_CHARGE)
                | X25_MQBIT | X25_OLDSOCKADDR;
        if (oldp -> xaddr_facilities & XS_HIPRIO)       /* Datapac specific */
                newp -> x25_opts.op_psize = X25_PS128;
        bcopy ((caddr_t)oldp -> xaddr_addr, newp -> x25_addr,
                | X25_MQBIT | X25_OLDSOCKADDR;
        if (oldp -> xaddr_facilities & XS_HIPRIO)       /* Datapac specific */
                newp -> x25_opts.op_psize = X25_PS128;
        bcopy ((caddr_t)oldp -> xaddr_addr, newp -> x25_addr,
-               (unsigned)min (oldp -> xaddr_len, sizeof (newp -> x25_addr) - 1));
-       bcopy ((caddr_t)oldp -> xaddr_proto, newp -> x25_udata, 4);
-       newp -> x25_udlen = 4;
-
+              (unsigned)min (oldp -> xaddr_len, sizeof (newp -> x25_addr) - 1));
+       if (bcmp ((caddr_t)oldp -> xaddr_proto, newp -> x25_udata, 4) != 0) {
+               bcopy ((caddr_t)oldp -> xaddr_proto, newp -> x25_udata, 4);
+               newp -> x25_udlen = 4;
+       }
        ocp = (caddr_t)oldp -> xaddr_userdata;
        ncp = newp -> x25_udata + 4;
        while (*ocp && ocp < (caddr_t)oldp -> xaddr_userdata + 12) {
        ocp = (caddr_t)oldp -> xaddr_userdata;
        ncp = newp -> x25_udata + 4;
        while (*ocp && ocp < (caddr_t)oldp -> xaddr_userdata + 12) {
+               if (newp -> x25_udlen == 0)
+                       newp -> x25_udlen = 4;
                *ncp++ = *ocp++;
                newp -> x25_udlen++;
        }
                *ncp++ = *ocp++;
                newp -> x25_udlen++;
        }
-
        bcopy ((caddr_t)newp, mtod (m, char *), sizeof (*newp));
        bcopy ((caddr_t)newp, mtod (m, char *), sizeof (*newp));
-       m->m_len = sizeof (*newp);
+       m -> m_len = sizeof (*newp);
 }
 
 /*
 }
 
 /*
@@ -462,57 +515,81 @@ register struct mbuf *m;
        }
 
        bcopy (newp -> x25_udata, (caddr_t)oldp -> xaddr_proto, 4);
        }
 
        bcopy (newp -> x25_udata, (caddr_t)oldp -> xaddr_proto, 4);
-       bcopy (newp -> x25_udata + 4, (caddr_t)oldp -> xaddr_userdata,
-               (unsigned)(newp -> x25_udlen - 4));
+       if (newp -> x25_udlen > 4)
+               bcopy (newp -> x25_udata + 4, (caddr_t)oldp -> xaddr_userdata,
+                       (unsigned)(newp -> x25_udlen - 4));
 
        bcopy ((caddr_t)oldp, mtod (m, char *), sizeof (*oldp));
        m -> m_len = sizeof (*oldp);
 }
 
 
        bcopy ((caddr_t)oldp, mtod (m, char *), sizeof (*oldp));
        m -> m_len = sizeof (*oldp);
 }
 
+
+pk_checksockaddr (m)
+struct mbuf *m;
+{
+       register struct sockaddr_x25 *sa = mtod (m, struct sockaddr_x25 *);
+       register char *cp;
+
+       if (m -> m_len != sizeof (struct sockaddr_x25))
+               return (1);
+       if (sa -> x25_family != AF_CCITT ||
+               sa -> x25_udlen > sizeof (sa -> x25_udata))
+               return (1);
+       for (cp = sa -> x25_addr; *cp; cp++) {
+               if (*cp < '0' || *cp > '9' ||
+                       cp >= &sa -> x25_addr[sizeof (sa -> x25_addr) - 1])
+                       return (1);
+       }
+       return (0);
+}
+
 pk_send (lcp, m)
 pk_send (lcp, m)
-register struct pklcd *lcp;
+struct pklcd *lcp;
 register struct mbuf *m;
 {
 register struct mbuf *m;
 {
+       int mqbit = 0, error = 0;
        register struct x25_packet *xp;
        register struct x25_packet *xp;
-       register struct mbuf *m0;
-       register int len;
+       register struct socket *so;
 
 
-       m0 = dtom ((xp = pk_template (lcp -> lcd_lcn, X25_DATA)));
-       m0 -> m_next = m;
+       if (m -> m_type == MT_OOBDATA) {
+               if (lcp -> lcd_intrconf_pending)
+                       error = ETOOMANYREFS;
+               if (m -> m_pkthdr.len > 32)
+                       error = EMSGSIZE;
+               M_PREPEND(m, PKHEADERLN, M_WAITOK);
+               if (m == 0 || error)
+                       goto bad;
+               *(mtod (m, octet *)) = 0;
+               xp = mtod (m, struct x25_packet *);
+               xp -> fmt_identifier = 1;
+               xp -> packet_type = X25_INTERRUPT;
+               SET_LCN(xp, lcp -> lcd_lcn);
+               sbinsertoob ( (so = lcp -> lcd_so) ?
+                       &so -> so_snd : &lcp -> lcd_sb, m);
+               goto send;
+       }
        /*
         * Application has elected (at call setup time) to prepend
         * a control byte to each packet written indicating m-bit
         * and q-bit status.  Examine and then discard this byte.
         */
        if (lcp -> lcd_flags & X25_MQBIT) {
        /*
         * Application has elected (at call setup time) to prepend
         * a control byte to each packet written indicating m-bit
         * and q-bit status.  Examine and then discard this byte.
         */
        if (lcp -> lcd_flags & X25_MQBIT) {
-               register octet *cp;
-
                if (m -> m_len < 1) {
                if (m -> m_len < 1) {
-                       m_freem (m0);
+                       m_freem (m);
                        return (EMSGSIZE);
                }
                        return (EMSGSIZE);
                }
-               cp = mtod (m, octet *);
-               if (*cp & 0x80)                                 /* XXX */
-                       xp -> q_bit = 1;
-               xp -> packet_type |= (*cp & 0x40) >> 2;         /* XXX */
+               mqbit = *(mtod (m, u_char *));
                m -> m_len--;
                m -> m_data++;
                m -> m_len--;
                m -> m_data++;
+               m -> m_pkthdr.len--;
        }
        }
-       len = m -> m_len;
-       while (m -> m_next) {
-               m = m -> m_next;
-               len += m -> m_len;
-       }
-       if (len > (1 << lcp -> lcd_packetsize)) {
-               m_freem (m0);
-               return (EMSGSIZE);
-       }
-       if (lcp -> lcd_so)
-               sbappendrecord (&lcp -> lcd_so -> so_snd, m0);
-       else
-               sbappendrecord (&lcp -> lcd_sb, m0);
-       lcp -> lcd_template = 0;
-       lcp -> lcd_txcnt++;
-       pk_output (lcp);
-       return (0);
+       error = pk_fragment (lcp, m, mqbit & 0x80, mqbit & 0x40, 1);
+send:
+       if (error == 0 && lcp -> lcd_state == DATA_TRANSFER)
+               lcp -> lcd_send (lcp); /* XXXXXXXXX fix pk_output!!! */
+       return (error);
+bad:
+       if (m)
+               m_freem (m);
+       return (error);
 }
 }