home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (c) University of British Columbia, 1984
- * Copyright (c) 1990 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.
- *
- * 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 7.16 (Berkeley) 6/27/91
- */
-
- #include "param.h"
- #include "systm.h"
- #include "mbuf.h"
- #include "socket.h"
- #include "socketvar.h"
- #include "protosw.h"
- #include "errno.h"
- #include "ioctl.h"
- #include "stat.h"
-
- #include "../net/if.h"
- #include "../net/route.h"
-
- #include "x25.h"
- #include "pk.h"
- #include "pk_var.h"
-
- /*
- *
- * 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]),
- 0, -1};
-
- /*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 {
- 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 (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.
- */
- ia -> ia_start = pk_start;
- s = splimp();
- 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:
- 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 *);
- 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) {
- 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);
- }
-