636 lines
16 KiB
C
636 lines
16 KiB
C
/* $NetBSD: pk_usrreq.c,v 1.23 2002/07/03 21:39:42 thorpej Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1984 University of British Columbia.
|
|
* Copyright (c) 1992 Computer Science Department IV,
|
|
* University of Erlangen-Nuremberg, Germany.
|
|
* Copyright (c) 1991, 1992, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to Berkeley by the
|
|
* Laboratory for Computation Vision and the Computer Science Department
|
|
* of the University of British Columbia and the Computer Science
|
|
* Department (IV) of the University of Erlangen-Nuremberg, Germany.
|
|
*
|
|
* 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.
|
|
*
|
|
* 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 8.2 (Berkeley) 1/9/95
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: pk_usrreq.c,v 1.23 2002/07/03 21:39:42 thorpej Exp $");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/socketvar.h>
|
|
#include <sys/protosw.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/proc.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/if_types.h>
|
|
#include <net/route.h>
|
|
|
|
#include <netccitt/x25.h>
|
|
#include <netccitt/pk.h>
|
|
#include <netccitt/pk_var.h>
|
|
#include <netccitt/pk_extern.h>
|
|
|
|
static void pk_setsockaddr __P((struct pklcd *, struct mbuf *));
|
|
static void pk_setpeeraddr __P((struct pklcd *, struct mbuf *));
|
|
static void old_to_new __P((struct mbuf *));
|
|
static void new_to_old __P((struct mbuf *));
|
|
|
|
static void
|
|
pk_setsockaddr(lcp, nam)
|
|
struct pklcd *lcp;
|
|
struct mbuf *nam;
|
|
{
|
|
|
|
nam->m_len = sizeof(struct sockaddr_x25);
|
|
bcopy(lcp->lcd_ceaddr, mtod(nam, caddr_t), (size_t)nam->m_len);
|
|
if (lcp->lcd_flags & X25_OLDSOCKADDR)
|
|
new_to_old(nam);
|
|
}
|
|
|
|
static void
|
|
pk_setpeeraddr(lcp, nam)
|
|
struct pklcd *lcp;
|
|
struct mbuf *nam;
|
|
{
|
|
|
|
nam->m_len = sizeof(struct sockaddr_x25);
|
|
bcopy(lcp->lcd_craddr, mtod(nam, caddr_t), (size_t)nam->m_len);
|
|
if (lcp->lcd_flags & X25_OLDSOCKADDR)
|
|
new_to_old(nam);
|
|
}
|
|
|
|
/*
|
|
*
|
|
* X.25 Packet level protocol interface to socket abstraction.
|
|
*
|
|
* Process an X.25 user request on a logical channel. If this is a send
|
|
* request then m is the mbuf chain of the send data. If this is a timer
|
|
* expiration (called from the software clock routine) them timertype is
|
|
* the particular timer.
|
|
*
|
|
*/
|
|
int
|
|
pk_usrreq(so, req, m, nam, control, p)
|
|
struct socket *so;
|
|
int req;
|
|
struct mbuf *m, *nam, *control;
|
|
struct proc *p;
|
|
{
|
|
struct pklcd *lcp;
|
|
int s;
|
|
int error = 0;
|
|
|
|
if (req == PRU_CONTROL)
|
|
return (pk_control(so, (long)m, (caddr_t)nam,
|
|
(struct ifnet *)control, p));
|
|
|
|
s = splsoftnet();
|
|
lcp = (struct pklcd *)so->so_pcb;
|
|
#ifdef DIAGNOSTIC
|
|
if (req != PRU_SEND && req != PRU_SENDOOB && control)
|
|
panic("pk_usrreq: unexpected control mbuf");
|
|
#endif
|
|
if (lcp == 0 && req != PRU_ATTACH) {
|
|
error = EINVAL;
|
|
goto release;
|
|
}
|
|
|
|
/*
|
|
pk_trace (pkcbhead, TR_USER, (struct pklcd *)0,
|
|
req, (struct x25_packet *)0);
|
|
*/
|
|
switch (req) {
|
|
|
|
/*
|
|
* X.25 attaches to socket via PRU_ATTACH and allocates a
|
|
* logical channel descriptor. If the socket is to receive
|
|
* connections, then the LISTEN state is entered.
|
|
*/
|
|
case PRU_ATTACH:
|
|
if (lcp != 0) {
|
|
error = EISCONN;
|
|
break;
|
|
}
|
|
lcp = pk_attach(so);
|
|
if (lcp == 0)
|
|
error = ENOBUFS;
|
|
break;
|
|
|
|
/*
|
|
* Detach a logical channel from the socket. If the state of
|
|
* the channel is embryonic, simply discard it. Otherwise we
|
|
* have to initiate a PRU_DISCONNECT which will finish later.
|
|
*/
|
|
case PRU_DETACH:
|
|
pk_disconnect(lcp);
|
|
break;
|
|
|
|
/*
|
|
* Give the socket an address.
|
|
*/
|
|
case PRU_BIND:
|
|
if (nam->m_len == sizeof(struct x25_sockaddr))
|
|
old_to_new(nam);
|
|
error = pk_bind(lcp, nam);
|
|
break;
|
|
|
|
/*
|
|
* Prepare to accept connections.
|
|
*/
|
|
case PRU_LISTEN:
|
|
error = pk_listen(lcp);
|
|
break;
|
|
|
|
/*
|
|
* Initiate a CALL REQUEST to peer entity. Enter state
|
|
* SENT_CALL and mark the socket as connecting. Set timer
|
|
* waiting for CALL ACCEPT or CLEAR.
|
|
*/
|
|
case PRU_CONNECT:
|
|
if (nam->m_len == sizeof(struct x25_sockaddr))
|
|
old_to_new(nam);
|
|
if (pk_checksockaddr(nam)) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
error = pk_connect(lcp, mtod(nam, struct sockaddr_x25 *));
|
|
break;
|
|
|
|
case PRU_CONNECT2:
|
|
error = EOPNOTSUPP;
|
|
break;
|
|
|
|
/*
|
|
* Initiate a disconnect to peer entity via a CLEAR REQUEST
|
|
* packet. The socket will be disconnected when we receive a
|
|
* confirmation or a clear collision.
|
|
*/
|
|
case PRU_DISCONNECT:
|
|
pk_disconnect(lcp);
|
|
break;
|
|
|
|
/*
|
|
* Accept an INCOMING CALL. Most of the work has already been
|
|
* done by pk_input. Just return the callers address to the
|
|
* user.
|
|
*/
|
|
case PRU_ACCEPT:
|
|
if (lcp->lcd_craddr == NULL)
|
|
break;
|
|
pk_setpeeraddr(lcp, nam);
|
|
break;
|
|
|
|
case PRU_SHUTDOWN:
|
|
socantsendmore(so);
|
|
break;
|
|
|
|
/*
|
|
* After a receive, we should send a RR.
|
|
*/
|
|
case PRU_RCVD:
|
|
pk_flowcontrol(lcp, /* sbspace (&so -> so_rcv) <= */ 0, 1);
|
|
break;
|
|
|
|
/*
|
|
* Do send by placing data on the socket output queue.
|
|
*/
|
|
case PRU_SEND:
|
|
if (control) {
|
|
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)
|
|
break;
|
|
}
|
|
if (m)
|
|
error = pk_send(m, lcp);
|
|
break;
|
|
|
|
/*
|
|
* Abort a virtual circuit. For example all completed calls
|
|
* waiting acceptance.
|
|
*/
|
|
case PRU_ABORT:
|
|
pk_disconnect(lcp);
|
|
break;
|
|
|
|
case PRU_SENSE:
|
|
/*
|
|
* stat: don't bother with a blocksize.
|
|
*/
|
|
splx(s);
|
|
return (0);
|
|
|
|
/*
|
|
* Receive INTERRUPT packet.
|
|
*/
|
|
case PRU_RCVOOB:
|
|
if (so->so_options & SO_OOBINLINE) {
|
|
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;
|
|
SB_EMPTY_FIXUP(&so->so_rcv);
|
|
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;
|
|
}
|
|
m->m_len = 1;
|
|
*mtod(m, char *) = lcp->lcd_intrdata;
|
|
break;
|
|
|
|
/*
|
|
* Send INTERRUPT packet.
|
|
*/
|
|
case PRU_SENDOOB:
|
|
if (control) {
|
|
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)
|
|
break;
|
|
}
|
|
if (m) {
|
|
MCHTYPE(m, MT_OOBDATA);
|
|
error = pk_send(m, lcp);
|
|
}
|
|
break;
|
|
|
|
case PRU_SOCKADDR:
|
|
if (lcp->lcd_ceaddr == 0) {
|
|
error = EADDRNOTAVAIL;
|
|
break;
|
|
}
|
|
pk_setsockaddr(lcp, nam);
|
|
break;
|
|
|
|
case PRU_PEERADDR:
|
|
if (lcp->lcd_state != DATA_TRANSFER) {
|
|
error = ENOTCONN;
|
|
break;
|
|
}
|
|
pk_setpeeraddr(lcp, nam);
|
|
break;
|
|
|
|
default:
|
|
panic("pk_usrreq");
|
|
}
|
|
|
|
release:
|
|
splx(s);
|
|
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 ia ->
|
|
* ia_start when called with SIOCSIFCONF_X25.
|
|
*/
|
|
/* ARGSUSED */
|
|
int
|
|
pk_start(lcp)
|
|
struct pklcd *lcp;
|
|
{
|
|
pk_output(lcp);
|
|
return (0); /* XXX pk_output should return a value */
|
|
}
|
|
|
|
struct sockaddr_x25 pk_sockmask = {
|
|
offsetof(struct sockaddr_x25, x25_addr[0]), /* x25_len */
|
|
0, /* x25_family */
|
|
-1, /* x25_net id */
|
|
};
|
|
|
|
/* ARGSUSED */
|
|
int
|
|
pk_control(so, cmd, data, ifp, p)
|
|
struct socket *so;
|
|
u_long cmd;
|
|
caddr_t data;
|
|
struct ifnet *ifp;
|
|
struct proc *p;
|
|
{
|
|
struct ifreq_x25 *ifr = (struct ifreq_x25 *) data;
|
|
struct ifaddr *ifa = 0;
|
|
struct x25_ifaddr *ia = 0;
|
|
int error = 0, s, old_maxlcn;
|
|
|
|
/*
|
|
* Find address for this interface, if it exists.
|
|
*/
|
|
if (ifp)
|
|
for (ifa = ifp->if_addrlist.tqh_first; ifa != 0;
|
|
ifa = ifa->ifa_list.tqe_next)
|
|
if (ifa->ifa_addr->sa_family == AF_CCITT)
|
|
break;
|
|
|
|
ia = (struct x25_ifaddr *) ifa;
|
|
switch (cmd) {
|
|
case SIOCGIFCONF_X25:
|
|
if (ifa == 0)
|
|
return (EADDRNOTAVAIL);
|
|
ifr->ifr_xc = ia->ia_xc;
|
|
return (0);
|
|
|
|
case SIOCSIFCONF_X25:
|
|
if (p == 0 || (error = suser(p->p_ucred, &p->p_acflag)))
|
|
return (EPERM);
|
|
if (ifp == 0)
|
|
panic("pk_control");
|
|
if (ifa == (struct ifaddr *) 0) {
|
|
MALLOC(ia, struct x25_ifaddr *, sizeof(*ia),
|
|
M_IFADDR, M_WAITOK);
|
|
if (ia == 0)
|
|
return (ENOBUFS);
|
|
bzero((caddr_t) ia, sizeof(*ia));
|
|
TAILQ_INSERT_TAIL(&ifp->if_addrlist, &ia->ia_ifa,
|
|
ifa_list);
|
|
ifa = &ia->ia_ifa;
|
|
IFAREF(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 if (ISISO8802(ifp) == 0) {
|
|
rtinit(ifa, (int) RTM_DELETE, 0);
|
|
}
|
|
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 */
|
|
struct pkcb *pkp;
|
|
FOR_ALL_PKCBS(pkp)
|
|
if (pkp->pk_ia == ia)
|
|
pk_resize(pkp);
|
|
}
|
|
/*
|
|
* Give the interface a chance to initialize if this
|
|
p * is its first address, and to validate the address.
|
|
*/
|
|
ia->ia_start = pk_start;
|
|
s = splnet();
|
|
if (ifp->if_ioctl)
|
|
error = (*ifp->if_ioctl) (ifp, SIOCSIFCONF_X25,
|
|
(caddr_t) ifa);
|
|
if (error)
|
|
ifp->if_flags &= ~IFF_UP;
|
|
else if (ISISO8802(ifp) == 0)
|
|
error = rtinit(ifa, (int) RTM_ADD, RTF_UP);
|
|
splx(s);
|
|
return (error);
|
|
|
|
default:
|
|
if (ifp == 0 || ifp->if_ioctl == 0)
|
|
return (EOPNOTSUPP);
|
|
return ((*ifp->if_ioctl) (ifp, cmd, data));
|
|
}
|
|
}
|
|
|
|
int
|
|
pk_ctloutput(cmd, so, level, optname, mp)
|
|
struct socket *so;
|
|
struct mbuf **mp;
|
|
int cmd, level, optname;
|
|
{
|
|
struct proc *p = curproc; /* XXX */
|
|
struct mbuf *m = *mp;
|
|
struct pklcd *lcp = (struct pklcd *) so->so_pcb;
|
|
int error = EOPNOTSUPP;
|
|
|
|
if (m == 0)
|
|
return (EINVAL);
|
|
if (cmd == PRCO_SETOPT)
|
|
switch (optname) {
|
|
case PK_FACILITIES:
|
|
if (m == 0)
|
|
return (EINVAL);
|
|
lcp->lcd_facilities = m;
|
|
*mp = 0;
|
|
return (0);
|
|
|
|
case PK_ACCTFILE:
|
|
if (p == 0 || (error = suser(p->p_ucred, &p->p_acflag)))
|
|
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) {
|
|
(void) m_freem(*mp);
|
|
*mp = 0;
|
|
}
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* Do an in-place conversion of an "old style"
|
|
* socket address to the new style
|
|
*/
|
|
|
|
static void
|
|
old_to_new(m)
|
|
struct mbuf *m;
|
|
{
|
|
struct x25_sockaddr *oldp;
|
|
struct sockaddr_x25 *newp;
|
|
char *ocp, *ncp;
|
|
struct sockaddr_x25 new;
|
|
|
|
oldp = mtod(m, struct x25_sockaddr *);
|
|
newp = &new;
|
|
bzero((caddr_t) newp, sizeof(*newp));
|
|
|
|
newp->x25_family = AF_CCITT;
|
|
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,
|
|
(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) {
|
|
if (newp->x25_udlen == 0)
|
|
newp->x25_udlen = 4;
|
|
*ncp++ = *ocp++;
|
|
newp->x25_udlen++;
|
|
}
|
|
bcopy((caddr_t) newp, mtod(m, char *), sizeof(*newp));
|
|
m->m_len = sizeof(*newp);
|
|
}
|
|
|
|
/*
|
|
* Do an in-place conversion of a new style
|
|
* socket address to the old style
|
|
*/
|
|
|
|
static void
|
|
new_to_old(m)
|
|
struct mbuf *m;
|
|
{
|
|
struct x25_sockaddr *oldp;
|
|
struct sockaddr_x25 *newp;
|
|
char *ocp, *ncp;
|
|
struct x25_sockaddr old;
|
|
|
|
oldp = &old;
|
|
newp = mtod(m, struct sockaddr_x25 *);
|
|
bzero((caddr_t) oldp, sizeof(*oldp));
|
|
|
|
oldp->xaddr_facilities = newp->x25_opts.op_flags & X25_REVERSE_CHARGE;
|
|
if (newp->x25_opts.op_psize == X25_PS128)
|
|
oldp->xaddr_facilities |= XS_HIPRIO; /* Datapac specific */
|
|
ocp = (char *) oldp->xaddr_addr;
|
|
ncp = newp->x25_addr;
|
|
while (*ncp) {
|
|
*ocp++ = *ncp++;
|
|
oldp->xaddr_len++;
|
|
}
|
|
|
|
bcopy(newp->x25_udata, (caddr_t) oldp->xaddr_proto, 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);
|
|
}
|
|
|
|
|
|
int
|
|
pk_checksockaddr(m)
|
|
struct mbuf *m;
|
|
{
|
|
struct sockaddr_x25 *sa = mtod(m, struct sockaddr_x25 *);
|
|
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);
|
|
}
|
|
|
|
int
|
|
pk_send(m, v)
|
|
struct mbuf *m;
|
|
void *v;
|
|
{
|
|
struct pklcd *lcp = v;
|
|
int mqbit = 0, error = 0;
|
|
struct x25_packet *xp;
|
|
struct socket *so;
|
|
|
|
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 *);
|
|
X25SBITS(xp->bits, 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) {
|
|
if (m->m_len < 1) {
|
|
m_freem(m);
|
|
return (EMSGSIZE);
|
|
}
|
|
mqbit = *(mtod(m, u_char *));
|
|
m->m_len--;
|
|
m->m_data++;
|
|
m->m_pkthdr.len--;
|
|
}
|
|
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);
|
|
}
|