/* $OpenBSD: pk_usrreq.c,v 1.9 2003/12/10 07:22:43 itojun Exp $ */ /* $NetBSD: pk_usrreq.c,v 1.10 1996/02/13 22:05:43 christos Exp $ */ /* * 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. 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 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void old_to_new(struct mbuf *); static void new_to_old(struct mbuf *); /* * * 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) struct socket *so; int req; struct mbuf *m, *nam; struct mbuf *control; { struct pklcd *lcp = (struct pklcd *) so->so_pcb; int error = 0; if (req == PRU_CONTROL) return (pk_control(so, (long) 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) { 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(m, lcp); 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) { 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; 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 */ int pk_start(lcp) 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 */ int pk_control(so, cmd, data, ifp) struct socket *so; u_long cmd; caddr_t data; struct ifnet *ifp; { 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 ((so->so_state & SS_PRIV) == 0) return (EPERM); if (ifp == 0) panic("pk_control"); if (ifa == (struct ifaddr *) 0) { MALLOC(ia, struct x25_ifaddr *, sizeof(*ia), M_IFADDR, M_WAITOK); bzero((caddr_t) ia, sizeof(*ia)); TAILQ_INSERT_TAIL(&ifp->if_addrlist, &ia->ia_ifa, ifa_list); 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 */ 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)); } } int pk_ctloutput(cmd, so, level, optname, mp) struct socket *so; struct mbuf **mp; int cmd, level, optname; { 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 ((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 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 (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); }