summaryrefslogtreecommitdiffstats
path: root/sys/netccitt/pk_usrreq.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netccitt/pk_usrreq.c')
-rw-r--r--sys/netccitt/pk_usrreq.c605
1 files changed, 605 insertions, 0 deletions
diff --git a/sys/netccitt/pk_usrreq.c b/sys/netccitt/pk_usrreq.c
new file mode 100644
index 0000000..1d1c0e7
--- /dev/null
+++ b/sys/netccitt/pk_usrreq.c
@@ -0,0 +1,605 @@
+/*
+ * Copyright (c) University of British Columbia, 1984
+ * Copyright (C) Computer Science Department IV,
+ * University of Erlangen-Nuremberg, Germany, 1992
+ * 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 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.1 (Berkeley) 6/10/93
+ * $Id$
+ */
+
+#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 <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>
+
+static old_to_new();
+static new_to_old();
+/*
+ *
+ * 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.
+ *
+ */
+
+pk_usrreq (so, req, m, nam, control)
+struct socket *so;
+int req;
+register struct mbuf *m, *nam;
+struct mbuf *control;
+{
+ register struct pklcd *lcp = (struct pklcd *) so -> so_pcb;
+ register int error = 0;
+
+ if (req == PRU_CONTROL)
+ return (pk_control (so, (int)m, (caddr_t)nam,
+ (struct ifnet *)control));
+ if (control && control -> m_len) {
+ error = EINVAL;
+ goto release;
+ }
+ if (lcp == NULL && 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) {
+ error = EISCONN;
+ /* Socket already connected. */
+ 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))
+ return (EINVAL);
+ error = pk_connect (lcp, mtod (nam, struct sockaddr_x25 *));
+ 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;
+ bcopy ((caddr_t)lcp -> lcd_craddr, mtod (nam, caddr_t),
+ sizeof (struct sockaddr_x25));
+ nam -> m_len = sizeof (struct sockaddr_x25);
+ if (lcp -> lcd_flags & X25_OLDSOCKADDR)
+ new_to_old (nam);
+ break;
+
+ /*
+ * After a receive, we should send a RR.
+ */
+ case PRU_RCVD:
+ pk_flowcontrol (lcp, /*sbspace (&so -> so_rcv) <= */ 0, 1);
+ 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.
+ */
+ case PRU_SEND:
+ 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;
+
+ /*
+ * Abort a virtual circuit. For example all completed calls
+ * waiting acceptance.
+ */
+ case PRU_ABORT:
+ pk_disconnect (lcp);
+ break;
+
+ /* Begin unimplemented hooks. */
+
+ case PRU_SHUTDOWN:
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_CONTROL:
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_SENSE:
+#ifdef BSD4_3
+ ((struct stat *)m) -> st_blksize = so -> so_snd.sb_hiwat;
+#else
+ error = EOPNOTSUPP;
+#endif
+ break;
+
+ /* End unimplemented hooks. */
+
+ case PRU_SOCKADDR:
+ if (lcp -> lcd_ceaddr == 0)
+ return (EADDRNOTAVAIL);
+ nam -> m_len = sizeof (struct sockaddr_x25);
+ bcopy ((caddr_t)lcp -> lcd_ceaddr, mtod (nam, caddr_t),
+ sizeof (struct sockaddr_x25));
+ if (lcp -> lcd_flags & X25_OLDSOCKADDR)
+ new_to_old (nam);
+ break;
+
+ case PRU_PEERADDR:
+ if (lcp -> lcd_state != DATA_TRANSFER)
+ return (ENOTCONN);
+ nam -> m_len = sizeof (struct sockaddr_x25);
+ bcopy (lcp -> lcd_craddr ? (caddr_t)lcp -> lcd_craddr :
+ (caddr_t)lcp -> lcd_ceaddr,
+ mtod (nam, caddr_t), sizeof (struct sockaddr_x25));
+ if (lcp -> lcd_flags & X25_OLDSOCKADDR)
+ new_to_old (nam);
+ break;
+
+ /*
+ * Receive INTERRUPT packet.
+ */
+ case PRU_RCVOOB:
+ 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;
+ }
+ m -> m_len = 1;
+ *mtod (m, char *) = lcp -> lcd_intrdata;
+ break;
+
+ default:
+ panic ("pk_usrreq");
+ }
+release:
+ if (control != NULL)
+ m_freem (control);
+ 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 */
+pk_start (lcp)
+register struct pklcd *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]), /* x25_len */
+ 0, /* x25_family */
+ -1, /* x25_net id */
+};
+
+/*ARGSUSED*/
+pk_control (so, cmd, data, ifp)
+struct socket *so;
+int cmd;
+caddr_t data;
+register struct ifnet *ifp;
+{
+ register struct ifreq_x25 *ifr = (struct ifreq_x25 *)data;
+ register struct ifaddr *ifa = 0;
+ register struct x25_ifaddr *ia = 0;
+ struct pklcd *dev_lcp = 0;
+ int error, s, old_maxlcn;
+ 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)
+ 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 ((so->so_state & SS_PRIV) == 0)
+ return (EPERM);
+ if (ifp == 0)
+ panic ("pk_control");
+ if (ifa == (struct ifaddr *)0) {
+ register struct mbuf *m;
+
+ MALLOC(ia, struct x25_ifaddr *, sizeof (*ia),
+ M_IFADDR, M_WAITOK);
+ if (ia == 0)
+ return (ENOBUFS);
+ 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;
+ } else
+ 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 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 */
+ register 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 = splimp();
+ 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));
+ }
+}
+
+pk_ctloutput (cmd, so, level, optname, mp)
+struct socket *so;
+struct mbuf **mp;
+int cmd, level, optname;
+{
+ register struct mbuf *m = *mp;
+ register 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 ((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) {
+ (void) m_freem (*mp);
+ *mp = 0;
+ }
+ return (error);
+
+}
+
+
+/*
+ * Do an in-place conversion of an "old style"
+ * socket address to the new style
+ */
+
+static
+old_to_new (m)
+register struct mbuf *m;
+{
+ register struct x25_sockaddr *oldp;
+ register struct sockaddr_x25 *newp;
+ register 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
+new_to_old (m)
+register struct mbuf *m;
+{
+ register struct x25_sockaddr *oldp;
+ register struct sockaddr_x25 *newp;
+ register 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);
+}
+
+
+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)
+struct pklcd *lcp;
+register struct mbuf *m;
+{
+ int mqbit = 0, error = 0;
+ register struct x25_packet *xp;
+ register 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);
+}
OpenPOWER on IntegriCloud