diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
commit | d6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch) | |
tree | ece253b876159b39c620e62b6c9b1174642e070e /sys/netccitt |
initial import of NetBSD tree
Diffstat (limited to 'sys/netccitt')
30 files changed, 11657 insertions, 0 deletions
diff --git a/sys/netccitt/README.hdlc b/sys/netccitt/README.hdlc new file mode 100644 index 00000000000..b7bff73faf5 --- /dev/null +++ b/sys/netccitt/README.hdlc @@ -0,0 +1,52 @@ +/* $NetBSD: README.hdlc,v 1.4 1994/06/29 06:36:58 cgd Exp $ */ + +/* + * Copyright (c) University of British Columbia, 1984 + * + * @(#)README.hdlc 8.1 (Berkeley) 6/10/93 + * + * X.25 HDLC DATA LINK LEVEL: + * + * + * This module implements the Link Level of the Open Systems Interconnect + * Model. The implementation is based on the ISO High-Level Data Link + * Control (HDLC). These procedures subscribe to the principles of the + * ISO-Class of Procedures for point-to-point. These procedures implement + * two-way asynchronous balanced mode (LAPB) as recommented by the CCITT. + * + * The HDLC protocol layer interface consists of the following procedures: + * Hd_init (pr_init) + * Hd_ouput (pr_output) + * Hd_input (pr_input) + * Hd_timer (pr_slowtimo) + * + * Note: Supervisory commands RR, RNR and REJ are not transmitted by this + * station. + * + * This station never enters a busy (RNR) condition. + * + * The "Generate_rr" variable can be set to FALSE. This means that + * we NEVER send an RR. This works just fine if the network level + * is X.25 packet protocol -- which it is. + * + * Currently, this is only a DTE implementation. + * + * Think about: + * If the remote is busy, no iframes are sent. The remote sends a RR + * to clear this condition. However, this RR may be damaged, causing + * a possible deadlock. A solution is to poll with iframe (P(S)==P(R) + * of RNR) indefinitly. + * + * + * Date: February 1984 + * + * Author: Gerald W. Neufeld + * + * Installation: Department of Computer Science + * University of British Columbia + * Vancouver, BC, CANADA. + * + * History: + * + * + */ diff --git a/sys/netccitt/README.packet b/sys/netccitt/README.packet new file mode 100644 index 00000000000..9d7cc5608f2 --- /dev/null +++ b/sys/netccitt/README.packet @@ -0,0 +1,38 @@ +/* $NetBSD: README.packet,v 1.4 1994/06/29 06:36:59 cgd Exp $ */ + +/* + * @(#)README.packet 8.1 (Berkeley) 6/10/93 + * + * X.25 NETWORK PACKET LEVEL: + * + * This implementation is based on Recommentation X.25 as agreed at the + * March 1976 and the February 1980 meetings of CCITT Study Group VII. + * However, not all aspects are implemented. The following is a list of + * features which are not yet or may never be implemented: + * + * 1. D bit + * 2. PVC + * 3. fast select + * + * + * Note: This implementation is for DTEs only. + * + * Currently, only the 1976 verison is implemented. + * + * + * Date: February, 1984 + * + * Author: Gerald W. Neufeld + * + * Installation: Department of Computer Science + * University of British Columbia + * Vancouver, BC, CANADA + * + * To Do: Find some reasonable heuristic for piggybacking packet + * level acks. + * + * Bugs: Clear might be sent before data is all out. + * + * History: + * + */ diff --git a/sys/netccitt/ccitt_proto.c b/sys/netccitt/ccitt_proto.c new file mode 100644 index 00000000000..4e1e88bb6d0 --- /dev/null +++ b/sys/netccitt/ccitt_proto.c @@ -0,0 +1,95 @@ +/* $NetBSD: ccitt_proto.c,v 1.5 1994/06/29 06:37:00 cgd Exp $ */ + +/* + * Copyright (c) University of British Columbia, 1984 + * Copyright (c) 1990, 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. + * + * 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. + * + * @(#)ccitt_proto.c 8.1 (Berkeley) 6/10/93 + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/protosw.h> +#include <sys/domain.h> + +#include <netccitt/x25.h> + +#include <net/radix.h> + +/* + * Definitions of protocols supported in the CCITT domain. + */ + +extern struct domain ccittdomain; +#define DOMAIN &ccittdomain + +#ifdef LLC +int llc_output(); +void llc_ctlinput(), llc_init(), llc_timer(); +#endif +#ifdef HDLC +int hd_output(); +void hd_ctlinput(), hd_init(), hd_timer(); +#endif +int pk_usrreq(), pk_ctloutput(); +void pk_timer(), pk_init(), pk_input(), pk_ctlinput(); + +struct protosw ccittsw[] = { +#ifdef LLC + { 0, DOMAIN, IEEEPROTO_802LLC,0, + 0, llc_output, llc_ctlinput, 0, + 0, + llc_init, 0, llc_timer, 0, + }, +#endif +#ifdef HDLC + { 0, DOMAIN, CCITTPROTO_HDLC,0, + 0, hd_output, hd_ctlinput, 0, + 0, + hd_init, 0, hd_timer, 0, + }, +#endif + { SOCK_STREAM, DOMAIN, CCITTPROTO_X25, PR_CONNREQUIRED|PR_ATOMIC|PR_WANTRCVD, + pk_input, 0, pk_ctlinput, pk_ctloutput, + pk_usrreq, + pk_init, 0, pk_timer, 0, + } +}; + +struct domain ccittdomain = + { AF_CCITT, "ccitt", 0, 0, 0, ccittsw, + &ccittsw[sizeof(ccittsw)/sizeof(ccittsw[0])], 0, + rn_inithead, 32, sizeof (struct sockaddr_x25) }; diff --git a/sys/netccitt/dll.h b/sys/netccitt/dll.h new file mode 100644 index 00000000000..0539124af33 --- /dev/null +++ b/sys/netccitt/dll.h @@ -0,0 +1,85 @@ +/* $NetBSD: dll.h,v 1.3 1995/03/26 20:33:43 jtc Exp $ */ + +/* + * Copyright (C) Dirk Husemann, Computer Science Department IV, + * University of Erlangen-Nuremberg, Germany, 1990, 1991, 1992 + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * 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. + * + * @(#)dll.h 8.1 (Berkeley) 6/10/93 + */ + +/* + * We define the additional PRC_* codes in here + */ +#ifdef _KERNEL +#ifndef PRC_IFUP +#define PRC_IFUP 3 +#endif +#define PRC_CONNECT_INDICATION 8 +#define PRC_CONNECT_REQUEST 9 +#define PRC_DISCONNECT_REQUEST 10 +#define PRC_DISCONNECT_INDICATION 11 +#define PRC_RESET_REQUEST 12 +#endif + +/* + * Data link layer configuration --- basically a copy of the relevant parts + * of x25config, implemented to become a little bit more network + * layer independent. (Probably only used for casting et al.) + */ +struct dllconfig { + u_short dllcfg_unused0:4, + dllcfg_unused1:4, + dllcfg_trace:1, /* link level tracing flag */ + dllcfg_window:7; /* link level window size */ + u_short dllcfg_xchxid:1, /* exchange XID (not yet) */ + dllcfg_unused2:7; /* here be dragons */ +}; + +struct dll_ctlinfo { + union { + struct { + struct dllconfig *dctli_up_cfg; + u_char dctli_up_lsap; + } CTLI_UP; + struct { + caddr_t dctli_down_pcb; + struct rtentry *dctli_down_rt; + struct dllconfig *dctli_down_llconf; + } CTLI_DOWN; + } CTLIun; +}; +#define dlcti_cfg CTLIun.CTLI_UP.dctli_up_cfg +#define dlcti_lsap CTLIun.CTLI_UP.dctli_up_lsap +#define dlcti_pcb CTLIun.CTLI_DOWN.dctli_down_pcb +#define dlcti_rt CTLIun.CTLI_DOWN.dctli_down_rt +#define dlcti_conf CTLIun.CTLI_DOWN.dctli_down_llconf diff --git a/sys/netccitt/hd_debug.c b/sys/netccitt/hd_debug.c new file mode 100644 index 00000000000..e99a77eba2e --- /dev/null +++ b/sys/netccitt/hd_debug.c @@ -0,0 +1,214 @@ +/* $NetBSD: hd_debug.c,v 1.5 1994/06/29 06:37:05 cgd Exp $ */ + +/* + * Copyright (c) University of British Columbia, 1984 + * Copyright (c) 1990, 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. + * + * 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. + * + * @(#)hd_debug.c 8.1 (Berkeley) 6/10/93 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/domain.h> +#include <sys/socket.h> +#include <sys/protosw.h> +#include <sys/errno.h> +#include <sys/time.h> +#include <sys/kernel.h> + +#include <net/if.h> + +#include <netccitt/hdlc.h> +#include <netccitt/hd_var.h> +#include <netccitt/x25.h> + +#ifdef HDLCDEBUG +#define NTRACE 32 + +struct hdlctrace { + struct hdcb *ht_hdp; + short ht_dir; + struct mbuf *ht_frame; + struct timeval ht_time; +} hdtrace[NTRACE]; + +int lasttracelogged, freezetrace; +#endif + +hd_trace (hdp, direction, frame) +struct hdcb *hdp; +register struct Hdlc_frame *frame; +{ + register char *s; + register int nr, pf, ns, i; + struct Hdlc_iframe *iframe = (struct Hdlc_iframe *) frame; + +#ifdef HDLCDEBUG + hd_savetrace (hdp, direction, frame); +#endif + if (hdp -> hd_xcp -> xc_ltrace) { + if (direction == RX) + printf ("F-In: "); + else if (direction == 2) + printf ("F-Xmt: "); + else + printf ("F-Out: "); + + nr = iframe -> nr; + pf = iframe -> pf; + ns = iframe -> ns; + + switch (hd_decode (hdp, frame)) { + case SABM: + printf ("SABM : PF=%d\n", pf); + break; + + case DISC: + printf ("DISC : PF=%d\n", pf); + break; + + case DM: + printf ("DM : PF=%d\n", pf); + break; + + case FRMR: + { + register struct Frmr_frame *f = (struct Frmr_frame *)frame; + + printf ("FRMR : PF=%d, TEXT=", pf); + for (s = (char *) frame, i = 0; i < 5; ++i, ++s) + printf ("%x ", (int) * s & 0xff); + printf ("\n"); + printf ("control=%x v(s)=%d v(r)=%d w%d x%d y%d z%d\n", + f->frmr_control, f->frmr_ns, f->frmr_nr, + f->frmr_w, f->frmr_x, f->frmr_y, f->frmr_z); + break; + } + + case UA: + printf ("UA : PF=%d\n", pf); + break; + + case RR: + printf ("RR : N(R)=%d, PF=%d\n", nr, pf); + break; + + case RNR: + printf ("RNR : N(R)=%d, PF=%d\n", nr, pf); + break; + + case REJ: + printf ("REJ : N(R)=%d, PF=%d\n", nr, pf); + break; + + case IFRAME: + { + register struct mbuf *m; + register int len = 0; + + for(m = dtom (frame); m; m = m -> m_next) + len += m -> m_len; + len -= HDHEADERLN; + printf ("IFRAME : N(R)=%d, PF=%d, N(S)=%d, DATA(%d)=", + nr, pf, ns, len); + for (s = (char *)iframe->i_field, i = 0; i < 3; ++i, ++s) + printf ("%x ", (int) *s & 0xff); + printf ("\n"); + break; + } + + default: + printf ("ILLEGAL: "); + for (s = (char *) frame, i = 0; i < 5; ++i, ++s) + printf ("%x ", (int) *s & 0xff); + printf ("\n"); + } + + } +} + +#ifdef HDLCDEBUG +static +hd_savetrace (hdp, dir, frame) +struct hdcb *hdp; +struct Hdlc_frame *frame; +{ + register struct hdlctrace *htp; + register struct mbuf *m; + + if (freezetrace) + return; + htp = &hdtrace[lasttracelogged]; + lasttracelogged = (lasttracelogged + 1) % NTRACE; + if (m = htp->ht_frame) + m_freem (m); + m = dtom (frame); + htp->ht_frame = m_copy (m, 0, m->m_len); + htp->ht_hdp = hdp; + htp->ht_dir = dir; + htp->ht_time = time; +} + +hd_dumptrace (hdp) +struct hdcb *hdp; +{ + register int i, ltrace; + register struct hdlctrace *htp; + + freezetrace = 1; + hd_status (hdp); + printf ("retransmit queue:"); + for (i = 0; i < 8; i++) + printf (" %x", hdp -> hd_retxq[i]); + printf ("\n"); + ltrace = hdp -> hd_xcp -> xc_ltrace; + hdp -> hd_xcp -> xc_ltrace = 1; + for (i = 0; i < NTRACE; i++) { + htp = &hdtrace[(lasttracelogged + i) % NTRACE]; + if (htp->ht_hdp != hdp || htp->ht_frame == 0) + continue; + printf ("%d/%d ", htp->ht_time.tv_sec & 0xff, + htp->ht_time.tv_usec / 10000); + hd_trace (htp->ht_hdp, htp->ht_dir, + mtod (htp->ht_frame, struct Hdlc_frame *)); + m_freem (htp->ht_frame); + htp->ht_frame = 0; + } + hdp -> hd_xcp -> xc_ltrace = ltrace; + freezetrace = 0; +} +#endif diff --git a/sys/netccitt/hd_input.c b/sys/netccitt/hd_input.c new file mode 100644 index 00000000000..d9511c462a5 --- /dev/null +++ b/sys/netccitt/hd_input.c @@ -0,0 +1,671 @@ +/* $NetBSD: hd_input.c,v 1.7 1994/06/29 06:37:07 cgd Exp $ */ + +/* + * Copyright (c) University of British Columbia, 1984 + * Copyright (c) 1990, 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. + * + * 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. + * + * @(#)hd_input.c 8.1 (Berkeley) 6/10/93 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/domain.h> +#include <sys/socket.h> +#include <sys/protosw.h> +#include <sys/errno.h> +#include <sys/time.h> +#include <sys/kernel.h> + +#include <net/if.h> + +#include <netccitt/hdlc.h> +#include <netccitt/hd_var.h> +#include <netccitt/x25.h> + +static frame_reject(); +static rej_routine(); +static free_iframes(); +/* + * HDLC INPUT INTERFACE + * + * This routine is called when the HDLC physical device has + * completed reading a frame. + */ + +hdintr () +{ + register struct mbuf *m; + register struct hdcb *hdp; + register struct ifnet *ifp; + register int s; + static struct ifnet *lastifp; + static struct hdcb *lasthdp; + + for (;;) { + s = splimp (); + IF_DEQUEUE (&hdintrq, m); + splx (s); + if (m == 0) + break; + if (m->m_len < HDHEADERLN) { + printf ("hdintr: packet too short (len=%d)\n", + m->m_len); + m_freem (m); + continue; + } + if ((m->m_flags & M_PKTHDR) == 0) + panic("hdintr"); + ifp = m->m_pkthdr.rcvif; + + /* + * look up the appropriate hdlc control block + */ + + if (ifp == lastifp) + hdp = lasthdp; + else { + for (hdp = hdcbhead; hdp; hdp = hdp->hd_next) + if (hdp->hd_ifp == ifp) + break; + if (hdp == 0) { + printf ("hdintr: unknown interface %x\n", ifp); + m_freem (m); + continue; + } + lastifp = ifp; + lasthdp = hdp; + } + + /* Process_rxframe returns FALSE if the frame was NOT queued + for the next higher layers. */ + if (process_rxframe (hdp, m) == FALSE) + m_freem (m); + } +} + +process_rxframe (hdp, fbuf) +register struct hdcb *hdp; +register struct mbuf *fbuf; +{ + register int queued = FALSE, frametype, pf; + register struct Hdlc_frame *frame; + + frame = mtod (fbuf, struct Hdlc_frame *); + pf = ((struct Hdlc_iframe *) frame) -> pf; + + hd_trace (hdp, RX, frame); + if (frame -> address != ADDRESS_A && frame -> address != ADDRESS_B) + return (queued); + + switch ((frametype = hd_decode (hdp, frame)) + hdp->hd_state) { + case DM + DISC_SENT: + case UA + DISC_SENT: + /* + * Link now closed. Leave timer running + * so hd_timer() can periodically check the + * status of interface driver flag bit IFF_UP. + */ + hdp->hd_state = DISCONNECTED; + break; + + case DM + INIT: + case UA + INIT: + /* + * This is a non-standard state change needed for DCEs + * that do dynamic link selection. We can't go into the + * usual "SEND DM" state because a DM is a SARM in LAP. + */ + hd_writeinternal (hdp, SABM, POLLOFF); + hdp->hd_state = SABM_SENT; + SET_TIMER (hdp); + break; + + case SABM + DM_SENT: + case SABM + WAIT_SABM: + hd_writeinternal (hdp, UA, pf); + case UA + SABM_SENT: + case UA + WAIT_UA: + KILL_TIMER (hdp); + hd_initvars (hdp); + hdp->hd_state = ABM; + hd_message (hdp, "Link level operational"); + /* Notify the packet level - to send RESTART. */ + (void) pk_ctlinput (PRC_LINKUP, hdp->hd_pkp); + break; + + case SABM + SABM_SENT: + /* Got a SABM collision. Acknowledge the remote's SABM + via UA but still wait for UA. */ + hd_writeinternal (hdp, UA, pf); + break; + + case SABM + ABM: + /* Request to reset the link from the remote. */ + KILL_TIMER (hdp); + hd_message (hdp, "Link reset"); +#ifdef HDLCDEBUG + hd_dumptrace (hdp); +#endif + hd_flush (hdp->hd_ifp); + hd_writeinternal (hdp, UA, pf); + hd_initvars (hdp); + (void) pk_ctlinput (PRC_LINKRESET, hdp->hd_pkp); + hdp->hd_resets++; + break; + + case SABM + WAIT_UA: + hd_writeinternal (hdp, UA, pf); + break; + + case DM + ABM: + hd_message (hdp, "DM received: link down"); +#ifdef HDLCDEBUG + hd_dumptrace (hdp); +#endif + (void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_pkp); + hd_flush (hdp->hd_ifp); + case DM + DM_SENT: + case DM + WAIT_SABM: + case DM + WAIT_UA: + hd_writeinternal (hdp, SABM, pf); + hdp->hd_state = SABM_SENT; + SET_TIMER (hdp); + break; + + case DISC + INIT: + case DISC + DM_SENT: + case DISC + SABM_SENT: + /* Note: This is a non-standard state change. */ + hd_writeinternal (hdp, UA, pf); + hd_writeinternal (hdp, SABM, POLLOFF); + hdp->hd_state = SABM_SENT; + SET_TIMER (hdp); + break; + + case DISC + WAIT_UA: + hd_writeinternal (hdp, DM, pf); + SET_TIMER (hdp); + hdp->hd_state = DM_SENT; + break; + + case DISC + ABM: + hd_message (hdp, "DISC received: link down"); + (void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_pkp); + case DISC + WAIT_SABM: + hd_writeinternal (hdp, UA, pf); + hdp->hd_state = DM_SENT; + SET_TIMER (hdp); + break; + + case UA + ABM: + hd_message (hdp, "UA received: link down"); + (void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_pkp); + case UA + WAIT_SABM: + hd_writeinternal (hdp, DM, pf); + hdp->hd_state = DM_SENT; + SET_TIMER (hdp); + break; + + case FRMR + DM_SENT: + hd_writeinternal (hdp, SABM, pf); + hdp->hd_state = SABM_SENT; + SET_TIMER (hdp); + break; + + case FRMR + WAIT_SABM: + hd_writeinternal (hdp, DM, pf); + hdp->hd_state = DM_SENT; + SET_TIMER (hdp); + break; + + case FRMR + ABM: + hd_message (hdp, "FRMR received: link down"); + (void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_pkp); +#ifdef HDLCDEBUG + hd_dumptrace (hdp); +#endif + hd_flush (hdp->hd_ifp); + hd_writeinternal (hdp, SABM, pf); + hdp->hd_state = WAIT_UA; + SET_TIMER (hdp); + break; + + case RR + ABM: + case RNR + ABM: + case REJ + ABM: + process_sframe (hdp, (struct Hdlc_sframe *)frame, frametype); + break; + + case IFRAME + ABM: + queued = process_iframe (hdp, fbuf, (struct Hdlc_iframe *)frame); + break; + + case IFRAME + SABM_SENT: + case RR + SABM_SENT: + case RNR + SABM_SENT: + case REJ + SABM_SENT: + hd_writeinternal (hdp, DM, POLLON); + hdp->hd_state = DM_SENT; + SET_TIMER (hdp); + break; + + case IFRAME + WAIT_SABM: + case RR + WAIT_SABM: + case RNR + WAIT_SABM: + case REJ + WAIT_SABM: + hd_writeinternal (hdp, FRMR, POLLOFF); + SET_TIMER (hdp); + break; + + case ILLEGAL + SABM_SENT: + hdp->hd_unknown++; + hd_writeinternal (hdp, DM, POLLOFF); + hdp->hd_state = DM_SENT; + SET_TIMER (hdp); + break; + + case ILLEGAL + ABM: + hd_message (hdp, "Unknown frame received: link down"); + (void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_pkp); + case ILLEGAL + WAIT_SABM: + hdp->hd_unknown++; +#ifdef HDLCDEBUG + hd_dumptrace (hdp); +#endif + hd_writeinternal (hdp, FRMR, POLLOFF); + hdp->hd_state = WAIT_SABM; + SET_TIMER (hdp); + break; + } + + return (queued); +} + +process_iframe (hdp, fbuf, frame) +register struct hdcb *hdp; +struct mbuf *fbuf; +register struct Hdlc_iframe *frame; +{ + register int nr = frame -> nr, + ns = frame -> ns, + pf = frame -> pf; + register int queued = FALSE; + + /* + * Validate the iframe's N(R) value. It's N(R) value must be in + * sync with our V(S) value and our "last received nr". + */ + + if (valid_nr (hdp, nr, FALSE) == FALSE) { + frame_reject (hdp, Z, frame); + return (queued); + } + + + /* + * This section tests the IFRAME for proper sequence. That is, it's + * sequence number N(S) MUST be equal to V(S). + */ + + if (ns != hdp->hd_vr) { + hdp->hd_invalid_ns++; + if (pf || (hdp->hd_condition & REJ_CONDITION) == 0) { + hdp->hd_condition |= REJ_CONDITION; + /* + * Flush the transmit queue. This is ugly but we + * have no choice. A reject response must be + * immediately sent to the DCE. Failure to do so + * may result in another out of sequence iframe + * arriving (and thus sending another reject) + * before the first reject is transmitted. This + * will cause the DCE to receive two or more + * rejects back to back, which must never happen. + */ + hd_flush (hdp->hd_ifp); + hd_writeinternal (hdp, REJ, pf); + } + return (queued); + } + hdp->hd_condition &= ~REJ_CONDITION; + + /* + * This section finally tests the IFRAME's sequence number against + * the window size (K) and the sequence number of the last frame + * we have acknowledged. If the IFRAME is completely correct then + * it is queued for the packet level. + */ + + if (ns != (hdp -> hd_lasttxnr + hdp -> hd_xcp -> xc_lwsize) % MODULUS) { + hdp -> hd_vr = (hdp -> hd_vr + 1) % MODULUS; + if (pf == 1) { + /* Must generate a RR or RNR with final bit on. */ + hd_writeinternal (hdp, RR, POLLON); + } else + /* + * Hopefully we can piggyback the RR, if not we will generate + * a RR when T3 timer expires. + */ + if (hdp -> hd_rrtimer == 0) + hdp->hd_rrtimer = hd_t3; + + /* Forward iframe to packet level of X.25. */ + fbuf -> m_data += HDHEADERLN; + fbuf -> m_len -= HDHEADERLN; + fbuf -> m_pkthdr.len -= HDHEADERLN; + fbuf -> m_pkthdr.rcvif = (struct ifnet *)hdp -> hd_pkp; +#ifdef BSD4_3 + fbuf->m_act = 0; /* probably not necessary */ +#else + { + register struct mbuf *m; + + for (m = fbuf; m -> m_next; m = m -> m_next) + m -> m_act = (struct mbuf *) 0; + m -> m_act = (struct mbuf *) 1; + } +#endif + pk_input (fbuf); + queued = TRUE; + hd_start (hdp); + } else { + /* + * Here if the remote station has transmitted more iframes then + * the number which have been acknowledged plus K. + */ + hdp->hd_invalid_ns++; + frame_reject (hdp, W, frame); + } + return (queued); +} + +/* + * This routine is used to determine if a value (the middle parameter) + * is between two other values. The low value is the first parameter + * the high value is the last parameter. The routine checks the middle + * value to see if it is within the range of the first and last values. + * The reason we need this routine is the values are modulo some base + * hence a simple test for greater or less than is not sufficient. + */ + +bool +range_check (rear, value, front) +int rear, + value, + front; +{ + register bool result = FALSE; + + if (front > rear) + result = (rear <= value) && (value <= front); + else + result = (rear <= value) || (value <= front); + + return (result); +} + +/* + * This routine handles all the frame reject conditions which can + * arise as a result of secondary processing. The frame reject + * condition Y (frame length error) are handled elsewhere. + */ + +static +frame_reject (hdp, rejectcode, frame) +struct hdcb *hdp; +struct Hdlc_iframe *frame; +{ + register struct Frmr_frame *frmr = &hd_frmr; + + frmr -> frmr_control = ((struct Hdlc_frame *) frame) -> control; + + frmr -> frmr_ns = frame -> ns; + frmr -> frmr_f1_0 = 0; + frmr -> frmr_nr = frame -> nr; + frmr -> frmr_f2_0 = 0; + + frmr -> frmr_0000 = 0; + frmr -> frmr_w = frmr -> frmr_x = frmr -> frmr_y = + frmr -> frmr_z = 0; + switch (rejectcode) { + case Z: + frmr -> frmr_z = 1;/* invalid N(R). */ + break; + + case Y: + frmr -> frmr_y = 1;/* iframe length error. */ + break; + + case X: + frmr -> frmr_x = 1;/* invalid information field. */ + frmr -> frmr_w = 1; + break; + + case W: + frmr -> frmr_w = 1;/* invalid N(S). */ + } + + hd_writeinternal (hdp, FRMR, POLLOFF); + + hdp->hd_state = WAIT_SABM; + SET_TIMER (hdp); +} + +/* + * This procedure is invoked when ever we receive a supervisor + * frame such as RR, RNR and REJ. All processing for these + * frames is done here. + */ + +process_sframe (hdp, frame, frametype) +register struct hdcb *hdp; +register struct Hdlc_sframe *frame; +int frametype; +{ + register int nr = frame -> nr, pf = frame -> pf, pollbit = 0; + + if (valid_nr (hdp, nr, pf) == TRUE) { + switch (frametype) { + case RR: + hdp->hd_condition &= ~REMOTE_RNR_CONDITION; + break; + + case RNR: + hdp->hd_condition |= REMOTE_RNR_CONDITION; + hdp->hd_retxcnt = 0; + break; + + case REJ: + hdp->hd_condition &= ~REMOTE_RNR_CONDITION; + rej_routine (hdp, nr); + } + + if (pf == 1) { + hdp->hd_retxcnt = 0; + hdp->hd_condition &= ~TIMER_RECOVERY_CONDITION; + + if (frametype == RR && hdp->hd_lastrxnr == hdp->hd_vs + && hdp->hd_timer == 0 && hdp->hd_txq.head == 0) + hd_writeinternal(hdp, RR, pf); + else + /* If any iframes have been queued because of the + timer condition, transmit then now. */ + if (hdp->hd_condition & REMOTE_RNR_CONDITION) { + /* Remote is busy or timer condition, so only + send one. */ + if (hdp->hd_vs != hdp->hd_retxqi) + hd_send_iframe (hdp, hdp->hd_retxq[hdp->hd_vs], pollbit); + } + else /* Flush the retransmit list first. */ + while (hdp->hd_vs != hdp->hd_retxqi) + hd_send_iframe (hdp, hdp->hd_retxq[hdp->hd_vs], POLLOFF); + } + + hd_start (hdp); + } else + frame_reject (hdp, Z, (struct Hdlc_iframe *)frame); /* Invalid N(R). */ +} + +/* + * This routine tests the validity of the N(R) which we have received. + * If it is ok, then all the iframes which it acknowledges (if any) + * will be freed. + */ + +bool +valid_nr (hdp, nr, finalbit) +register struct hdcb *hdp; +register int finalbit; +{ + /* Make sure it really does acknowledge something. */ + if (hdp->hd_lastrxnr == nr) + return (TRUE); + + /* + * This section validates the frame's N(R) value. It's N(R) value + * must be in syncronization with our V(S) value and our "last + * received nr" variable. If it is correct then we are able to send + * more IFRAME's, else frame reject condition is entered. + */ + + if (range_check (hdp->hd_lastrxnr, nr, hdp->hd_vs) == FALSE) { + if ((hdp->hd_condition & TIMER_RECOVERY_CONDITION) && + range_check (hdp->hd_vs, nr, hdp->hd_xx) == TRUE) + hdp->hd_vs = nr; + + else { + hdp->hd_invalid_nr++; + return (FALSE); + } + } + + /* + * If we get to here, we do have a valid frame but it might be out + * of sequence. However, we should still accept the receive state + * number N(R) since it has already passed our previous test and it + * does acknowledge frames which we are sending. + */ + + KILL_TIMER (hdp); + free_iframes (hdp, &nr, finalbit);/* Free all acknowledged iframes */ + if (nr != hdp->hd_vs) + SET_TIMER (hdp); + + return (TRUE); +} + +/* + * This routine determines how many iframes need to be retransmitted. + * It then resets the Send State Variable V(S) to accomplish this. + */ + +static +rej_routine (hdp, rejnr) +register struct hdcb *hdp; +register int rejnr; +{ + register int anchor; + + /* + * Flush the output queue. Any iframes queued for + * transmission will be out of sequence. + */ + + hd_flush (hdp->hd_ifp); + + /* + * Determine how many frames should be re-transmitted. In the case + * of a normal REJ this should be 1 to K. In the case of a timer + * recovery REJ (ie. a REJ with the Final Bit on) this could be 0. + */ + + anchor = hdp->hd_vs; + if (hdp->hd_condition & TIMER_RECOVERY_CONDITION) + anchor = hdp->hd_xx; + + anchor = (anchor - rejnr + 8) % MODULUS; + + if (anchor > 0) { + + /* There is at least one iframe to retransmit. */ + KILL_TIMER (hdp); + hdp->hd_vs = rejnr; + + while (hdp->hd_vs != hdp->hd_retxqi) + hd_send_iframe (hdp, hdp->hd_retxq[hdp->hd_vs], POLLOFF); + + } + hd_start (hdp); +} + +/* + * This routine frees iframes from the retransmit queue. It is called + * when a previously written iframe is acknowledged. + */ + +static +free_iframes (hdp, nr, finalbit) +register struct hdcb *hdp; +int *nr; +register int finalbit; + +{ + register int i, k; + + /* + * We need to do the following because of a funny quirk in the + * protocol. This case occures when in Timer recovery condition + * we get a N(R) which acknowledges all the outstanding iframes + * but with the Final Bit off. In this case we need to save the last + * iframe for possible retransmission even though it has already been + * acknowledged! + */ + + if ((hdp->hd_condition & TIMER_RECOVERY_CONDITION) && *nr == hdp->hd_xx && finalbit == 0) { + *nr = (*nr - 1 + 8) % MODULUS; +/* printf ("QUIRK\n"); */ + } + + k = (*nr - hdp->hd_lastrxnr + 8) % MODULUS; + + /* Loop here freeing all acknowledged iframes. */ + for (i = 0; i < k; ++i) { + m_freem (hdp->hd_retxq[hdp->hd_lastrxnr]); + hdp->hd_retxq[hdp->hd_lastrxnr] = 0; + hdp->hd_lastrxnr = (hdp->hd_lastrxnr + 1) % MODULUS; + } + +} diff --git a/sys/netccitt/hd_output.c b/sys/netccitt/hd_output.c new file mode 100644 index 00000000000..d0e5ada43c1 --- /dev/null +++ b/sys/netccitt/hd_output.c @@ -0,0 +1,249 @@ +/* $NetBSD: hd_output.c,v 1.5 1994/06/29 06:37:11 cgd Exp $ */ + +/* + * Copyright (c) University of British Columbia, 1984 + * Copyright (c) 1990, 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. + * + * 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. + * + * @(#)hd_output.c 8.1 (Berkeley) 6/10/93 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/domain.h> +#include <sys/socket.h> +#include <sys/syslog.h> +#include <sys/protosw.h> +#include <sys/errno.h> +#include <sys/time.h> +#include <sys/kernel.h> + +#include <net/if.h> + +#include <netccitt/hdlc.h> +#include <netccitt/hd_var.h> +#include <netccitt/x25.h> + +/* + * HDLC OUTPUT INTERFACE + * + * This routine is called when the X.25 packet layer output routine + * has a information frame (iframe) to write. It is also called + * by the input and control routines of the HDLC layer. + */ + +hd_output (hdp, m0) +register struct hdcb *hdp; +struct mbuf *m0; +{ + struct x25config *xcp; + register struct mbuf *m = m0; + int len; + + if (m == NULL) + panic ("hd_output"); + if ((m->m_flags & M_PKTHDR) == 0) + panic ("hd_output 2"); + + if (hdp->hd_state != ABM) { + m_freem (m); + return; + } + + /* + * Make room for the hdlc header either by prepending + * another mbuf, or by adjusting the offset and length + * of the first mbuf in the mbuf chain. + */ + + M_PREPEND(m, HDHEADERLN, M_DONTWAIT); + if (m == NULL) + return; + for (len = 0; m; m = m->m_next) + len += m->m_len; + m = m0; + m->m_pkthdr.len = len; + + hd_append (&hdp->hd_txq, m); + hd_start (hdp); +} + +hd_start (hdp) +register struct hdcb *hdp; +{ + register struct mbuf *m; + + /* + * The iframe is only transmitted if all these conditions are FALSE. + * The iframe remains queued (hdp->hd_txq) however and will be + * transmitted as soon as these conditions are cleared. + */ + + while (!(hdp->hd_condition & (TIMER_RECOVERY_CONDITION | REMOTE_RNR_CONDITION | REJ_CONDITION))) { + if (hdp->hd_vs == (hdp->hd_lastrxnr + hdp->hd_xcp->xc_lwsize) % MODULUS) { + + /* We have now exceeded the maximum number of + outstanding iframes. Therefore, we must wait + until at least one is acknowledged if this + condition is not turned off before we are + requested to write another iframe. */ + hdp->hd_window_condition++; + break; + } + + /* hd_remove top iframe from transmit queue. */ + if ((m = hd_remove (&hdp->hd_txq)) == NULL) + break; + + hd_send_iframe (hdp, m, POLLOFF); + } +} + +/* + * This procedure is passed a buffer descriptor for an iframe. It builds + * the rest of the control part of the frame and then writes it out. It + * also starts the acknowledgement timer and keeps the iframe in the + * Retransmit queue (Retxq) just in case we have to do this again. + * + * Note: This routine is also called from hd_input.c when retransmission + * of old frames is required. + */ + +hd_send_iframe (hdp, buf, poll_bit) +register struct hdcb *hdp; +register struct mbuf *buf; +int poll_bit; +{ + register struct Hdlc_iframe *iframe; + struct mbuf *m; + + KILL_TIMER (hdp); + + if (buf == 0) { + printf ("hd_send_iframe: zero arg\n"); +#ifdef HDLCDEBUG + hd_status (hdp); + hd_dumptrace (hdp); +#endif + hdp->hd_vs = (hdp->hd_vs + 7) % MODULUS; + return; + } + iframe = mtod (buf, struct Hdlc_iframe *); + + iframe -> hdlc_0 = 0; + iframe -> nr = hdp->hd_vr; + iframe -> pf = poll_bit; + iframe -> ns = hdp->hd_vs; + iframe -> address = ADDRESS_B; + hdp->hd_lasttxnr = hdp->hd_vr; + hdp->hd_rrtimer = 0; + + if (hdp->hd_vs == hdp->hd_retxqi) { + /* Check for retransmissions. */ + /* Put iframe only once in the Retransmission queue. */ + hdp->hd_retxq[hdp->hd_retxqi] = buf; + hdp->hd_retxqi = (hdp->hd_retxqi + 1) % MODULUS; + hdp->hd_iframes_out++; + } + + hdp->hd_vs = (hdp->hd_vs + 1) % MODULUS; + + hd_trace (hdp, TX, (struct Hdlc_frame *)iframe); + + /* Write buffer on device. */ + m = hdp->hd_dontcopy ? buf : m_copy(buf, 0, (int)M_COPYALL); + if (m == 0) { + printf("hdlc: out of mbufs\n"); + return; + } + (*hdp->hd_output)(hdp, m); + SET_TIMER (hdp); +} + +hd_ifoutput(hdp, m) +register struct mbuf *m; +register struct hdcb *hdp; +{ + /* + * Queue message on interface, and start output if interface + * not yet active. + */ + register struct ifnet *ifp = hdp->hd_ifp; + int s = splimp(); + + if (IF_QFULL(&ifp->if_snd)) { + IF_DROP(&ifp->if_snd); + /* printf("%s%d: HDLC says OK to send but queue full, may hang\n", + ifp->if_name, ifp->if_unit);*/ + m_freem(m); + } else { + IF_ENQUEUE(&ifp->if_snd, m); + if ((ifp->if_flags & IFF_OACTIVE) == 0) + (*ifp->if_start)(ifp); + } + splx(s); +} + + +/* + * This routine gets control when the timer expires because we have not + * received an acknowledgement for a iframe. + */ + +hd_resend_iframe (hdp) +register struct hdcb *hdp; +{ + + if (hdp->hd_retxcnt++ < hd_n2) { + if (!(hdp->hd_condition & TIMER_RECOVERY_CONDITION)) { + hdp->hd_xx = hdp->hd_vs; + hdp->hd_condition |= TIMER_RECOVERY_CONDITION; + } + + hdp->hd_vs = hdp->hd_lastrxnr; + hd_send_iframe (hdp, hdp->hd_retxq[hdp->hd_vs], POLLON); + } else { + /* At this point we have not received a RR even after N2 + retries - attempt to reset link. */ + + hd_initvars (hdp); + hd_writeinternal (hdp, SABM, POLLOFF); + hdp->hd_state = WAIT_UA; + SET_TIMER (hdp); + hd_message (hdp, "Timer recovery failed: link down"); + (void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_pkp); + } +} diff --git a/sys/netccitt/hd_subr.c b/sys/netccitt/hd_subr.c new file mode 100644 index 00000000000..db8ec819296 --- /dev/null +++ b/sys/netccitt/hd_subr.c @@ -0,0 +1,393 @@ +/* $NetBSD: hd_subr.c,v 1.5 1994/06/29 06:37:13 cgd Exp $ */ + +/* + * Copyright (c) University of British Columbia, 1984 + * Copyright (c) 1990, 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. + * + * 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. + * + * @(#)hd_subr.c 8.1 (Berkeley) 6/10/93 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/domain.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/protosw.h> +#include <sys/errno.h> +#include <sys/time.h> +#include <sys/kernel.h> + +#include <net/if.h> + +#include <netccitt/hdlc.h> +#include <netccitt/hd_var.h> +#include <netccitt/x25.h> +#include <netccitt/pk_var.h> + +hd_init () +{ + + hdintrq.ifq_maxlen = IFQ_MAXLEN; +} + +hd_ctlinput (prc, addr) +struct sockaddr *addr; +{ + register struct x25config *xcp = (struct x25config *)addr; + register struct hdcb *hdp; + register struct ifaddr *ifa; + struct ifnet *ifp; + caddr_t pk_newlink(); + + if (addr->sa_family != AF_CCITT) + return (EAFNOSUPPORT); + if (xcp->xc_lptype != HDLCPROTO_LAPB) + return (EPROTONOSUPPORT); + ifa = ifa_ifwithaddr(addr); + if (ifa == 0 || ifa->ifa_addr->sa_family != AF_CCITT || + (ifp = ifa->ifa_ifp) == 0) + panic ("hd_ctlinput"); + for (hdp = hdcbhead; hdp; hdp = hdp->hd_next) + if (hdp->hd_ifp == ifp) + break; + + if (hdp == 0) { /* new interface */ + int error, hd_ifoutput(), hd_output(); + + /* an hdcb is now too big to fit in an mbuf */ + MALLOC(hdp, struct hdcb *, sizeof (*hdp), M_PCB, M_DONTWAIT); + if (hdp == 0) + return (ENOBUFS); + bzero((caddr_t)hdp, sizeof(*hdp)); + hdp->hd_pkp = + (caddr_t) pk_newlink ((struct x25_ifaddr *) ifa, + (caddr_t) hdp); + ((struct x25_ifaddr *)ifa)->ia_pkcb = + (struct pkcb *) hdp->hd_pkp; + if (hdp -> hd_pkp == 0) { + free(hdp, M_PCB); + return (ENOBUFS); + } + hdp->hd_ifp = ifp; + hdp->hd_ifa = ifa; + hdp->hd_xcp = xcp; + hdp->hd_state = INIT; + hdp->hd_output = hd_ifoutput; + hdp->hd_next = hdcbhead; + hdcbhead = hdp; + } else if (hdp->hd_pkp == 0) { /* interface got reconfigured */ + hdp->hd_pkp = + (caddr_t) pk_newlink ((struct x25_ifaddr *) ifa, + (caddr_t) hdp); + ((struct x25_ifaddr *)ifa)->ia_pkcb = + (struct pkcb *) hdp->hd_pkp; + if (hdp -> hd_pkp == 0) { + free(hdp, M_PCB); + return (ENOBUFS); + } + } + + switch (prc) { + case PRC_IFUP: + if (xcp->xc_lwsize == 0 || + xcp->xc_lwsize > MAX_WINDOW_SIZE) + xcp->xc_lwsize = MAX_WINDOW_SIZE; + if (hdp->hd_state == INIT) + SET_TIMER (hdp); + break; + + case PRC_IFDOWN: + if (hdp->hd_state == ABM) + hd_message (hdp, "Operator shutdown: link closed"); + (void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_pkp); + + /* fall thru to ... */ + + case PRC_DISCONNECT_REQUEST: + /* drop reference to pkcb --- it's dead meat */ + hdp->hd_pkp = (caddr_t) 0; + ((struct x25_ifaddr *)ifa)->ia_pkcb = (struct pkcb *) 0; + + hd_writeinternal (hdp, DISC, POLLON); + hdp->hd_state = DISC_SENT; + SET_TIMER (hdp); + } + return (0); +} + +hd_initvars (hdp) +register struct hdcb *hdp; +{ + register struct mbuf *m; + register int i; + + /* Clear Transmit queue. */ + while ((m = hd_remove (&hdp->hd_txq)) != NULL) + m_freem (m); + + /* Clear Retransmit queue. */ + i = hdp->hd_lastrxnr; + while (i != hdp->hd_retxqi) { + m_freem (hdp->hd_retxq[i]); + i = (i + 1) % MODULUS; + } + hdp->hd_retxqi = 0; + + hdp->hd_vs = hdp->hd_vr = 0; + hdp->hd_lasttxnr = hdp->hd_lastrxnr = 0; + hdp->hd_rrtimer = 0; + KILL_TIMER(hdp); + hdp->hd_retxcnt = 0; + hdp->hd_condition = 0; +} + +hd_decode (hdp, frame) +register struct hdcb *hdp; +struct Hdlc_frame *frame; +{ + register int frametype = ILLEGAL; + register struct Hdlc_iframe *iframe = (struct Hdlc_iframe *) frame; + register struct Hdlc_sframe *sframe = (struct Hdlc_sframe *) frame; + register struct Hdlc_uframe *uframe = (struct Hdlc_uframe *) frame; + + if (iframe -> hdlc_0 == 0) { + frametype = IFRAME; + hdp->hd_iframes_in++; + } + + else if (sframe -> hdlc_01 == 1) { + /* Supervisory format. */ + switch (sframe -> s2) { + case 0: + frametype = RR; + hdp->hd_rrs_in++; + break; + + case 1: + frametype = RNR; + hdp->hd_rnrs_in++; + break; + + case 2: + frametype = REJ; + hdp->hd_rejs_in++; + } + } + else if (uframe -> hdlc_11 == 3) { + /* Unnumbered format. */ + switch (uframe -> m3) { + case 0: + frametype = DM; + break; + + case 1: + frametype = SABM; + break; + + case 2: + frametype = DISC; + break; + + case 3: + frametype = UA; + break; + + case 4: + frametype = FRMR; + hdp->hd_frmrs_in++; + } + } + return (frametype); +} + +/* + * This routine is called when the HDLC layer internally generates a + * command or response for the remote machine ( eg. RR, UA etc. ). + * Only supervisory or unnumbered frames are processed. + */ + +hd_writeinternal (hdp, frametype, pf) +register struct hdcb *hdp; +register int frametype, pf; +{ + register struct mbuf *buf; + struct Hdlc_frame *frame; + register struct Hdlc_sframe *sframe; + register struct Hdlc_uframe *uframe; + + MGETHDR (buf, M_DONTWAIT, MT_HEADER); + if (buf == 0) + return; + frame = mtod (buf, struct Hdlc_frame *); + sframe = mtod (buf, struct Hdlc_sframe *); + uframe = mtod (buf, struct Hdlc_uframe *); + + /* Assume a response - address structure for DTE */ + frame -> address = ADDRESS_A; + buf -> m_len = 2; + buf -> m_act = buf -> m_next = NULL; + + switch (frametype) { + case RR: + frame -> control = RR_CONTROL; + hdp->hd_rrs_out++; + break; + + case RNR: + frame -> control = RNR_CONTROL; + hdp->hd_rnrs_out++; + break; + + case REJ: + frame -> control = REJ_CONTROL; + hdp->hd_rejs_out++; + break; + + case SABM: + frame -> control = SABM_CONTROL; + frame -> address = ADDRESS_B; + break; + + case DISC: + if ((hdp->hd_ifp->if_flags & IFF_UP) == 0) { + hdp->hd_state = DISCONNECTED; + (void) m_freem (buf); + hd_flush (hdp->hd_ifp); + return; + } + frame -> control = DISC_CONTROL; + frame -> address = ADDRESS_B; + break; + + case DM: + frame -> control = DM_CONTROL; + break; + + case UA: + frame -> control = UA_CONTROL; + break; + + case FRMR: + frame -> control = FRMR_CONTROL; + bcopy ((caddr_t)&hd_frmr, (caddr_t)frame -> info, 3); + buf -> m_len = 5; + hdp->hd_frmrs_out++; + + } + + if (sframe -> hdlc_01 == 1) { + /* Supervisory format - RR, REJ, or RNR. */ + sframe -> nr = hdp->hd_vr; + sframe -> pf = pf; + hdp->hd_lasttxnr = hdp->hd_vr; + hdp->hd_rrtimer = 0; + } + else + uframe -> pf = pf; + + hd_trace (hdp, TX, frame); + buf -> m_pkthdr.len = buf -> m_len; + (*hdp->hd_output) (hdp, buf); +} + +struct mbuf * +hd_remove (q) +struct hdtxq *q; +{ + register struct mbuf *m; + + m = q -> head; + if (m) { + if ((q -> head = m -> m_act) == NULL) + q -> tail = NULL; + m -> m_act = 0; + } + return (m); +} + +hd_append (q, m) +register struct hdtxq *q; +register struct mbuf *m; +{ + + m -> m_act = NULL; + if (q -> tail == NULL) + q -> head = m; + else + q -> tail -> m_act = m; + q -> tail = m; +} + +hd_flush (ifp) +struct ifnet *ifp; +{ + register struct mbuf *m; + register int s; + + while (1) { + s = splimp (); + IF_DEQUEUE (&ifp->if_snd, m); + splx (s); + if (m == 0) + break; + m_freem (m); + } +} + +hd_message (hdp, msg) +struct hdcb *hdp; +char *msg; +{ + char *format_ntn (); + + if (hdcbhead -> hd_next) + printf ("HDLC(%s): %s\n", format_ntn (hdp->hd_xcp), msg); + else + printf ("HDLC: %s\n", msg); +} + +#ifdef HDLCDEBUG +hd_status (hdp) +struct hdcb *hdp; +{ + printf ("HDLC STATUS:\n V(S)=%d, V(R)=%d, retxqi=%d,\n", + hdp->hd_vs, hdp->hd_vr, hdp->hd_retxqi); + + printf ("Last_rx_nr=%d, Last_tx_nr=%d,\n Condition=%d, Xx=%d\n", + hdp->hd_lastrxnr, hdp->hd_lasttxnr, hdp->hd_condition, hdp->hd_xx); +} +#endif diff --git a/sys/netccitt/hd_timer.c b/sys/netccitt/hd_timer.c new file mode 100644 index 00000000000..8f934ff5436 --- /dev/null +++ b/sys/netccitt/hd_timer.c @@ -0,0 +1,149 @@ +/* $NetBSD: hd_timer.c,v 1.5 1994/06/29 06:37:15 cgd Exp $ */ + +/* + * Copyright (c) University of British Columbia, 1984 + * Copyright (c) 1990, 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. + * + * 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. + * + * @(#)hd_timer.c 8.1 (Berkeley) 6/10/93 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/domain.h> +#include <sys/socket.h> +#include <sys/protosw.h> +#include <sys/errno.h> +#include <sys/time.h> +#include <sys/kernel.h> + +#include <net/if.h> + +#include <netccitt/hdlc.h> +#include <netccitt/hd_var.h> +#include <netccitt/x25.h> + +/* + * these can be patched with adb if the + * default values are inappropriate + */ + +int hd_t1 = T1; +int hd_t3 = T3; +int hd_n2 = N2; + +/* + * HDLC TIMER + * + * This routine is called every 500ms by the kernel. Decrement timer by this + * amount - if expired then process the event. + */ + +hd_timer () +{ + register struct hdcb *hdp; + register int s = splimp (); + + for (hdp = hdcbhead; hdp; hdp = hdp->hd_next) { + if (hdp->hd_rrtimer && (--hdp->hd_rrtimer == 0)) { + if (hdp->hd_lasttxnr != hdp->hd_vr) + hd_writeinternal (hdp, RR, POLLOFF); + } + + if (!(hdp->hd_timer && --hdp->hd_timer == 0)) + continue; + + switch (hdp->hd_state) { + case INIT: + case DISC_SENT: + hd_writeinternal (hdp, DISC, POLLON); + break; + + case ABM: + if (hdp->hd_lastrxnr != hdp->hd_vs) { /* XXX */ + hdp->hd_timeouts++; + hd_resend_iframe (hdp); + } + break; + + case WAIT_SABM: + hd_writeinternal (hdp, FRMR, POLLOFF); + if (++hdp->hd_retxcnt == hd_n2) { + hdp->hd_retxcnt = 0; + hd_writeinternal (hdp, SABM, POLLOFF); + hdp->hd_state = WAIT_UA; + } + break; + + case DM_SENT: + if (++hdp->hd_retxcnt == hd_n2) { + /* Notify the packet level. */ + (void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_pkp); + hdp->hd_retxcnt = 0; + hdp->hd_state = SABM_SENT; + hd_writeinternal (hdp, SABM, POLLOFF); + } else + hd_writeinternal (hdp, DM, POLLOFF); + break; + + case WAIT_UA: + if (++hdp->hd_retxcnt == hd_n2) { + hdp->hd_retxcnt = 0; + hd_writeinternal (hdp, DM, POLLOFF); + hdp->hd_state = DM_SENT; + } else + hd_writeinternal (hdp, SABM, POLLOFF); + break; + + case SABM_SENT: + /* Do this indefinitely. */ + hd_writeinternal (hdp, SABM, POLLON); + break; + + case DISCONNECTED: + /* + * Poll the interface driver flags waiting + * for the IFF_UP bit to come on. + */ + if (hdp->hd_ifp->if_flags & IFF_UP) + hdp->hd_state = INIT; + + } + SET_TIMER (hdp); + } + + splx (s); +} diff --git a/sys/netccitt/hd_var.h b/sys/netccitt/hd_var.h new file mode 100644 index 00000000000..5a511da4d2c --- /dev/null +++ b/sys/netccitt/hd_var.h @@ -0,0 +1,109 @@ +/* $NetBSD: hd_var.h,v 1.6 1995/03/26 20:33:44 jtc Exp $ */ + +/* + * Copyright (c) University of British Columbia, 1984 + * Copyright (c) 1990, 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. + * + * 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. + * + * @(#)hd_var.h 8.1 (Berkeley) 6/10/93 + */ + +/* + * + * hdlc control block + * + */ + +struct hdtxq { + struct mbuf *head; + struct mbuf *tail; +}; + +struct hdcb { + struct hdcb *hd_next; /* pointer to next hdlc control block */ + char hd_state; /* link state */ + char hd_vs; /* send state variable */ + char hd_vr; /* receive state variable */ + char hd_lastrxnr; /* last received N(R) */ + char hd_lasttxnr; /* last transmitted N(R) */ + char hd_condition; +#define TIMER_RECOVERY_CONDITION 0x01 +#define REJ_CONDITION 0x02 +#define REMOTE_RNR_CONDITION 0X04 + char hd_retxcnt; + char hd_xx; + struct hdtxq hd_txq; + struct mbuf *hd_retxq[MODULUS]; + char hd_retxqi; + char hd_rrtimer; + char hd_timer; +#define SET_TIMER(hdp) hdp->hd_timer = hd_t1 +#define KILL_TIMER(hdp) hdp->hd_timer = 0 + char hd_dontcopy; /* if-driver doesn't free I-frames */ + struct ifnet *hd_ifp; /* device's network visible interface */ + struct ifaddr *hd_ifa; /* device's X.25 network address */ + struct x25config *hd_xcp; + caddr_t hd_pkp; /* Level III junk */ + int (*hd_output)(); /* separate entry for HDLC direct output */ + + /* link statistics */ + + long hd_iframes_in; + long hd_iframes_out; + long hd_rrs_in; + long hd_rrs_out; + short hd_rejs_in; + short hd_rejs_out; + long hd_window_condition; + short hd_invalid_ns; + short hd_invalid_nr; + short hd_timeouts; + short hd_resets; + short hd_unknown; + short hd_frmrs_in; + short hd_frmrs_out; + short hd_rnrs_in; + short hd_rnrs_out; +}; + +#ifdef _KERNEL +struct hdcb *hdcbhead; /* head of linked list of hdcb's */ +struct Frmr_frame hd_frmr; /* rejected frame diagnostic info */ +struct ifqueue hdintrq; /* hdlc packet input queue */ + +int hd_t1; /* timer T1 value */ +int hd_t3; /* RR send timer */ +int hd_n2; /* frame retransmission limit */ +#endif diff --git a/sys/netccitt/hdlc.h b/sys/netccitt/hdlc.h new file mode 100644 index 00000000000..ace9af41cc9 --- /dev/null +++ b/sys/netccitt/hdlc.h @@ -0,0 +1,158 @@ +/* $NetBSD: hdlc.h,v 1.5 1994/06/29 06:37:17 cgd Exp $ */ + +/*- + * Copyright (c) University of British Columbia, 1984 + * Copyright (c) 1991, 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. + * + * 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. + * + * @(#)hdlc.h 8.1 (Berkeley) 6/10/93 + */ + +#ifndef ORDER4 +#define FALSE 0 +#define TRUE 1 +typedef u_char octet; +typedef char bool; + +/* + * HDLC Packet format definitions + * This will eventually have to be rewritten without reference + * to bit fields, to be compliant with ANSI C and alignment safe. + */ + +#if BYTE_ORDER == BIG_ENDIAN +#define ORDER4(a, b, c, d) a , b , c , d +#define ORDER5(a, b, c, d, e) a , b , c , d , e +#endif + +#if BYTE_ORDER == LITTLE_ENDIAN +#define ORDER4(a, b, c, d) d , c , b , a +#define ORDER5(a, b, c, d, e) e , d , c , b , a +#endif +#endif + +#define MAX_INFO_LEN 4096+3+4 +#define ADDRESS_A 3 /* B'00000011' */ +#define ADDRESS_B 1 /* B'00000001' */ + +struct Hdlc_iframe { + octet address; + octet ORDER4(nr:3, pf:1, ns:3, hdlc_0:1); + octet i_field[MAX_INFO_LEN]; +}; + +struct Hdlc_sframe { + octet address; + octet ORDER4(nr:3, pf:1, s2:2, hdlc_01:2); +}; + +struct Hdlc_uframe { + octet address; + octet ORDER4(m3:3, pf:1, m2:2, hdlc_11:2); +}; + +struct Frmr_frame { + octet address; + octet control; + octet frmr_control; + octet ORDER4(frmr_nr:3, frmr_f1_0:1, frmr_ns:3, frmr_f2_0:1); + octet ORDER5(frmr_0000:4, frmr_z:1, frmr_y:1, frmr_x:1, frmr_w:1); +}; + +#define HDHEADERLN 2 +#define MINFRLN 2 /* Minimum frame length. */ + +struct Hdlc_frame { + octet address; + octet control; + octet info[3]; /* min for FRMR */ +}; + +#define SABM_CONTROL 057 /* B'00101111' */ +#define UA_CONTROL 0143 /* B'01100011' */ +#define DISC_CONTROL 0103 /* B'01000011' */ +#define DM_CONTROL 017 /* B'00001111' */ +#define FRMR_CONTROL 0207 /* B'10000111' */ +#define RR_CONTROL 01 /* B'00000001' */ +#define RNR_CONTROL 05 /* B'00000101' */ +#define REJ_CONTROL 011 /* B'00001001' */ + +#define POLLOFF 0 +#define POLLON 1 + +/* Define Link State constants. */ + +#define INIT 0 +#define DM_SENT 1 +#define SABM_SENT 2 +#define ABM 3 +#define WAIT_SABM 4 +#define WAIT_UA 5 +#define DISC_SENT 6 +#define DISCONNECTED 7 +#define MAXSTATE 8 + +/* The following constants are used in a switch statement to process + frames read from the communications line. */ + +#define SABM 0 * MAXSTATE +#define DM 1 * MAXSTATE +#define DISC 2 * MAXSTATE +#define UA 3 * MAXSTATE +#define FRMR 4 * MAXSTATE +#define RR 5 * MAXSTATE +#define RNR 6 * MAXSTATE +#define REJ 7 * MAXSTATE +#define IFRAME 8 * MAXSTATE +#define ILLEGAL 9 * MAXSTATE + +#define T1 (3 * PR_SLOWHZ) /* IFRAME TIMEOUT - 3 seconds */ +#define T3 (T1 / 2) /* RR generate timeout - 1.5 seconds */ +#define N2 10 +#define MODULUS 8 +#define MAX_WINDOW_SIZE 7 + +#define Z 0 +#define Y 1 +#define X 2 +#define W 3 +#define A 4 + +#define TX 0 +#define RX 1 + +bool range_check (); +bool valid_nr (); +struct mbuf *hd_remove (); diff --git a/sys/netccitt/if_x25subr.c b/sys/netccitt/if_x25subr.c new file mode 100644 index 00000000000..42b15f0621c --- /dev/null +++ b/sys/netccitt/if_x25subr.c @@ -0,0 +1,803 @@ +/* $NetBSD: if_x25subr.c,v 1.11 1995/06/15 22:38:20 cgd Exp $ */ + +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * 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. + * + * @(#)if_x25subr.c 8.1 (Berkeley) 6/10/93 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/syslog.h> + +#include <net/if.h> +#include <net/if_types.h> +#include <net/netisr.h> +#include <net/route.h> + +#include <netccitt/x25.h> +#include <netccitt/x25err.h> +#include <netccitt/pk.h> +#include <netccitt/pk_var.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_var.h> +#endif + +#ifdef NS +#include <netns/ns.h> +#include <netns/ns_if.h> +#endif + +#ifdef ISO +int tp_incoming(); +#include <netiso/argo_debug.h> +#include <netiso/iso.h> +#include <netiso/iso_var.h> +#endif + +LIST_HEAD(, llinfo_x25) llinfo_x25; +#ifndef _offsetof +#define _offsetof(t, m) ((int)((caddr_t)&((t *)0)->m)) +#endif +struct sockaddr *x25_dgram_sockmask; +struct sockaddr_x25 x25_dgmask = { + _offsetof(struct sockaddr_x25, x25_udata[1]), /* _len */ + 0, /* _family */ + 0, /* _net */ + { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /* _addr */ + {0}, /* opts */ + -1, /* _udlen */ + {-1} /* _udata */ +}; + +struct if_x25stats { + int ifx_wrongplen; + int ifx_nophdr; +} if_x25stats; +int x25_autoconnect = 0; + +#define senderr(x) {error = x; goto bad;} +/* + * Ancillary routines + */ +static struct llinfo_x25 * +x25_lxalloc(rt) +register struct rtentry *rt; +{ + register struct llinfo_x25 *lx; + register struct sockaddr *dst = rt_key(rt); + register struct ifaddr *ifa; + + MALLOC(lx, struct llinfo_x25 *, sizeof (*lx), M_PCB, M_NOWAIT); + if (lx == 0) + return lx; + Bzero(lx, sizeof(*lx)); + lx->lx_rt = rt; + lx->lx_family = dst->sa_family; + rt->rt_refcnt++; + if (rt->rt_llinfo) { + LIST_INSERT_AFTER( + (struct llinfo_x25 *)rt->rt_llinfo, lx, lx_list); + } else { + rt->rt_llinfo = (caddr_t)lx; + LIST_INSERT_HEAD(&llinfo_x25, lx, lx_list); + } + for (ifa = rt->rt_ifp->if_addrlist.tqh_first; ifa != 0; + ifa = ifa->ifa_list.tqe_next) { + if (ifa->ifa_addr->sa_family == AF_CCITT) + lx->lx_ia = (struct x25_ifaddr *)ifa; + } + return lx; +} +x25_lxfree(lx) +register struct llinfo_x25 *lx; +{ + register struct rtentry *rt = lx->lx_rt; + register struct pklcd *lcp = lx->lx_lcd; + + if (lcp) { + lcp->lcd_upper = 0; + pk_disconnect(lcp); + } + if ((rt->rt_llinfo == (caddr_t)lx) && (lx->lx_list.le_next->lx_rt == rt)) + rt->rt_llinfo = (caddr_t)lx->lx_list.le_next; + else + rt->rt_llinfo = 0; + RTFREE(rt); + LIST_REMOVE(lx, lx_list); + FREE(lx, M_PCB); +} +/* + * Process a x25 packet as datagram; + */ +x25_ifinput(lcp, m) +struct pklcd *lcp; +register struct mbuf *m; +{ + struct llinfo_x25 *lx = (struct llinfo_x25 *)lcp->lcd_upnext; + register struct ifnet *ifp; + struct ifqueue *inq; + extern struct timeval time; + int s, len, isr; + + if (m == 0 || lcp->lcd_state != DATA_TRANSFER) { + x25_connect_callback(lcp, 0); + return; + } + pk_flowcontrol(lcp, 0, 1); /* Generate RR */ + ifp = m->m_pkthdr.rcvif; + ifp->if_lastchange = time; + switch (m->m_type) { + default: + if (m) + m_freem(m); + return; + + case MT_DATA: + /* FALLTHROUGH */; + } + switch (lx->lx_family) { +#ifdef INET + case AF_INET: + isr = NETISR_IP; + inq = &ipintrq; + break; + +#endif +#ifdef NS + case AF_NS: + isr = NETISR_NS; + inq = &nsintrq; + break; + +#endif +#ifdef ISO + case AF_ISO: + isr = NETISR_ISO; + inq = &clnlintrq; + break; +#endif + default: + m_freem(m); + ifp->if_noproto++; + return; + } + s = splimp(); + schednetisr(isr); + if (IF_QFULL(inq)) { + IF_DROP(inq); + m_freem(m); + } else { + IF_ENQUEUE(inq, m); + ifp->if_ibytes += m->m_pkthdr.len; + } + splx(s); +} +x25_connect_callback(lcp, m) +register struct pklcd *lcp; +register struct mbuf *m; +{ + register struct llinfo_x25 *lx = (struct llinfo_x25 *)lcp->lcd_upnext; + int do_clear = 1; + if (m == 0) + goto refused; + if (m->m_type != MT_CONTROL) { + printf("x25_connect_callback: should panic\n"); + goto refused; + } + switch (pk_decode(mtod(m, struct x25_packet *))) { + case CALL_ACCEPTED: + lcp->lcd_upper = x25_ifinput; + if (lcp->lcd_sb.sb_mb) + lcp->lcd_send(lcp); /* XXX start queued packets */ + return; + default: + do_clear = 0; + refused: + lcp->lcd_upper = 0; + lx->lx_lcd = 0; + if (do_clear) + pk_disconnect(lcp); + return; + } +} +#define SA(p) ((struct sockaddr *)(p)) +#define RT(p) ((struct rtentry *)(p)) + +x25_dgram_incoming(lcp, m0) +register struct pklcd *lcp; +struct mbuf *m0; +{ + register struct rtentry *rt, *nrt; + register struct mbuf *m = m0->m_next; /* m0 has calling sockaddr_x25 */ + void x25_rtrequest(); + + rt = rtalloc1(SA(&lcp->lcd_faddr), 0); + if (rt == 0) { +refuse: lcp->lcd_upper = 0; + pk_close(lcp); + return; + } + rt->rt_refcnt--; + if ((nrt = RT(rt->rt_llinfo)) == 0 || rt_mask(rt) != x25_dgram_sockmask) + goto refuse; + if ((nrt->rt_flags & RTF_UP) == 0) { + rt->rt_llinfo = (caddr_t)rtalloc1(rt->rt_gateway, 0); + rtfree(nrt); + if ((nrt = RT(rt->rt_llinfo)) == 0) + goto refuse; + nrt->rt_refcnt--; + } + if (nrt->rt_ifa == 0 || nrt->rt_ifa->ifa_rtrequest != x25_rtrequest) + goto refuse; + lcp->lcd_send(lcp); /* confirm call */ + x25_rtattach(lcp, nrt); + m_freem(m); +} + +/* + * X.25 output routine. + */ +x25_ifoutput(ifp, m0, dst, rt) +struct ifnet *ifp; +struct mbuf *m0; +struct sockaddr *dst; +register struct rtentry *rt; +{ + register struct mbuf *m = m0; + register struct llinfo_x25 *lx; + struct pklcd *lcp; + int s, error = 0; + +int plen; +for (plen = 0; m; m = m->m_next) + plen += m->m_len; +m = m0; + + if ((ifp->if_flags & IFF_UP) == 0) + senderr(ENETDOWN); + while (rt == 0 || (rt->rt_flags & RTF_GATEWAY)) { + if (rt) { + if (rt->rt_llinfo) { + rt = (struct rtentry *)rt->rt_llinfo; + continue; + } + dst = rt->rt_gateway; + } + if ((rt = rtalloc1(dst, 1)) == 0) + senderr(EHOSTUNREACH); + rt->rt_refcnt--; + } + /* + * Sanity checks. + */ + if ((rt->rt_ifp != ifp) || + (rt->rt_flags & (RTF_CLONING | RTF_GATEWAY)) || + ((lx = (struct llinfo_x25 *)rt->rt_llinfo) == 0)) { + senderr(ENETUNREACH); + } +if ((m->m_flags & M_PKTHDR) == 0) { + if_x25stats.ifx_nophdr++; + m = m_gethdr(M_NOWAIT, MT_HEADER); + if (m == 0) + senderr(ENOBUFS); + m->m_pkthdr.len = plen; + m->m_next = m0; +} +if (plen != m->m_pkthdr.len) { + if_x25stats.ifx_wrongplen++; + m->m_pkthdr.len = plen; +} +next_circuit: + lcp = lx->lx_lcd; + if (lcp == 0) { + lx->lx_lcd = lcp = pk_attach((struct socket *)0); + if (lcp == 0) + senderr(ENOBUFS); + lcp->lcd_upper = x25_connect_callback; + lcp->lcd_upnext = (caddr_t)lx; + lcp->lcd_packetsize = lx->lx_ia->ia_xc.xc_psize; + lcp->lcd_flags = X25_MBS_HOLD; + } + switch (lcp->lcd_state) { + case READY: + if (dst->sa_family == AF_INET && + ifp->if_type == IFT_X25DDN && + rt->rt_gateway->sa_family != AF_CCITT) + x25_ddnip_to_ccitt(dst, rt); + if (rt->rt_gateway->sa_family != AF_CCITT) { + if ((rt->rt_flags & RTF_XRESOLVE) == 0) + senderr(EHOSTUNREACH); + } else if (x25_autoconnect) + error = pk_connect(lcp, + (struct sockaddr_x25 *)rt->rt_gateway); + if (error) + senderr(error); + /* FALLTHROUGH */ + case SENT_CALL: + case DATA_TRANSFER: + if (sbspace(&lcp->lcd_sb) < 0) { + lx = lx->lx_list.le_next; + if (lx->lx_rt != rt) + senderr(ENOSPC); + goto next_circuit; + } + if (lx->lx_ia) + lcp->lcd_dg_timer = + lx->lx_ia->ia_xc.xc_dg_idletimo; + pk_send(lcp, m); + break; + default: + /* + * We count on the timer routine to close idle + * connections, if there are not enough circuits to go + * around. + * + * So throw away data for now. + * After we get it all working, we'll rewrite to handle + * actively closing connections (other than by timers), + * when circuits get tight. + * + * In the DDN case, the imp itself closes connections + * under heavy load. + */ + error = ENOBUFS; + bad: + if (m) + m_freem(m); + } + return (error); +} + +/* + * Simpleminded timer routine. + */ +x25_iftimeout(ifp) +struct ifnet *ifp; +{ + register struct pkcb *pkcb = 0; + register struct pklcd **lcpp, *lcp; + int s = splimp(); + + FOR_ALL_PKCBS(pkcb) + if (pkcb->pk_ia->ia_ifp == ifp) + for (lcpp = pkcb->pk_chan + pkcb->pk_maxlcn; + --lcpp > pkcb->pk_chan;) + if ((lcp = *lcpp) && + lcp->lcd_state == DATA_TRANSFER && + (lcp->lcd_flags & X25_DG_CIRCUIT) && + (lcp->lcd_dg_timer && --lcp->lcd_dg_timer == 0)) { + lcp->lcd_upper(lcp, 0); + } + splx(s); +} +/* + * This routine gets called when validating additions of new routes + * or deletions of old ones. + */ +void +x25_rtrequest(cmd, rt, dst) + register struct rtentry *rt; + struct sockaddr *dst; +{ + register struct llinfo_x25 *lx = (struct llinfo_x25 *)rt->rt_llinfo; + register struct sockaddr_x25 *sa =(struct sockaddr_x25 *)rt->rt_gateway; + register struct pklcd *lcp; + + /* would put this pk_init, except routing table doesn't + exist yet. */ + if (x25_dgram_sockmask == 0) { + struct radix_node *rn_addmask(); + x25_dgram_sockmask = + SA(rn_addmask((caddr_t)&x25_dgmask, 0, 4)->rn_key); + } + if (rt->rt_flags & RTF_GATEWAY) { + if (rt->rt_llinfo) + RTFREE((struct rtentry *)rt->rt_llinfo); + rt->rt_llinfo = (cmd == RTM_ADD) ? + (caddr_t)rtalloc1(rt->rt_gateway, 1) : 0; + return; + } + if ((rt->rt_flags & RTF_HOST) == 0) + return; + if (cmd == RTM_DELETE) { + while (rt->rt_llinfo) + x25_lxfree((struct llinfo *)rt->rt_llinfo); + x25_rtinvert(RTM_DELETE, rt->rt_gateway, rt); + return; + } + if (lx == 0 && (lx = x25_lxalloc(rt)) == 0) + return; + if ((lcp = lx->lx_lcd) && lcp->lcd_state != READY) { + /* + * This can only happen on a RTM_CHANGE operation + * though cmd will be RTM_ADD. + */ + if (lcp->lcd_ceaddr && + Bcmp(rt->rt_gateway, lcp->lcd_ceaddr, + lcp->lcd_ceaddr->x25_len) != 0) { + x25_rtinvert(RTM_DELETE, lcp->lcd_ceaddr, rt); + lcp->lcd_upper = 0; + pk_disconnect(lcp); + } + lcp = 0; + } + x25_rtinvert(RTM_ADD, rt->rt_gateway, rt); +} + +int x25_dont_rtinvert = 0; + +x25_rtinvert(cmd, sa, rt) +register struct sockaddr *sa; +register struct rtentry *rt; +{ + struct rtentry *rt2 = 0; + /* + * rt_gateway contains PID indicating which proto + * family on the other end, so will be different + * from general host route via X.25. + */ + if (rt->rt_ifp->if_type == IFT_X25DDN || x25_dont_rtinvert) + return; + if (sa->sa_family != AF_CCITT) + return; + if (cmd != RTM_DELETE) { + rtrequest(RTM_ADD, sa, rt_key(rt), x25_dgram_sockmask, + RTF_PROTO2, &rt2); + if (rt2) { + rt2->rt_llinfo = (caddr_t) rt; + rt->rt_refcnt++; + } + return; + } + rt2 = rt; + if ((rt = rtalloc1(sa, 0)) == 0 || + (rt->rt_flags & RTF_PROTO2) == 0 || + rt->rt_llinfo != (caddr_t)rt2) { + printf("x25_rtchange: inverse route screwup\n"); + return; + } else + rt2->rt_refcnt--; + rtrequest(RTM_DELETE, sa, rt_key(rt2), x25_dgram_sockmask, + 0, (struct rtentry **) 0); +} + +static struct sockaddr_x25 blank_x25 = {sizeof blank_x25, AF_CCITT}; +/* + * IP to X25 address routine copyright ACC, used by permission. + */ +union imp_addr { + struct in_addr ip; + struct imp { + u_char s_net; + u_char s_host; + u_char s_lh; + u_char s_impno; + } imp; +}; + +/* + * The following is totally bogus and here only to preserve + * the IP to X.25 translation. + */ +x25_ddnip_to_ccitt(src, rt) +struct sockaddr_in *src; +register struct rtentry *rt; +{ + register struct sockaddr_x25 *dst = (struct sockaddr_x25 *)rt->rt_gateway; + union imp_addr imp_addr; + int imp_no, imp_port, temp; + char *x25addr = dst->x25_addr; + + + imp_addr.ip = src->sin_addr; + *dst = blank_x25; + if ((imp_addr.imp.s_net & 0x80) == 0x00) { /* class A */ + imp_no = imp_addr.imp.s_impno; + imp_port = imp_addr.imp.s_host; + } else if ((imp_addr.imp.s_net & 0xc0) == 0x80) { /* class B */ + imp_no = imp_addr.imp.s_impno; + imp_port = imp_addr.imp.s_lh; + } else { /* class C */ + imp_no = imp_addr.imp.s_impno / 32; + imp_port = imp_addr.imp.s_impno % 32; + } + + x25addr[0] = 12; /* length */ + /* DNIC is cleared by struct copy above */ + + if (imp_port < 64) { /* Physical: 0000 0 IIIHH00 [SS] *//* s_impno + * -> III, s_host -> HH */ + x25addr[5] = 0; /* set flag bit */ + x25addr[6] = imp_no / 100; + x25addr[7] = (imp_no % 100) / 10; + x25addr[8] = imp_no % 10; + x25addr[9] = imp_port / 10; + x25addr[10] = imp_port % 10; + } else { /* Logical: 0000 1 RRRRR00 [SS] *//* s + * _host * 256 + s_impno -> RRRRR */ + temp = (imp_port << 8) + imp_no; + x25addr[5] = 1; + x25addr[6] = temp / 10000; + x25addr[7] = (temp % 10000) / 1000; + x25addr[8] = (temp % 1000) / 100; + x25addr[9] = (temp % 100) / 10; + x25addr[10] = temp % 10; + } +} + +/* + * This routine is a sketch and is not to be believed!!!!! + * + * This is a utility routine to be called by x25 devices when a + * call request is honored with the intent of starting datagram forwarding. + */ +x25_dg_rtinit(dst, ia, af) +struct sockaddr_x25 *dst; +register struct x25_ifaddr *ia; +{ + struct sockaddr *sa = 0; + struct rtentry *rt; + struct in_addr my_addr; + static struct sockaddr_in sin = {sizeof(sin), AF_INET}; + + if (ia->ia_ifp->if_type == IFT_X25DDN && af == AF_INET) { + /* + * Inverse X25 to IP mapping copyright and courtesy ACC. + */ + int imp_no, imp_port, temp; + union imp_addr imp_addr; + { + /* + * First determine our IP addr for network + */ + register struct in_ifaddr *ina; + + for (ina = in_ifaddr.tqh_first; ina != 0; + ina = ina->ia_list.tqe_next) + if (ina->ia_ifp == ia->ia_ifp) { + my_addr = ina->ia_addr.sin_addr; + break; + } + } + { + + register char *x25addr = dst->x25_addr; + + switch (x25addr[5] & 0x0f) { + case 0: /* Physical: 0000 0 IIIHH00 [SS] */ + imp_no = + ((int) (x25addr[6] & 0x0f) * 100) + + ((int) (x25addr[7] & 0x0f) * 10) + + ((int) (x25addr[8] & 0x0f)); + + + imp_port = + ((int) (x25addr[9] & 0x0f) * 10) + + ((int) (x25addr[10] & 0x0f)); + break; + case 1: /* Logical: 0000 1 RRRRR00 [SS] */ + temp = ((int) (x25addr[6] & 0x0f) * 10000) + + ((int) (x25addr[7] & 0x0f) * 1000) + + ((int) (x25addr[8] & 0x0f) * 100) + + ((int) (x25addr[9] & 0x0f) * 10) + + ((int) (x25addr[10] & 0x0f)); + + imp_port = temp >> 8; + imp_no = temp & 0xff; + break; + default: + return (0L); + } + imp_addr.ip = my_addr; + if ((imp_addr.imp.s_net & 0x80) == 0x00) { + /* class A */ + imp_addr.imp.s_host = imp_port; + imp_addr.imp.s_impno = imp_no; + imp_addr.imp.s_lh = 0; + } else if ((imp_addr.imp.s_net & 0xc0) == 0x80) { + /* class B */ + imp_addr.imp.s_lh = imp_port; + imp_addr.imp.s_impno = imp_no; + } else { + /* class C */ + imp_addr.imp.s_impno = (imp_no << 5) + imp_port; + } + } + sin.sin_addr = imp_addr.ip; + sa = (struct sockaddr *)&sin; + } else { + /* + * This uses the X25 routing table to do inverse + * lookup of x25 address to sockaddr. + */ + if (rt = rtalloc1(SA(dst), 0)) { + sa = rt->rt_gateway; + rt->rt_refcnt--; + } + } + /* + * Call to rtalloc1 will create rtentry for reverse path + * to callee by virtue of cloning magic and will allocate + * space for local control block. + */ + if (sa && (rt = rtalloc1(sa, 1))) + rt->rt_refcnt--; +} +int x25_startproto = 1; + +pk_init() +{ + /* + * warning, sizeof (struct sockaddr_x25) > 32, + * but contains no data of interest beyond 32 + */ + if (x25_startproto) { + pk_protolisten(0xcc, 1, x25_dgram_incoming); + pk_protolisten(0x81, 1, x25_dgram_incoming); + } +} + +struct x25_dgproto { + u_char spi; + u_char spilen; + int (*f)(); +} x25_dgprototab[] = { +#if defined(ISO) && defined(TPCONS) +{ 0x0, 0, tp_incoming}, +#endif +{ 0xcc, 1, x25_dgram_incoming}, +{ 0xcd, 1, x25_dgram_incoming}, +{ 0x81, 1, x25_dgram_incoming}, +}; + +pk_user_protolisten(info) +register u_char *info; +{ + register struct x25_dgproto *dp = x25_dgprototab + + ((sizeof x25_dgprototab) / (sizeof *dp)); + register struct pklcd *lcp; + + while (dp > x25_dgprototab) + if ((--dp)->spi == info[0]) + goto gotspi; + return ESRCH; + +gotspi: if (info[1]) + return pk_protolisten(dp->spi, dp->spilen, dp->f); + for (lcp = pk_listenhead; lcp; lcp = lcp->lcd_listen) + if (lcp->lcd_laddr.x25_udlen == dp->spilen && + Bcmp(&dp->spi, lcp->lcd_laddr.x25_udata, dp->spilen) == 0) { + pk_disconnect(lcp); + return 0; + } + return ESRCH; +} + +/* + * This routine transfers an X.25 circuit to or from a routing entry. + * If the supplied circuit is * in DATA_TRANSFER state, it is added to the + * routing entry. If freshly allocated, it glues back the vc from + * the rtentry to the socket. + */ +pk_rtattach(so, m0) +register struct socket *so; +struct mbuf *m0; +{ + register struct pklcd *lcp = (struct pklcd *)so->so_pcb; + register struct mbuf *m = m0; + struct sockaddr *dst = mtod(m, struct sockaddr *); + register struct rtentry *rt = rtalloc1(dst, 0); + register struct llinfo_x25 *lx; + caddr_t cp; +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) +#define transfer_sockbuf(s, f, l) \ + while (m = (s)->sb_mb)\ + {(s)->sb_mb = m->m_act; m->m_act = 0; sbfree((s), m); f(l, m);} + + if (rt) + rt->rt_refcnt--; + cp = (dst->sa_len < m->m_len) ? ROUNDUP(dst->sa_len) + (caddr_t)dst : 0; + while (rt && + ((cp == 0 && rt_mask(rt) != 0) || + (cp != 0 && (rt_mask(rt) == 0 || + Bcmp(cp, rt_mask(rt), rt_mask(rt)->sa_len)) != 0))) + rt = (struct rtentry *)rt->rt_nodes->rn_dupedkey; + if (rt == 0 || (rt->rt_flags & RTF_GATEWAY) || + (lx = (struct llinfo_x25 *)rt->rt_llinfo) == 0) + return ESRCH; + if (lcp == 0) + return ENOTCONN; + switch (lcp->lcd_state) { + default: + return ENOTCONN; + + case READY: + /* Detach VC from rtentry */ + if (lx->lx_lcd == 0) + return ENOTCONN; + lcp->lcd_so = 0; + pk_close(lcp); + lcp = lx->lx_lcd; + if (lx->lx_list.le_next->lx_rt == rt) + x25_lxfree(lx); + lcp->lcd_so = so; + lcp->lcd_upper = 0; + lcp->lcd_upnext = 0; + transfer_sockbuf(&lcp->lcd_sb, sbappendrecord, &so->so_snd); + soisconnected(so); + return 0; + + case DATA_TRANSFER: + /* Add VC to rtentry */ + lcp->lcd_so = 0; + lcp->lcd_sb = so->so_snd; /* structure copy */ + bzero((caddr_t)&so->so_snd, sizeof(so->so_snd)); /* XXXXXX */ + so->so_pcb = 0; + x25_rtattach(lcp, rt); + transfer_sockbuf(&so->so_rcv, x25_ifinput, lcp); + soisdisconnected(so); + } + return 0; +} +x25_rtattach(lcp0, rt) +register struct pklcd *lcp0; +struct rtentry *rt; +{ + register struct llinfo_x25 *lx = (struct llinfo_x25 *)rt->rt_llinfo; + register struct pklcd *lcp; + register struct mbuf *m; + if (lcp = lx->lx_lcd) { /* adding an additional VC */ + if (lcp->lcd_state == READY) { + transfer_sockbuf(&lcp->lcd_sb, pk_output, lcp0); + lcp->lcd_upper = 0; + pk_close(lcp); + } else { + lx = x25_lxalloc(rt); + if (lx == 0) + return ENOBUFS; + } + } + lx->lx_lcd = lcp = lcp0; + lcp->lcd_upper = x25_ifinput; + lcp->lcd_upnext = (caddr_t)lx; +} diff --git a/sys/netccitt/llc_input.c b/sys/netccitt/llc_input.c new file mode 100644 index 00000000000..1ee073b03dc --- /dev/null +++ b/sys/netccitt/llc_input.c @@ -0,0 +1,470 @@ +/* $NetBSD: llc_input.c,v 1.2 1994/06/29 06:37:21 cgd Exp $ */ + +/* + * Copyright (C) Dirk Husemann, Computer Science Department IV, + * University of Erlangen-Nuremberg, Germany, 1990, 1991, 1992 + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Dirk Husemann 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. + * + * @(#)llc_input.c 8.1 (Berkeley) 6/10/93 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/domain.h> +#include <sys/socket.h> +#include <sys/protosw.h> +#include <sys/errno.h> +#include <sys/time.h> +#include <sys/kernel.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_llc.h> +#include <net/route.h> + +#include <netccitt/dll.h> +#include <netccitt/llc_var.h> + +/* + * This module implements LLC as specified by ISO 8802-2. + */ + + +/* + * llcintr() handles all LLC frames (except ISO CLNS ones for the time being) + * and tries to pass them on to the appropriate network layer entity. + */ +void +llcintr() +{ + register struct mbuf *m; + register int i; + register int frame_kind; + register u_char cmdrsp; + struct llc_linkcb *linkp; + struct rtentry *sirt; + struct npaidbentry *sapinfo; + struct sdl_hdr *sdlhdr; + struct llc *frame; + char *c; + long expected_len; + + struct ifnet *ifp; + struct rtentry *llrt; + struct rtentry *nlrt; + + for (;;) { + i = splimp(); + IF_DEQUEUE(&llcintrq, m); + splx(i); + if (m == 0) + break; +#ifdef DIAGNOSTIC + if ((m->m_flags & M_PKTHDR) == 0) + panic("llcintr no HDR"); +#endif + /* + * Get ifp this packet was received on + */ + ifp = m->m_pkthdr.rcvif; + + sdlhdr = mtod(m, struct sdl_hdr *); + + /* + * [Copied from net/ip_input.c] + * + * Check that the amount of data in the buffers is + * at least as much as the LLC header tells us. + * Trim mbufs if longer than expected. + * Drop packets if shorter than we think they are. + * + * Layout of mbuf chain at this point: + * + * +-------------------------------+----+ -\ + * | sockaddr_dl src - sdlhdr_src | 20 | \ + * +-------------------------------+----+ | + * | sockaddr_dl dst - sdlhdr_dst | 20 | > sizeof(struct sdl_hdr) == 44 + * +-------------------------------+----+ | + * | LLC frame len - sdlhdr_len | 04 | / + * +-------------------------------+----+ -/ + * / + * | m_next + * \ + * +----------------------------+----+ -\ + * | llc DSAP | 01 | \ + * +----------------------------+----+ | + * | llc SSAP | 01 | | + * +----------------------------+----+ > sdlhdr_len + * | llc control | 01 | | + * +----------------------------+----+ | + * | ... | | / + * -/ + * + * Thus the we expect to have exactly + * (sdlhdr->sdlhdr_len+sizeof(struct sdl_hdr)) in the mbuf chain + */ + expected_len = sdlhdr->sdlhdr_len + sizeof(struct sdl_hdr); + + if (m->m_pkthdr.len < expected_len) { + m_freem(m); + continue; + } + if (m->m_pkthdr.len > expected_len) { + if (m->m_len == m->m_pkthdr.len) { + m->m_len = expected_len; + m->m_pkthdr.len = expected_len; + } else + m_adj(m, expected_len - m->m_pkthdr.len); + } + + /* + * Get llc header + */ + if (m->m_len > sizeof(struct sdl_hdr)) + frame = mtod((struct mbuf *)((struct sdl_hdr*)(m+1)), + struct llc *); + else frame = mtod(m->m_next, struct llc *); + if (frame == (struct llc *) NULL) + panic("llcintr no llc header"); + + /* + * Now check for bogus I/S frame, i.e. those with a control + * field telling us they're an I/S frame yet their length + * is less than the established I/S frame length (DSAP + SSAP + + * control + N(R)&P/F = 4) --- we drop those suckers + */ + if (((frame->llc_control & 0x03) != 0x03) + && ((expected_len - sizeof(struct sdl_hdr)) < LLC_ISFRAMELEN)) { + m_freem(m); + printf("llc: hurz error\n"); + continue; + } + + /* + * Get link control block for the addressed link connection. + * If there is none we take care of it later on. + */ + cmdrsp = (frame->llc_ssap & 0x01); + frame->llc_ssap &= ~0x01; + if (llrt = rtalloc1((struct sockaddr *)&sdlhdr->sdlhdr_src, 0)) + llrt->rt_refcnt--; +#ifdef notyet + else llrt = npaidb_enter(&sdlhdr->sdlhdr_src, 0, 0, 0); +#endif /* notyet */ + else { + /* + * We cannot do anything currently here as we + * don't `know' this link --- drop it + */ + m_freem(m); + continue; + } + linkp = ((struct npaidbentry *)(llrt->rt_llinfo))->np_link; + nlrt = ((struct npaidbentry *)(llrt->rt_llinfo))->np_rt; + + /* + * If the link is not existing right now, we can try and look up + * the SAP info block. + */ + if ((linkp == 0) && frame->llc_ssap) + sapinfo = llc_getsapinfo(frame->llc_dsap, ifp); + + /* + * Handle XID and TEST frames + * XID: if DLSAP == 0, return type-of-services + * window-0 + * DLSAP-0 + * format-identifier-? + * if DLSAP != 0, locate sapcb and return + * type-of-services + * SAP-window + * format-identifier-? + * TEST: swap (snpah_dst, snpah_src) and return frame + * + * Also toggle the CMD/RESP bit + * + * Is this behaviour correct? Check ISO 8802-2 (90)! + */ + frame_kind = llc_decode(frame, (struct llc_linkcb *)0); + switch(frame_kind) { + case LLCFT_XID: + if (linkp || sapinfo) { + if (linkp) + frame->llc_window = linkp->llcl_window; + else frame->llc_window = sapinfo->si_window; + frame->llc_fid = 9; /* XXX */ + frame->llc_class = sapinfo->si_class; + frame->llc_ssap = frame->llc_dsap; + } else { + frame->llc_window = 0; + frame->llc_fid = 9; + frame->llc_class = 1; + frame->llc_dsap = frame->llc_ssap = 0; + } + + /* fall thru to */ + case LLCFT_TEST: + sdl_swapaddr(&(mtod(m, struct sdl_hdr *)->sdlhdr_dst), + &(mtod(m, struct sdl_hdr *)->sdlhdr_src)); + + /* Now set the CMD/RESP bit */ + frame->llc_ssap |= (cmdrsp == 0x0 ? 0x1 : 0x0); + + /* Ship it out again */ + (*ifp->if_output)(ifp, m, + (struct sockaddr *) &(mtod(m, struct sdl_hdr *)->sdlhdr_dst), + (struct rtentry *) 0); + continue; + } + + /* + * Create link control block in case it is not existing + */ + if (linkp == 0 && sapinfo) { + if ((linkp = llc_newlink(&sdlhdr->sdlhdr_src, ifp, nlrt, + (nlrt == 0) ? 0 : nlrt->rt_llinfo, + llrt)) == 0) { + printf("llcintr: couldn't create new link\n"); + m_freem(m); + continue; + } + ((struct npaidbentry *)llrt->rt_llinfo)->np_link = linkp; + } else if (linkp == 0) { + /* The link is not known to us, drop the frame and continue */ + m_freem(m); + continue; + } + + /* + * Drop SNPA header and get rid of empty mbuf at the + * front of the mbuf chain (I don't like 'em) + */ + m_adj(m, sizeof(struct sdl_hdr)); + /* + * LLC_UFRAMELEN is sufficient, m_pullup() will pull up + * the min(m->m_len, maxprotohdr_len [=40]) thus doing + * the trick ... + */ + if ((m = m_pullup(m, LLC_UFRAMELEN))) + /* + * Pass it on thru the elements of procedure + */ + llc_input(linkp, m, cmdrsp); + } + return; +} + +/* + * llc_input() --- We deal with the various incoming frames here. + * Basically we (indirectly) call the appropriate + * state handler function that's pointed to by + * llcl_statehandler. + * + * The statehandler returns an action code --- + * further actions like + * o notify network layer + * o block further sending + * o deblock link + * o ... + * are then enacted accordingly. + */ +llc_input(struct llc_linkcb *linkp, struct mbuf *m, u_char cmdrsp) +{ + int frame_kind; + int pollfinal; + int action = 0; + struct llc *frame; + struct ifnet *ifp = linkp->llcl_if; + + if ((frame = mtod(m, struct llc *)) == (struct llc *) 0) { + m_freem(m); + return 0; + } + pollfinal = ((frame->llc_control & 0x03) == 0x03) ? + LLCGBITS(frame->llc_control, u_pf) : + LLCGBITS(frame->llc_control_ext, s_pf); + + /* + * first decode the frame + */ + frame_kind = llc_decode(frame, linkp); + + switch (action = llc_statehandler(linkp, frame, frame_kind, cmdrsp, + pollfinal)) { + case LLC_DATA_INDICATION: + m_adj(m, LLC_ISFRAMELEN); + if (m = m_pullup(m, NLHDRSIZEGUESS)) { + m->m_pkthdr.rcvif = (struct ifnet *)linkp->llcl_nlnext; + (*linkp->llcl_sapinfo->si_input)(m); + } + break; + } + + /* release mbuf if not an info frame */ + if (action != LLC_DATA_INDICATION && m) + m_freem(m); + + /* try to get frames out ... */ + llc_start(linkp); + + return 0; +} + +/* + * This routine is called by configuration setup. It sets up a station control + * block and notifies all registered upper level protocols. + */ +caddr_t +llc_ctlinput(int prc, struct sockaddr *addr, caddr_t info) +{ + struct ifnet *ifp; + struct ifaddr *ifa; + struct dll_ctlinfo *ctlinfo = (struct dll_ctlinfo *)info; + u_char sap; + struct dllconfig *config; + caddr_t pcb; + struct rtentry *nlrt; + struct rtentry *llrt; + struct llc_linkcb *linkp; + register int i; + + /* info must point to something valid at all times */ + if (info == 0) + return 0; + + if (prc == PRC_IFUP || prc == PRC_IFDOWN) { + /* we use either this set ... */ + ifa = ifa_ifwithaddr(addr); + ifp = ifa ? ifa->ifa_ifp : 0; + if (ifp == 0) + return 0; + + sap = ctlinfo->dlcti_lsap; + config = ctlinfo->dlcti_cfg; + pcb = (caddr_t) 0; + nlrt = (struct rtentry *) 0; + } else { + /* or this one */ + sap = 0; + config = (struct dllconfig *) 0; + pcb = ctlinfo->dlcti_pcb; + nlrt = ctlinfo->dlcti_rt; + + if ((llrt = rtalloc1(nlrt->rt_gateway, 0))) + llrt->rt_refcnt--; + else return 0; + + linkp = ((struct npaidbentry *)llrt->rt_llinfo)->np_link; + } + + switch (prc) { + case PRC_IFUP: + (void) llc_setsapinfo(ifp, addr->sa_family, sap, config); + return 0; + + case PRC_IFDOWN: { + register struct llc_linkcb *linkp; + register struct llc_linkcb *nlinkp; + register int i; + + /* + * All links are accessible over the doubly linked list llccb_q + */ + if (!LQEMPTY) { + /* + * A for-loop is not that great an idea as the linkp + * will get deleted by llc_timer() + */ + linkp = LQFIRST; + while (LQVALID(linkp)) { + nlinkp = LQNEXT(linkp); + if (linkp->llcl_if = ifp) { + i = splimp(); + (void)llc_statehandler(linkp, (struct llc *)0, + NL_DISCONNECT_REQUEST, + 0, 1); + splx(i); + } + linkp = nlinkp; + } + } + } + + case PRC_CONNECT_REQUEST: + if (linkp == 0) { + if ((linkp = llc_newlink((struct sockaddr_dl *) nlrt->rt_gateway, + nlrt->rt_ifp, nlrt, + pcb, llrt)) == 0) + return (0); + ((struct npaidbentry *)llrt->rt_llinfo)->np_link = linkp; + i = splimp(); + (void)llc_statehandler(linkp, (struct llc *) 0, + NL_CONNECT_REQUEST, 0, 1); + splx(i); + } + return ((caddr_t)linkp); + + case PRC_DISCONNECT_REQUEST: + if (linkp == 0) + panic("no link control block!"); + + i = splimp(); + (void)llc_statehandler(linkp, (struct llc *) 0, + NL_DISCONNECT_REQUEST, 0, 1); + splx(i); + + /* + * The actual removal of the link control block is done by the + * cleaning neutrum (i.e. llc_timer()). + */ + break; + + case PRC_RESET_REQUEST: + if (linkp == 0) + panic("no link control block!"); + + i = splimp(); + (void)llc_statehandler(linkp, (struct llc *) 0, + NL_RESET_REQUEST, 0, 1); + splx(i); + + break; + + } + + return 0; +} diff --git a/sys/netccitt/llc_output.c b/sys/netccitt/llc_output.c new file mode 100644 index 00000000000..70df6288398 --- /dev/null +++ b/sys/netccitt/llc_output.c @@ -0,0 +1,306 @@ +/* $NetBSD: llc_output.c,v 1.2 1994/06/29 06:37:23 cgd Exp $ */ + +/* + * Copyright (C) Dirk Husemann, Computer Science Department IV, + * University of Erlangen-Nuremberg, Germany, 1990, 1991, 1992 + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Dirk Husemann 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. + * + * @(#)llc_output.c 8.1 (Berkeley) 6/10/93 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/domain.h> +#include <sys/socket.h> +#include <sys/protosw.h> +#include <sys/errno.h> +#include <sys/time.h> +#include <sys/kernel.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_llc.h> +#include <net/route.h> + +#include <netccitt/dll.h> +#include <netccitt/llc_var.h> + +/* + * llc_output() --- called by an upper layer (network layer) entity whenever + * there is an INFO frame to be transmitted. We enqueue the + * info frame and call llc_start() to do the actual sending. + */ + +llc_output(struct llc_linkcb *linkp, struct mbuf *m) +{ + register int i; + + i = splimp(); + LLC_ENQUEUE(linkp, m); + llc_start(linkp); + splx(i); + +} + + +/* + * llc_start() --- We try to subsequently dequeue all the frames available and + * send them out. + */ +void +llc_start(struct llc_linkcb *linkp) +{ + register int i; + register struct mbuf *m; + int action; + + while ((LLC_STATEEQ(linkp, NORMAL) || LLC_STATEEQ(linkp, BUSY) || + LLC_STATEEQ(linkp, REJECT)) && + (linkp->llcl_slotsfree > 0) && + (LLC_GETFLAG(linkp, REMOTE_BUSY) == 0)) { + LLC_DEQUEUE(linkp, m); + if (m == NULL) + break; + LLC_SETFRAME(linkp, m); + (void)llc_statehandler(linkp, (struct llc *) 0, NL_DATA_REQUEST, + 0, 0); + } +} + + +/* + * llc_send() --- Handles single frames. If dealing with INFO frames we need to + * prepend the LLC header, otherwise we just allocate an mbuf. + * In both cases the actual send is done by llc_rawsend(). + */ +llc_send(struct llc_linkcb *linkp, int frame_kind, int cmdrsp, int pollfinal) +{ + register struct mbuf *m = (struct mbuf *)0; + register struct llc *frame; + + if (frame_kind == LLCFT_INFO) + m = linkp->llcl_output_buffers[llc_seq2slot(linkp, + linkp->llcl_vs)]; + LLC_GETHDR(frame, m); + + /* pass it on to llc_rawsend() */ + llc_rawsend(linkp, m, frame, frame_kind, linkp->llcl_vs, cmdrsp, pollfinal); + + if (frame_kind == LLCFT_INFO) + LLC_INC(linkp->llcl_vs); + + return 0; +} + +/* + * llc_resend() --- llc_resend() retransmits all unacknowledged INFO frames. + */ +llc_resend(struct llc_linkcb *linkp, int cmdrsp, int pollfinal) +{ + register struct llc *frame; + register struct mbuf *m; + register int seq, slot; + + if (linkp->llcl_slotsfree < linkp->llcl_window) + /* assert lock between nr_received & V(S) */ + if (linkp->llcl_nr_received != linkp->llcl_vs) + panic("llc: V(S) != N(R) received\n"); + + for (slot = llc_seq2slot(linkp, linkp->llcl_vs); + slot != linkp->llcl_freeslot; + LLC_INC(linkp->llcl_vs), + slot = llc_seq2slot(linkp, linkp->llcl_vs)) { + m = linkp->llcl_output_buffers[slot]; + LLC_GETHDR(frame, m); + llc_rawsend(linkp, m, frame, LLCFT_INFO, linkp->llcl_vs, + cmdrsp, pollfinal); + pollfinal = 0; + } + + return 0; +} + +/* + * llc_rawsend() --- constructs an LLC frame and sends it out via the + * associated interface of the link control block. + * + * We need to make sure that outgoing frames have the correct length, + * in particular the 4 byte ones (RR, RNR, REJ) as LLC_GETHDR() will + * set the mbuf len to 3 as default len for non INFO frames ... + * + * Frame kind Length (w/o MAC header, {D,S}SAP incl.) + * -------------------------------------------------------------- + * DISC, SABME, UA, DM 3 bytes ({D,S}SAP + CONTROL) + * RR, RNR, REJ 4 bytes ({D,S}SAP + CONTROL0 + CONTROL1) + * XID 6 bytes ({D,S}SAP + CONTROL0 + FI,CLASS,WINDOW) + * FRMR 7 bytes ({D,S}SAP + CONTROL0 + REJ CONTROL,V(S),V(R),CAUSE) + * INFO 4 -- MTU + * UI, TEST 3 -- MTU + * + */ +#define LLC_SETLEN(m, l) (m)->m_pkthdr.len = (m)->m_len = (l) + +llc_rawsend(struct llc_linkcb *linkp, struct mbuf *m, struct llc *frame, + int frame_kind, int vs, int cmdrsp, int pollfinal) +{ + register short adjust = LLC_UFRAMELEN; + struct ifnet *ifp; + + switch (frame_kind) { + /* supervisory and information frames */ + case LLCFT_INFO: + frame->llc_control = LLC_INFO; + LLCSBITS(frame->llc_control, i_ns, vs); + LLCSBITS(frame->llc_control_ext, i_nr, linkp->llcl_vr); + adjust = LLC_ISFRAMELEN; + break; + case LLCFT_RR: + frame->llc_control = LLC_RR; + LLC_SETLEN(m, LLC_ISFRAMELEN); + LLCSBITS(frame->llc_control_ext, s_nr, linkp->llcl_vr); + adjust = LLC_ISFRAMELEN; + break; + case LLCFT_RNR: + frame->llc_control = LLC_RNR; + LLC_SETLEN(m, LLC_ISFRAMELEN); + LLCSBITS(frame->llc_control_ext, s_nr, linkp->llcl_vr); + adjust = LLC_ISFRAMELEN; + break; + case LLCFT_REJ: + frame->llc_control = LLC_REJ; + LLC_SETLEN(m, LLC_ISFRAMELEN); + LLCSBITS(frame->llc_control_ext, s_nr, linkp->llcl_vr); + adjust = LLC_ISFRAMELEN; + break; + /* unnumbered frames */ + case LLCFT_DM: + frame->llc_control = LLC_DM; + break; + case LLCFT_SABME: + frame->llc_control = LLC_SABME; + break; + case LLCFT_DISC: + frame->llc_control = LLC_DISC; + break; + case LLCFT_UA: + frame->llc_control = LLC_UA; + break; + case LLCFT_UI: + frame->llc_control = LLC_UI; + break; + case LLCFT_FRMR: + frame->llc_control = LLC_FRMR; + /* get more space --- FRMR frame are longer then usual */ + LLC_SETLEN(m, LLC_FRMRLEN); + bcopy((caddr_t) &linkp->llcl_frmrinfo, + (caddr_t) &frame->llc_frmrinfo, + sizeof(struct frmrinfo)); + break; + default: + /* + * We don't send {XID, TEST} frames + */ + if (m) + m_freem(m); + return; + } + + /* + * Fill in DSAP/SSAP + */ + frame->llc_dsap = frame->llc_ssap = LLSAPADDR(&linkp->llcl_addr); + frame->llc_ssap |= cmdrsp; + + /* + * Check for delayed action pending. ISO 8802-2, 7.9.2 (5) + * and ISO 8802-2, 7.9.2.3 (32), (34), (36) pertain to this + * piece of code --- hopefully we got it right here (i.e. + * in the spirit of (32), (34), and (36) ... + */ + switch (frame_kind) { + case LLCFT_RR: + case LLCFT_RNR: + case LLCFT_REJ: + case LLCFT_INFO: + switch (LLC_GETFLAG(linkp, DACTION)) { + case LLC_DACKCMD: + case LLC_DACKRSP: + LLC_STOPTIMER(linkp, DACTION); + break; + case LLC_DACKCMDPOLL: + if (cmdrsp == LLC_CMD) { + pollfinal = 1; + LLC_STOPTIMER(linkp, DACTION); + } + break; + case LLC_DACKRSPFINAL: + if (cmdrsp == LLC_RSP) { + pollfinal = 1; + LLC_STOPTIMER(linkp, DACTION); + } + break; + } + break; + } + + if (adjust == LLC_UFRAMELEN) + LLCSBITS(frame->llc_control, u_pf, pollfinal); + else LLCSBITS(frame->llc_control_ext, s_pf, pollfinal); + + /* + * Get interface to send frame onto + */ + ifp = linkp->llcl_if; + if (frame_kind == LLCFT_INFO) { + /* + * send out a copy of the frame, retain the + * original + */ + (*ifp->if_output)(ifp, m_copy(m, 0, (int)M_COPYALL), + rt_key(linkp->llcl_nlrt), + linkp->llcl_nlrt); + /* + * Account for the LLC header and let it ``disappear'' + * as the raw info frame payload is what we hold in + * the output_buffers of the link. + */ + m_adj(m, LLC_ISFRAMELEN); + } else (*ifp->if_output)(ifp, m, + rt_key(linkp->llcl_nlrt), + linkp->llcl_nlrt); +} + diff --git a/sys/netccitt/llc_subr.c b/sys/netccitt/llc_subr.c new file mode 100644 index 00000000000..b5e294cbabb --- /dev/null +++ b/sys/netccitt/llc_subr.c @@ -0,0 +1,2360 @@ +/* $NetBSD: llc_subr.c,v 1.3 1995/06/13 05:38:51 mycroft Exp $ */ + +/* + * Copyright (C) Dirk Husemann, Computer Science Department IV, + * University of Erlangen-Nuremberg, Germany, 1990, 1991, 1992 + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Dirk Husemann 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. + * + * @(#)llc_subr.c 8.1 (Berkeley) 6/10/93 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/domain.h> +#include <sys/socket.h> +#include <sys/protosw.h> +#include <sys/socketvar.h> +#include <sys/errno.h> +#include <sys/time.h> +#include <sys/kernel.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_llc.h> +#include <net/route.h> + +#include <netccitt/dll.h> +#include <netccitt/llc_var.h> + +/* + * Frame names for diagnostic messages + */ +char *frame_names[] = { "INFO", "RR", "RNR", "REJ", "DM", "SABME", "DISC", + "UA", "FRMR", "UI", "XID", "TEST", "ILLEGAL", "TIMER", "N2xT1"}; + + +/* + * Trace level + */ +int llc_tracelevel = LLCTR_URGENT; + +/* + * Values for accessing various bitfields + */ +struct bitslice llc_bitslice[] = { +/* mask, shift value */ + { 0x1, 0x0 }, + { 0xfe, 0x1 }, + { 0x3, 0x0 }, + { 0xc, 0x2 }, + { 0x10, 0x4 }, + { 0xe0, 0x5 }, + { 0x1f, 0x0 } +}; + +/* + * We keep the link control blocks on a doubly linked list - + * primarily for checking in llc_time() + */ + +struct llccb_q llccb_q = { &llccb_q, &llccb_q }; + +/* + * Flag for signalling wether route tree for AF_LINK has been + * initialized yet. + */ + +int af_link_rts_init_done = 0; + + +/* + * Functions dealing with struct sockaddr_dl */ + +/* Compare sdl_a w/ sdl_b */ + +sdl_cmp(struct sockaddr_dl *sdl_a, struct sockaddr_dl *sdl_b) +{ + if (LLADDRLEN(sdl_a) != LLADDRLEN(sdl_b)) + return(1); + return(bcmp((caddr_t) sdl_a->sdl_data, (caddr_t) sdl_b->sdl_data, + LLADDRLEN(sdl_a))); +} + +/* Copy sdl_f to sdl_t */ + +sdl_copy(struct sockaddr_dl *sdl_f, struct sockaddr_dl *sdl_t) +{ + bcopy((caddr_t) sdl_f, (caddr_t) sdl_t, sdl_f->sdl_len); +} + +/* Swap sdl_a w/ sdl_b */ + +sdl_swapaddr(struct sockaddr_dl *sdl_a, struct sockaddr_dl *sdl_b) +{ + struct sockaddr_dl sdl_tmp; + + sdl_copy(sdl_a, &sdl_tmp); + sdl_copy(sdl_b, sdl_a); + sdl_copy(&sdl_tmp, sdl_b); +} + +/* Fetch the sdl of the associated if */ + +struct sockaddr_dl * +sdl_getaddrif(struct ifnet *ifp) +{ + register struct ifaddr *ifa; + + for (ifa = ifp->if_addrlist.tqh_first; ifa != 0; + ifa = ifa->ifa_list.tqe_next) + if (ifa->ifa_addr->sa_family == AF_LINK) + return((struct sockaddr_dl *)(ifa->ifa_addr)); + + return((struct sockaddr_dl *)0); +} + +/* Check addr of interface with the one given */ + +sdl_checkaddrif(struct ifnet *ifp, struct sockaddr_dl *sdl_c) +{ + register struct ifaddr *ifa; + + for (ifa = ifp->if_addrlist.tqh_first; ifa != 0; + ifa = ifa->ifa_list.tqe_next) + if (ifa->ifa_addr->sa_family == AF_LINK && + !sdl_cmp((struct sockaddr_dl *)(ifa->ifa_addr), sdl_c)) + return(1); + + return(0); +} + +/* Build an sdl from MAC addr, DLSAP addr, and interface */ + +sdl_setaddrif(struct ifnet *ifp, u_char *mac_addr, u_char dlsap_addr, + u_char mac_len, struct sockaddr_dl *sdl_to) +{ + register struct sockaddr_dl *sdl_tmp; + + if ((sdl_tmp = sdl_getaddrif(ifp)) ) { + sdl_copy(sdl_tmp, sdl_to); + bcopy((caddr_t) mac_addr, (caddr_t) LLADDR(sdl_to), mac_len); + *(LLADDR(sdl_to)+mac_len) = dlsap_addr; + sdl_to->sdl_alen = mac_len+1; + return(1); + } else return(0); +} + +/* Fill out the sdl header aggregate */ + +sdl_sethdrif(struct ifnet *ifp, u_char *mac_src, u_char dlsap_src, u_char *mac_dst, + u_char dlsap_dst, u_char mac_len, struct sdl_hdr *sdlhdr_to) +{ + if ( !sdl_setaddrif(ifp, mac_src, dlsap_src, mac_len, + &sdlhdr_to->sdlhdr_src) || + !sdl_setaddrif(ifp, mac_dst, dlsap_dst, mac_len, + &sdlhdr_to->sdlhdr_dst) ) + return(0); + else return(1); +} + +static struct sockaddr_dl sap_saddr; +static struct sockaddr_dl sap_sgate = { + sizeof(struct sockaddr_dl), /* _len */ + AF_LINK /* _af */ +}; + +/* + * Set sapinfo for SAP address, llcconfig, af, and interface + */ +struct npaidbentry * +llc_setsapinfo(struct ifnet *ifp, u_char af, u_char sap, struct dllconfig *llconf) +{ + struct protosw *pp; + struct sockaddr_dl *ifdl_addr; + struct rtentry *sirt = (struct rtentry *)0; + struct npaidbentry *sapinfo; + u_char saploc; + int size = sizeof(struct npaidbentry); + + USES_AF_LINK_RTS; + + /* + * We rely/assume that only STREAM protocols will make use of + * connection oriented LLC2. If this will one day not be the + * case this will obviously fail. + */ + pp = pffindtype (af, SOCK_STREAM); + if (pp == 0 || pp->pr_input == 0 || pp->pr_ctlinput == 0) { + printf("network level protosw error"); + return 0; + } + + /* + * We need a way to jot down the LLC2 configuration for + * a certain LSAP address. To do this we enter + * a "route" for the SAP. + */ + ifdl_addr = sdl_getaddrif(ifp); + sdl_copy(ifdl_addr, &sap_saddr); + sdl_copy(ifdl_addr, &sap_sgate); + saploc = LLSAPLOC(&sap_saddr, ifp); + sap_saddr.sdl_data[saploc] = sap; + sap_saddr.sdl_alen++; + + /* now enter it */ + rtrequest(RTM_ADD, (struct sockaddr *)&sap_saddr, + (struct sockaddr *)&sap_sgate, 0, 0, &sirt); + if (sirt == 0) + return 0; + + /* Plug in config information in rt->rt_llinfo */ + + sirt->rt_llinfo = malloc(size , M_PCB, M_WAITOK); + sapinfo = (struct npaidbentry *) sirt->rt_llinfo; + if (sapinfo) { + bzero ((caddr_t)sapinfo, size); + /* + * For the time being we support LLC CLASS II here + * only + */ + sapinfo->si_class = LLC_CLASS_II; + sapinfo->si_window = llconf->dllcfg_window; + sapinfo->si_trace = llconf->dllcfg_trace; + if (sapinfo->si_trace) + llc_tracelevel--; + else llc_tracelevel++; + sapinfo->si_input = pp->pr_input; + sapinfo->si_ctlinput = (caddr_t (*)())pp->pr_ctlinput; + + return (sapinfo); + } + + return 0; +} + +/* + * Get sapinfo for SAP address and interface + */ +struct npaidbentry * +llc_getsapinfo(u_char sap, struct ifnet *ifp) +{ + struct sockaddr_dl *ifdl_addr; + struct sockaddr_dl si_addr; + struct rtentry *sirt; + u_char saploc; + + USES_AF_LINK_RTS; + + ifdl_addr = sdl_getaddrif(ifp); + sdl_copy(ifdl_addr, &si_addr); + saploc = LLSAPLOC(&si_addr, ifp); + si_addr.sdl_data[saploc] = sap; + si_addr.sdl_alen++; + + if ((sirt = rtalloc1((struct sockaddr *)&si_addr, 0))) + sirt->rt_refcnt--; + else return(0); + + return((struct npaidbentry *)sirt->rt_llinfo); +} + +/* + * llc_seq2slot() --- We only allocate enough memory to hold the window. This + * introduces the necessity to keep track of two ``pointers'' + * + * o llcl_freeslot the next free slot to be used + * this one advances modulo llcl_window + * o llcl_projvs the V(S) associated with the next frame + * to be set via llcl_freeslot + * this one advances modulo LLC_MAX_SEQUENCE + * + * A new frame is inserted at llcl_output_buffers[llcl_freeslot], after + * which both llcl_freeslot and llcl_projvs are incremented. + * + * The slot sl(sn) for any given sequence number sn is given by + * + * sl(sn) = (llcl_freeslot + llcl_window - 1 - (llcl_projvs + + * LLC_MAX_SEQUENCE- sn) % LLC_MAX_SEQUENCE) % + * llcl_window + * + * i.e. we first calculate the number of frames we need to ``go back'' + * from the current one (really the next one, but that doesn't matter as + * llcl_projvs is likewise of by plus one) and subtract that from the + * pointer to the most recently taken frame (llcl_freeslot - 1). + */ + +short +llc_seq2slot(struct llc_linkcb *linkp, short seqn) +{ + register sn = 0; + + sn = (linkp->llcl_freeslot + linkp->llcl_window - + (linkp->llcl_projvs + LLC_MAX_SEQUENCE - seqn) % + LLC_MAX_SEQUENCE) % linkp->llcl_window; + + return sn; +} + +/* + * LLC2 link state handler + * + * There is in most cases one function per LLC2 state. The LLC2 standard + * ISO 8802-2 allows in some cases for ambiguities, i.e. we have the choice + * to do one thing or the other. Right now I have just chosen one but have also + * indicated the spot by "multiple possibilities". One could make the behavior + * in those cases configurable, allowing the superuser to enter a profile word + * (32/64 bits, whatever is needed) that would suit her needs [I quite like + * that idea, perhaps I'll get around to it]. + * + * [Preceeding each state handler function is the description as taken from + * ISO 8802-2, section 7.9.2.1] + */ + +/* + * ADM --- The connection component is in the asynchronous disconnected mode. + * It can accept an SABME PDU from a remote LLC SSAP or, at the request + * of the service access point user, can initiate an SABME PDU + * transmission to a remote LLC DSAP, to establish a data link + * connection. It also responds to a DISC command PDU and to any + * command PDU with the P bit set to ``1''. + */ +int +llc_state_ADM(struct llc_linkcb *linkp, struct llc *frame, int frame_kind, + int cmdrsp, int pollfinal) +{ + int action = 0; + + switch(frame_kind + cmdrsp) { + case NL_CONNECT_REQUEST: + llc_send(linkp, LLCFT_SABME, LLC_CMD, pollfinal); + LLC_SETFLAG(linkp, P, pollfinal); + LLC_SETFLAG(linkp, S, 0); + linkp->llcl_retry = 0; + LLC_NEWSTATE(linkp, SETUP); + break; + case LLCFT_SABME + LLC_CMD: + /* + * ISO 8802-2, table 7-1, ADM state says to set + * the P flag, yet this will cause an SABME [P] to be + * answered with an UA only, not an UA [F], all + * other `disconnected' states set the F flag, so ... + */ + LLC_SETFLAG(linkp, F, pollfinal); + LLC_NEWSTATE(linkp, CONN); + action = LLC_CONNECT_INDICATION; + break; + case LLCFT_DISC + LLC_CMD: + llc_send(linkp, LLCFT_DM, LLC_RSP, pollfinal); + break; + default: + if (cmdrsp == LLC_CMD && pollfinal == 1) + llc_send(linkp, LLCFT_DM, LLC_RSP, 1); + /* remain in ADM state */ + } + + return action; +} + +/* + * CONN --- The local connection component has received an SABME PDU from a + * remote LLC SSAP, and it is waiting for the local user to accept or + * refuse the connection. + */ +int +llc_state_CONN(struct llc_linkcb *linkp, struct llc *frame, int frame_kind, + int cmdrsp, int pollfinal) +{ + int action = 0; + + switch(frame_kind + cmdrsp) { + case NL_CONNECT_RESPONSE: + llc_send(linkp, LLCFT_UA, LLC_RSP, LLC_GETFLAG(linkp, F)); + LLC_RESETCOUNTER(linkp); + LLC_SETFLAG(linkp, P, 0); + LLC_SETFLAG(linkp, REMOTE_BUSY, 0); + LLC_NEWSTATE(linkp, NORMAL); + break; + case NL_DISCONNECT_REQUEST: + llc_send(linkp, LLCFT_DM, LLC_RSP, LLC_GETFLAG(linkp, F)); + LLC_NEWSTATE(linkp, ADM); + break; + case LLCFT_SABME + LLC_CMD: + LLC_SETFLAG(linkp, F, pollfinal); + break; + case LLCFT_DM + LLC_RSP: + LLC_NEWSTATE(linkp, ADM); + action = LLC_DISCONNECT_INDICATION; + break; + /* all other frames effect nothing here */ + } + + return action; +} + +/* + * RESET_WAIT --- The local connection component is waiting for the local user + * to indicate a RESET_REQUEST or a DISCONNECT_REQUEST. + */ +int +llc_state_RESET_WAIT(struct llc_linkcb *linkp, struct llc *frame, int frame_kind, + int cmdrsp, int pollfinal) +{ + int action = 0; + + switch(frame_kind + cmdrsp) { + case NL_RESET_REQUEST: + if (LLC_GETFLAG(linkp, S) == 0) { + llc_send(linkp, LLCFT_SABME, LLC_CMD, pollfinal); + LLC_SETFLAG(linkp, P, pollfinal); + LLC_START_ACK_TIMER(linkp); + linkp->llcl_retry = 0; + LLC_NEWSTATE(linkp, RESET); + } else { + llc_send(linkp, LLCFT_UA, LLC_RSP, + LLC_GETFLAG(linkp, F)); + LLC_RESETCOUNTER(linkp); + LLC_SETFLAG(linkp, P, 0); + LLC_SETFLAG(linkp, REMOTE_BUSY, 0); + LLC_NEWSTATE(linkp, NORMAL); + action = LLC_RESET_CONFIRM; + } + break; + case NL_DISCONNECT_REQUEST: + if (LLC_GETFLAG(linkp, S) == 0) { + llc_send(linkp, LLCFT_DISC, LLC_CMD, pollfinal); + LLC_SETFLAG(linkp, P, pollfinal); + LLC_START_ACK_TIMER(linkp); + linkp->llcl_retry = 0; + LLC_NEWSTATE(linkp, D_CONN); + } else { + llc_send(linkp, LLCFT_DM, LLC_RSP, + LLC_GETFLAG(linkp, F)); + LLC_NEWSTATE(linkp, ADM); + } + break; + case LLCFT_DM + LLC_RSP: + LLC_NEWSTATE(linkp, ADM); + action = LLC_DISCONNECT_INDICATION; + break; + case LLCFT_SABME + LLC_CMD: + LLC_SETFLAG(linkp, S, 1); + LLC_SETFLAG(linkp, F, pollfinal); + break; + case LLCFT_DISC + LLC_CMD: + llc_send(linkp, LLCFT_DM, LLC_RSP, pollfinal); + LLC_NEWSTATE(linkp, ADM); + action = LLC_DISCONNECT_INDICATION; + break; + } + + return action; +} + +/* + * RESET_CHECK --- The local connection component is waiting for the local user + * to accept or refuse a remote reset request. + */ +int +llc_state_RESET_CHECK(struct llc_linkcb *linkp, struct llc *frame, int frame_kind, + int cmdrsp, int pollfinal) +{ + int action = 0; + + switch(frame_kind + cmdrsp) { + case NL_RESET_RESPONSE: + llc_send(linkp, LLCFT_UA, LLC_RSP, LLC_GETFLAG(linkp, F)); + LLC_RESETCOUNTER(linkp); + LLC_SETFLAG(linkp, P, 0); + LLC_SETFLAG(linkp, REMOTE_BUSY, 0); + LLC_NEWSTATE(linkp, NORMAL); + break; + case NL_DISCONNECT_REQUEST: + llc_send(linkp, LLCFT_DM, LLC_RSP, LLC_GETFLAG(linkp, F)); + LLC_NEWSTATE(linkp, ADM); + break; + case LLCFT_DM + LLC_RSP: + action = LLC_DISCONNECT_INDICATION; + break; + case LLCFT_SABME + LLC_CMD: + LLC_SETFLAG(linkp, F, pollfinal); + break; + case LLCFT_DISC + LLC_CMD: + llc_send(linkp, LLCFT_DM, LLC_RSP, pollfinal); + LLC_NEWSTATE(linkp, ADM); + action = LLC_DISCONNECT_INDICATION; + break; + } + + return action; +} + +/* + * SETUP --- The connection component has transmitted an SABME command PDU to a + * remote LLC DSAP and is waiting for a reply. + */ +int +llc_state_SETUP(struct llc_linkcb *linkp, struct llc *frame, int frame_kind, + int cmdrsp, int pollfinal) +{ + int action = 0; + + switch(frame_kind + cmdrsp) { + case LLCFT_SABME + LLC_CMD: + LLC_RESETCOUNTER(linkp); + llc_send(linkp, LLCFT_UA, LLC_RSP, pollfinal); + LLC_SETFLAG(linkp, S, 1); + break; + case LLCFT_UA + LLC_RSP: + if (LLC_GETFLAG(linkp, P) == pollfinal) { + LLC_STOP_ACK_TIMER(linkp); + LLC_RESETCOUNTER(linkp); + LLC_UPDATE_P_FLAG(linkp, cmdrsp, pollfinal); + LLC_SETFLAG(linkp, REMOTE_BUSY, 0); + LLC_NEWSTATE(linkp, NORMAL); + action = LLC_CONNECT_CONFIRM; + } + break; + case LLC_ACK_TIMER_EXPIRED: + if (LLC_GETFLAG(linkp, S) == 1) { + LLC_SETFLAG(linkp, P, 0); + LLC_SETFLAG(linkp, REMOTE_BUSY, 0), + LLC_NEWSTATE(linkp, NORMAL); + action = LLC_CONNECT_CONFIRM; + } else if (linkp->llcl_retry < llc_n2) { + llc_send(linkp, LLCFT_SABME, LLC_CMD, pollfinal); + LLC_SETFLAG(linkp, P, pollfinal); + LLC_START_ACK_TIMER(linkp); + linkp->llcl_retry++; + } else { + LLC_NEWSTATE(linkp, ADM); + action = LLC_DISCONNECT_INDICATION; + } + break; + case LLCFT_DISC + LLC_CMD: + llc_send(linkp, LLCFT_DM, LLC_RSP, pollfinal); + LLC_STOP_ACK_TIMER(linkp); + LLC_NEWSTATE(linkp, ADM); + action = LLC_DISCONNECT_INDICATION; + break; + case LLCFT_DM + LLC_RSP: + LLC_STOP_ACK_TIMER(linkp); + LLC_NEWSTATE(linkp, ADM); + action = LLC_DISCONNECT_INDICATION; + break; + } + + return action; +} + +/* + * RESET --- As a result of a service access point user request or the receipt + * of a FRMR response PDU, the local connection component has sent an + * SABME command PDU to the remote LLC DSAP to reset the data link + * connection and is waiting for a reply. + */ +int +llc_state_RESET(struct llc_linkcb *linkp, struct llc *frame, int frame_kind, + int cmdrsp, int pollfinal) +{ + int action = 0; + + switch(frame_kind + cmdrsp) { + case LLCFT_SABME + LLC_CMD: + LLC_RESETCOUNTER(linkp); + LLC_SETFLAG(linkp, S, 1); + llc_send(linkp, LLCFT_UA, LLC_RSP, pollfinal); + break; + case LLCFT_UA + LLC_RSP: + if (LLC_GETFLAG(linkp, P) == pollfinal) { + LLC_STOP_ACK_TIMER(linkp); + LLC_RESETCOUNTER(linkp); + LLC_UPDATE_P_FLAG(linkp, cmdrsp, pollfinal); + LLC_SETFLAG(linkp, REMOTE_BUSY, 0); + LLC_NEWSTATE(linkp, NORMAL); + action = LLC_RESET_CONFIRM; + } + break; + case LLC_ACK_TIMER_EXPIRED: + if (LLC_GETFLAG(linkp, S) == 1) { + LLC_SETFLAG(linkp, P, 0); + LLC_SETFLAG(linkp, REMOTE_BUSY, 0); + LLC_NEWSTATE(linkp, NORMAL); + action = LLC_RESET_CONFIRM; + } else if (linkp->llcl_retry < llc_n2) { + llc_send(linkp, LLCFT_SABME, LLC_CMD, pollfinal); + LLC_SETFLAG(linkp, P, pollfinal); + LLC_START_ACK_TIMER(linkp); + linkp->llcl_retry++; + } else { + LLC_NEWSTATE(linkp, ADM); + action = LLC_DISCONNECT_INDICATION; + } + break; + case LLCFT_DISC + LLC_CMD: + llc_send(linkp, LLCFT_DM, LLC_RSP, pollfinal); + LLC_STOP_ACK_TIMER(linkp); + LLC_NEWSTATE(linkp, ADM); + action = LLC_DISCONNECT_INDICATION; + break; + case LLCFT_DM + LLC_RSP: + LLC_STOP_ACK_TIMER(linkp); + LLC_NEWSTATE(linkp, ADM); + action = LLC_DISCONNECT_INDICATION; + break; + } + + return action; +} + +/* + * D_CONN --- At the request of the service access point user, the local LLC + * has sent a DISC command PDU to the remote LLC DSAP and is waiting + * for a reply. + */ +int +llc_state_D_CONN(struct llc_linkcb *linkp, struct llc *frame, int frame_kind, + int cmdrsp, int pollfinal) +{ + int action = 0; + + switch(frame_kind + cmdrsp) { + case LLCFT_SABME + LLC_CMD: + llc_send(linkp, LLCFT_DM, LLC_RSP, pollfinal); + LLC_STOP_ACK_TIMER(linkp); + LLC_NEWSTATE(linkp, ADM); + break; + case LLCFT_UA + LLC_RSP: + if (LLC_GETFLAG(linkp, P) == pollfinal) { + LLC_STOP_ACK_TIMER(linkp); + LLC_NEWSTATE(linkp, ADM); + } + break; + case LLCFT_DISC + LLC_CMD: + llc_send(linkp, LLCFT_UA, LLC_RSP, pollfinal); + break; + case LLCFT_DM + LLC_RSP: + LLC_STOP_ACK_TIMER(linkp); + LLC_NEWSTATE(linkp, ADM); + break; + case LLC_ACK_TIMER_EXPIRED: + if (linkp->llcl_retry < llc_n2) { + llc_send(linkp, LLCFT_DISC, LLC_CMD, pollfinal); + LLC_SETFLAG(linkp, P, pollfinal); + LLC_START_ACK_TIMER(linkp); + linkp->llcl_retry++; + } else LLC_NEWSTATE(linkp, ADM); + break; + } + + return action; +} + +/* + * ERROR --- The local connection component has detected an error in a received + * PDU and has sent a FRMR response PDU. It is waiting for a reply from + * the remote connection component. + */ +int +llc_state_ERROR(struct llc_linkcb *linkp, struct llc *frame, int frame_kind, + int cmdrsp, int pollfinal) +{ + int action = 0; + + switch(frame_kind + cmdrsp) { + case LLCFT_SABME + LLC_CMD: + LLC_STOP_ACK_TIMER(linkp); + LLC_NEWSTATE(linkp, RESET_CHECK); + action = LLC_RESET_INDICATION_REMOTE; + break; + case LLCFT_DISC + LLC_CMD: + llc_send(linkp, LLCFT_UA, LLC_RSP, pollfinal); + LLC_STOP_ACK_TIMER(linkp); + LLC_NEWSTATE(linkp, ADM); + action = LLC_DISCONNECT_INDICATION; + break; + case LLCFT_DM + LLC_RSP: + LLC_STOP_ACK_TIMER(linkp); + LLC_NEWSTATE(linkp, ADM); + action = LLC_DISCONNECT_INDICATION; + break; + case LLCFT_FRMR + LLC_RSP: + LLC_STOP_ACK_TIMER(linkp); + LLC_SETFLAG(linkp, S, 0); + LLC_NEWSTATE(linkp, RESET_WAIT); + action = LLC_FRMR_RECEIVED; + break; + case LLC_ACK_TIMER_EXPIRED: + if (linkp->llcl_retry < llc_n2) { + llc_send(linkp, LLCFT_FRMR, LLC_RSP, 0); + LLC_START_ACK_TIMER(linkp); + linkp->llcl_retry++; + } else { + LLC_SETFLAG(linkp, S, 0); + LLC_NEWSTATE(linkp, RESET_WAIT); + action = LLC_RESET_INDICATION_LOCAL; + } + break; + default: + if (cmdrsp == LLC_CMD){ + llc_send(linkp, LLCFT_FRMR, LLC_RSP, pollfinal); + LLC_START_ACK_TIMER(linkp); + } + break; + + } + + return action; +} + +/* + * NORMAL, BUSY, REJECT, AWAIT, AWAIT_BUSY, and AWAIT_REJECT all share + * a common core state handler. + */ +int +llc_state_NBRAcore(struct llc_linkcb *linkp, struct llc *frame, int frame_kind, + int cmdrsp, int pollfinal) +{ + int action = 0; + + switch(frame_kind + cmdrsp) { + case NL_DISCONNECT_REQUEST: + llc_send(linkp, LLCFT_DISC, LLC_CMD, pollfinal); + LLC_SETFLAG(linkp, P, pollfinal); + LLC_STOP_ALL_TIMERS(linkp); + LLC_START_ACK_TIMER(linkp); + linkp->llcl_retry = 0; + LLC_NEWSTATE(linkp, D_CONN); + break; + case NL_RESET_REQUEST: + llc_send(linkp, LLCFT_SABME, LLC_CMD, pollfinal); + LLC_SETFLAG(linkp, P, pollfinal); + LLC_STOP_ALL_TIMERS(linkp); + LLC_START_ACK_TIMER(linkp); + linkp->llcl_retry = 0; + LLC_SETFLAG(linkp, S, 0); + LLC_NEWSTATE(linkp, RESET); + break; + case LLCFT_SABME + LLC_CMD: + LLC_SETFLAG(linkp, F, pollfinal); + LLC_STOP_ALL_TIMERS(linkp); + LLC_NEWSTATE(linkp, RESET_CHECK); + action = LLC_RESET_INDICATION_REMOTE; + break; + case LLCFT_DISC + LLC_CMD: + llc_send(linkp, LLCFT_UA, LLC_RSP, pollfinal); + LLC_STOP_ALL_TIMERS(linkp); + LLC_NEWSTATE(linkp, ADM); + action = LLC_DISCONNECT_INDICATION; + break; + case LLCFT_FRMR + LLC_RSP: + LLC_STOP_ALL_TIMERS(linkp); + LLC_SETFLAG(linkp, S, 0); + LLC_NEWSTATE(linkp, RESET_WAIT); + action = LLC_FRMR_RECEIVED; + break; + case LLCFT_DM + LLC_RSP: + LLC_STOP_ALL_TIMERS(linkp); + LLC_NEWSTATE(linkp, ADM); + action = LLC_DISCONNECT_INDICATION; + break; + case LLC_INVALID_NR + LLC_CMD: + case LLC_INVALID_NS + LLC_CMD: + LLC_SETFRMR(linkp, frame, cmdrsp, + (frame_kind == LLC_INVALID_NR ? LLC_FRMR_Z : + (LLC_FRMR_V | LLC_FRMR_W))); + llc_send(linkp, LLCFT_FRMR, LLC_RSP, pollfinal); + LLC_STOP_ALL_TIMERS(linkp); + LLC_START_ACK_TIMER(linkp); + linkp->llcl_retry = 0; + LLC_NEWSTATE(linkp, ERROR); + action = LLC_FRMR_SENT; + break; + case LLC_INVALID_NR + LLC_RSP: + case LLC_INVALID_NS + LLC_RSP: + case LLCFT_UA + LLC_RSP: + case LLC_BAD_PDU: { + char frmrcause = 0; + + switch (frame_kind) { + case LLC_INVALID_NR: frmrcause = LLC_FRMR_Z; break; + case LLC_INVALID_NS: frmrcause = LLC_FRMR_V | LLC_FRMR_W; break; + default: frmrcause = LLC_FRMR_W; + } + LLC_SETFRMR(linkp, frame, cmdrsp, frmrcause); + llc_send(linkp, LLCFT_FRMR, LLC_RSP, 0); + LLC_STOP_ALL_TIMERS(linkp); + LLC_START_ACK_TIMER(linkp); + linkp->llcl_retry = 0; + LLC_NEWSTATE(linkp, ERROR); + action = LLC_FRMR_SENT; + break; + } + default: + if (cmdrsp == LLC_RSP && pollfinal == 1 && + LLC_GETFLAG(linkp, P) == 0) { + LLC_SETFRMR(linkp, frame, cmdrsp, LLC_FRMR_W); + LLC_STOP_ALL_TIMERS(linkp); + LLC_START_ACK_TIMER(linkp); + linkp->llcl_retry = 0; + LLC_NEWSTATE(linkp, ERROR); + action = LLC_FRMR_SENT; + } + break; + case LLC_P_TIMER_EXPIRED: + case LLC_ACK_TIMER_EXPIRED: + case LLC_REJ_TIMER_EXPIRED: + case LLC_BUSY_TIMER_EXPIRED: + if (linkp->llcl_retry >= llc_n2) { + LLC_STOP_ALL_TIMERS(linkp); + LLC_SETFLAG(linkp, S, 0); + LLC_NEWSTATE(linkp, RESET_WAIT); + action = LLC_RESET_INDICATION_LOCAL; + } + break; + } + + return action; +} + +/* + * NORMAL --- A data link connection exists between the local LLC service access + * point and the remote LLC service access point. Sending and + * reception of information and supervisory PDUs can be performed. + */ +int +llc_state_NORMAL(struct llc_linkcb *linkp, struct llc *frame, int frame_kind, + int cmdrsp, int pollfinal) +{ + int action = LLC_PASSITON; + + switch(frame_kind + cmdrsp) { + case NL_DATA_REQUEST: + if (LLC_GETFLAG(linkp, REMOTE_BUSY) == 0) { +#ifdef not_now + if (LLC_GETFLAG(linkp, P) == 0) { + /* multiple possibilities */ + llc_send(linkp, LLCFT_INFO, LLC_CMD, 1); + LLC_START_P_TIMER(linkp); + if (LLC_TIMERXPIRED(linkp, ACK) != LLC_TIMER_RUNNING) + LLC_START_ACK_TIMER(linkp); + } else { +#endif + /* multiple possibilities */ + llc_send(linkp, LLCFT_INFO, LLC_CMD, 0); + if (LLC_TIMERXPIRED(linkp, ACK) != LLC_TIMER_RUNNING) + LLC_START_ACK_TIMER(linkp); +#ifdef not_now + } +#endif + action = 0; + } + break; + case LLC_LOCAL_BUSY_DETECTED: + if (LLC_GETFLAG(linkp, P) == 0) { + /* multiple possibilities --- action-wise */ + /* multiple possibilities --- CMD/RSP-wise */ + llc_send(linkp, LLCFT_RNR, LLC_CMD, 0); + LLC_START_P_TIMER(linkp); + LLC_SETFLAG(linkp, DATA, 0); + LLC_NEWSTATE(linkp, BUSY); + action = 0; + } else { + /* multiple possibilities --- CMD/RSP-wise */ + llc_send(linkp, LLCFT_RNR, LLC_CMD, 0); + LLC_SETFLAG(linkp, DATA, 0); + LLC_NEWSTATE(linkp, BUSY); + action = 0; + } + break; + case LLC_INVALID_NS + LLC_CMD: + case LLC_INVALID_NS + LLC_RSP: { + register int p = LLC_GETFLAG(linkp, P); + register int nr = LLCGBITS(frame->llc_control_ext, s_nr); + + if (cmdrsp == LLC_CMD && pollfinal == 1) { + llc_send(linkp, LLCFT_REJ, LLC_RSP, 1); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_START_REJ_TIMER(linkp); + LLC_NEWSTATE(linkp, REJECT); + action = 0; + } else if (pollfinal == 0 && p == 1) { + llc_send(linkp, LLCFT_REJ, LLC_CMD, 0); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_START_REJ_TIMER(linkp); + LLC_NEWSTATE(linkp, REJECT); + action = 0; + } else if ((pollfinal == 0 && p == 0) || + (pollfinal == 1 && p == 1 && cmdrsp == LLC_RSP)) { + llc_send(linkp, LLCFT_REJ, LLC_CMD, 1); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_START_P_TIMER(linkp); + LLC_START_REJ_TIMER(linkp); + if (cmdrsp == LLC_RSP && pollfinal == 1) { + LLC_CLEAR_REMOTE_BUSY(linkp, action); + } else action = 0; + LLC_NEWSTATE(linkp, REJECT); + } + break; + } + case LLCFT_INFO + LLC_CMD: + case LLCFT_INFO + LLC_RSP: { + register int p = LLC_GETFLAG(linkp, P); + register int nr = LLCGBITS(frame->llc_control_ext, s_nr); + + if (cmdrsp == LLC_CMD && pollfinal == 1) { + LLC_INC(linkp->llcl_vr); + LLC_SENDACKNOWLEDGE(linkp, LLC_RSP, 1); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + action = LLC_DATA_INDICATION; + } else if (pollfinal == 0 && p == 1) { + LLC_INC(linkp->llcl_vr); + LLC_SENDACKNOWLEDGE(linkp, LLC_CMD, 0); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + action = LLC_DATA_INDICATION; + } else if ((pollfinal == 0 && p == 0 && cmdrsp == LLC_CMD) || + (pollfinal == p && cmdrsp == LLC_RSP)) { + LLC_INC(linkp->llcl_vr); + LLC_UPDATE_P_FLAG(linkp, cmdrsp, pollfinal); + LLC_SENDACKNOWLEDGE(linkp, LLC_CMD, 0); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + if (cmdrsp == LLC_RSP && pollfinal == 1) + LLC_CLEAR_REMOTE_BUSY(linkp, action); + action = LLC_DATA_INDICATION; + } + break; + } + case LLCFT_RR + LLC_CMD: + case LLCFT_RR + LLC_RSP: { + register int p = LLC_GETFLAG(linkp, P); + register int nr = LLCGBITS(frame->llc_control_ext, s_nr); + + if (cmdrsp == LLC_CMD && pollfinal == 1) { + LLC_SENDACKNOWLEDGE(linkp, LLC_RSP, 1); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + } else if ((pollfinal == 0) || + (cmdrsp == LLC_RSP && pollfinal == 1 && p == 1)) { + LLC_UPDATE_P_FLAG(linkp, cmdrsp, pollfinal); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + } + break; + } + case LLCFT_RNR + LLC_CMD: + case LLCFT_RNR + LLC_RSP: { + register int p = LLC_GETFLAG(linkp, P); + register int nr = LLCGBITS(frame->llc_control_ext, s_nr); + + if (cmdrsp == LLC_CMD && pollfinal == 1) { + llc_send(linkp, LLCFT_RR, LLC_RSP, 1); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_SET_REMOTE_BUSY(linkp, action); + } else if ((pollfinal == 0) || + (cmdrsp == LLC_RSP && pollfinal == 1 && p == 1)) { + LLC_UPDATE_P_FLAG(linkp, cmdrsp, pollfinal); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_SET_REMOTE_BUSY(linkp, action); + } + break; + } + case LLCFT_REJ + LLC_CMD: + case LLCFT_REJ + LLC_RSP: { + register int p = LLC_GETFLAG(linkp, P); + register int nr = LLCGBITS(frame->llc_control_ext, s_nr); + + if (cmdrsp == LLC_CMD && pollfinal == 1) { + linkp->llcl_vs = nr; + LLC_UPDATE_NR_RECEIVED(linkp, nr); + llc_resend(linkp, LLC_RSP, 1); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + } else if (pollfinal == 0 && p == 1) { + linkp->llcl_vs = nr; + LLC_UPDATE_NR_RECEIVED(linkp, nr); + llc_resend(linkp, LLC_CMD, 0); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + } else if ((pollfinal == 0 && p == 0 && cmdrsp == LLC_CMD) || + (pollfinal == p && cmdrsp == LLC_RSP)) { + linkp->llcl_vs = nr; + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_START_P_TIMER(linkp); + llc_resend(linkp, LLC_CMD, 1); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + } + break; + } + case NL_INITIATE_PF_CYCLE: + if (LLC_GETFLAG(linkp, P) == 0) { + llc_send(linkp, LLCFT_RR, LLC_CMD, 1); + LLC_START_P_TIMER(linkp); + action = 0; + } + break; + case LLC_P_TIMER_EXPIRED: + if (linkp->llcl_retry < llc_n2) { + llc_send(linkp, LLCFT_RR, LLC_CMD, 1); + LLC_START_P_TIMER(linkp); + linkp->llcl_retry++; + LLC_NEWSTATE(linkp, AWAIT); + action = 0; + } + break; + case LLC_ACK_TIMER_EXPIRED: + case LLC_BUSY_TIMER_EXPIRED: + if ((LLC_GETFLAG(linkp, P) == 0) + && (linkp->llcl_retry < llc_n2)) { + llc_send(linkp, LLCFT_RR, LLC_CMD, 1); + LLC_START_P_TIMER(linkp); + linkp->llcl_retry++; + LLC_NEWSTATE(linkp, AWAIT); + action = 0; + } + break; + } + if (action == LLC_PASSITON) + action = llc_state_NBRAcore(linkp, frame, frame_kind, + cmdrsp, pollfinal); + + return action; +} + +/* + * BUSY --- A data link connection exists between the local LLC service access + * point and the remote LLC service access point. I PDUs may be sent. + * Local conditions make it likely that the information feld of + * received I PDUs will be ignored. Supervisory PDUs may be both sent + * and received. + */ +int +llc_state_BUSY(struct llc_linkcb *linkp, struct llc *frame, int frame_kind, + int cmdrsp, int pollfinal) +{ + int action = LLC_PASSITON; + + switch(frame_kind + cmdrsp) { + case NL_DATA_REQUEST: + if (LLC_GETFLAG(linkp, REMOTE_BUSY) == 0) + if (LLC_GETFLAG(linkp, P) == 0) { + llc_send(linkp, LLCFT_INFO, LLC_CMD, 1); + LLC_START_P_TIMER(linkp); + if (LLC_TIMERXPIRED(linkp, ACK) != LLC_TIMER_RUNNING) + LLC_START_ACK_TIMER(linkp); + action = 0; + } else { + llc_send(linkp, LLCFT_INFO, LLC_CMD, 0); + if (LLC_TIMERXPIRED(linkp, ACK) != LLC_TIMER_RUNNING) + LLC_START_ACK_TIMER(linkp); + action = 0; + } + break; + case LLC_LOCAL_BUSY_CLEARED: { + register int p = LLC_GETFLAG(linkp, P); + register int df = LLC_GETFLAG(linkp, DATA); + + switch (df) { + case 1: + if (p == 0) { + /* multiple possibilities */ + llc_send(linkp, LLCFT_REJ, LLC_CMD, 1); + LLC_START_REJ_TIMER(linkp); + LLC_START_P_TIMER(linkp); + LLC_NEWSTATE(linkp, REJECT); + action = 0; + } else { + llc_send(linkp, LLCFT_REJ, LLC_CMD, 0); + LLC_START_REJ_TIMER(linkp); + LLC_NEWSTATE(linkp, REJECT); + action = 0; + } + break; + case 0: + if (p == 0) { + /* multiple possibilities */ + llc_send(linkp, LLCFT_RR, LLC_CMD, 1); + LLC_START_P_TIMER(linkp); + LLC_NEWSTATE(linkp, NORMAL); + action = 0; + } else { + llc_send(linkp, LLCFT_RR, LLC_CMD, 0); + LLC_NEWSTATE(linkp, NORMAL); + action = 0; + } + break; + case 2: + if (p == 0) { + /* multiple possibilities */ + llc_send(linkp, LLCFT_RR, LLC_CMD, 1); + LLC_START_P_TIMER(linkp); + LLC_NEWSTATE(linkp, REJECT); + action = 0; + } else { + llc_send(linkp, LLCFT_RR, LLC_CMD, 0); + LLC_NEWSTATE(linkp, REJECT); + action =0; + } + break; + } + break; + } + case LLC_INVALID_NS + LLC_CMD: + case LLC_INVALID_NS + LLC_RSP: { + register int p = LLC_GETFLAG(linkp, P); + register int nr = LLCGBITS(frame->llc_control_ext, s_nr); + + if (cmdrsp == LLC_CMD && pollfinal == 1) { + llc_send(linkp, LLCFT_RNR, LLC_RSP, 1); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + if (LLC_GETFLAG(linkp, DATA) == 0) + LLC_SETFLAG(linkp, DATA, 1); + action = 0; + } else if ((cmdrsp == LLC_CMD && pollfinal == 0 && p == 0) || + (cmdrsp == LLC_RSP && pollfinal == p)) { + llc_send(linkp, LLCFT_RNR, LLC_CMD, 0); + LLC_UPDATE_P_FLAG(linkp, cmdrsp, pollfinal); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + if (LLC_GETFLAG(linkp, DATA) == 0) + LLC_SETFLAG(linkp, DATA, 1); + if (cmdrsp == LLC_RSP && pollfinal == 1) { + LLC_CLEAR_REMOTE_BUSY(linkp, action); + } else action = 0; + } else if (pollfinal == 0 && p == 1) { + llc_send(linkp, LLCFT_RNR, LLC_RSP, 1); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + if (LLC_GETFLAG(linkp, DATA) == 0) + LLC_SETFLAG(linkp, DATA, 1); + action = 0; + } + break; + } + case LLCFT_INFO + LLC_CMD: + case LLCFT_INFO + LLC_RSP: { + register int p = LLC_GETFLAG(linkp, P); + register int nr = LLCGBITS(frame->llc_control_ext, s_nr); + + if (cmdrsp == LLC_CMD && pollfinal == 1) { + LLC_INC(linkp->llcl_vr); + llc_send(linkp, LLCFT_RNR, LLC_RSP, 1); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + if (LLC_GETFLAG(linkp, DATA) == 2) + LLC_STOP_REJ_TIMER(linkp); + LLC_SETFLAG(linkp, DATA, 0); + action = LLC_DATA_INDICATION; + } else if ((cmdrsp == LLC_CMD && pollfinal == 0 && p == 0) || + (cmdrsp == LLC_RSP && pollfinal == p)) { + LLC_INC(linkp->llcl_vr); + llc_send(linkp, LLCFT_RNR, LLC_CMD, 1); + LLC_START_P_TIMER(linkp); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + if (LLC_GETFLAG(linkp, DATA) == 2) + LLC_STOP_REJ_TIMER(linkp); + if (cmdrsp == LLC_RSP && pollfinal == 1) + LLC_CLEAR_REMOTE_BUSY(linkp, action); + action = LLC_DATA_INDICATION; + } else if (pollfinal == 0 && p == 1) { + LLC_INC(linkp->llcl_vr); + llc_send(linkp, LLCFT_RNR, LLC_CMD, 0); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + if (LLC_GETFLAG(linkp, DATA) == 2) + LLC_STOP_REJ_TIMER(linkp); + LLC_SETFLAG(linkp, DATA, 0); + action = LLC_DATA_INDICATION; + } + break; + } + case LLCFT_RR + LLC_CMD: + case LLCFT_RR + LLC_RSP: + case LLCFT_RNR + LLC_CMD: + case LLCFT_RNR + LLC_RSP: { + register int p = LLC_GETFLAG(linkp, P); + register int nr = LLCGBITS(frame->llc_control_ext, s_nr); + + if (cmdrsp == LLC_CMD && pollfinal == 1) { + llc_send(linkp, LLCFT_RNR, LLC_RSP, 1); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + if (frame_kind == LLCFT_RR) { + LLC_CLEAR_REMOTE_BUSY(linkp, action); + } else { + LLC_SET_REMOTE_BUSY(linkp, action); + } + } else if (pollfinal = 0 || + (cmdrsp == LLC_RSP && pollfinal == 1)) { + LLC_UPDATE_P_FLAG(linkp, cmdrsp, pollfinal); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + if (frame_kind == LLCFT_RR) { + LLC_CLEAR_REMOTE_BUSY(linkp, action); + } else { + LLC_SET_REMOTE_BUSY(linkp, action); + } + } + break; + } + case LLCFT_REJ + LLC_CMD: + case LLCFT_REJ + LLC_RSP: { + register int p = LLC_GETFLAG(linkp, P); + register int nr = LLCGBITS(frame->llc_control_ext, s_nr); + + if (cmdrsp == LLC_CMD && pollfinal == 1) { + linkp->llcl_vs = nr; + LLC_UPDATE_NR_RECEIVED(linkp, nr); + llc_send(linkp, LLCFT_RNR, LLC_RSP, 1); + llc_resend(linkp, LLC_CMD, 0); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + } else if ((cmdrsp == LLC_CMD && pollfinal == 0 && p == 0) || + (cmdrsp == LLC_RSP && pollfinal == p)) { + linkp->llcl_vs = nr; + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_UPDATE_P_FLAG(linkp, cmdrsp, pollfinal); + llc_resend(linkp, LLC_CMD, 0); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + } else if (pollfinal == 0 && p == 1) { + linkp->llcl_vs = nr; + LLC_UPDATE_NR_RECEIVED(linkp, nr); + llc_resend(linkp, LLC_CMD, 0); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + } + break; + } + case NL_INITIATE_PF_CYCLE: + if (LLC_GETFLAG(linkp, P) == 0) { + llc_send(linkp, LLCFT_RNR, LLC_CMD, 1); + LLC_START_P_TIMER(linkp); + action = 0; + } + break; + case LLC_P_TIMER_EXPIRED: + /* multiple possibilities */ + if (linkp->llcl_retry < llc_n2) { + llc_send(linkp, LLCFT_RNR, LLC_CMD, 1); + LLC_START_P_TIMER(linkp); + linkp->llcl_retry++; + LLC_NEWSTATE(linkp, AWAIT_BUSY); + action = 0; + } + break; + case LLC_ACK_TIMER_EXPIRED: + case LLC_BUSY_TIMER_EXPIRED: + if (LLC_GETFLAG(linkp, P) == 0 && linkp->llcl_retry < llc_n2) { + llc_send(linkp, LLCFT_RNR, LLC_CMD, 1); + LLC_START_P_TIMER(linkp); + linkp->llcl_retry++; + LLC_NEWSTATE(linkp, AWAIT_BUSY); + action = 0; + } + break; + case LLC_REJ_TIMER_EXPIRED: + if (linkp->llcl_retry < llc_n2) + if (LLC_GETFLAG(linkp, P) == 0) { + /* multiple possibilities */ + llc_send(linkp, LLCFT_RNR, LLC_CMD, 1); + LLC_START_P_TIMER(linkp); + linkp->llcl_retry++; + LLC_SETFLAG(linkp, DATA, 1); + LLC_NEWSTATE(linkp, AWAIT_BUSY); + action = 0; + } else{ + LLC_SETFLAG(linkp, DATA, 1); + LLC_NEWSTATE(linkp, BUSY); + action = 0; + } + + break; + } + if (action == LLC_PASSITON) + action = llc_state_NBRAcore(linkp, frame, frame_kind, + cmdrsp, pollfinal); + + return action; +} + +/* + * REJECT --- A data link connection exists between the local LLC service + * access point and the remote LLC service access point. The local + * connection component has requested that the remote connection + * component resend a specific I PDU that the local connection + * componnent has detected as being out of sequence. Both I PDUs and + * supervisory PDUs may be sent and received. + */ +int +llc_state_REJECT(struct llc_linkcb *linkp, struct llc *frame, int frame_kind, + int cmdrsp, int pollfinal) +{ + int action = LLC_PASSITON; + + switch(frame_kind + cmdrsp) { + case NL_DATA_REQUEST: + if (LLC_GETFLAG(linkp, P) == 0) { + llc_send(linkp, LLCFT_INFO, LLC_CMD, 1); + LLC_START_P_TIMER(linkp); + if (LLC_TIMERXPIRED(linkp, ACK) != LLC_TIMER_RUNNING) + LLC_START_ACK_TIMER(linkp); + LLC_NEWSTATE(linkp, REJECT); + action = 0; + } else { + llc_send(linkp, LLCFT_INFO, LLC_CMD, 0); + if (LLC_TIMERXPIRED(linkp, ACK) != LLC_TIMER_RUNNING) + LLC_START_ACK_TIMER(linkp); + LLC_NEWSTATE(linkp, REJECT); + action = 0; + } + break; + case NL_LOCAL_BUSY_DETECTED: + if (LLC_GETFLAG(linkp, P) == 0) { + llc_send(linkp, LLCFT_RNR, LLC_CMD, 1); + LLC_START_P_TIMER(linkp); + LLC_SETFLAG(linkp, DATA, 2); + LLC_NEWSTATE(linkp, BUSY); + action = 0; + } else { + llc_send(linkp, LLCFT_RNR, LLC_CMD, 0); + LLC_SETFLAG(linkp, DATA, 2); + LLC_NEWSTATE(linkp, BUSY); + action = 0; + } + break; + case LLC_INVALID_NS + LLC_CMD: + case LLC_INVALID_NS + LLC_RSP: { + register int p = LLC_GETFLAG(linkp, P); + register int nr = LLCGBITS(frame->llc_control_ext, s_nr); + + if (cmdrsp == LLC_CMD && pollfinal == 1) { + llc_send(linkp, LLCFT_RR, LLC_RSP, 1); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + action = 0; + } else if (pollfinal == 0 || + (cmdrsp == LLC_RSP && pollfinal == 1 && p == 1)) { + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_UPDATE_P_FLAG(linkp, cmdrsp, pollfinal); + if (cmdrsp == LLC_RSP && pollfinal == 1) { + LLC_CLEAR_REMOTE_BUSY(linkp, action); + } else action = 0; + } + break; + } + case LLCFT_INFO + LLC_CMD: + case LLCFT_INFO + LLC_RSP: { + register int p = LLC_GETFLAG(linkp, P); + register int nr = LLCGBITS(frame->llc_control_ext, s_nr); + + if (cmdrsp == LLC_CMD && pollfinal == 1) { + LLC_INC(linkp->llcl_vr); + LLC_SENDACKNOWLEDGE(linkp, LLC_RSP, 1); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_STOP_REJ_TIMER(linkp); + LLC_NEWSTATE(linkp, NORMAL); + action = LLC_DATA_INDICATION; + } else if ((cmdrsp = LLC_RSP && pollfinal == p) || + (cmdrsp == LLC_CMD && pollfinal == 0 && p == 0)) { + LLC_INC(linkp->llcl_vr); + LLC_SENDACKNOWLEDGE(linkp, LLC_CMD, 1); + LLC_START_P_TIMER(linkp); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + if (cmdrsp == LLC_RSP && pollfinal == 1) + LLC_CLEAR_REMOTE_BUSY(linkp, action); + LLC_STOP_REJ_TIMER(linkp); + LLC_NEWSTATE(linkp, NORMAL); + action = LLC_DATA_INDICATION; + } else if (pollfinal == 0 && p == 1) { + LLC_INC(linkp->llcl_vr); + LLC_SENDACKNOWLEDGE(linkp, LLC_CMD, 0); + LLC_STOP_REJ_TIMER(linkp); + LLC_NEWSTATE(linkp, NORMAL); + action = LLC_DATA_INDICATION; + } + break; + } + case LLCFT_RR + LLC_CMD: + case LLCFT_RR + LLC_RSP: { + register int p = LLC_GETFLAG(linkp, P); + register int nr = LLCGBITS(frame->llc_control_ext, s_nr); + + if (cmdrsp == LLC_CMD && pollfinal == 1) { + LLC_SENDACKNOWLEDGE(linkp, LLC_RSP, 1); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + } else if (pollfinal == 0 || + (cmdrsp == LLC_RSP && pollfinal == 1 && p == 1)) { + LLC_UPDATE_P_FLAG(linkp, cmdrsp, pollfinal); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + } + break; + } + case LLCFT_RNR + LLC_CMD: + case LLCFT_RNR + LLC_RSP: { + register int p = LLC_GETFLAG(linkp, P); + register int nr = LLCGBITS(frame->llc_control_ext, s_nr); + + if (cmdrsp == LLC_CMD && pollfinal == 1) { + llc_send(linkp, LLCFT_RR, LLC_RSP, 1); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_SET_REMOTE_BUSY(linkp, action); + } else if (pollfinal == 0 || + (cmdrsp == LLC_RSP && pollfinal == 1 && p == 1)) { + LLC_UPDATE_P_FLAG(linkp, cmdrsp, pollfinal); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + action = 0; + } + break; + } + case LLCFT_REJ + LLC_CMD: + case LLCFT_REJ + LLC_RSP: { + register int p = LLC_GETFLAG(linkp, P); + register int nr = LLCGBITS(frame->llc_control_ext, s_nr); + + if (cmdrsp == LLC_CMD && pollfinal == 1) { + linkp->llcl_vs = nr; + LLC_UPDATE_NR_RECEIVED(linkp, nr); + llc_resend(linkp, LLC_RSP, 1); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + } else if ((cmdrsp == LLC_CMD && pollfinal == 0 && p == 0) || + (cmdrsp == LLC_RSP && pollfinal == p)) { + linkp->llcl_vs = nr; + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_UPDATE_P_FLAG(linkp, cmdrsp, pollfinal); + llc_resend(linkp, LLC_CMD, 0); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + } else if (pollfinal == 0 && p == 1) { + linkp->llcl_vs = nr; + LLC_UPDATE_NR_RECEIVED(linkp, nr); + llc_resend(linkp, LLC_CMD, 0); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + } + break; + } + case NL_INITIATE_PF_CYCLE: + if (LLC_GETFLAG(linkp, P) == 0) { + llc_send(linkp, LLCFT_RR, LLC_CMD, 1); + LLC_START_P_TIMER(linkp); + action = 0; + } + break; + case LLC_REJ_TIMER_EXPIRED: + if (LLC_GETFLAG(linkp, P) == 0 && linkp->llcl_retry < llc_n2) { + llc_send(linkp, LLCFT_REJ, LLC_CMD, 1); + LLC_START_P_TIMER(linkp); + LLC_START_REJ_TIMER(linkp); + linkp->llcl_retry++; + action = 0; + } + case LLC_P_TIMER_EXPIRED: + if (linkp->llcl_retry < llc_n2) { + llc_send(linkp, LLCFT_RR, LLC_CMD, 1); + LLC_START_P_TIMER(linkp); + LLC_START_REJ_TIMER(linkp); + linkp->llcl_retry++; + LLC_NEWSTATE(linkp, AWAIT_REJECT); + action = 0; + } + break; + case LLC_ACK_TIMER_EXPIRED: + case LLC_BUSY_TIMER_EXPIRED: + if (LLC_GETFLAG(linkp, P) == 0 && linkp->llcl_retry < llc_n2) { + llc_send(linkp, LLCFT_RR, LLC_CMD, 1); + LLC_START_P_TIMER(linkp); + LLC_START_REJ_TIMER(linkp); + linkp->llcl_retry++; + /* + * I cannot locate the description of RESET_V(S) + * in ISO 8802-2, table 7-1, state REJECT, last event, + * and assume they meant to set V(S) to 0 ... + */ + linkp->llcl_vs = 0; /* XXX */ + LLC_NEWSTATE(linkp, AWAIT_REJECT); + action = 0; + } + + break; + } + if (action == LLC_PASSITON) + action = llc_state_NBRAcore(linkp, frame, frame_kind, + cmdrsp, pollfinal); + + return action; +} + +/* + * AWAIT --- A data link connection exists between the local LLC service access + * point and the remote LLC service access point. The local LLC is + * performing a timer recovery operation and has sent a command PDU + * with the P bit set to ``1'', and is awaiting an acknowledgement + * from the remote LLC. I PDUs may be received but not sent. + * Supervisory PDUs may be both sent and received. + */ +int +llc_state_AWAIT(struct llc_linkcb *linkp, struct llc *frame, int frame_kind, + int cmdrsp, int pollfinal) +{ + int action = LLC_PASSITON; + + switch(frame_kind + cmdrsp) { + case LLC_LOCAL_BUSY_DETECTED: + llc_send(linkp, LLCFT_RNR, LLC_CMD, 0); + LLC_SETFLAG(linkp, DATA, 0); + LLC_NEWSTATE(linkp, AWAIT_BUSY); + action = 0; + break; + case LLC_INVALID_NS + LLC_CMD: + case LLC_INVALID_NS + LLC_RSP: { + register int p = LLC_GETFLAG(linkp, P); + register int nr = LLCGBITS(frame->llc_control_ext, s_nr); + + if (cmdrsp == LLC_CMD && pollfinal == 1) { + llc_send(linkp, LLCFT_REJ, LLC_RSP, 1); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_START_REJ_TIMER(linkp); + LLC_NEWSTATE(linkp, AWAIT_REJECT); + action = 0; + } else if (cmdrsp == LLC_RSP && pollfinal == 1) { + llc_send(linkp, LLCFT_REJ, LLC_CMD, 0); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + linkp->llcl_vs = nr; + LLC_STOP_P_TIMER(linkp); + llc_resend(linkp, LLC_CMD, 0); + LLC_START_REJ_TIMER(linkp); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + LLC_NEWSTATE(linkp, REJECT); + } else if (pollfinal == 0) { + llc_send(linkp, LLCFT_REJ, LLC_CMD, 0); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_START_REJ_TIMER(linkp); + LLC_NEWSTATE(linkp, AWAIT_REJECT); + action = 0; + } + break; + } + case LLCFT_INFO + LLC_RSP: + case LLCFT_INFO + LLC_CMD: { + register int p = LLC_GETFLAG(linkp, P); + register int nr = LLCGBITS(frame->llc_control_ext, s_nr); + + LLC_INC(linkp->llcl_vr); + if (cmdrsp == LLC_CMD && pollfinal == 1) { + llc_send(linkp, LLCFT_RR, LLC_RSP, 1); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + action = LLC_DATA_INDICATION; + } else if (cmdrsp == LLC_RSP && pollfinal == 1) { + LLC_UPDATE_NR_RECEIVED(linkp, nr); + linkp->llcl_vs = nr; + llc_resend(linkp, LLC_CMD, 1); + LLC_START_P_TIMER(linkp); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + LLC_NEWSTATE(linkp, NORMAL); + action = LLC_DATA_INDICATION; + } else if (pollfinal == 0) { + llc_send(linkp, LLCFT_RR, LLC_CMD, 0); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + action = LLC_DATA_INDICATION; + } + break; + } + case LLCFT_RR + LLC_CMD: + case LLCFT_RR + LLC_RSP: + case LLCFT_REJ + LLC_CMD: + case LLCFT_REJ + LLC_RSP: { + register int p = LLC_GETFLAG(linkp, P); + register int nr = LLCGBITS(frame->llc_control_ext, s_nr); + + if (cmdrsp == LLC_CMD && pollfinal == 1) { + llc_send(linkp, LLCFT_RR, LLC_RSP, 1); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + } else if (cmdrsp == LLC_RSP && pollfinal == 1) { + LLC_UPDATE_NR_RECEIVED(linkp, nr); + linkp->llcl_vs = nr; + LLC_STOP_P_TIMER(linkp); + llc_resend(linkp, LLC_CMD, 0); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + LLC_NEWSTATE(linkp, NORMAL); + } else if (pollfinal == 0) { + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + } + break; + } + case LLCFT_RNR + LLC_CMD: + case LLCFT_RNR + LLC_RSP: { + register int p = LLC_GETFLAG(linkp, P); + register int nr = LLCGBITS(frame->llc_control_ext, s_nr); + + if (pollfinal == 1 && cmdrsp == LLC_CMD) { + llc_send(linkp, LLCFT_RR, LLC_RSP, 1); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_SET_REMOTE_BUSY(linkp, action); + } else if (pollfinal == 1 && cmdrsp == LLC_RSP) { + LLC_UPDATE_NR_RECEIVED(linkp, nr); + linkp->llcl_vs = nr; + LLC_STOP_P_TIMER(linkp); + LLC_SET_REMOTE_BUSY(linkp, action); + LLC_NEWSTATE(linkp, NORMAL); + } else if (pollfinal == 0) { + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_SET_REMOTE_BUSY(linkp, action); + } + break; + } + case LLC_P_TIMER_EXPIRED: + if (linkp->llcl_retry < llc_n2) { + llc_send(linkp, LLCFT_RR, LLC_CMD, 1); + LLC_START_P_TIMER(linkp); + linkp->llcl_retry++; + action = 0; + } + break; + } + if (action == LLC_PASSITON) + action = llc_state_NBRAcore(linkp, frame, frame_kind, + cmdrsp, pollfinal); + + return action; +} + +/* + * AWAIT_BUSY --- A data link connection exists between the local LLC service + * access point and the remote LLC service access point. The + * local LLC is performing a timer recovery operation and has + * sent a command PDU with the P bit set to ``1'', and is + * awaiting an acknowledgement from the remote LLC. I PDUs may + * not be sent. Local conditions make it likely that the + * information feld of receoved I PDUs will be ignored. + * Supervisory PDUs may be both sent and received. + */ +int +llc_state_AWAIT_BUSY(struct llc_linkcb *linkp, struct llc *frame, int frame_kind, + int cmdrsp, int pollfinal) +{ + int action = LLC_PASSITON; + + switch(frame_kind + cmdrsp) { + case LLC_LOCAL_BUSY_CLEARED: + switch (LLC_GETFLAG(linkp, DATA)) { + case 1: + llc_send(linkp, LLCFT_REJ, LLC_CMD, 0); + LLC_START_REJ_TIMER(linkp); + LLC_NEWSTATE(linkp, AWAIT_REJECT); + action = 0; + break; + case 0: + llc_send(linkp, LLCFT_RR, LLC_CMD, 0); + LLC_NEWSTATE(linkp, AWAIT); + action = 0; + break; + case 2: + llc_send(linkp, LLCFT_RR, LLC_CMD, 0); + LLC_NEWSTATE(linkp, AWAIT_REJECT); + action = 0; + break; + } + break; + case LLC_INVALID_NS + LLC_CMD: + case LLC_INVALID_NS + LLC_RSP: { + register int p = LLC_GETFLAG(linkp, P); + register int nr = LLCGBITS(frame->llc_control_ext, s_nr); + + if (cmdrsp == LLC_CMD && pollfinal == 1) { + llc_send(linkp, LLCFT_RNR, LLC_RSP, 1); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_SETFLAG(linkp, DATA, 1); + action = 0; + } else if (cmdrsp == LLC_RSP && pollfinal == 1) { + /* optionally */ + llc_send(linkp, LLCFT_RNR, LLC_CMD, 0); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + linkp->llcl_vs = nr; + LLC_STOP_P_TIMER(linkp); + LLC_SETFLAG(linkp, DATA, 1); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + llc_resend(linkp, LLC_CMD, 0); + LLC_NEWSTATE(linkp, BUSY); + } else if (pollfinal == 0) { + /* optionally */ + llc_send(linkp, LLCFT_RNR, LLC_CMD, 0); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_SETFLAG(linkp, DATA, 1); + action = 0; + } + } + case LLCFT_INFO + LLC_CMD: + case LLCFT_INFO + LLC_RSP: { + register int p = LLC_GETFLAG(linkp, P); + register int nr = LLCGBITS(frame->llc_control_ext, s_nr); + + if (cmdrsp == LLC_CMD && pollfinal == 1) { + llc_send(linkp, LLCFT_RNR, LLC_RSP, 1); + LLC_INC(linkp->llcl_vr); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_SETFLAG(linkp, DATA, 0); + action = LLC_DATA_INDICATION; + } else if (cmdrsp == LLC_RSP && pollfinal == 1) { + llc_send(linkp, LLCFT_RNR, LLC_CMD, 1); + LLC_INC(linkp->llcl_vr); + LLC_START_P_TIMER(linkp); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + linkp->llcl_vs = nr; + LLC_SETFLAG(linkp, DATA, 0); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + llc_resend(linkp, LLC_CMD, 0); + LLC_NEWSTATE(linkp, BUSY); + action = LLC_DATA_INDICATION; + } else if (pollfinal == 0) { + llc_send(linkp, LLCFT_RNR, LLC_CMD, 0); + LLC_INC(linkp->llcl_vr); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_SETFLAG(linkp, DATA, 0); + action = LLC_DATA_INDICATION; + } + break; + } + case LLCFT_RR + LLC_CMD: + case LLCFT_REJ + LLC_CMD: + case LLCFT_RR + LLC_RSP: + case LLCFT_REJ + LLC_RSP: { + register int p = LLC_GETFLAG(linkp, P); + register int nr = LLCGBITS(frame->llc_control_ext, s_nr); + + if (cmdrsp == LLC_CMD && pollfinal == 1) { + llc_send(linkp, LLCFT_RNR, LLC_RSP, 1); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + } else if (cmdrsp == LLC_RSP && pollfinal == 1) { + LLC_UPDATE_NR_RECEIVED(linkp, nr); + linkp->llcl_vs = nr; + LLC_STOP_P_TIMER(linkp); + llc_resend(linkp, LLC_CMD, 0); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + LLC_NEWSTATE(linkp, BUSY); + } else if (pollfinal == 0) { + LLC_UPDATE_NR_RECEIVED(linkp, nr); + linkp->llcl_vs = nr; + LLC_STOP_P_TIMER(linkp); + llc_resend(linkp, LLC_CMD, 0); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + } + break; + } + case LLCFT_RNR + LLC_CMD: + case LLCFT_RNR + LLC_RSP: { + register int p = LLC_GETFLAG(linkp, P); + register int nr = LLCGBITS(frame->llc_control_ext, s_nr); + + if (cmdrsp == LLC_CMD && pollfinal == 1) { + llc_send(linkp, LLCFT_RNR, LLC_RSP, 1); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_SET_REMOTE_BUSY(linkp, action); + } else if (cmdrsp == LLC_RSP && pollfinal == 1) { + LLC_UPDATE_NR_RECEIVED(linkp, nr); + linkp->llcl_vs = nr; + LLC_STOP_P_TIMER(linkp); + LLC_SET_REMOTE_BUSY(linkp, action); + LLC_NEWSTATE(linkp, BUSY); + } else if (pollfinal == 0) { + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_SET_REMOTE_BUSY(linkp, action); + } + break; + } + case LLC_P_TIMER_EXPIRED: + if (linkp->llcl_retry < llc_n2) { + llc_send(linkp, LLCFT_RNR, LLC_CMD, 1); + LLC_START_P_TIMER(linkp); + linkp->llcl_retry++; + action = 0; + } + break; + } + if (action == LLC_PASSITON) + action = llc_state_NBRAcore(linkp, frame, frame_kind, + cmdrsp, pollfinal); + + return action; +} + +/* + * AWAIT_REJECT --- A data link connection exists between the local LLC service + * access point and the remote LLC service access point. The + * local connection component has requested that the remote + * connection component re-transmit a specific I PDU that the + * local connection component has detected as being out of + * sequence. Before the local LLC entered this state it was + * performing a timer recovery operation and had sent a + * command PDU with the P bit set to ``1'', and is still + * awaiting an acknowledgment from the remote LLC. I PDUs may + * be received but not transmitted. Supervisory PDUs may be + * both transmitted and received. + */ +int +llc_state_AWAIT_REJECT(struct llc_linkcb *linkp, struct llc *frame, int frame_kind, + int cmdrsp, int pollfinal) +{ + int action = LLC_PASSITON; + + switch(frame_kind + cmdrsp) { + case LLC_LOCAL_BUSY_DETECTED: + llc_send(linkp, LLCFT_RNR, LLC_CMD, 0); + LLC_SETFLAG(linkp, DATA, 2); + LLC_NEWSTATE(linkp, AWAIT_BUSY); + action = 0; + break; + case LLC_INVALID_NS + LLC_CMD: + case LLC_INVALID_NS + LLC_RSP: { + register int nr = LLCGBITS(frame->llc_control_ext, s_nr); + + if (cmdrsp == LLC_CMD && pollfinal == 1) { + llc_send(linkp, LLCFT_RR, LLC_RSP, 1); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + action = 0; + } else if (cmdrsp == LLC_RSP && pollfinal == 1) { + LLC_UPDATE_NR_RECEIVED(linkp, nr); + linkp->llcl_vs = nr; + llc_resend(linkp, LLC_CMD, 1); + LLC_START_P_TIMER(linkp); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + LLC_NEWSTATE(linkp, REJECT); + } else if (pollfinal == 0) { + LLC_UPDATE_NR_RECEIVED(linkp, nr); + action = 0; + } + break; + } + case LLCFT_INFO + LLC_CMD: + case LLCFT_INFO + LLC_RSP: { + register int nr = LLCGBITS(frame->llc_control_ext, s_nr); + + if (cmdrsp == LLC_CMD && pollfinal == 1) { + LLC_INC(linkp->llcl_vr); + llc_send(linkp, LLCFT_RR, LLC_RSP, 1); + LLC_STOP_REJ_TIMER(linkp); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_NEWSTATE(linkp, AWAIT); + action = LLC_DATA_INDICATION; + } else if (cmdrsp == LLC_RSP && pollfinal == 1) { + LLC_INC(linkp->llcl_vr); + LLC_STOP_P_TIMER(linkp); + LLC_STOP_REJ_TIMER(linkp); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + linkp->llcl_vs = nr; + llc_resend(linkp, LLC_CMD, 0); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + LLC_NEWSTATE(linkp, NORMAL); + action = LLC_DATA_INDICATION; + } else if (pollfinal == 0) { + LLC_INC(linkp->llcl_vr); + llc_send(linkp, LLCFT_RR, LLC_CMD, 0); + LLC_STOP_REJ_TIMER(linkp); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_NEWSTATE(linkp, AWAIT); + action = LLC_DATA_INDICATION; + } + break; + } + case LLCFT_RR + LLC_CMD: + case LLCFT_REJ + LLC_CMD: + case LLCFT_RR + LLC_RSP: + case LLCFT_REJ + LLC_RSP: { + register int nr = LLCGBITS(frame->llc_control_ext, s_nr); + + if (cmdrsp == LLC_CMD && pollfinal == 1) { + llc_send(linkp, LLCFT_RR, LLC_RSP, 1); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + } else if (cmdrsp == LLC_RSP && pollfinal == 1) { + LLC_UPDATE_NR_RECEIVED(linkp, nr); + linkp->llcl_vs = nr; + llc_resend(linkp, LLC_CMD, 1); + LLC_START_P_TIMER(linkp); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + LLC_NEWSTATE(linkp, REJECT); + } else if (pollfinal == 0) { + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_CLEAR_REMOTE_BUSY(linkp, action); + } + break; + } + case LLCFT_RNR + LLC_CMD: + case LLCFT_RNR + LLC_RSP: { + register int nr = LLCGBITS(frame->llc_control_ext, s_nr); + + if (cmdrsp == LLC_CMD && pollfinal == 1) { + llc_send(linkp, LLCFT_RR, LLC_RSP, 1); + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_SET_REMOTE_BUSY(linkp, action); + } else if (cmdrsp == LLC_RSP && pollfinal == 1) { + LLC_UPDATE_NR_RECEIVED(linkp, nr); + linkp->llcl_vs = nr; + LLC_STOP_P_TIMER(linkp); + LLC_SET_REMOTE_BUSY(linkp, action); + LLC_NEWSTATE(linkp, REJECT); + } else if (pollfinal == 0) { + LLC_UPDATE_NR_RECEIVED(linkp, nr); + LLC_SET_REMOTE_BUSY(linkp, action); + } + break; + } + case LLC_P_TIMER_EXPIRED: + if (linkp->llcl_retry < llc_n2) { + llc_send(linkp, LLCFT_REJ, LLC_CMD, 1); + LLC_START_P_TIMER(linkp); + linkp->llcl_retry++; + action = 0; + } + break; + } + if (action == LLC_PASSITON) + action = llc_state_NBRAcore(linkp, frame, frame_kind, + cmdrsp, pollfinal); + + return action; +} + + +/* + * llc_statehandler() --- Wrapper for llc_state_*() functions. + * Deals with action codes and checks for + * ``stuck'' links. + */ + +int +llc_statehandler(struct llc_linkcb *linkp, struct llc *frame, int frame_kind, + int cmdrsp, int pollfinal) +{ + register int action = 0; + + /* + * To check for ``zombie'' links each time llc_statehandler() gets called + * the AGE timer of linkp is reset. If it expires llc_timer() will + * take care of the link --- i.e. kill it 8=) + */ + LLC_STARTTIMER(linkp, AGE); + + /* + * Now call the current statehandler function. + */ + action = (*linkp->llcl_statehandler)(linkp, frame, frame_kind, + cmdrsp, pollfinal); +once_more_and_again: + switch (action) { + case LLC_CONNECT_INDICATION: { + int naction; + + LLC_TRACE(linkp, LLCTR_INTERESTING, "CONNECT INDICATION"); + linkp->llcl_nlnext = + (*linkp->llcl_sapinfo->si_ctlinput) + (PRC_CONNECT_INDICATION, + (struct sockaddr *) &linkp->llcl_addr, (caddr_t) linkp); + if (linkp->llcl_nlnext == 0) + naction = NL_DISCONNECT_REQUEST; + else naction = NL_CONNECT_RESPONSE; + action = (*linkp->llcl_statehandler)(linkp, frame, naction, 0, 0); + goto once_more_and_again; + } + case LLC_CONNECT_CONFIRM: + /* llc_resend(linkp, LLC_CMD, 0); */ + llc_start(linkp); + break; + case LLC_DISCONNECT_INDICATION: + LLC_TRACE(linkp, LLCTR_INTERESTING, "DISCONNECT INDICATION"); + (*linkp->llcl_sapinfo->si_ctlinput) + (PRC_DISCONNECT_INDICATION, + (struct sockaddr *) &linkp->llcl_addr, linkp->llcl_nlnext); + break; + /* internally visible only */ + case LLC_RESET_CONFIRM: + case LLC_RESET_INDICATION_LOCAL: + /* + * not much we can do here, the state machine either makes it or + * brakes it ... + */ + break; + case LLC_RESET_INDICATION_REMOTE: + LLC_TRACE(linkp, LLCTR_SHOULDKNOW, "RESET INDICATION (REMOTE)"); + action = (*linkp->llcl_statehandler)(linkp, frame, + NL_RESET_RESPONSE, 0, 0); + goto once_more_and_again; + case LLC_FRMR_SENT: + LLC_TRACE(linkp, LLCTR_URGENT, "FRMR SENT"); + break; + case LLC_FRMR_RECEIVED: + LLC_TRACE(linkp, LLCTR_URGEN, "FRMR RECEIVED"); + action = (*linkp->llcl_statehandler)(linkp, frame, + NL_RESET_REQUEST, 0, 0); + + goto once_more_and_again; + case LLC_REMOTE_BUSY: + LLC_TRACE(linkp, LLCTR_SHOULDKNOW, "REMOTE BUSY"); + break; + case LLC_REMOTE_NOT_BUSY: + LLC_TRACE(linkp, LLCTR_SHOULDKNOW, "REMOTE BUSY CLEARED"); + /* + * try to get queued frames out + */ + llc_start(linkp); + break; + } + + /* + * Only LLC_DATA_INDICATION is for the time being + * passed up to the network layer entity. + * The remaining action codes are for the time + * being visible internally only. + * However, this can/may be changed if necessary. + */ + + return action; +} + + +/* + * Core LLC2 routines + */ + +/* + * The INIT call. This routine is called once after the system is booted. + */ + +llc_init() +{ + llcintrq.ifq_maxlen = IFQ_MAXLEN; +} + + +/* + * In case of a link reset we need to shuffle the frames queued inside the + * LLC2 window. + */ + +void +llc_resetwindow(struct llc_linkcb *linkp) +{ + register struct mbuf *mptr = (struct mbuf *) 0; + register struct mbuf *anchor = (struct mbuf *)0; + register short i; + + /* Pick up all queued frames and collect them in a linked mbuf list */ + if (linkp->llcl_slotsfree != linkp->llcl_window) { + i = llc_seq2slot(linkp, linkp->llcl_nr_received); + anchor = mptr = linkp->llcl_output_buffers[i]; + for (; i != linkp->llcl_freeslot; + i = llc_seq2slot(linkp, i+1)) { + if (linkp->llcl_output_buffers[i]) { + mptr->m_nextpkt = linkp->llcl_output_buffers[i]; + mptr = mptr->m_nextpkt; + } else panic("LLC2 window broken"); + } + } + /* clean closure */ + if (mptr) + mptr->m_nextpkt = (struct mbuf *) 0; + + /* Now --- plug 'em in again */ + if (anchor != (struct mbuf *)0) { + for (i = 0, mptr = anchor; mptr != (struct mbuf *) 0; i++) { + linkp->llcl_output_buffers[i] = mptr; + mptr = mptr->m_nextpkt; + linkp->llcl_output_buffers[i]->m_nextpkt = (struct mbuf *)0; + } + linkp->llcl_freeslot = i; + } else linkp->llcl_freeslot = 0; + + /* We're resetting the link, the next frame to be acknowledged is 0 */ + linkp->llcl_nr_received = 0; + + /* set distance between LLC2 sequence number and the top of window to 0 */ + linkp->llcl_projvs = linkp->llcl_freeslot; + + return; +} + +/* + * llc_newlink() --- We allocate enough memory to contain a link control block + * and initialize it properly. We don't intiate the actual setup + * of the LLC2 link here. + */ +struct llc_linkcb * +llc_newlink(struct sockaddr_dl *dst, struct ifnet *ifp, struct rtentry *nlrt, + caddr_t nlnext, struct rtentry *llrt) +{ + struct llc_linkcb *nlinkp; + u_char sap = LLSAPADDR(dst); + short llcwindow; + + + /* allocate memory for link control block */ + MALLOC(nlinkp, struct llc_linkcb *, sizeof(struct llc_linkcb), + M_PCB, M_DONTWAIT); + if (nlinkp == 0) + return (NULL); + bzero((caddr_t)nlinkp, sizeof(struct llc_linkcb)); + + /* copy link address */ + sdl_copy(dst, &nlinkp->llcl_addr); + + /* hold on to the network layer route entry */ + nlinkp->llcl_nlrt = nlrt; + + /* likewise the network layer control block */ + nlinkp->llcl_nlnext = nlnext; + + /* jot down the link layer route entry */ + nlinkp->llcl_llrt = llrt; + + /* reset writeq */ + nlinkp->llcl_writeqh = nlinkp->llcl_writeqt = NULL; + + /* setup initial state handler function */ + nlinkp->llcl_statehandler = llc_state_ADM; + + /* hold on to interface pointer */ + nlinkp->llcl_if = ifp; + + /* get service access point information */ + nlinkp->llcl_sapinfo = llc_getsapinfo(sap, ifp); + + /* get window size from SAP info block */ + if ((llcwindow = nlinkp->llcl_sapinfo->si_window) == 0) + llcwindow = LLC_MAX_WINDOW; + + /* allocate memory for window buffer */ + MALLOC(nlinkp->llcl_output_buffers, struct mbuf **, + llcwindow*sizeof(struct mbuf *), M_PCB, M_DONTWAIT); + if (nlinkp->llcl_output_buffers == 0) { + FREE(nlinkp, M_PCB); + return(NULL); + } + bzero((caddr_t)nlinkp->llcl_output_buffers, + llcwindow*sizeof(struct mbuf *)); + + /* set window size & slotsfree */ + nlinkp->llcl_slotsfree = nlinkp->llcl_window = llcwindow; + + /* enter into linked listed of link control blocks */ + insque(nlinkp, &llccb_q); + + return(nlinkp); +} + +/* + * llc_dellink() --- farewell to link control block + */ +llc_dellink(struct llc_linkcb *linkp) +{ + register struct mbuf *m; + register struct mbuf *n; + register struct npaidbentry *sapinfo = linkp->llcl_sapinfo; + register i; + + /* notify upper layer of imminent death */ + if (linkp->llcl_nlnext && sapinfo->si_ctlinput) + (*sapinfo->si_ctlinput) + (PRC_DISCONNECT_INDICATION, + (struct sockaddr *)&linkp->llcl_addr, linkp->llcl_nlnext); + + /* pull the plug */ + if (linkp->llcl_llrt) + ((struct npaidbentry *)(linkp->llcl_llrt->rt_llinfo))->np_link + = (struct llc_linkcb *) 0; + + /* leave link control block queue */ + remque(linkp); + + /* drop queued packets */ + for (m = linkp->llcl_writeqh; m;) { + n = m->m_act; + m_freem(m); + m = n; + } + + /* drop packets in the window */ + for(i = 0; i < linkp->llcl_window; i++) + if (linkp->llcl_output_buffers[i]) + m_freem(linkp->llcl_output_buffers[i]); + + /* return the window space */ + FREE((caddr_t)linkp->llcl_output_buffers, M_PCB); + + /* return the control block space --- now it's gone ... */ + FREE((caddr_t)linkp, M_PCB); +} + +llc_decode(struct llc* frame, struct llc_linkcb * linkp) +{ + register int ft = LLC_BAD_PDU; + + if ((frame->llc_control & 01) == 0) { + ft = LLCFT_INFO; + /* S or U frame ? */ + } else switch (frame->llc_control) { + + /* U frames */ + case LLC_UI: + case LLC_UI_P: ft = LLC_UI; break; + case LLC_DM: + case LLC_DM_P: ft =LLCFT_DM; break; + case LLC_DISC: + case LLC_DISC_P: ft = LLCFT_DISC; break; + case LLC_UA: + case LLC_UA_P: ft = LLCFT_UA; break; + case LLC_SABME: + case LLC_SABME_P: ft = LLCFT_SABME; break; + case LLC_FRMR: + case LLC_FRMR_P: ft = LLCFT_FRMR; break; + case LLC_XID: + case LLC_XID_P: ft = LLCFT_XID; break; + case LLC_TEST: + case LLC_TEST_P: ft = LLCFT_TEST; break; + + /* S frames */ + case LLC_RR: ft = LLCFT_RR; break; + case LLC_RNR: ft = LLCFT_RNR; break; + case LLC_REJ: ft = LLCFT_REJ; break; + } /* switch */ + + if (linkp) { + switch (ft) { + case LLCFT_INFO: + if (LLCGBITS(frame->llc_control, i_ns) != linkp->llcl_vr) { + ft = LLC_INVALID_NS; + break; + } + /* fall thru --- yeeeeeee */ + case LLCFT_RR: + case LLCFT_RNR: + case LLCFT_REJ: + /* splash! */ + if (LLC_NR_VALID(linkp, LLCGBITS(frame->llc_control_ext, + s_nr)) == 0) + ft = LLC_INVALID_NR; + break; + } + } + + return ft; +} + +/* + * llc_anytimersup() --- Checks if at least one timer is still up and running. + */ +int +llc_anytimersup(struct llc_linkcb * linkp) +{ + register int i; + + FOR_ALL_LLC_TIMERS(i) + if (linkp->llcl_timers[i] > 0) + break; + if (i == LLC_AGE_SHIFT) + return 0; + else return 1; +} + +/* + * llc_link_dump() - dump link info + */ + +#define SAL(s) ((struct sockaddr_dl *)&(s)->llcl_addr) +#define CHECK(l, s) if (LLC_STATEEQ(l, s)) return #s + +char *timer_names[] = {"ACK", "P", "BUSY", "REJ", "AGE"}; + +char * +llc_getstatename(struct llc_linkcb *linkp) +{ + CHECK(linkp, ADM); + CHECK(linkp, CONN); + CHECK(linkp, RESET_WAIT); + CHECK(linkp, RESET_CHECK); + CHECK(linkp, SETUP); + CHECK(linkp, RESET); + CHECK(linkp, D_CONN); + CHECK(linkp, ERROR); + CHECK(linkp, NORMAL); + CHECK(linkp, BUSY); + CHECK(linkp, REJECT); + CHECK(linkp, AWAIT); + CHECK(linkp, AWAIT_BUSY); + CHECK(linkp, AWAIT_REJECT); + + return "UNKNOWN - eh?"; +} + +void +llc_link_dump(struct llc_linkcb* linkp, const char *message) +{ + register int i; + register char *state; + + /* print interface */ + printf("if %s%d\n", linkp->llcl_if->if_name, linkp->llcl_if->if_unit); + + /* print message */ + printf(">> %s <<\n", message); + + /* print MAC and LSAP */ + printf("llc addr "); + for (i = 0; i < (SAL(linkp)->sdl_alen)-2; i++) + printf("%x:", (char)*(LLADDR(SAL(linkp))+i) & 0xff); + printf("%x,", (char)*(LLADDR(SAL(linkp))+i) & 0xff); + printf("%x\n", (char)*(LLADDR(SAL(linkp))+i+1) & 0xff); + + /* print state we're in and timers */ + printf("state %s, ", llc_getstatename(linkp)); + for (i = LLC_ACK_SHIFT; i < LLC_AGE_SHIFT; i++) + printf("%s-%c %d/", timer_names[i], + (linkp->llcl_timerflags & (1<<i) ? 'R' : 'S'), + linkp->llcl_timers[i]); + printf("%s-%c %d\n", timer_names[i], (linkp->llcl_timerflags & (1<<i) ? + 'R' : 'S'), linkp->llcl_timers[i]); + + /* print flag values */ + printf("flags P %d/F %d/S %d/DATA %d/REMOTE_BUSY %d\n", + LLC_GETFLAG(linkp, P), LLC_GETFLAG(linkp, S), + LLC_GETFLAG(linkp, DATA), LLC_GETFLAG(linkp, REMOTE_BUSY)); + + /* print send and receive state variables, ack, and window */ + printf("V(R) %d/V(S) %d/N(R) received %d/window %d/freeslot %d\n", + linkp->llcl_vs, linkp->llcl_vr, linkp->llcl_nr_received, + linkp->llcl_window, linkp->llcl_freeslot); + + /* further expansions can follow here */ + +} + +void +llc_trace(struct llc_linkcb *linkp, int level, const char *message) +{ + if (linkp->llcl_sapinfo->si_trace && level > llc_tracelevel) + llc_link_dump(linkp, message); + + return; +} diff --git a/sys/netccitt/llc_timer.c b/sys/netccitt/llc_timer.c new file mode 100644 index 00000000000..a404da99591 --- /dev/null +++ b/sys/netccitt/llc_timer.c @@ -0,0 +1,182 @@ +/* $NetBSD: llc_timer.c,v 1.2 1994/06/29 06:37:26 cgd Exp $ */ + +/* + * Copyright (C) Dirk Husemann, Computer Science Department IV, + * University of Erlangen-Nuremberg, Germany, 1990, 1991, 1992 + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Dirk Husemann 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. + * + * @(#)llc_timer.c 8.1 (Berkeley) 6/10/93 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/domain.h> +#include <sys/socket.h> +#include <sys/protosw.h> +#include <sys/errno.h> +#include <sys/time.h> +#include <sys/kernel.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_llc.h> + +#include <netccitt/dll.h> +#include <netccitt/llc_var.h> + + +/* + * Various timer values. They can be adjusted + * by patching the binary with adb if necessary. + */ +/* ISO 8802-2 timers */ +int llc_n2 = LLC_N2_VALUE; +int llc_ACK_timer = LLC_ACK_TIMER; +int llc_P_timer = LLC_P_TIMER; +int llc_BUSY_timer = LLC_BUSY_TIMER; +int llc_REJ_timer = LLC_REJ_TIMER; +/* Implementation specific timers */ +int llc_AGE_timer = LLC_AGE_TIMER; +int llc_DACTION_timer = LLC_DACTION_TIMER; + +/* + * The timer routine. We are called every 500ms by the kernel. + * Handle the various virtual timers. + */ + +void +llc_timer() +{ + register struct llc_linkcb *linkp; + register struct llc_linkcb *nlinkp; + register int timer; + register int action; + register int s = splimp(); + + /* + * All links are accessible over the doubly linked list llccb_q + */ + if (!LQEMPTY) { + /* + * A for-loop is not that great an idea as the linkp + * might get deleted if the age timer has expired ... + */ + linkp = LQFIRST; + while (LQVALID(linkp)) { + nlinkp = LQNEXT(linkp); + /* + * Check implementation specific timers first + */ + /* The delayed action/acknowledge idle timer */ + switch (LLC_TIMERXPIRED(linkp, DACTION)) { + case LLC_TIMER_RUNNING: + LLC_AGETIMER(linkp, DACTION); + break; + case LLC_TIMER_EXPIRED: { + register int cmdrsp; + register int pollfinal; + + switch (LLC_GETFLAG(linkp, DACTION)) { + case LLC_DACKCMD: + cmdrsp = LLC_CMD, pollfinal = 0; + break; + case LLC_DACKCMDPOLL: + cmdrsp = LLC_CMD, pollfinal = 1; + break; + case LLC_DACKRSP: + cmdrsp = LLC_RSP, pollfinal = 0; + break; + case LLC_DACKRSPFINAL: + cmdrsp = LLC_RSP, pollfinal = 1; + break; + } + llc_send(linkp, LLCFT_RR, cmdrsp, pollfinal); + LLC_STOPTIMER(linkp, DACTION); + break; + } + } + /* The link idle timer */ + switch (LLC_TIMERXPIRED(linkp, AGE)) { + case LLC_TIMER_RUNNING: + LLC_AGETIMER(linkp, AGE); + break; + case LLC_TIMER_EXPIRED: + /* + * Only crunch the link when really no + * timers are running any more. + */ + if (llc_anytimersup(linkp) == 0) { + llc_dellink(linkp); + LLC_STOPTIMER(linkp, AGE); + goto gone; + } else { + LLC_STARTTIMER(linkp, AGE); + } + break; + } + /* + * Now, check all the ISO 8802-2 timers + */ + FOR_ALL_LLC_TIMERS(timer) { + action = 0; + if ((linkp->llcl_timerflags & (1<<timer)) && + (linkp->llcl_timers[timer] == 0)) { + switch (timer) { + case LLC_ACK_SHIFT: + action = LLC_ACK_TIMER_EXPIRED; + break; + case LLC_P_SHIFT: + action = LLC_P_TIMER_EXPIRED; + break; + case LLC_BUSY_SHIFT: + action = LLC_BUSY_TIMER_EXPIRED; + break; + case LLC_REJ_SHIFT: + action = LLC_REJ_TIMER_EXPIRED; + break; + } + linkp->llcl_timerflags &= ~(1<<timer); + (void)llc_statehandler(linkp, (struct llc *)0, action, 0, 1); + } else if (linkp->llcl_timers[timer] > 0) + linkp->llcl_timers[timer]--; + } + +gone: linkp = nlinkp; + } + } + splx (s); +} diff --git a/sys/netccitt/llc_var.h b/sys/netccitt/llc_var.h new file mode 100644 index 00000000000..63f4f9a0856 --- /dev/null +++ b/sys/netccitt/llc_var.h @@ -0,0 +1,661 @@ +/* $NetBSD: llc_var.h,v 1.4 1995/03/29 22:09:12 briggs Exp $ */ + +/* + * Copyright (C) Dirk Husemann, Computer Science Department IV, + * University of Erlangen-Nuremberg, Germany, 1990, 1991, 1992 + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Dirk Husemann 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. + * + * @(#)llc_var.h 8.1 (Berkeley) 6/10/93 + */ + +#ifdef __STDC__ +/* + * Forward structure declarations for function prototypes [sic]. + */ +struct llc; +#endif + +#define NPAIDB_LINK 0 + +struct npaidbentry { + union { + /* MAC,DLSAP -> CONS */ + struct { + struct llc_linkcb *NE_link; + struct rtentry *NE_rt; + } NE; + /* SAP info for unconfigured incoming calls */ + struct { + u_short SI_class; +#define LLC_CLASS_I 0x1 +#define LLC_CLASS_II 0x3 +#define LLC_CLASS_III 0x4 /* Future */ +#define LLC_CLASS_IV 0x7 /* Future */ + u_short SI_window; + u_short SI_trace; + u_short SI_xchxid; + void (*SI_input) + __P((struct mbuf *)); + caddr_t (*SI_ctlinput) + __P((int, struct sockaddr *, caddr_t)); + } SI; + } NESIun; +}; +#define np_link NESIun.NE.NE_link +#define np_rt NESIun.NE.NE_rt +#define si_class NESIun.SI.SI_class +#define si_window NESIun.SI.SI_window +#define si_trace NESIun.SI.SI_trace +#define si_xchxid NESIun.SI.SI_xchxid +#define si_input NESIun.SI.SI_input +#define si_ctlinput NESIun.SI.SI_ctlinput + +#define NPDL_SAPNETMASK 0x7e + +/* + * Definitions for accessing bitfields/bitslices inside + * LLC2 headers + */ +struct bitslice { + unsigned int bs_mask; + unsigned int bs_shift; +}; + + +#define i_z 0 +#define i_ns 1 +#define i_pf 0 +#define i_nr 1 +#define s_oz 2 +#define s_selector 3 +#define s_pf 0 +#define s_nr 1 +#define u_bb 2 +#define u_select_other 3 +#define u_pf 4 +#define u_select 5 +#define f_vs 1 +#define f_cr 0 +#define f_vr 1 +#define f_wxyzv 6 + +#define LLCGBITS(Arg, Index) (((Arg) & llc_bitslice[(Index)].bs_mask) >> llc_bitslice[(Index)].bs_shift) +#define LLCSBITS(Arg, Index, Val) (Arg) |= (((Val) << llc_bitslice[(Index)].bs_shift) & llc_bitslice[(Index)].bs_mask) +#define LLCCSBITS(Arg, Index, Val) (Arg) = (((Val) << llc_bitslice[(Index)].bs_shift) & llc_bitslice[(Index)].bs_mask) + +extern struct bitslice llc_bitslice[]; + +#define LLC_CMD 0 +#define LLC_RSP 1 +#define LLC_MAXCMDRSP 2 + +/* + * LLC events --- These events may either be frames received from the + * remote LLC DSAP, request from the network layer user, + * timer events from llc_timer(), or diagnostic events from + * llc_input(). + */ + +/* LLC frame types */ +#define LLCFT_INFO 0 * LLC_MAXCMDRSP +#define LLCFT_RR 1 * LLC_MAXCMDRSP +#define LLCFT_RNR 2 * LLC_MAXCMDRSP +#define LLCFT_REJ 3 * LLC_MAXCMDRSP +#define LLCFT_DM 4 * LLC_MAXCMDRSP +#define LLCFT_SABME 5 * LLC_MAXCMDRSP +#define LLCFT_DISC 6 * LLC_MAXCMDRSP +#define LLCFT_UA 7 * LLC_MAXCMDRSP +#define LLCFT_FRMR 8 * LLC_MAXCMDRSP +#define LLCFT_UI 9 * LLC_MAXCMDRSP +#define LLCFT_XID 10 * LLC_MAXCMDRSP +#define LLCFT_TEST 11 * LLC_MAXCMDRSP + +/* LLC2 timer events */ +#define LLC_ACK_TIMER_EXPIRED 12 * LLC_MAXCMDRSP +#define LLC_P_TIMER_EXPIRED 13 * LLC_MAXCMDRSP +#define LLC_REJ_TIMER_EXPIRED 14 * LLC_MAXCMDRSP +#define LLC_BUSY_TIMER_EXPIRED 15 * LLC_MAXCMDRSP + +/* LLC2 diagnostic events */ +#define LLC_INVALID_NR 16 * LLC_MAXCMDRSP +#define LLC_INVALID_NS 17 * LLC_MAXCMDRSP +#define LLC_BAD_PDU 18 * LLC_MAXCMDRSP +#define LLC_LOCAL_BUSY_DETECTED 19 * LLC_MAXCMDRSP +#define LLC_LOCAL_BUSY_CLEARED 20 * LLC_MAXCMDRSP + +/* Network layer user requests */ +/* + * NL_CONNECT_REQUEST --- The user has requested that a data link connection + * be established with a remote LLC DSAP. + */ +#define NL_CONNECT_REQUEST 21 * LLC_MAXCMDRSP +/* + * NL_CONNECT_RESPONSE --- The user has accepted the data link connection. + */ +#define NL_CONNECT_RESPONSE 22 * LLC_MAXCMDRSP +/* + * NL_RESET_REQUEST --- The user has requested that the data link with the + * remote LLC DSAP be reset. + */ +#define NL_RESET_REQUEST 23 * LLC_MAXCMDRSP +/* + * NL_RESET_RESPONSE --- The user has accepted the reset of the data link + * connection. + */ +#define NL_RESET_RESPONSE 24 * LLC_MAXCMDRSP +/* + * NL_DISCONNECT_REQUEST --- The user has requested that the data link + * connection with remote LLC DSAP be terminated. + */ +#define NL_DISCONNECT_REQUEST 25 * LLC_MAXCMDRSP +/* + * NL_DATA_REQUEST --- The user has requested that a data unit be sent ot the + * remote LLC DSAP. + */ +#define NL_DATA_REQUEST 26 * LLC_MAXCMDRSP +/* + * NL_INITIATE_PF_CYCLE --- The local LLC wants to initiate a P/F cycle. + */ +#define NL_INITIATE_PF_CYCLE 27 * LLC_MAXCMDRSP +/* + * NL_LOCAL_BUSY_DETECTED --- The local entity has encountered a busy condition + */ +#define NL_LOCAL_BUSY_DETECTED 28 * LLC_MAXCMDRSP + +#define LLCFT_NONE 255 + +/* return message from state handlers */ + +/* + * LLC_CONNECT_INDICATION --- Inform the user that a connection has been + * requested by a remote LLC SSAP. + */ +#define LLC_CONNECT_INDICATION 1 +/* + * LLC_CONNECT_CONFIRM --- The connection service component indicates that the + * remote network entity has accepted the connection. + */ +#define LLC_CONNECT_CONFIRM 2 +/* + * LLC_DISCONNECT_INDICATION --- Inform the user that the remote network + * entity has intiated disconnection of the data + * link connection. + */ +#define LLC_DISCONNECT_INDICATION 3 +/* + * LLC_RESET_CONFIRM --- The connection service component indicates that the + * remote network entity has accepted the reset. + */ +#define LLC_RESET_CONFIRM 4 +/* + * LLC_RESET_INDICATION_REMOTE --- The remote network entity or remote peer + * has initiated a reset of the data link + * connection. + */ +#define LLC_RESET_INDICATION_REMOTE 5 +/* + * LLC_RESET_INDICATION_LOCAL --- The local LLC has determined that the data + * link connection is in need of + * reinitialization. + */ +#define LLC_RESET_INDICATION_LOCAL 6 +/* + * LLC_FRMR_RECEIVED --- The local connection service component has received a + * FRMR response PDU. + */ +#define LLC_FRMR_RECEIVED 7 +/* + * LLC_FRMR_SENT --- The local connection component has received an ivalid + * PDU, and has sent a FRMR response PDU. + */ +#define LLC_FRMR_SENT 8 +/* + * LLC_DATA_INDICATION --- The connection service component passes the data + * unit from the received I PDU to the user. + */ +#define LLC_DATA_INDICATION 9 +/* + * LLC_REMOTE_NOT_BUSY --- The remote LLC DSAP is no longer busy. The local + * connection service component will now accept a + * DATA_REQUEST. + */ +#define LLC_REMOTE_NOT_BUSY 10 +/* + * LLC_REMOTE_BUSY --- The remote LLC DSAP is busy. The local connection + * service component will not accept a DATA_REQUEST. + */ +#define LLC_REMOTE_BUSY 11 + +/* Internal return code */ +#define LLC_PASSITON 255 + +#define INFORMATION_CONTROL 0x00 +#define SUPERVISORY_CONTROL 0x02 +#define UNUMBERED_CONTROL 0x03 + +/* + * Other necessary definitions + */ + +#define LLC_MAX_SEQUENCE 128 +#define LLC_MAX_WINDOW 127 +#define LLC_WINDOW_SIZE 7 + +/* + * Don't we love this one? CCITT likes to suck on bits 8=) + */ +#define NLHDRSIZEGUESS 3 + +/* + * LLC control block + */ + +struct llc_linkcb { + struct llccb_q { + struct llccb_q *q_forw; /* admin chain */ + struct llccb_q *q_backw; + } llcl_q; + struct npaidbentry *llcl_sapinfo; /* SAP information */ + struct sockaddr_dl llcl_addr; /* link snpa address */ + struct rtentry *llcl_nlrt; /* layer 3 -> LLC */ + struct rtentry *llcl_llrt; /* LLC -> layer 3 */ + struct ifnet *llcl_if; /* our interface */ + caddr_t llcl_nlnext; /* cb for network layer */ + struct mbuf *llcl_writeqh; /* Write queue head */ + struct mbuf *llcl_writeqt; /* Write queue tail */ + struct mbuf **llcl_output_buffers; + short llcl_timers[6]; /* timer array */ + long llcl_timerflags; /* flags signalling running timers */ + int (*llcl_statehandler) + __P((struct llc_linkcb *, struct llc *, int, int, int)); + int llcl_P_flag; + int llcl_F_flag; + int llcl_S_flag; + int llcl_DATA_flag; + int llcl_REMOTE_BUSY_flag; + int llcl_DACTION_flag; /* delayed action */ + int llcl_retry; + /* + * The following components deal --- in one way or the other --- + * with the LLC2 window. Indicated by either [L] or [W] is the + * domain of the specific component: + * + * [L] The domain is 0--LLC_MAX_WINDOW + * [W] The domain is 0--llcl_window + */ + short llcl_vr; /* next to receive [L] */ + short llcl_vs; /* next to send [L] */ + short llcl_nr_received; /* next frame to b ack'd [L] */ + short llcl_freeslot; /* next free slot [W] */ + short llcl_projvs; /* V(S) associated with freeslot */ + short llcl_slotsfree; /* free slots [W] */ + short llcl_window; /* window size */ + /* + * In llcl_frmrinfo we jot down the last frmr info field, which we + * need to do as we need to be able to resend it in the ERROR state. + */ + struct frmrinfo llcl_frmrinfo; /* last FRMR info field */ +}; +#define llcl_frmr_pdu0 llcl_frmrinfo.rej_pdu_0 +#define llcl_frmr_pdu1 llcl_frmrinfo.rej_pdu_1 +#define llcl_frmr_control llcl_frmrinfo.frmr_control +#define llcl_frmr_control_ext llcl_frmrinfo.frmr_control_ext +#define llcl_frmr_cause llcl_frmrinfo.frmr_cause + +#define LQNEXT(l) (struct llc_linkcb *)((l)->llcl_q.q_forw) +#define LQEMPTY (llccb_q.q_forw == &llccb_q) +#define LQFIRST (struct llc_linkcb *)(llccb_q.q_forw) +#define LQVALID(l) (!((struct llccb_q *)(l) == &llccb_q)) + +#define LLC_ENQUEUE(l, m) if ((l)->llcl_writeqh == NULL) { \ + (l)->llcl_writeqh = (m); \ + (l)->llcl_writeqt = (m); \ + } else { \ + (l)->llcl_writeqt->m_nextpkt = (m); \ + (l)->llcl_writeqt = (m); \ + } + +#define LLC_DEQUEUE(l, m) if ((l)->llcl_writeqh == NULL) \ + (m) = NULL; \ + else { \ + (m) = (l)->llcl_writeqh; \ + (l)->llcl_writeqh = (l)->llcl_writeqh->m_nextpkt; \ + } + +#define LLC_SETFRAME(l, m) { \ + if ((l)->llcl_slotsfree > 0) { \ + (l)->llcl_slotsfree--; \ + (l)->llcl_output_buffers[(l)->llcl_freeslot] = (m); \ + (l)->llcl_freeslot = ((l)->llcl_freeslot+1) % (l)->llcl_window; \ + LLC_INC((l)->llcl_projvs); \ + } \ + } + +/* + * handling of sockaddr_dl's + */ + +#define LLADDRLEN(s) ((s)->sdl_alen + (s)->sdl_nlen) +#define LLSAPADDR(s) ((s)->sdl_data[LLADDRLEN(s)-1] & 0xff) +#define LLSAPLOC(s, if) ((s)->sdl_nlen + (if)->if_addrlen) + +struct sdl_hdr { + struct sockaddr_dl sdlhdr_dst; + struct sockaddr_dl sdlhdr_src; + long sdlhdr_len; +}; + +#define LLC_GETHDR(f,m) { \ + struct mbuf *_m = (struct mbuf *) (m); \ + if (_m) { \ + M_PREPEND(_m, LLC_ISFRAMELEN, M_DONTWAIT); \ + bzero(mtod(_m, caddr_t), LLC_ISFRAMELEN); \ + } else { \ + MGETHDR (_m, M_DONTWAIT, MT_HEADER); \ + if (_m != NULL) { \ + _m->m_pkthdr.len = _m->m_len = LLC_UFRAMELEN; \ + _m->m_next = _m->m_act = NULL; \ + bzero(mtod(_m, caddr_t), LLC_UFRAMELEN); \ + } else return; \ + } \ + (m) = _m; \ + (f) = mtod(m, struct llc *); \ + } + +#define LLC_NEWSTATE(l, LLCstate) (l)->llcl_statehandler = llc_state_##LLCstate +#define LLC_STATEEQ(l, LLCstate) ((l)->llcl_statehandler == llc_state_##LLCstate ? 1 : 0) + +#define LLC_ACK_SHIFT 0 +#define LLC_P_SHIFT 1 +#define LLC_BUSY_SHIFT 2 +#define LLC_REJ_SHIFT 3 +#define LLC_AGE_SHIFT 4 +#define LLC_DACTION_SHIFT 5 + +#define LLC_TIMER_NOTRUNNING 0 +#define LLC_TIMER_RUNNING 1 +#define LLC_TIMER_EXPIRED 2 + +#define LLC_STARTTIMER(l, LLCtimer) { \ + (l)->llcl_timers[LLC_##LLCtimer##_SHIFT] = llc_##LLCtimer##_timer; \ + (l)->llcl_timerflags |= (1<<LLC_##LLCtimer##_SHIFT); \ + } +#define LLC_STOPTIMER(l, LLCtimer) { \ + (l)->llcl_timers[LLC_##LLCtimer##_SHIFT] = 0; \ + (l)->llcl_timerflags &= ~(1<<LLC_##LLCtimer##_SHIFT); \ + } +#define LLC_AGETIMER(l, LLCtimer) if ((l)->llcl_timers[LLC_##LLCtimer##_SHIFT] > 0) \ + (l)->llcl_timers[LLC_##LLCtimer##_SHIFT]--; + +#define LLC_TIMERXPIRED(l, LLCtimer) \ + (((l)->llcl_timerflags & (1<<LLC_##LLCtimer##_SHIFT)) ? \ + (((l)->llcl_timers[LLC_##LLCtimer##_SHIFT] == 0 ) ? \ + LLC_TIMER_EXPIRED : LLC_TIMER_RUNNING) : LLC_TIMER_NOTRUNNING) + +#define FOR_ALL_LLC_TIMERS(t) for ((t) = LLC_ACK_SHIFT; (t) < LLC_AGE_SHIFT; (t)++) + +#define LLC_SETFLAG(l, LLCflag, v) (l)->llcl_##LLCflag##_flag = (v) +#define LLC_GETFLAG(l, LLCflag) (l)->llcl_##LLCflag##_flag + +#define LLC_RESETCOUNTER(l) { \ + (l)->llcl_vs = (l)->llcl_vr = (l)->llcl_retry = 0; \ + llc_resetwindow((l)); \ + } + +/* + * LLC2 macro definitions + */ + + +#define LLC_START_ACK_TIMER(l) LLC_STARTTIMER((l), ACK) +#define LLC_STOP_ACK_TIMER(l) LLC_STOPTIMER((l), ACK) +#define LLC_START_REJ_TIMER(l) LLC_STARTTIMER((l), REJ) +#define LLC_STOP_REJ_TIMER(l) LLC_STOPTIMER((l), REJ) +#define LLC_START_P_TIMER(l) { \ + LLC_STARTTIMER((l), P); \ + if (LLC_GETFLAG((l), P) == 0) \ + (l)->llcl_retry = 0; \ + LLC_SETFLAG((l), P, 1); \ + } +#define LLC_STOP_P_TIMER(l) { \ + LLC_STOPTIMER((l), P); \ + LLC_SETFLAG((l), P, 0); \ + } +#define LLC_STOP_ALL_TIMERS(l) { \ + LLC_STOPTIMER((l), ACK); \ + LLC_STOPTIMER((l), REJ); \ + LLC_STOPTIMER((l), BUSY); \ + LLC_STOPTIMER((l), P); \ + } + + +#define LLC_INC(i) (i) = ((i)+1) % LLC_MAX_SEQUENCE + +#define LLC_NR_VALID(l, nr) ((l)->llcl_vs < (l)->llcl_nr_received ? \ + (((nr) >= (l)->llcl_nr_received) || \ + ((nr) <= (l)->llcl_vs) ? 1 : 0) : \ + (((nr) <= (l)->llcl_vs) && \ + ((nr) >= (l)->llcl_nr_received) ? 1 : 0)) + +#define LLC_UPDATE_P_FLAG(l, cr, pf) { \ + if ((cr) == LLC_RSP && (pf) == 1) { \ + LLC_SETFLAG((l), P, 0); \ + LLC_STOPTIMER((l), P); \ + } \ + } + +#define LLC_UPDATE_NR_RECEIVED(l, nr) { \ + while ((l)->llcl_nr_received != (nr)) { \ + struct mbuf *_m; \ + register short seq; \ + if (_m = (l)->llcl_output_buffers[seq = llc_seq2slot((l), (l)->llcl_nr_received)]) \ + m_freem(_m); \ + (l)->llcl_output_buffers[seq] = NULL; \ + LLC_INC((l)->llcl_nr_received); \ + (l)->llcl_slotsfree++; \ + } \ + (l)->llcl_retry = 0; \ + if ((l)->llcl_slotsfree < (l)->llcl_window) { \ + LLC_START_ACK_TIMER(l); \ + } else LLC_STOP_ACK_TIMER(l); \ + LLC_STARTTIMER((l), DACTION); \ + } + +#define LLC_SET_REMOTE_BUSY(l,a) { \ + if (LLC_GETFLAG((l), REMOTE_BUSY) == 0) { \ + LLC_SETFLAG((l), REMOTE_BUSY, 1); \ + LLC_STARTTIMER((l), BUSY); \ + (a) = LLC_REMOTE_BUSY; \ + } else { \ + (a) = 0; \ + } \ + } +#define LLC_CLEAR_REMOTE_BUSY(l,a) { \ + if (LLC_GETFLAG((l), REMOTE_BUSY) == 1) { \ + LLC_SETFLAG((l), REMOTE_BUSY, 1); \ + LLC_STOPTIMER((l), BUSY); \ + if (LLC_STATEEQ((l), NORMAL) || \ + LLC_STATEEQ((l), REJECT) || \ + LLC_STATEEQ((l), BUSY)) \ + llc_resend((l), LLC_CMD, 0); \ + (a) = LLC_REMOTE_NOT_BUSY; \ + } else { \ + (a) = 0; \ + } \ + } + +#define LLC_DACKCMD 0x1 +#define LLC_DACKCMDPOLL 0x2 +#define LLC_DACKRSP 0x3 +#define LLC_DACKRSPFINAL 0x4 + +#define LLC_SENDACKNOWLEDGE(l, cmd, pf) { \ + if ((cmd) == LLC_CMD) { \ + LLC_SETFLAG((l), DACTION, ((pf) == 0 ? LLC_DACKCMD : LLC_DACKCMDPOLL)); \ + } else { \ + LLC_SETFLAG((l), DACTION, ((pf) == 0 ? LLC_DACKRSP : LLC_DACKRSPFINAL)); \ + } \ + } + +#define LLC_FRMR_W (1<<0) +#define LLC_FRMR_X (1<<1) +#define LLC_FRMR_Y (1<<2) +#define LLC_FRMR_Z (1<<3) +#define LLC_FRMR_V (1<<4) + +#define LLC_SETFRMR(l, f, cr, c) { \ + if ((f)->llc_control & 0x3) { \ + (l)->llcl_frmr_pdu0 = (f)->llc_control; \ + (l)->llcl_frmr_pdu1 = 0; \ + } else { \ + (l)->llcl_frmr_pdu0 = (f)->llc_control; \ + (l)->llcl_frmr_pdu1 = (f)->llc_control_ext; \ + } \ + LLCCSBITS((l)->llcl_frmr_control, f_vs, (l)->llcl_vs); \ + LLCCSBITS((l)->llcl_frmr_control_ext, f_cr, (cr)); \ + LLCSBITS((l)->llcl_frmr_control_ext, f_vr, (l)->llcl_vr); \ + LLCCSBITS((l)->llcl_frmr_cause, f_wxyzv, (c)); \ + } + +/* + * LLC tracing levels: + * LLCTR_INTERESTING interesting event, we might care to know about + * it, but then again, we might not ... + * LLCTR_SHOULDKNOW we probably should know about this event + * LLCTR_URGENT something has gone utterly wrong ... + */ +#define LLCTR_INTERESTING 1 +#define LLCTR_SHOULDKNOW 2 +#define LLCTR_URGENT 3 + +#ifdef LLCDEBUG +#define LLC_TRACE(lp, l, msg) llc_trace((lp), (l), (msg)) +#else /* LLCDEBUG */ +#define LLC_TRACE(lp, l, msg) /* NOOP */ +#endif /* LLCDEBUG */ + +#define LLC_N2_VALUE 15 /* up to 15 retries */ +#define LLC_ACK_TIMER 10 /* 5 secs */ +#define LLC_P_TIMER 4 /* 2 secs */ +#define LLC_BUSY_TIMER 12 /* 6 secs */ +#define LLC_REJ_TIMER 12 /* 6 secs */ +#define LLC_AGE_TIMER 40 /* 20 secs */ +#define LLC_DACTION_TIMER 2 /* 1 secs */ + +#if defined (_KERNEL) && defined(LLC) +extern int llc_n2; +extern int llc_ACK_timer; +extern int llc_P_timer; +extern int llc_REJ_timer; +extern int llc_BUSY_timer; +extern int llc_AGE_timer; +extern int llc_DACTION_timer; + +extern int af_link_rts_init_done; + +#define USES_AF_LINK_RTS { \ + if (!af_link_rts_init_done) { \ + rn_inithead((void **)&rt_tables[AF_LINK], 32); \ + af_link_rts_init_done++; \ + } \ + } + +struct ifqueue llcintrq; + +extern struct llccb_q llccb_q; +extern char *frame_names[]; + +/* + * Function prototypes + */ +int sdl_cmp __P((struct sockaddr_dl *, struct sockaddr_dl *)); +int sdl_copy __P((struct sockaddr_dl *, struct sockaddr_dl *)); +int sdl_swapaddr __P((struct sockaddr_dl *, struct sockaddr_dl *)); +int sdl_checkaddrif __P((struct ifnet *, struct sockaddr_dl *)); +int sdl_setaddrif __P((struct ifnet *, u_char *, u_char, u_char, + struct sockaddr_dl *)); +int sdl_sethdrif __P((struct ifnet *, u_char *, u_char, u_char *, u_char, u_char, + struct sdl_hdr *)); +struct npaidbentry *llc_setsapinfo __P((struct ifnet *, u_char, u_char, + struct dllconfig *)); +struct npaidbentry *llc_getsapinfo __P((u_char, struct ifnet *)); +struct rtentry *npaidb_enrich __P((short, caddr_t, struct sockaddr_dl *)); +int npaidb_destroy __P((struct rtentry *)); +short llc_seq2slot __P((struct llc_linkcb *, short)); +int llc_state_ADM __P((struct llc_linkcb *, struct llc *, int, int, int)); +int llc_state_CONN __P((struct llc_linkcb *, struct llc *, int, int, int)); +int llc_state_RESET_WAIT __P((struct llc_linkcb *, struct llc *, + int, int, int)); +int llc_state_RESET_CHECK __P((struct llc_linkcb *, struct llc *, + int, int, int)); +int llc_state_SETUP __P((struct llc_linkcb *, struct llc *, int, int, int)); +int llc_state_RESET __P((struct llc_linkcb *, struct llc *, int, int, int)); +int llc_state_D_CONN __P((struct llc_linkcb *, struct llc *, int, int, int)); +int llc_state_ERROR __P((struct llc_linkcb *, struct llc *, int, int, int)); +int llc_state_NBRAcore __P((struct llc_linkcb *, struct llc *, int, int, int)); +int llc_state_NORMAL __P((struct llc_linkcb *, struct llc *, int, int, int)); +int llc_state_BUSY __P((struct llc_linkcb *, struct llc *, int, int, int)); +int llc_state_REJECT __P((struct llc_linkcb *, struct llc *, int, int, int)); +int llc_state_AWAIT __P((struct llc_linkcb *, struct llc *, int, int, int)); +int llc_state_AWAIT_BUSY __P((struct llc_linkcb *, struct llc *, int, int, int)); +int llc_state_AWAIT_REJECT __P((struct llc_linkcb *, struct llc *, int, int, int)); +int llc_statehandler __P((struct llc_linkcb *, struct llc *, int, int, int)); +int llc_init __P((void)); +struct llc_linkcb *llc_newlink __P((struct sockaddr_dl *, struct ifnet *, + struct rtentry *, caddr_t, struct rtentry *)); +int llc_dellink __P((struct llc_linkcb *)); +int llc_anytimersup __P((struct llc_linkcb *)); +char * llc_getstatename __P((struct llc_linkcb *)); +void llc_link_dump __P((struct llc_linkcb *, const char *)); +void llc_trace __P((struct llc_linkcb *, int, const char *)); +void llc_resetwindow __P((struct llc_linkcb *)); +int llc_decode __P((struct llc *, struct llc_linkcb *)); +void llc_timer __P((void)); +void llcintr __P((void)); +int llc_input __P((struct llc_linkcb *, struct mbuf *, u_char)); +caddr_t llc_ctlinput __P((int, struct sockaddr *, caddr_t)); +int llc_output __P((struct llc_linkcb *, struct mbuf *)); +void llc_start __P((struct llc_linkcb *)); +int llc_send __P((struct llc_linkcb *, int, int, int)); +int llc_resend __P((struct llc_linkcb *, int, int)); +int llc_rawsend __P((struct llc_linkcb *, struct mbuf *, struct llc *, int, int, + int, int)); +int cons_rtrequest __P((int, struct rtentry *, struct sockaddr *)); +long x25_llcglue __P((int, struct sockaddr *)); + +#endif + + diff --git a/sys/netccitt/pk.h b/sys/netccitt/pk.h new file mode 100644 index 00000000000..10b2fa218cf --- /dev/null +++ b/sys/netccitt/pk.h @@ -0,0 +1,209 @@ +/* $NetBSD: pk.h,v 1.5 1994/06/29 06:37:29 cgd Exp $ */ + +/* + * Copyright (c) University of British Columbia, 1984 + * Copyright (c) 1990, 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. + * + * 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.h 8.1 (Berkeley) 6/10/93 + */ + +/* + * + * X.25 Packet Level Definitions: + * + */ + +/* Packet type identifier field defintions. */ + +#define X25_CALL 11 +#define X25_CALL_ACCEPTED 15 +#define X25_CLEAR 19 +#define X25_CLEAR_CONFIRM 23 +#define X25_DATA 0 +#define X25_INTERRUPT 35 +#define X25_INTERRUPT_CONFIRM 39 + +#define X25_RR 1 +#define X25_RNR 5 +#define X25_REJECT 9 +#define X25_RESET 27 +#define X25_RESET_CONFIRM 31 +#define X25_DIAGNOSTIC 241 + +#define X25_RESTART 251 +#define X25_RESTART_CONFIRM 255 + +/* Restart cause field definitions. */ + +#define X25_RESTART_DTE_ORIGINATED 0 +#define X25_RESTART_LOCAL_PROCEDURE_ERROR 1 +#define X25_RESTART_NETWORK_CONGESTION 3 +#define X25_RESTART_NETWORK_OPERATIONAL 7 +#define X25_RESTART_DTE_ORIGINATED2 128 + + +/* Miscellaneous definitions. */ + +#define DATA_PACKET_DESIGNATOR 0x01 +#define RR_OR_RNR_PACKET_DESIGNATOR 0x02 +#define RR_PACKET_DESIGNATOR 0x04 + +#define DEFAULT_WINDOW_SIZE 2 +#define MODULUS 8 + +#define ADDRLN 1 +#define MAXADDRLN 15 +#define FACILITIESLN 1 +#define MAXFACILITIESLN 10 +#define MAXUSERDATA 16 +#define MAXCALLINFOLN 1+15+1+10+16 + +#define PACKET_OK 0 +#define IGNORE_PACKET 1 +#define ERROR_PACKET 2 + +typedef char bool; +#define FALSE 0 +#define TRUE 1 + +/* + * X.25 Packet format definitions + * This will eventually have to be rewritten without reference + * to bit fields, to be ansi C compliant and allignment safe. + */ + +typedef u_char octet; + +struct x25_calladdr { + octet addrlens; + octet address_field[MAXADDRLN]; +}; + +struct x25_packet { + octet bits; + octet logical_channel_number; + octet packet_type; + octet packet_data; +}; +#define packet_cause packet_data + +struct data_packet { + octet bits; +}; + +#define FACILITIES_REVERSE_CHARGE 0x1 +#define FACILITIES_THROUGHPUT 0x2 +#define FACILITIES_PACKETSIZE 0x42 +#define FACILITIES_WINDOWSIZE 0x43 + +#define PKHEADERLN 3 + +#define DP(xp) (((struct data_packet *)&(xp) -> packet_type) -> bits) +#define PS(xp) X25GBITS(DP(xp), p_s) +#define PR(xp) X25GBITS(DP(xp), p_r) +#define MBIT(xp) X25GBITS(DP(xp), m_bit) +#define SPR(xp, v) X25SBITS(DP(xp), p_r, (v)) +#define SPS(xp, v) X25SBITS(DP(xp), p_s, (v)) +#define SMBIT(xp, v) X25SBITS(DP(xp), m_bit, (v)) + +#define LCN(xp) (xp -> logical_channel_number + \ + (X25GBITS(xp -> bits, lc_group_number) ? (X25GBITS(xp -> bits, lc_group_number) << 8) : 0)) +#define SET_LCN(xp, lcn) ((xp -> logical_channel_number = lcn), \ + (X25SBITS(xp -> bits, lc_group_number, lcn > 255 ? lcn >> 8 : 0))) + +struct mbuf *pk_template (); + +/* Define X.25 packet level states. */ + +/* Call setup and clearing substates. */ + +#define LISTEN 0 +#define READY 1 +#define RECEIVED_CALL 2 +#define SENT_CALL 3 +#define DATA_TRANSFER 4 +#define RECEIVED_CLEAR 5 +#define SENT_CLEAR 6 + +/* DTE states. */ + +#define DTE_WAITING 7 +#define DTE_RECEIVED_RESTART 8 +#define DTE_SENT_RESTART 9 +#define DTE_READY 0 + +/* Cleaning out ... */ + +#define LCN_ZOMBIE 10 + +#define MAXSTATES 11 + +/* + * The following definitions are used in a switch statement after + * determining the packet type. These values are returned by the + * pk_decode procedure. + */ + +#define CALL 0 * MAXSTATES +#define CALL_ACCEPTED 1 * MAXSTATES +#define CLEAR 2 * MAXSTATES +#define CLEAR_CONF 3 * MAXSTATES +#define DATA 4 * MAXSTATES +#define INTERRUPT 5 * MAXSTATES +#define INTERRUPT_CONF 6 * MAXSTATES +#define RR 7 * MAXSTATES +#define RNR 8 * MAXSTATES +#define RESET 9 * MAXSTATES +#define RESET_CONF 10 * MAXSTATES +#define RESTART 11 * MAXSTATES +#define RESTART_CONF 12 * MAXSTATES +#define REJECT 13 * MAXSTATES +#define DIAG_TYPE 14 * MAXSTATES +#define INVALID_PACKET 15 * MAXSTATES +#define DELETE_PACKET INVALID_PACKET + +/* + * The following definitions are used by the restart procedures + * for noting wether the PLE is supposed to behave as DTE or DCE + * (essentially necessary for operation over LLC2) + */ +#define DTE_DXERESOLVING 0x0001 +#define DTE_PLAYDTE 0x0002 +#define DTE_PLAYDCE 0x0004 +#define DTE_CONNECTPENDING 0x0010 +#define DTE_PRETENDDTE 0x0020 + +#define MAXRESTARTCOLLISIONS 10 diff --git a/sys/netccitt/pk_acct.c b/sys/netccitt/pk_acct.c new file mode 100644 index 00000000000..44e579864d8 --- /dev/null +++ b/sys/netccitt/pk_acct.c @@ -0,0 +1,147 @@ +/* $NetBSD: pk_acct.c,v 1.8 1994/12/14 19:03:39 mycroft Exp $ */ + +/* + * Copyright (c) University of British Columbia, 1984 + * Copyright (c) 1990, 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. + * + * 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_acct.c 8.1 (Berkeley) 6/10/93 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/namei.h> +#include <sys/proc.h> +#include <sys/vnode.h> +#include <sys/kernel.h> +#include <sys/file.h> +#include <sys/socket.h> +#include <sys/socketvar.h> + +#include <net/if.h> + +#include <netccitt/x25.h> +#include <netccitt/pk.h> +#include <netccitt/pk_var.h> +#include <netccitt/x25acct.h> + + +struct vnode *pkacctp; +/* + * Turn on packet accounting + */ + +pk_accton (path) + char *path; +{ + register struct vnode *vp = NULL; + struct nameidata nd; + struct vnode *oacctp = pkacctp; + struct proc *p = curproc; + int error; + + if (path == 0) + goto close; + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, path, p); + if (error = vn_open (&nd, FWRITE, 0644)) + return (error); + vp = nd.ni_vp; + VOP_UNLOCK(vp); + if (vp -> v_type != VREG) { + vrele (vp); + return (EACCES); + } + pkacctp = vp; + if (oacctp) { + close: + error = vn_close (oacctp, FWRITE, p -> p_ucred, p); + } + return (error); +} + +/* + * Write a record on the accounting file. + */ + +pk_acct (lcp) +register struct pklcd *lcp; +{ + register struct vnode *vp; + register struct sockaddr_x25 *sa; + register char *src, *dst; + register int len; + register long etime; + static struct x25acct acbuf; + + if ((vp = pkacctp) == 0) + return; + bzero ((caddr_t)&acbuf, sizeof (acbuf)); + if (lcp -> lcd_ceaddr != 0) + sa = lcp -> lcd_ceaddr; + else if (lcp -> lcd_craddr != 0) { + sa = lcp -> lcd_craddr; + acbuf.x25acct_callin = 1; + } else + return; + + if (sa -> x25_opts.op_flags & X25_REVERSE_CHARGE) + acbuf.x25acct_revcharge = 1; + acbuf.x25acct_stime = lcp -> lcd_stime; + acbuf.x25acct_etime = time.tv_sec - acbuf.x25acct_stime; + acbuf.x25acct_uid = curproc -> p_cred -> p_ruid; + acbuf.x25acct_psize = sa -> x25_opts.op_psize; + acbuf.x25acct_net = sa -> x25_net; + /* + * Convert address to bcd + */ + src = sa -> x25_addr; + dst = acbuf.x25acct_addr; + for (len = 0; *src; len++) + if (len & 01) + *dst++ |= *src++ & 0xf; + else + *dst = *src++ << 4; + acbuf.x25acct_addrlen = len; + + bcopy (sa -> x25_udata, acbuf.x25acct_udata, + sizeof (acbuf.x25acct_udata)); + acbuf.x25acct_txcnt = lcp -> lcd_txcnt; + acbuf.x25acct_rxcnt = lcp -> lcd_rxcnt; + + (void) vn_rdwr(UIO_WRITE, vp, (caddr_t)&acbuf, sizeof (acbuf), + (off_t)0, UIO_SYSSPACE, IO_UNIT|IO_APPEND, + curproc -> p_ucred, (int *)0, + (struct proc *)0); +} diff --git a/sys/netccitt/pk_debug.c b/sys/netccitt/pk_debug.c new file mode 100644 index 00000000000..f7a755d6061 --- /dev/null +++ b/sys/netccitt/pk_debug.c @@ -0,0 +1,142 @@ +/* $NetBSD: pk_debug.c,v 1.5 1994/06/29 06:37:31 cgd Exp $ */ + +/* + * Copyright (c) University of British Columbia, 1984 + * Copyright (c) 1990, 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. + * + * 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_debug.c 8.1 (Berkeley) 6/10/93 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/protosw.h> +#include <sys/socketvar.h> +#include <sys/errno.h> + +#include <net/if.h> + +#include <netccitt/x25.h> +#include <netccitt/pk.h> +#include <netccitt/pk_var.h> + +char *pk_state[] = { + "Listen", "Ready", "Received-Call", + "Sent-Call", "Data-Transfer","Received-Clear", + "Sent-Clear", +}; + +char *pk_name[] = { + "Call", "Call-Conf", "Clear", + "Clear-Conf", "Data", "Intr", "Intr-Conf", + "Rr", "Rnr", "Reset", "Reset-Conf", + "Restart", "Restart-Conf", "Reject", "Diagnostic", + "Invalid" +}; + +pk_trace (xcp, m, dir) +struct x25config *xcp; +register struct mbuf *m; +char *dir; +{ + register char *s; + struct x25_packet *xp = mtod(m, struct x25_packet *); + register int i, len = 0, cnt = 0; + + if (xcp -> xc_ptrace == 0) + return; + + i = pk_decode (xp) / MAXSTATES; + for (; m; m = m -> m_next) { + len = len + m -> m_len; + ++cnt; + } + printf ("LCN=%d %s: %s #=%d, len=%d ", + LCN(xp), dir, pk_name[i], cnt, len); + for (s = (char *) xp, i = 0; i < 5; ++i, ++s) + printf ("%x ", (int) * s & 0xff); + printf ("\n"); +} + +mbuf_cache(c, m) +register struct mbuf_cache *c; +struct mbuf *m; +{ + register struct mbuf **mp; + + if (c->mbc_size != c->mbc_oldsize) { + unsigned zero_size, copy_size; + unsigned new_size = c->mbc_size * sizeof(m); + caddr_t cache = (caddr_t)c->mbc_cache; + + if (new_size) { + c->mbc_cache = (struct mbuf **) + malloc(new_size, M_MBUF, M_NOWAIT); + if (c->mbc_cache == 0) { + c->mbc_cache = (struct mbuf **)cache; + return; + } + c->mbc_num %= c->mbc_size; + } else + c->mbc_cache = 0; + if (c->mbc_size < c->mbc_oldsize) { + register struct mbuf **mplim; + mp = c->mbc_size + (struct mbuf **)cache; + mplim = c->mbc_oldsize + (struct mbuf **)cache; + while (mp < mplim) + m_freem(*mp++); + zero_size = 0; + } else + zero_size = (c->mbc_size - c->mbc_oldsize) * sizeof(m); + copy_size = new_size - zero_size; + c->mbc_oldsize = c->mbc_size; + if (copy_size) + bcopy(cache, (caddr_t)c->mbc_cache, copy_size); + if (cache) + free(cache, M_MBUF); + if (zero_size) + bzero(copy_size + (caddr_t)c->mbc_cache, zero_size); + } + if (c->mbc_size == 0) + return; + mp = c->mbc_cache + c->mbc_num; + c->mbc_num = (1 + c->mbc_num) % c->mbc_size; + if (*mp) + m_freem(*mp); + if (*mp = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) + (*mp)->m_flags |= m->m_flags & 0x08; +} diff --git a/sys/netccitt/pk_input.c b/sys/netccitt/pk_input.c new file mode 100644 index 00000000000..343c82c1fcb --- /dev/null +++ b/sys/netccitt/pk_input.c @@ -0,0 +1,1121 @@ +/* $NetBSD: pk_input.c,v 1.6 1994/06/29 06:37:33 cgd 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. 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_input.c 8.1 (Berkeley) 6/10/93 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/protosw.h> +#include <sys/socketvar.h> +#include <sys/errno.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_llc.h> +#include <net/route.h> + +#include <netccitt/dll.h> +#include <netccitt/x25.h> +#include <netccitt/pk.h> +#include <netccitt/pk_var.h> +#include <netccitt/llc_var.h> + +struct pkcb_q pkcb_q = {&pkcb_q, &pkcb_q}; + +/* + * ccittintr() is the generic interrupt handler for HDLC, LLC2, and X.25. This + * allows to have kernel running X.25 but no HDLC or LLC2 or both (in case we + * employ boards that do all the stuff themselves, e.g. ADAX X.25 or TPS ISDN.) + */ +void +ccittintr () +{ + extern struct ifqueue pkintrq; + extern struct ifqueue hdintrq; + extern struct ifqueue llcintrq; + +#ifdef HDLC + if (hdintrq.ifq_len) + hdintr (); +#endif +#ifdef LLC + if (llcintrq.ifq_len) + llcintr (); +#endif + if (pkintrq.ifq_len) + pkintr (); +} + +struct pkcb * +pk_newlink (ia, llnext) +struct x25_ifaddr *ia; +caddr_t llnext; +{ + register struct x25config *xcp = &ia -> ia_xc; + register struct pkcb *pkp; + register struct pklcd *lcp; + register struct protosw *pp; + unsigned size; + + pp = pffindproto (AF_CCITT, (int) xcp -> xc_lproto, 0); + if (pp == 0 || pp -> pr_output == 0) { + pk_message (0, xcp, "link level protosw error"); + return ((struct pkcb *)0); + } + /* + * Allocate a network control block structure + */ + size = sizeof (struct pkcb); + pkp = (struct pkcb *) malloc (size, M_PCB, M_WAITOK); + if (pkp == 0) + return ((struct pkcb *)0); + bzero ((caddr_t) pkp, size); + pkp -> pk_lloutput = pp -> pr_output; + pkp -> pk_llctlinput = (caddr_t (*)()) pp -> pr_ctlinput; + pkp -> pk_xcp = xcp; + pkp -> pk_ia = ia; + pkp -> pk_state = DTE_WAITING; + pkp -> pk_llnext = llnext; + insque (pkp, &pkcb_q); + + /* + * set defaults + */ + + if (xcp -> xc_pwsize == 0) + xcp -> xc_pwsize = DEFAULT_WINDOW_SIZE; + if (xcp -> xc_psize == 0) + xcp -> xc_psize = X25_PS128; + /* + * Allocate logical channel descriptor vector + */ + + (void) pk_resize (pkp); + return (pkp); +} + + +pk_dellink (pkp) +register struct pkcb *pkp; +{ + register int i; + register struct protosw *pp; + + /* + * Essentially we have the choice to + * (a) go ahead and let the route be deleted and + * leave the pkcb associated with that route + * as it is, i.e. the connections stay open + * (b) do a pk_disconnect() on all channels associated + * with the route via the pkcb and then proceed. + * + * For the time being we stick with (b) + */ + + for (i = 1; i < pkp -> pk_maxlcn; ++i) + if (pkp -> pk_chan[i]) + pk_disconnect (pkp -> pk_chan[i]); + + /* + * Free the pkcb + */ + + /* + * First find the protoswitch to get hold of the link level + * protocol to be notified that the packet level entity is + * dissolving ... + */ + pp = pffindproto (AF_CCITT, (int) pkp -> pk_xcp -> xc_lproto, 0); + if (pp == 0 || pp -> pr_output == 0) { + pk_message (0, pkp -> pk_xcp, "link level protosw error"); + return (EPROTONOSUPPORT); + } + + pkp -> pk_refcount--; + if (!pkp -> pk_refcount) { + struct dll_ctlinfo ctlinfo; + + remque (pkp); + if (pkp -> pk_rt -> rt_llinfo == (caddr_t) pkp) + pkp -> pk_rt -> rt_llinfo = (caddr_t) NULL; + + /* + * Tell the link level that the pkcb is dissolving + */ + if (pp -> pr_ctlinput && pkp -> pk_llnext) { + ctlinfo.dlcti_pcb = pkp -> pk_llnext; + ctlinfo.dlcti_rt = pkp -> pk_rt; + (pp -> pr_ctlinput)(PRC_DISCONNECT_REQUEST, + pkp -> pk_xcp, &ctlinfo); + } + free ((caddr_t) pkp -> pk_chan, M_IFADDR); + free ((caddr_t) pkp, M_PCB); + } + + return (0); +} + + +pk_resize (pkp) +register struct pkcb *pkp; +{ + struct pklcd *dev_lcp = 0; + struct x25config *xcp = pkp -> pk_xcp; + if (pkp -> pk_chan && + (pkp -> pk_maxlcn != xcp -> xc_maxlcn)) { + pk_restart (pkp, X25_RESTART_NETWORK_CONGESTION); + dev_lcp = pkp -> pk_chan[0]; + free ((caddr_t) pkp -> pk_chan, M_IFADDR); + pkp -> pk_chan = 0; + } + if (pkp -> pk_chan == 0) { + unsigned size; + pkp -> pk_maxlcn = xcp -> xc_maxlcn; + size = (pkp -> pk_maxlcn + 1) * sizeof (struct pklcd *); + pkp -> pk_chan = + (struct pklcd **) malloc (size, M_IFADDR, M_WAITOK); + if (pkp -> pk_chan) { + bzero ((caddr_t) pkp -> pk_chan, size); + /* + * Allocate a logical channel descriptor for lcn 0 + */ + if (dev_lcp == 0 && + (dev_lcp = pk_attach ((struct socket *)0)) == 0) + return (ENOBUFS); + dev_lcp -> lcd_state = READY; + dev_lcp -> lcd_pkp = pkp; + pkp -> pk_chan[0] = dev_lcp; + } else { + if (dev_lcp) + pk_close (dev_lcp); + return (ENOBUFS); + } + } + return 0; +} + +/* + * This procedure is called by the link level whenever the link + * becomes operational, is reset, or when the link goes down. + */ +/*VARARGS*/ +caddr_t +pk_ctlinput (code, src, addr) + struct sockaddr *src; + caddr_t addr; +{ + register struct pkcb *pkp = (struct pkcb *) addr; + + switch (code) { + case PRC_LINKUP: + if (pkp -> pk_state == DTE_WAITING) + pk_restart (pkp, X25_RESTART_NETWORK_CONGESTION); + break; + + case PRC_LINKDOWN: + pk_restart (pkp, -1); /* Clear all active circuits */ + pkp -> pk_state = DTE_WAITING; + break; + + case PRC_LINKRESET: + pk_restart (pkp, X25_RESTART_NETWORK_CONGESTION); + break; + + case PRC_CONNECT_INDICATION: { + struct rtentry *llrt; + + if ((llrt = rtalloc1(src, 0)) == 0) + return 0; + else llrt -> rt_refcnt--; + + pkp = (((struct npaidbentry *) llrt -> rt_llinfo) -> np_rt) ? + (struct pkcb *)(((struct npaidbentry *) llrt -> rt_llinfo) -> np_rt -> rt_llinfo) : (struct pkcb *) 0; + if (pkp == (struct pkcb *) 0) + return 0; + pkp -> pk_llnext = addr; + + return ((caddr_t) pkp); + } + case PRC_DISCONNECT_INDICATION: + pk_restart (pkp, -1) ; /* Clear all active circuits */ + pkp -> pk_state = DTE_WAITING; + pkp -> pk_llnext = (caddr_t) 0; + } + return (0); +} +struct ifqueue pkintrq; +/* + * This routine is called if there are semi-smart devices that do HDLC + * in hardware and want to queue the packet and call level 3 directly + */ +pkintr () +{ + register struct mbuf *m; + register struct ifaddr *ifa; + register struct ifnet *ifp; + register int s; + + for (;;) { + s = splimp (); + IF_DEQUEUE (&pkintrq, m); + splx (s); + if (m == 0) + break; + if (m -> m_len < PKHEADERLN) { + printf ("pkintr: packet too short (len=%d)\n", + m -> m_len); + m_freem (m); + continue; + } + pk_input (m); + } +} +struct mbuf *pk_bad_packet; +struct mbuf_cache pk_input_cache = {0 }; +/* + * X.25 PACKET INPUT + * + * This procedure is called by a link level procedure whenever + * an information frame is received. It decodes the packet and + * demultiplexes based on the logical channel number. + * + * We change the original conventions of the UBC code here -- + * since there may be multiple pkcb's for a given interface + * of type 802.2 class 2, we retrieve which one it is from + * m_pkthdr.rcvif (which has been overwritten by lower layers); + * That field is then restored for the benefit of upper layers which + * may make use of it, such as CLNP. + * + */ + +#define RESTART_DTE_ORIGINATED(xp) (((xp) -> packet_cause == X25_RESTART_DTE_ORIGINATED) || \ + ((xp) -> packet_cause >= X25_RESTART_DTE_ORIGINATED2)) + +pk_input (m) +register struct mbuf *m; +{ + register struct x25_packet *xp; + register struct pklcd *lcp; + register struct socket *so = 0; + register struct pkcb *pkp; + int ptype, lcn, lcdstate = LISTEN; + + if (pk_input_cache.mbc_size || pk_input_cache.mbc_oldsize) + mbuf_cache (&pk_input_cache, m); + if ((m -> m_flags & M_PKTHDR) == 0) + panic ("pkintr"); + + if ((pkp = (struct pkcb *) m -> m_pkthdr.rcvif) == 0) + return; + xp = mtod (m, struct x25_packet *); + ptype = pk_decode (xp); + lcn = LCN(xp); + lcp = pkp -> pk_chan[lcn]; + + /* + * If the DTE is in Restart state, then it will ignore data, + * interrupt, call setup and clearing, flow control and reset + * packets. + */ + if (lcn < 0 || lcn > pkp -> pk_maxlcn) { + pk_message (lcn, pkp -> pk_xcp, "illegal lcn"); + m_freem (m); + return; + } + + pk_trace (pkp -> pk_xcp, m, "P-In"); + + if (pkp -> pk_state != DTE_READY && ptype != RESTART && ptype != RESTART_CONF) { + m_freem (m); + return; + } + if (lcp) { + so = lcp -> lcd_so; + lcdstate = lcp -> lcd_state; + } else { + if (ptype == CLEAR) { /* idle line probe (Datapac specific) */ + /* send response on lcd 0's output queue */ + lcp = pkp -> pk_chan[0]; + lcp -> lcd_template = pk_template (lcn, X25_CLEAR_CONFIRM); + pk_output (lcp); + m_freem (m); + return; + } + if (ptype != CALL) + ptype = INVALID_PACKET; + } + + if (lcn == 0 && ptype != RESTART && ptype != RESTART_CONF) { + pk_message (0, pkp -> pk_xcp, "illegal ptype (%d, %s) on lcn 0", + ptype, pk_name[ptype / MAXSTATES]); + if (pk_bad_packet) + m_freem (pk_bad_packet); + pk_bad_packet = m; + return; + } + + m -> m_pkthdr.rcvif = pkp -> pk_ia -> ia_ifp; + + switch (ptype + lcdstate) { + /* + * Incoming Call packet received. + */ + case CALL + LISTEN: + pk_incoming_call (pkp, m); + break; + + /* + * Call collision: Just throw this "incoming call" away since + * the DCE will ignore it anyway. + */ + case CALL + SENT_CALL: + pk_message ((int) lcn, pkp -> pk_xcp, + "incoming call collision"); + break; + + /* + * Call confirmation packet received. This usually means our + * previous connect request is now complete. + */ + case CALL_ACCEPTED + SENT_CALL: + MCHTYPE(m, MT_CONTROL); + pk_call_accepted (lcp, m); + break; + + /* + * This condition can only happen if the previous state was + * SENT_CALL. Just ignore the packet, eventually a clear + * confirmation should arrive. + */ + case CALL_ACCEPTED + SENT_CLEAR: + break; + + /* + * Clear packet received. This requires a complete tear down + * of the virtual circuit. Free buffers and control blocks. + * and send a clear confirmation. + */ + case CLEAR + READY: + case CLEAR + RECEIVED_CALL: + case CLEAR + SENT_CALL: + case CLEAR + DATA_TRANSFER: + lcp -> lcd_state = RECEIVED_CLEAR; + lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_CLEAR_CONFIRM); + pk_output (lcp); + pk_clearcause (pkp, xp); + if (lcp -> lcd_upper) { + MCHTYPE(m, MT_CONTROL); + lcp -> lcd_upper (lcp, m); + } + pk_close (lcp); + lcp = 0; + break; + + /* + * Clear collision: Treat this clear packet as a confirmation. + */ + case CLEAR + SENT_CLEAR: + pk_close (lcp); + break; + + /* + * Clear confirmation received. This usually means the virtual + * circuit is now completely removed. + */ + case CLEAR_CONF + SENT_CLEAR: + pk_close (lcp); + break; + + /* + * A clear confirmation on an unassigned logical channel - just + * ignore it. Note: All other packets on an unassigned channel + * results in a clear. + */ + case CLEAR_CONF + READY: + case CLEAR_CONF + LISTEN: + break; + + /* + * Data packet received. Pass on to next level. Move the Q and M + * bits into the data portion for the next level. + */ + case DATA + DATA_TRANSFER: + if (lcp -> lcd_reset_condition) { + ptype = DELETE_PACKET; + break; + } + + /* + * Process the P(S) flow control information in this Data packet. + * Check that the packets arrive in the correct sequence and that + * they are within the "lcd_input_window". Input window rotation is + * initiated by the receive interface. + */ + + if (PS(xp) != ((lcp -> lcd_rsn + 1) % MODULUS) || + PS(xp) == ((lcp -> lcd_input_window + lcp -> lcd_windowsize) % MODULUS)) { + m_freem (m); + pk_procerror (RESET, lcp, "p(s) flow control error", 1); + break; + } + lcp -> lcd_rsn = PS(xp); + + if (pk_ack (lcp, PR(xp)) != PACKET_OK) { + m_freem (m); + break; + } + m -> m_data += PKHEADERLN; + m -> m_len -= PKHEADERLN; + m -> m_pkthdr.len -= PKHEADERLN; + + lcp -> lcd_rxcnt++; + if (lcp -> lcd_flags & X25_MBS_HOLD) { + register struct mbuf *n = lcp -> lcd_cps; + int mbit = MBIT(xp); + octet q_and_d_bits; + + if (n) { + n -> m_pkthdr.len += m -> m_pkthdr.len; + while (n -> m_next) + n = n -> m_next; + n -> m_next = m; + m = lcp -> lcd_cps; + + if (lcp -> lcd_cpsmax && + n -> m_pkthdr.len > lcp -> lcd_cpsmax) { + pk_procerror (RESET, lcp, + "C.P.S. overflow", 128); + return; + } + q_and_d_bits = 0xc0 & *(octet *) xp; + xp = (struct x25_packet *) + (mtod (m, octet *) - PKHEADERLN); + *(octet *) xp |= q_and_d_bits; + } + if (mbit) { + lcp -> lcd_cps = m; + pk_flowcontrol (lcp, 0, 1); + return; + } + lcp -> lcd_cps = 0; + } + if (so == 0) + break; + if (lcp -> lcd_flags & X25_MQBIT) { + octet t = (X25GBITS(xp -> bits, q_bit)) ? t = 0x80 : 0; + + if (MBIT(xp)) + t |= 0x40; + m -> m_data -= 1; + m -> m_len += 1; + m -> m_pkthdr.len += 1; + *mtod (m, octet *) = t; + } + + /* + * Discard Q-BIT packets if the application + * doesn't want to be informed of M and Q bit status + */ + if (X25GBITS(xp -> bits, q_bit) + && (lcp -> lcd_flags & X25_MQBIT) == 0) { + m_freem (m); + /* + * NB. This is dangerous: sending a RR here can + * cause sequence number errors if a previous data + * packet has not yet been passed up to the application + * (RR's are normally generated via PRU_RCVD). + */ + pk_flowcontrol (lcp, 0, 1); + } else { + sbappendrecord (&so -> so_rcv, m); + sorwakeup (so); + } + break; + + /* + * Interrupt packet received. + */ + case INTERRUPT + DATA_TRANSFER: + if (lcp -> lcd_reset_condition) + break; + lcp -> lcd_intrdata = xp -> packet_data; + lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_INTERRUPT_CONFIRM); + pk_output (lcp); + m -> m_data += PKHEADERLN; + m -> m_len -= PKHEADERLN; + m -> m_pkthdr.len -= PKHEADERLN; + MCHTYPE(m, MT_OOBDATA); + if (so) { + if (so -> so_options & SO_OOBINLINE) + sbinsertoob (&so -> so_rcv, m); + else + m_freem (m); + sohasoutofband (so); + } + break; + + /* + * Interrupt confirmation packet received. + */ + case INTERRUPT_CONF + DATA_TRANSFER: + if (lcp -> lcd_reset_condition) + break; + if (lcp -> lcd_intrconf_pending == TRUE) + lcp -> lcd_intrconf_pending = FALSE; + else + pk_procerror (RESET, lcp, "unexpected packet", 43); + break; + + /* + * Receiver ready received. Rotate the output window and output + * any data packets waiting transmission. + */ + case RR + DATA_TRANSFER: + if (lcp -> lcd_reset_condition || + pk_ack (lcp, PR(xp)) != PACKET_OK) { + ptype = DELETE_PACKET; + break; + } + if (lcp -> lcd_rnr_condition == TRUE) + lcp -> lcd_rnr_condition = FALSE; + pk_output (lcp); + break; + + /* + * Receiver Not Ready received. Packets up to the P(R) can be + * be sent. Condition is cleared with a RR. + */ + case RNR + DATA_TRANSFER: + if (lcp -> lcd_reset_condition || + pk_ack (lcp, PR(xp)) != PACKET_OK) { + ptype = DELETE_PACKET; + break; + } + lcp -> lcd_rnr_condition = TRUE; + break; + + /* + * Reset packet received. Set state to FLOW_OPEN. The Input and + * Output window edges ar set to zero. Both the send and receive + * numbers are reset. A confirmation is returned. + */ + case RESET + DATA_TRANSFER: + if (lcp -> lcd_reset_condition) + /* Reset collision. Just ignore packet. */ + break; + + pk_resetcause (pkp, xp); + lcp -> lcd_window_condition = lcp -> lcd_rnr_condition = + lcp -> lcd_intrconf_pending = FALSE; + lcp -> lcd_output_window = lcp -> lcd_input_window = + lcp -> lcd_last_transmitted_pr = 0; + lcp -> lcd_ssn = 0; + lcp -> lcd_rsn = MODULUS - 1; + + lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_RESET_CONFIRM); + pk_output (lcp); + + pk_flush (lcp); + if (so == 0) + break; + wakeup ((caddr_t) & so -> so_timeo); + sorwakeup (so); + sowwakeup (so); + break; + + /* + * Reset confirmation received. + */ + case RESET_CONF + DATA_TRANSFER: + if (lcp -> lcd_reset_condition) { + lcp -> lcd_reset_condition = FALSE; + pk_output (lcp); + } + else + pk_procerror (RESET, lcp, "unexpected packet", 32); + break; + + case DATA + SENT_CLEAR: + ptype = DELETE_PACKET; + case RR + SENT_CLEAR: + case RNR + SENT_CLEAR: + case INTERRUPT + SENT_CLEAR: + case INTERRUPT_CONF + SENT_CLEAR: + case RESET + SENT_CLEAR: + case RESET_CONF + SENT_CLEAR: + /* Just ignore p if we have sent a CLEAR already. + */ + break; + + /* + * Restart sets all the permanent virtual circuits to the "Data + * Transfer" stae and all the switched virtual circuits to the + * "Ready" state. + */ + case RESTART + READY: + switch (pkp -> pk_state) { + case DTE_SENT_RESTART: + /* + * Restart collision. + * If case the restart cause is "DTE originated" we + * have a DTE-DTE situation and are trying to resolve + * who is going to play DTE/DCE [ISO 8208:4.2-4.5] + */ + if (RESTART_DTE_ORIGINATED(xp)) { + pk_restart (pkp, X25_RESTART_DTE_ORIGINATED); + pk_message (0, pkp -> pk_xcp, + "RESTART collision"); + if ((pkp -> pk_restartcolls++) > MAXRESTARTCOLLISIONS) { + pk_message (0, pkp -> pk_xcp, + "excessive RESTART collisions"); + pkp -> pk_restartcolls = 0; + } + break; + } + pkp -> pk_state = DTE_READY; + pkp -> pk_dxerole |= DTE_PLAYDTE; + pkp -> pk_dxerole &= ~DTE_PLAYDCE; + pk_message (0, pkp -> pk_xcp, + "Packet level operational"); + pk_message (0, pkp -> pk_xcp, + "Assuming DTE role"); + if (pkp -> pk_dxerole & DTE_CONNECTPENDING) + pk_callcomplete (pkp); + break; + + default: + pk_restart (pkp, -1); + pk_restartcause (pkp, xp); + pkp -> pk_chan[0] -> lcd_template = pk_template (0, + X25_RESTART_CONFIRM); + pk_output (pkp -> pk_chan[0]); + pkp -> pk_state = DTE_READY; + pkp -> pk_dxerole |= RESTART_DTE_ORIGINATED(xp) ? DTE_PLAYDCE : + DTE_PLAYDTE; + if (pkp -> pk_dxerole & DTE_PLAYDTE) { + pkp -> pk_dxerole &= ~DTE_PLAYDCE; + pk_message (0, pkp -> pk_xcp, + "Assuming DTE role"); + } else { + pkp -> pk_dxerole &= ~DTE_PLAYDTE; + pk_message (0, pkp -> pk_xcp, + "Assuming DCE role"); + } + if (pkp -> pk_dxerole & DTE_CONNECTPENDING) + pk_callcomplete (pkp); + } + break; + + /* + * Restart confirmation received. All logical channels are set + * to READY. + */ + case RESTART_CONF + READY: + switch (pkp -> pk_state) { + case DTE_SENT_RESTART: + pkp -> pk_state = DTE_READY; + pkp -> pk_dxerole |= DTE_PLAYDTE; + pkp -> pk_dxerole &= ~DTE_PLAYDCE; + pk_message (0, pkp -> pk_xcp, + "Packet level operational"); + pk_message (0, pkp -> pk_xcp, + "Assuming DTE role"); + if (pkp -> pk_dxerole & DTE_CONNECTPENDING) + pk_callcomplete (pkp); + break; + + default: + /* Restart local procedure error. */ + pk_restart (pkp, X25_RESTART_LOCAL_PROCEDURE_ERROR); + pkp -> pk_state = DTE_SENT_RESTART; + pkp -> pk_dxerole &= ~(DTE_PLAYDTE | DTE_PLAYDCE); + } + break; + + default: + if (lcp) { + pk_procerror (CLEAR, lcp, "unknown packet error", 33); + pk_message (lcn, pkp -> pk_xcp, + "\"%s\" unexpected in \"%s\" state", + pk_name[ptype/MAXSTATES], pk_state[lcdstate]); + } else + pk_message (lcn, pkp -> pk_xcp, + "packet arrived on unassigned lcn"); + break; + } + if (so == 0 && lcp && lcp -> lcd_upper && lcdstate == DATA_TRANSFER) { + if (ptype != DATA && ptype != INTERRUPT) + MCHTYPE(m, MT_CONTROL); + lcp -> lcd_upper (lcp, m); + } else if (ptype != DATA && ptype != INTERRUPT) + m_freem (m); +} + +static +prune_dnic (from, to, dnicname, xcp) +char *from, *to, *dnicname; +register struct x25config *xcp; +{ + register char *cp1 = from, *cp2 = from; + if (xcp -> xc_prepnd0 && *cp1 == '0') { + from = ++cp1; + goto copyrest; + } + if (xcp -> xc_nodnic) { + for (cp1 = dnicname; *cp2 = *cp1++;) + cp2++; + cp1 = from; + } +copyrest: + for (cp1 = dnicname; *cp2 = *cp1++;) + cp2++; +} +/* static */ +pk_simple_bsd (from, to, lower, len) +register octet *from, *to; +register len, lower; +{ + register int c; + while (--len >= 0) { + c = *from; + if (lower & 0x01) + *from++; + else + c >>= 4; + c &= 0x0f; c |= 0x30; *to++ = c; lower++; + } + *to = 0; +} + +/*static octet * */ +pk_from_bcd (a, iscalling, sa, xcp) +register struct x25_calladdr *a; +register struct sockaddr_x25 *sa; +register struct x25config *xcp; +{ + octet buf[MAXADDRLN+1]; + octet *cp; + unsigned count; + + bzero ((caddr_t) sa, sizeof (*sa)); + sa -> x25_len = sizeof (*sa); + sa -> x25_family = AF_CCITT; + if (iscalling) { + cp = a -> address_field + (X25GBITS(a -> addrlens, called_addrlen) / 2); + count = X25GBITS(a -> addrlens, calling_addrlen); + pk_simple_bsd (cp, buf, X25GBITS(a -> addrlens, called_addrlen), count); + } else { + count = X25GBITS(a -> addrlens, called_addrlen); + pk_simple_bsd (a -> address_field, buf, 0, count); + } + if (xcp -> xc_addr.x25_net && (xcp -> xc_nodnic || xcp -> xc_prepnd0)) { + octet dnicname[sizeof (long) * NBBY/3 + 2]; + + sprintf ((char *) dnicname, "%d", xcp -> xc_addr.x25_net); + prune_dnic ((char *) buf, sa -> x25_addr, dnicname, xcp); + } else + bcopy ((caddr_t) buf, (caddr_t) sa -> x25_addr, count + 1); +} + +static +save_extra (m0, fp, so) +struct mbuf *m0; +octet *fp; +struct socket *so; +{ + register struct mbuf *m; + struct cmsghdr cmsghdr; + if (m = m_copy (m, 0, (int)M_COPYALL)) { + int off = fp - mtod (m0, octet *); + int len = m -> m_pkthdr.len - off + sizeof (cmsghdr); + cmsghdr.cmsg_len = len; + cmsghdr.cmsg_level = AF_CCITT; + cmsghdr.cmsg_type = PK_FACILITIES; + m_adj (m, off); + M_PREPEND (m, sizeof (cmsghdr), M_DONTWAIT); + if (m == 0) + return; + bcopy ((caddr_t)&cmsghdr, mtod (m, caddr_t), sizeof (cmsghdr)); + MCHTYPE(m, MT_CONTROL); + sbappendrecord (&so -> so_rcv, m); + } +} + +/* + * This routine handles incoming call packets. It matches the protocol + * field on the Call User Data field (usually the first four bytes) with + * sockets awaiting connections. + */ + +pk_incoming_call (pkp, m0) +struct mbuf *m0; +struct pkcb *pkp; +{ + register struct pklcd *lcp = 0, *l; + register struct sockaddr_x25 *sa; + register struct x25_calladdr *a; + register struct socket *so = 0; + struct x25_packet *xp = mtod (m0, struct x25_packet *); + struct mbuf *m; + struct x25config *xcp = pkp -> pk_xcp; + int len = m0 -> m_pkthdr.len; + unsigned udlen; + char *errstr = "server unavailable"; + octet *u, *facp; + int lcn = LCN(xp); + + /* First, copy the data from the incoming call packet to a X25 address + descriptor. It is to be regretted that you have + to parse the facilities into a sockaddr to determine + if reverse charging is being requested */ + if ((m = m_get (M_DONTWAIT, MT_SONAME)) == 0) + return; + sa = mtod (m, struct sockaddr_x25 *); + a = (struct x25_calladdr *) &xp -> packet_data; + facp = u = (octet *) (a -> address_field + + ((X25GBITS(a -> addrlens, called_addrlen) + X25GBITS(a -> addrlens, calling_addrlen) + 1) / 2)); + u += *u + 1; + udlen = min (16, ((octet *) xp) + len - u); + if (udlen < 0) + udlen = 0; + pk_from_bcd (a, 1, sa, pkp -> pk_xcp); /* get calling address */ + pk_parse_facilities (facp, sa); + bcopy ((caddr_t) u, sa -> x25_udata, udlen); + sa -> x25_udlen = udlen; + + /* + * Now, loop through the listen sockets looking for a match on the + * PID. That is the first few octets of the user data field. + * This is the closest thing to a port number for X.25 packets. + * It does provide a way of multiplexing services at the user level. + */ + + for (l = pk_listenhead; l; l = l -> lcd_listen) { + struct sockaddr_x25 *sxp = l -> lcd_ceaddr; + + if (bcmp (sxp -> x25_udata, u, sxp -> x25_udlen)) + continue; + if (sxp -> x25_net && + sxp -> x25_net != xcp -> xc_addr.x25_net) + continue; + /* + * don't accept incoming calls with the D-Bit on + * unless the server agrees + */ + if (X25GBITS(xp -> bits, d_bit) && !(sxp -> x25_opts.op_flags & X25_DBIT)) { + errstr = "incoming D-Bit mismatch"; + break; + } + /* + * don't accept incoming collect calls unless + * the server sets the reverse charging option. + */ + if ((sxp -> x25_opts.op_flags & (X25_OLDSOCKADDR|X25_REVERSE_CHARGE)) == 0 && + sa -> x25_opts.op_flags & X25_REVERSE_CHARGE) { + errstr = "incoming collect call refused"; + break; + } + if (l -> lcd_so) { + if (so = sonewconn (l -> lcd_so, SS_ISCONNECTED)) + lcp = (struct pklcd *) so -> so_pcb; + } else + lcp = pk_attach ((struct socket *) 0); + if (lcp == 0) { + /* + * Insufficient space or too many unaccepted + * connections. Just throw the call away. + */ + errstr = "server malfunction"; + break; + } + lcp -> lcd_upper = l -> lcd_upper; + lcp -> lcd_upnext = l -> lcd_upnext; + lcp -> lcd_lcn = lcn; + lcp -> lcd_state = RECEIVED_CALL; + sa -> x25_opts.op_flags |= (sxp -> x25_opts.op_flags & + ~X25_REVERSE_CHARGE) | l -> lcd_flags; + pk_assoc (pkp, lcp, sa); + lcp -> lcd_faddr = *sa; + lcp -> lcd_laddr.x25_udlen = sxp -> x25_udlen; + lcp -> lcd_craddr = &lcp -> lcd_faddr; + lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_CALL_ACCEPTED); + if (lcp -> lcd_flags & X25_DBIT) { + if (X25GBITS(xp -> bits, d_bit)) + X25SBITS(mtod (lcp -> lcd_template, + struct x25_packet *) -> bits, d_bit, 1); + else + lcp -> lcd_flags &= ~X25_DBIT; + } + if (so) { + pk_output (lcp); + soisconnected (so); + if (so -> so_options & SO_OOBINLINE) + save_extra (m0, facp, so); + } else if (lcp -> lcd_upper) { + (*lcp -> lcd_upper) (lcp, m0); + } + (void) m_free (m); + return; + } + + /* + * If the call fails for whatever reason, we still need to build a + * skeleton LCD in order to be able to properly receive the CLEAR + * CONFIRMATION. + */ +#ifdef WATERLOO /* be explicit */ + if (l == 0 && bcmp (sa -> x25_udata, "ean", 3) == 0) + pk_message (lcn, pkp -> pk_xcp, "host=%s ean%c: %s", + sa -> x25_addr, sa -> x25_udata[3] & 0xff, errstr); + else if (l == 0 && bcmp (sa -> x25_udata, "\1\0\0\0", 4) == 0) + pk_message (lcn, pkp -> pk_xcp, "host=%s x29d: %s", + sa -> x25_addr, errstr); + else +#endif + pk_message (lcn, pkp -> pk_xcp, "host=%s pid=%x %x %x %x: %s", + sa -> x25_addr, sa -> x25_udata[0] & 0xff, + sa -> x25_udata[1] & 0xff, sa -> x25_udata[2] & 0xff, + sa -> x25_udata[3] & 0xff, errstr); + if ((lcp = pk_attach ((struct socket *)0)) == 0) { + (void) m_free (m); + return; + } + lcp -> lcd_lcn = lcn; + lcp -> lcd_state = RECEIVED_CALL; + pk_assoc (pkp, lcp, sa); + (void) m_free (m); + pk_clear (lcp, 0, 1); +} + +pk_call_accepted (lcp, m) +struct pklcd *lcp; +struct mbuf *m; +{ + register struct x25_calladdr *ap; + register octet *fcp; + struct x25_packet *xp = mtod (m, struct x25_packet *); + int len = m -> m_len; + + lcp -> lcd_state = DATA_TRANSFER; + if (lcp -> lcd_so) + soisconnected (lcp -> lcd_so); + if ((lcp -> lcd_flags & X25_DBIT) && (X25GBITS(xp -> bits, d_bit) == 0)) + lcp -> lcd_flags &= ~X25_DBIT; + if (len > 3) { + ap = (struct x25_calladdr *) &xp -> packet_data; + fcp = (octet *) ap -> address_field + (X25GBITS(ap -> addrlens, calling_addrlen) + + X25GBITS(ap -> addrlens, called_addrlen) + 1) / 2; + if (fcp + *fcp <= ((octet *) xp) + len) + pk_parse_facilities (fcp, lcp -> lcd_ceaddr); + } + pk_assoc (lcp -> lcd_pkp, lcp, lcp -> lcd_ceaddr); + if (lcp -> lcd_so == 0 && lcp -> lcd_upper) + lcp -> lcd_upper (lcp, m); +} + +pk_parse_facilities (fcp, sa) +register octet *fcp; +register struct sockaddr_x25 *sa; +{ + register octet *maxfcp; + + maxfcp = fcp + *fcp; + fcp++; + while (fcp < maxfcp) { + /* + * Ignore national DCE or DTE facilities + */ + if (*fcp == 0 || *fcp == 0xff) + break; + switch (*fcp) { + case FACILITIES_WINDOWSIZE: + sa -> x25_opts.op_wsize = fcp[1]; + fcp += 3; + break; + + case FACILITIES_PACKETSIZE: + sa -> x25_opts.op_psize = fcp[1]; + fcp += 3; + break; + + case FACILITIES_THROUGHPUT: + sa -> x25_opts.op_speed = fcp[1]; + fcp += 2; + break; + + case FACILITIES_REVERSE_CHARGE: + if (fcp[1] & 01) + sa -> x25_opts.op_flags |= X25_REVERSE_CHARGE; + /* + * Datapac specific: for a X.25(1976) DTE, bit 2 + * indicates a "hi priority" (eg. international) call. + */ + if (fcp[1] & 02 && sa -> x25_opts.op_psize == 0) + sa -> x25_opts.op_psize = X25_PS128; + fcp += 2; + break; + + default: +/*printf("unknown facility %x, class=%d\n", *fcp, (*fcp & 0xc0) >> 6);*/ + switch ((*fcp & 0xc0) >> 6) { + case 0: /* class A */ + fcp += 2; + break; + + case 1: + fcp += 3; + break; + + case 2: + fcp += 4; + break; + + case 3: + fcp++; + fcp += *fcp; + } + } + } +} diff --git a/sys/netccitt/pk_llcsubr.c b/sys/netccitt/pk_llcsubr.c new file mode 100644 index 00000000000..552552bdbe2 --- /dev/null +++ b/sys/netccitt/pk_llcsubr.c @@ -0,0 +1,371 @@ +/* $NetBSD: pk_llcsubr.c,v 1.3 1995/03/08 02:14:01 cgd Exp $ */ + +/* + * Copyright (C) Dirk Husemann, Computer Science Department IV, + * University of Erlangen-Nuremberg, Germany, 1990, 1991, 1992 + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Dirk Husemann 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_llcsubr.c 8.1 (Berkeley) 6/10/93 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/domain.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/protosw.h> +#include <sys/errno.h> +#include <sys/time.h> +#include <sys/kernel.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_llc.h> +#include <net/if_types.h> +#include <net/route.h> + +#include <netccitt/dll.h> +#include <netccitt/x25.h> +#include <netccitt/pk.h> +#include <netccitt/pk_var.h> +#include <netccitt/llc_var.h> + + +/* + * Routing support for X.25 + * + * We distinguish between two cases: + * RTF_HOST: + * rt_key(rt) X.25 address of host + * rt_gateway SNPA (MAC+DLSAP) address of host + * rt_llinfo pkcb for rt_key(rt) + * + * RTF_GATEWAY + * rt_key(rt) X.25 address of host or suitably masked network + * rt_gateway X.25 address of next X.25 gateway (switch) + * rt_llinfo rtentry for rt_gateway address + * ought to be of type RTF_HOST + * + * + * Mapping of X.121 to pkcbs: + * + * HDLC uses the DTE-DCE model of X.25, therefore we need a many-to-one + * relationship, i.e.: + * + * {X.121_a, X.121_b, X.121_c, ..., X.121_i} -> pkcb_0 + * + * LLC2 utilizes the DTE-DTE model of X.25, resulting effectively in a + * one-to-one relationship, i.e.: + * + * {X.121_j} -> pkcb_1a + * {X.121_k} -> pkcb_1b + * ... + * {X.121_q} -> pkcb_1q + * + * It might make sense to allow a many-to-one relation for LLC2 also, + * + * {X.121_r, X.121_s, X.121_t, X.121_u} -> pkcb_2a + * + * This would make addresses X.121_[r-u] essentially aliases of one + * address ({X.121_[r-u]} would constitute a representative set). + * + * Each one-to-one relation must obviously be entered individually with + * a route add command, whereas a many-to-one relationship can be + * either entered individually or generated by using a netmask. + * + * To facilitate dealings the many-to-one case for LLC2 can only be + * established via a netmask. + * + */ + +#define XTRACTPKP(rt) ((rt)->rt_flags & RTF_GATEWAY ? \ + ((rt)->rt_llinfo ? \ + (struct pkcb *) ((struct rtentry *)((rt)->rt_llinfo))->rt_llinfo : \ + (struct pkcb *) NULL) : \ + (struct pkcb *)((rt)->rt_llinfo)) + +#define equal(a1, a2) (bcmp((caddr_t)(a1), \ + (caddr_t)(a2), \ + (a1)->sa_len) == 0) +#define XIFA(rt) ((struct x25_ifaddr *)((rt)->rt_ifa)) +#define SA(s) ((struct sockaddr *)s) + +int +cons_rtrequest(int cmd, struct rtentry *rt, struct sockaddr *dst) +{ + register struct pkcb *pkp; + register int i; + register char one_to_one; + struct pkcb *pk_newlink(); + struct rtentry *npaidb_enter(); + + pkp = XTRACTPKP(rt); + + switch(cmd) { + case RTM_RESOLVE: + case RTM_ADD: + if (pkp) + return(EEXIST); + + if (rt->rt_flags & RTF_GATEWAY) { + if (rt->rt_llinfo) + RTFREE((struct rtentry *)rt->rt_llinfo); + rt->rt_llinfo = (caddr_t) rtalloc1(rt->rt_gateway, 1); + return(0); + } + /* + * Assumptions: (1) ifnet structure is filled in + * (2) at least the pkcb created via + * x25config (ifconfig?) has been + * set up already. + * (3) HDLC interfaces have an if_type of + * IFT_X25{,DDN}, LLC2 interfaces + * anything else (any better way to + * do this?) + * + */ + if (!rt->rt_ifa) + return (ENETDOWN); + + /* + * We differentiate between dealing with a many-to-one + * (HDLC: DTE-DCE) and a one-to-one (LLC2: DTE-DTE) + * relationship (by looking at the if type). + * + * Only in case of the many-to-one relationship (HDLC) + * we set the ia->ia_pkcb pointer to the pkcb allocated + * via pk_newlink() as we will use just that one pkcb for + * future route additions (the rtentry->rt_llinfo pointer + * points to the pkcb allocated for that route). + * + * In case of the one-to-one relationship (LLC2) we + * create a new pkcb (via pk_newlink()) for each new rtentry. + * + * NOTE: Only in case of HDLC does ia->ia_pkcb point + * to a pkcb, in the LLC2 case it doesn't (as we don't + * need it here)! + */ + one_to_one = ISISO8802(rt->rt_ifp); + + if (!(pkp = XIFA(rt)->ia_pkcb) && !one_to_one) + XIFA(rt)->ia_pkcb = pkp = + pk_newlink(XIFA(rt), (caddr_t) 0); + else if (one_to_one && + !equal(rt->rt_gateway, rt->rt_ifa->ifa_addr)) { + pkp = pk_newlink(XIFA(rt), (caddr_t) 0); + /* + * We also need another route entry for mapping + * MAC+LSAP->X.25 address + */ + pkp->pk_llrt = npaidb_enter(rt->rt_gateway, rt_key(rt), rt, 0); + } + if (pkp) { + if (!pkp->pk_rt) + pkp->pk_rt = rt; + pkp->pk_refcount++; + } + rt->rt_llinfo = (caddr_t) pkp; + + return(0); + + case RTM_DELETE: + { + /* + * The pkp might be empty if we are dealing + * with an interface route entry for LLC2, in this + * case we don't need to do anything ... + */ + if (pkp) { + if ( rt->rt_flags & RTF_GATEWAY ) { + if (rt->rt_llinfo) + RTFREE((struct rtentry *)rt->rt_llinfo); + return(0); + } + + if (pkp->pk_llrt) + npaidb_destroy(pkp->pk_llrt); + + pk_dellink (pkp); + + return(0); + } + } + } +} + +/* + * Network Protocol Addressing Information DataBase (npaidb) + * + * To speed up locating the entity dealing with an LLC packet use is made + * of a routing tree. This npaidb routing tree is handled + * by the normal rn_*() routines just like (almost) any other routing tree. + * + * The mapping being done by the npaidb_*() routines is as follows: + * + * Key: MAC,LSAP (enhancing struct sockaddr_dl) + * Gateway: sockaddr_x25 (i.e. X.25 address - X.121 or NSAP) + * Llinfo: npaidbentry { + * struct llc_linkcb *npaidb_linkp; + * struct rtentry *npaidb_rt; + * } + * + * Using the npaidbentry provided by llinfo we can then access + * + * o the pkcb by using (struct pkcb *) (npaidb_rt->rt_llinfo) + * o the linkcb via npaidb_linkp + * + * The following functions are provided + * + * o npaidb_enter(struct sockaddr_dl *sdl, struct sockaddr_x25 *sx25, + * struct struct llc_linkcb *link, struct rtentry *rt) + * + * o npaidb_enrich(short type, caddr_t info) + * + */ + +struct sockaddr_dl npdl_netmask = { + sizeof(struct sockaddr_dl), /* _len */ + 0, /* _family */ + 0, /* _index */ + 0, /* _type */ + -1, /* _nlen */ + -1, /* _alen */ + -1, /* _slen */ + { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /* _data */ +}; +struct sockaddr npdl_dummy; + +int npdl_datasize = sizeof(struct sockaddr_dl)- + ((int)((caddr_t)&((struct sockaddr_dl *)0)->sdl_data[0])); + +struct rtentry * +npaidb_enter(struct sockaddr_dl *key, struct sockaddr *value, + struct rtentry *rt, struct llc_linkcb *link) +{ + struct rtentry *nprt; register int i; + + USES_AF_LINK_RTS; + + if ((nprt = rtalloc1(SA(key), 0)) == 0) { + register u_int size = sizeof(struct npaidbentry); + register u_char saploc = LLSAPLOC(key, rt->rt_ifp); + + /* + * set up netmask: LLC2 packets have the lowest bit set in + * response packets (e.g. 0x7e for command packets, 0x7f for + * response packets), to facilitate the lookup we use a netmask + * of 11111110 for the SAP position. The remaining positions + * are zeroed out. + */ + npdl_netmask.sdl_data[saploc] = NPDL_SAPNETMASK; + bzero((caddr_t)&npdl_netmask.sdl_data[saploc+1], + npdl_datasize-saploc-1); + + if (value == 0) + value = &npdl_dummy; + + /* now enter it */ + rtrequest(RTM_ADD, SA(key), SA(value), + SA(&npdl_netmask), 0, &nprt); + + /* and reset npdl_netmask */ + for (i = saploc; i < npdl_datasize; i++) + npdl_netmask.sdl_data[i] = -1; + + nprt->rt_llinfo = malloc(size , M_PCB, M_WAITOK); + if (nprt->rt_llinfo) { + bzero (nprt->rt_llinfo, size); + ((struct npaidbentry *) (nprt->rt_llinfo))->np_rt = rt; + } + } else nprt->rt_refcnt--; + return nprt; +} + +struct rtentry * +npaidb_enrich(short type, caddr_t info, struct sockaddr_dl *sdl) +{ + struct rtentry *rt; + + USES_AF_LINK_RTS; + + if (rt = rtalloc1((struct sockaddr *)sdl, 0)) { + rt->rt_refcnt--; + switch (type) { + case NPAIDB_LINK: + ((struct npaidbentry *)(rt->rt_llinfo))->np_link = + (struct llc_linkcb *) info; + break; + } + return rt; + } + + return ((struct rtentry *) 0); + +} + +npaidb_destroy(struct rtentry *rt) +{ + USES_AF_LINK_RTS; + + if (rt->rt_llinfo) + free((caddr_t) rt->rt_llinfo, M_PCB); + return(rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway, rt_mask(rt), + 0, 0)); +} + + +#ifdef LLC +/* + * Glue between X.25 and LLC2 + */ +long +x25_llcglue(int prc, struct sockaddr *addr) +{ + register struct sockaddr_x25 *sx25 = (struct sockaddr_x25 *)addr; + register struct x25_ifaddr *x25ifa; + struct dll_ctlinfo ctlinfo; + + if((x25ifa = (struct x25_ifaddr *)ifa_ifwithaddr(addr)) == 0) + return 0; + + ctlinfo.dlcti_cfg = + (struct dllconfig *)(((struct sockaddr_x25 *)(&x25ifa->ia_xc))+1); + ctlinfo.dlcti_lsap = LLC_X25_LSAP; + + return ((long)llc_ctlinput(prc, addr, (caddr_t)&ctlinfo)); +} +#endif /* LLC */ diff --git a/sys/netccitt/pk_output.c b/sys/netccitt/pk_output.c new file mode 100644 index 00000000000..9bc91325fec --- /dev/null +++ b/sys/netccitt/pk_output.c @@ -0,0 +1,218 @@ +/* $NetBSD: pk_output.c,v 1.6 1994/09/20 06:41:04 cgd 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. 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_output.c 8.1 (Berkeley) 6/10/93 + */ + +#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 <net/if.h> + +#include <netccitt/x25.h> +#include <netccitt/pk.h> +#include <netccitt/pk_var.h> + +struct mbuf_cache pk_output_cache = {0 }, pk_input_cache; +struct mbuf *nextpk (); + +pk_output (lcp) +register struct pklcd *lcp; +{ + register struct x25_packet *xp; + register struct mbuf *m; + register struct pkcb *pkp = lcp -> lcd_pkp; + + if (lcp == 0 || pkp == 0) { + printf ("pk_output: zero arg\n"); + return; + } + + while ((m = nextpk (lcp)) != NULL) { + xp = mtod (m, struct x25_packet *); + + switch (pk_decode (xp) + lcp -> lcd_state) { + /* + * All the work is already done - just set the state and + * pass to peer. + */ + case CALL + READY: + lcp -> lcd_state = SENT_CALL; + lcp -> lcd_timer = pk_t21; + break; + + /* + * Just set the state to allow packet to flow and send the + * confirmation. + */ + case CALL_ACCEPTED + RECEIVED_CALL: + lcp -> lcd_state = DATA_TRANSFER; + break; + + /* + * Just set the state. Keep the LCD around till the clear + * confirmation is returned. + */ + case CLEAR + RECEIVED_CALL: + case CLEAR + SENT_CALL: + case CLEAR + DATA_TRANSFER: + lcp -> lcd_state = SENT_CLEAR; + lcp -> lcd_retry = 0; + /* fall through */ + + case CLEAR + SENT_CLEAR: + lcp -> lcd_timer = pk_t23; + lcp -> lcd_retry++; + break; + + case CLEAR_CONF + RECEIVED_CLEAR: + case CLEAR_CONF + SENT_CLEAR: + case CLEAR_CONF + READY: + lcp -> lcd_state = READY; + break; + + case DATA + DATA_TRANSFER: + SPS(xp, lcp -> lcd_ssn); + lcp -> lcd_input_window = + (lcp -> lcd_rsn + 1) % MODULUS; + SPR(xp, lcp -> lcd_input_window); + lcp -> lcd_last_transmitted_pr = lcp -> lcd_input_window; + lcp -> lcd_ssn = (lcp -> lcd_ssn + 1) % MODULUS; + if (lcp -> lcd_ssn == ((lcp -> lcd_output_window + lcp -> lcd_windowsize) % MODULUS)) + lcp -> lcd_window_condition = TRUE; + break; + + case INTERRUPT + DATA_TRANSFER: +#ifdef ancient_history + xp -> packet_data = 0; +#endif + lcp -> lcd_intrconf_pending = TRUE; + break; + + case INTERRUPT_CONF + DATA_TRANSFER: + break; + + case RR + DATA_TRANSFER: + case RNR + DATA_TRANSFER: + lcp -> lcd_input_window = + (lcp -> lcd_rsn + 1) % MODULUS; + SPR(xp, lcp -> lcd_input_window); + lcp -> lcd_last_transmitted_pr = lcp -> lcd_input_window; + break; + + case RESET + DATA_TRANSFER: + lcp -> lcd_reset_condition = TRUE; + break; + + case RESET_CONF + DATA_TRANSFER: + lcp -> lcd_reset_condition = FALSE; + break; + + /* + * A restart should be only generated internally. Therefore + * all logic for restart is in the pk_restart routine. + */ + case RESTART + READY: + lcp -> lcd_timer = pk_t20; + break; + + /* + * Restarts are all handled internally. Therefore all the + * logic for the incoming restart packet is handled in the + * pk_input routine. + */ + case RESTART_CONF + READY: + break; + + default: + m_freem (m); + return; + } + + /* Trace the packet. */ + pk_trace (pkp -> pk_xcp, m, "P-Out"); + + /* Pass the packet on down to the link layer */ + if (pk_input_cache.mbc_size || pk_input_cache.mbc_oldsize) { + m->m_flags |= 0x08; + mbuf_cache(&pk_input_cache, m); + } + (*pkp -> pk_lloutput) (pkp -> pk_llnext, m, pkp -> pk_rt); + } +} + +/* + * This procedure returns the next packet to send or null. A + * packet is composed of one or more mbufs. + */ + +struct mbuf * +nextpk (lcp) +struct pklcd *lcp; +{ + register struct mbuf *m, *n; + struct socket *so = lcp -> lcd_so; + register struct sockbuf *sb = (so ? &so->so_snd : &lcp->lcd_sb); + + if (lcp -> lcd_template) { + m = lcp -> lcd_template; + lcp -> lcd_template = NULL; + } else { + if (lcp -> lcd_rnr_condition || lcp -> lcd_window_condition || + lcp -> lcd_reset_condition) + return (NULL); + + if ((m = sb -> sb_mb) == 0) + return (NULL); + + sb -> sb_mb = m -> m_nextpkt; + m->m_act = 0; + for (n = m; n; n = n -> m_next) + sbfree (sb, n); + } + return (m); +} diff --git a/sys/netccitt/pk_subr.c b/sys/netccitt/pk_subr.c new file mode 100644 index 00000000000..99a93b05ac2 --- /dev/null +++ b/sys/netccitt/pk_subr.c @@ -0,0 +1,1194 @@ +/* $NetBSD: pk_subr.c,v 1.10 1995/08/17 02:57:25 mycroft 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. 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_subr.c 8.1 (Berkeley) 6/10/93 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/protosw.h> +#include <sys/socketvar.h> +#include <sys/errno.h> +#include <sys/time.h> +#include <sys/kernel.h> + +#include <net/if.h> +#include <net/route.h> + +#include <netccitt/dll.h> +#include <netccitt/x25.h> +#include <netccitt/x25err.h> +#include <netccitt/pk.h> +#include <netccitt/pk_var.h> + +int pk_sendspace = 1024 * 2 + 8; +int pk_recvspace = 1024 * 2 + 8; + +struct pklcd_q pklcd_q = {&pklcd_q, &pklcd_q}; + +struct x25bitslice x25_bitslice[] = { +/* mask, shift value */ + { 0xf0, 0x4 }, + { 0xf, 0x0 }, + { 0x80, 0x7 }, + { 0x40, 0x6 }, + { 0x30, 0x4 }, + { 0xe0, 0x5 }, + { 0x10, 0x4 }, + { 0xe, 0x1 }, + { 0x1, 0x0 } +}; + + +/* + * Attach X.25 protocol to socket, allocate logical channel descripter + * and buffer space, and enter LISTEN state if we are to accept + * IN-COMMING CALL packets. + * + */ + +struct pklcd * +pk_attach (so) +struct socket *so; +{ + register struct pklcd *lcp; + register int error = ENOBUFS; + int pk_output (); + + MALLOC(lcp, struct pklcd *, sizeof (*lcp), M_PCB, M_NOWAIT); + if (lcp) { + bzero ((caddr_t)lcp, sizeof (*lcp)); + insque (&lcp -> lcd_q, &pklcd_q); + lcp -> lcd_state = READY; + lcp -> lcd_send = pk_output; + if (so) { + error = soreserve (so, pk_sendspace, pk_recvspace); + lcp -> lcd_so = so; + if (so -> so_options & SO_ACCEPTCONN) + lcp -> lcd_state = LISTEN; + } else + sbreserve (&lcp -> lcd_sb, pk_sendspace); + } + if (so) { + so -> so_pcb = lcp; + so -> so_error = error; + } + return (lcp); +} + +/* + * Disconnect X.25 protocol from socket. + */ + +pk_disconnect (lcp) +register struct pklcd *lcp; +{ + register struct socket *so = lcp -> lcd_so; + register struct pklcd *l, *p; + + switch (lcp -> lcd_state) { + case LISTEN: + for (p = 0, l = pk_listenhead; l && l != lcp; p = l, l = l -> lcd_listen); + if (p == 0) { + if (l != 0) + pk_listenhead = l -> lcd_listen; + } + else + if (l != 0) + p -> lcd_listen = l -> lcd_listen; + pk_close (lcp); + break; + + case READY: + pk_acct (lcp); + pk_close (lcp); + break; + + case SENT_CLEAR: + case RECEIVED_CLEAR: + break; + + default: + pk_acct (lcp); + if (so) { + soisdisconnecting (so); + sbflush (&so -> so_rcv); + } + pk_clear (lcp, 241, 0); /* Normal Disconnect */ + + } +} + +/* + * Close an X.25 Logical Channel. Discard all space held by the + * connection and internal descriptors. Wake up any sleepers. + */ + +pk_close (lcp) +struct pklcd *lcp; +{ + register struct socket *so = lcp -> lcd_so; + + /* + * If the X.25 connection is torn down due to link + * level failure (e.g. LLC2 FRMR) and at the same the user + * level is still filling up the socket send buffer that + * send buffer is locked. An attempt to sbflush () that send + * buffer will lead us into - no, not temptation but - panic! + * So - we'll just check wether the send buffer is locked + * and if that's the case we'll mark the lcp as zombie and + * have the pk_timer () do the cleaning ... + */ + + if (so && so -> so_snd.sb_flags & SB_LOCK) + lcp -> lcd_state = LCN_ZOMBIE; + else + pk_freelcd (lcp); + + if (so == NULL) + return; + + so -> so_pcb = 0; + soisdisconnected (so); + /* sofree (so); /* gak!!! you can't do that here */ +} + +/* + * Create a template to be used to send X.25 packets on a logical + * channel. It allocates an mbuf and fills in a skeletal packet + * depending on its type. This packet is passed to pk_output where + * the remainer of the packet is filled in. +*/ + +struct mbuf * +pk_template (lcn, type) +int lcn, type; +{ + register struct mbuf *m; + register struct x25_packet *xp; + + MGETHDR (m, M_DONTWAIT, MT_HEADER); + if (m == 0) + panic ("pk_template"); + m -> m_act = 0; + + /* + * Efficiency hack: leave a four byte gap at the beginning + * of the packet level header with the hope that this will + * be enough room for the link level to insert its header. + */ + m -> m_data += max_linkhdr; + m -> m_pkthdr.len = m -> m_len = PKHEADERLN; + + xp = mtod (m, struct x25_packet *); + *(long *)xp = 0; /* ugly, but fast */ +/* xp -> q_bit = 0;*/ + X25SBITS(xp -> bits, fmt_identifier, 1); +/* xp -> lc_group_number = 0;*/ + + SET_LCN(xp, lcn); + xp -> packet_type = type; + + return (m); +} + +/* + * This routine restarts all the virtual circuits. Actually, + * the virtual circuits are not "restarted" as such. Instead, + * any active switched circuit is simply returned to READY + * state. + */ + +pk_restart (pkp, restart_cause) +register struct pkcb *pkp; +int restart_cause; +{ + register struct mbuf *m; + register struct pklcd *lcp; + register int i; + + /* Restart all logical channels. */ + if (pkp -> pk_chan == 0) + return; + + /* + * Don't do this if we're doing a restart issued from + * inside pk_connect () --- which is only done if and + * only if the X.25 link is down, i.e. a RESTART needs + * to be done to get it up. + */ + if (!(pkp -> pk_dxerole & DTE_CONNECTPENDING)) { + for (i = 1; i <= pkp -> pk_maxlcn; ++i) + if ((lcp = pkp -> pk_chan[i]) != NULL) { + if (lcp -> lcd_so) { + lcp -> lcd_so -> so_error = ENETRESET; + pk_close (lcp); + } else { + pk_flush (lcp); + lcp -> lcd_state = READY; + if (lcp -> lcd_upper) + lcp -> lcd_upper (lcp, 0); + } + } + } + + if (restart_cause < 0) + return; + + pkp -> pk_state = DTE_SENT_RESTART; + pkp -> pk_dxerole &= ~(DTE_PLAYDCE | DTE_PLAYDTE); + lcp = pkp -> pk_chan[0]; + m = lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_RESTART); + m -> m_pkthdr.len = m -> m_len += 2; + mtod (m, struct x25_packet *) -> packet_data = 0; /* DTE only */ + mtod (m, octet *)[4] = restart_cause; + pk_output (lcp); +} + + +/* + * This procedure frees up the Logical Channel Descripter. + */ + +pk_freelcd (lcp) +register struct pklcd *lcp; +{ + if (lcp == NULL) + return; + + if (lcp -> lcd_lcn > 0) + lcp -> lcd_pkp -> pk_chan[lcp -> lcd_lcn] = NULL; + + pk_flush (lcp); + remque (&lcp -> lcd_q); + free ((caddr_t)lcp, M_PCB); +} + +static struct x25_ifaddr * +pk_ifwithaddr (sx) + struct sockaddr_x25 *sx; +{ + struct ifnet *ifp; + struct ifaddr *ifa; + register struct x25_ifaddr *ia; + char *addr = sx -> x25_addr; + + for (ifp = ifnet.tqh_first; ifp != 0; ifp = ifp->if_list.tqe_next) + for (ifa = ifp->if_addrlist.tqh_first; ifa != 0; + ifa = ifa->ifa_list.tqe_next) + if (ifa -> ifa_addr -> sa_family == AF_CCITT) { + ia = (struct x25_ifaddr *)ifa; + if (bcmp (addr, ia -> ia_xc.xc_addr.x25_addr, + 16) == 0) + return (ia); + + } + return ((struct x25_ifaddr *)0); +} + + +/* + * Bind a address and protocol value to a socket. The important + * part is the protocol value - the first four characters of the + * Call User Data field. + */ + +#define XTRACTPKP(rt) ((rt) -> rt_flags & RTF_GATEWAY ? \ + ((rt) -> rt_llinfo ? \ + (struct pkcb *) ((struct rtentry *)((rt) -> rt_llinfo)) -> rt_llinfo : \ + (struct pkcb *) NULL) : \ + (struct pkcb *)((rt) -> rt_llinfo)) + +pk_bind (lcp, nam) +struct pklcd *lcp; +struct mbuf *nam; +{ + register struct pklcd *pp; + register struct sockaddr_x25 *sa; + + if (nam == NULL) + return (EADDRNOTAVAIL); + if (lcp -> lcd_ceaddr) /* XXX */ + return (EADDRINUSE); + if (pk_checksockaddr (nam)) + return (EINVAL); + sa = mtod (nam, struct sockaddr_x25 *); + + /* + * If the user wishes to accept calls only from a particular + * net (net != 0), make sure the net is known + */ + + if (sa -> x25_addr[0]) { + if (!pk_ifwithaddr (sa)) + return (ENETUNREACH); + } else if (sa -> x25_net) { + if (!ifa_ifwithnet ((struct sockaddr *)sa)) + return (ENETUNREACH); + } + + /* + * For ISO's sake permit default listeners, but only one such . . . + */ + for (pp = pk_listenhead; pp; pp = pp -> lcd_listen) { + register struct sockaddr_x25 *sa2 = pp -> lcd_ceaddr; + if ((sa2 -> x25_udlen == sa -> x25_udlen) && + (sa2 -> x25_udlen == 0 || + (bcmp (sa2 -> x25_udata, sa -> x25_udata, + min (sa2 -> x25_udlen, sa -> x25_udlen)) == 0))) + return (EADDRINUSE); + } + lcp -> lcd_laddr = *sa; + lcp -> lcd_ceaddr = &lcp -> lcd_laddr; + return (0); +} + +/* + * Include a bound control block in the list of listeners. + */ +pk_listen (lcp) +register struct pklcd *lcp; +{ + register struct pklcd **pp; + + if (lcp -> lcd_ceaddr == 0) + return (EDESTADDRREQ); + + lcp -> lcd_state = LISTEN; + /* + * Add default listener at end, any others at start. + */ + if (lcp -> lcd_ceaddr -> x25_udlen == 0) { + for (pp = &pk_listenhead; *pp; ) + pp = &((*pp) -> lcd_listen); + *pp = lcp; + } else { + lcp -> lcd_listen = pk_listenhead; + pk_listenhead = lcp; + } + return (0); +} +/* + * Include a listening control block for the benefit of other protocols. + */ +pk_protolisten (spi, spilen, callee) +int (*callee) (); +{ + register struct pklcd *lcp = pk_attach ((struct socket *)0); + register struct mbuf *nam; + register struct sockaddr_x25 *sa; + int error = ENOBUFS; + + if (lcp) { + if (nam = m_getclr (M_DONTWAIT, MT_SONAME)) { + sa = mtod (nam, struct sockaddr_x25 *); + sa -> x25_family = AF_CCITT; + sa -> x25_len = nam -> m_len = sizeof (*sa); + sa -> x25_udlen = spilen; + sa -> x25_udata[0] = spi; + lcp -> lcd_upper = callee; + lcp -> lcd_flags = X25_MBS_HOLD; + if ((error = pk_bind (lcp, nam)) == 0) + error = pk_listen (lcp); + (void) m_free (nam); + } + if (error) + pk_freelcd (lcp); + } + return error; /* Hopefully Zero !*/ +} + +/* + * Associate a logical channel descriptor with a network. + * Fill in the default network specific parameters and then + * set any parameters explicitly specified by the user or + * by the remote DTE. + */ + +pk_assoc (pkp, lcp, sa) +register struct pkcb *pkp; +register struct pklcd *lcp; +register struct sockaddr_x25 *sa; +{ + + lcp -> lcd_pkp = pkp; + lcp -> lcd_packetsize = pkp -> pk_xcp -> xc_psize; + lcp -> lcd_windowsize = pkp -> pk_xcp -> xc_pwsize; + lcp -> lcd_rsn = MODULUS - 1; + pkp -> pk_chan[lcp -> lcd_lcn] = lcp; + + if (sa -> x25_opts.op_psize) + lcp -> lcd_packetsize = sa -> x25_opts.op_psize; + else + sa -> x25_opts.op_psize = lcp -> lcd_packetsize; + if (sa -> x25_opts.op_wsize) + lcp -> lcd_windowsize = sa -> x25_opts.op_wsize; + else + sa -> x25_opts.op_wsize = lcp -> lcd_windowsize; + sa -> x25_net = pkp -> pk_xcp -> xc_addr.x25_net; + lcp -> lcd_flags |= sa -> x25_opts.op_flags; + lcp -> lcd_stime = time.tv_sec; +} + +pk_connect (lcp, sa) +register struct pklcd *lcp; +register struct sockaddr_x25 *sa; +{ + register struct pkcb *pkp; + register struct rtentry *rt; + register struct rtentry *nrt; + + struct rtentry *npaidb_enter (); + struct pkcb *pk_newlink (); + + if (sa -> x25_addr[0] == '\0') + return (EDESTADDRREQ); + + /* + * Is the destination address known? + */ + if (!(rt = rtalloc1 ((struct sockaddr *)sa, 1))) + return (ENETUNREACH); + + if (!(pkp = XTRACTPKP(rt))) + pkp = pk_newlink ((struct x25_ifaddr *) (rt -> rt_ifa), + (caddr_t) 0); + + /* + * Have we entered the LLC address? + */ + if (nrt = npaidb_enter (rt -> rt_gateway, rt_key (rt), rt, 0)) + pkp -> pk_llrt = nrt; + + /* + * Have we allocated an LLC2 link yet? + */ + if (pkp -> pk_llnext == (caddr_t)0 && pkp -> pk_llctlinput) { + struct dll_ctlinfo ctlinfo; + + ctlinfo.dlcti_rt = rt; + ctlinfo.dlcti_pcb = (caddr_t) pkp; + ctlinfo.dlcti_conf = + (struct dllconfig *) (&((struct x25_ifaddr *)(rt -> rt_ifa)) -> ia_xc); + pkp -> pk_llnext = + (pkp -> pk_llctlinput) (PRC_CONNECT_REQUEST, 0, &ctlinfo); + } + + if (pkp -> pk_state != DTE_READY && pkp -> pk_state != DTE_WAITING) + return (ENETDOWN); + if ((lcp -> lcd_lcn = pk_getlcn (pkp)) == 0) + return (EMFILE); + + lcp -> lcd_faddr = *sa; + lcp -> lcd_ceaddr = & lcp -> lcd_faddr; + pk_assoc (pkp, lcp, lcp -> lcd_ceaddr); + + /* + * If the link is not up yet, initiate an X.25 RESTART + */ + if (pkp -> pk_state == DTE_WAITING) { + pkp -> pk_dxerole |= DTE_CONNECTPENDING; + pk_ctlinput (PRC_LINKUP, (struct sockaddr *)0, pkp); + if (lcp -> lcd_so) + soisconnecting (lcp -> lcd_so); + return 0; + } + + if (lcp -> lcd_so) + soisconnecting (lcp -> lcd_so); + lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_CALL); + pk_callrequest (lcp, lcp -> lcd_ceaddr, pkp -> pk_xcp); + return (*pkp -> pk_ia -> ia_start) (lcp); +} + +/* + * Complete all pending X.25 call requests --- this gets called after + * the X.25 link has been restarted. + */ +#define RESHUFFLELCN(maxlcn, lcn) ((maxlcn) - (lcn) + 1) + +pk_callcomplete (pkp) + register struct pkcb *pkp; +{ + register struct pklcd *lcp; + register int i; + register int ni; + + + if (pkp -> pk_dxerole & DTE_CONNECTPENDING) + pkp -> pk_dxerole &= ~DTE_CONNECTPENDING; + else return; + + if (pkp -> pk_chan == 0) + return; + + /* + * We pretended to be a DTE for allocating lcns, if + * it turns out that we are in reality performing as a + * DCE we need to reshuffle the lcps. + * + * /+---------------+-------- - + * / | a (maxlcn-1) | \ + * / +---------------+ \ + * +--- * | b (maxlcn-2) | \ + * | \ +---------------+ \ + * r | \ | c (maxlcn-3) | \ + * e | \+---------------+ | + * s | | . | + * h | | . | m + * u | | . | a + * f | | . | x + * f | | . | l + * l | /+---------------+ | c + * e | / | c' ( 3 ) | | n + * | / +---------------+ | + * +--> * | b' ( 2 ) | / + * \ +---------------+ / + * \ | a' ( 1 ) | / + * \+---------------+ / + * | 0 | / + * +---------------+-------- - + * + */ + if (pkp -> pk_dxerole & DTE_PLAYDCE) { + /* Sigh, reshuffle it */ + for (i = pkp -> pk_maxlcn; i > 0; --i) + if (pkp -> pk_chan[i]) { + ni = RESHUFFLELCN(pkp -> pk_maxlcn, i); + pkp -> pk_chan[ni] = pkp -> pk_chan[i]; + pkp -> pk_chan[i] = NULL; + pkp -> pk_chan[ni] -> lcd_lcn = ni; + } + } + + for (i = 1; i <= pkp -> pk_maxlcn; ++i) + if ((lcp = pkp -> pk_chan[i]) != NULL) { + /* if (lcp -> lcd_so) + soisconnecting (lcp -> lcd_so); */ + lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_CALL); + pk_callrequest (lcp, lcp -> lcd_ceaddr, pkp -> pk_xcp); + (*pkp -> pk_ia -> ia_start) (lcp); + } +} + +struct bcdinfo { + octet *cp; + unsigned posn; +}; +/* + * Build the rest of the CALL REQUEST packet. Fill in calling + * address, facilities fields and the user data field. + */ + +pk_callrequest (lcp, sa, xcp) +struct pklcd *lcp; +register struct sockaddr_x25 *sa; +register struct x25config *xcp; +{ + register struct x25_calladdr *a; + register struct mbuf *m = lcp -> lcd_template; + register struct x25_packet *xp = mtod (m, struct x25_packet *); + struct bcdinfo b; + + if (lcp -> lcd_flags & X25_DBIT) + X25SBITS(xp -> bits, d_bit, 1); + a = (struct x25_calladdr *) &xp -> packet_data; + b.cp = (octet *) a -> address_field; + b.posn = 0; + X25SBITS(a -> addrlens, called_addrlen, to_bcd (&b, sa, xcp)); + X25SBITS(a -> addrlens, calling_addrlen, to_bcd (&b, &xcp -> xc_addr, xcp)); + if (b.posn & 0x01) + *b.cp++ &= 0xf0; + m -> m_pkthdr.len = m -> m_len += b.cp - (octet *) a; + + if (lcp -> lcd_facilities) { + m -> m_pkthdr.len += + (m -> m_next = lcp -> lcd_facilities) -> m_pkthdr.len; + lcp -> lcd_facilities = 0; + } else + pk_build_facilities (m, sa, (int)xcp -> xc_type); + + m_copyback (m, m -> m_pkthdr.len, sa -> x25_udlen, sa -> x25_udata); +} + +pk_build_facilities (m, sa, type) +register struct mbuf *m; +struct sockaddr_x25 *sa; +{ + register octet *cp; + register octet *fcp; + register int revcharge; + + cp = mtod (m, octet *) + m -> m_len; + fcp = cp + 1; + revcharge = sa -> x25_opts.op_flags & X25_REVERSE_CHARGE ? 1 : 0; + /* + * This is specific to Datapac X.25(1976) DTEs. International + * calls must have the "hi priority" bit on. + */ + if (type == X25_1976 && sa -> x25_opts.op_psize == X25_PS128) + revcharge |= 02; + if (revcharge) { + *fcp++ = FACILITIES_REVERSE_CHARGE; + *fcp++ = revcharge; + } + switch (type) { + case X25_1980: + case X25_1984: + *fcp++ = FACILITIES_PACKETSIZE; + *fcp++ = sa -> x25_opts.op_psize; + *fcp++ = sa -> x25_opts.op_psize; + + *fcp++ = FACILITIES_WINDOWSIZE; + *fcp++ = sa -> x25_opts.op_wsize; + *fcp++ = sa -> x25_opts.op_wsize; + } + *cp = fcp - cp - 1; + m -> m_pkthdr.len = (m -> m_len += *cp + 1); +} + +to_bcd (b, sa, xcp) +register struct bcdinfo *b; +struct sockaddr_x25 *sa; +register struct x25config *xcp; +{ + register char *x = sa -> x25_addr; + unsigned start = b -> posn; + /* + * The nodnic and prepnd0 stuff looks tedious, + * but it does allow full X.121 addresses to be used, + * which is handy for routing info (& OSI type 37 addresses). + */ + if (xcp -> xc_addr.x25_net && (xcp -> xc_nodnic || xcp -> xc_prepnd0)) { + char dnicname[sizeof (long) * NBBY/3 + 2]; + register char *p = dnicname; + + sprintf (p, "%d", xcp -> xc_addr.x25_net & 0x7fff); + for (; *p; p++) /* *p == 0 means dnic matched */ + if ((*p ^ *x++) & 0x0f) + break; + if (*p || xcp -> xc_nodnic == 0) + x = sa -> x25_addr; + if (*p && xcp -> xc_prepnd0) { + if ((b -> posn)++ & 0x01) + *(b -> cp)++; + else + *(b -> cp) = 0; + } + } + while (*x) + if ((b -> posn)++ & 0x01) + *(b -> cp)++ |= *x++ & 0x0F; + else + *(b -> cp) = *x++ << 4; + return ((b -> posn) - start); +} + +/* + * This routine gets the first available logical channel number. The + * search is + * - from the highest number to lowest number if playing DTE, and + * - from lowest to highest number if playing DCE. + */ + +pk_getlcn (pkp) +register struct pkcb *pkp; +{ + register int i; + + if (pkp -> pk_chan == 0) + return (0); + if ( pkp -> pk_dxerole & DTE_PLAYDCE ) { + for (i = 1; i <= pkp -> pk_maxlcn; ++i) + if (pkp -> pk_chan[i] == NULL) + break; + } else { + for (i = pkp -> pk_maxlcn; i > 0; --i) + if (pkp -> pk_chan[i] == NULL) + break; + } + i = ( i > pkp -> pk_maxlcn ? 0 : i ); + return (i); +} + +/* + * This procedure sends a CLEAR request packet. The lc state is + * set to "SENT_CLEAR". + */ + +pk_clear (lcp, diagnostic, abortive) +register struct pklcd *lcp; +{ + register struct mbuf *m = pk_template (lcp -> lcd_lcn, X25_CLEAR); + + m -> m_len += 2; + m -> m_pkthdr.len += 2; + mtod (m, struct x25_packet *) -> packet_data = 0; + mtod (m, octet *)[4] = diagnostic; + if (lcp -> lcd_facilities) { + m -> m_next = lcp -> lcd_facilities; + m -> m_pkthdr.len += m -> m_next -> m_len; + lcp -> lcd_facilities = 0; + } + if (abortive) + lcp -> lcd_template = m; + else { + struct socket *so = lcp -> lcd_so; + struct sockbuf *sb = so ? & so -> so_snd : & lcp -> lcd_sb; + sbappendrecord (sb, m); + } + pk_output (lcp); + +} + +/* + * This procedure generates RNR's or RR's to inhibit or enable + * inward data flow, if the current state changes (blocked ==> open or + * vice versa), or if forced to generate one. One forces RNR's to ack data. + */ +pk_flowcontrol (lcp, inhibit, forced) +register struct pklcd *lcp; +{ + inhibit = (inhibit != 0); + if (lcp == 0 || lcp -> lcd_state != DATA_TRANSFER || + (forced == 0 && lcp -> lcd_rxrnr_condition == inhibit)) + return; + lcp -> lcd_rxrnr_condition = inhibit; + lcp -> lcd_template = + pk_template (lcp -> lcd_lcn, inhibit ? X25_RNR : X25_RR); + pk_output (lcp); +} + +/* + * This procedure sends a RESET request packet. It re-intializes + * virtual circuit. + */ + +static +pk_reset (lcp, diagnostic) +register struct pklcd *lcp; +{ + register struct mbuf *m; + register struct socket *so = lcp -> lcd_so; + + if (lcp -> lcd_state != DATA_TRANSFER) + return; + + if (so) + so -> so_error = ECONNRESET; + lcp -> lcd_reset_condition = TRUE; + + /* Reset all the control variables for the channel. */ + pk_flush (lcp); + lcp -> lcd_window_condition = lcp -> lcd_rnr_condition = + lcp -> lcd_intrconf_pending = FALSE; + lcp -> lcd_rsn = MODULUS - 1; + lcp -> lcd_ssn = 0; + lcp -> lcd_output_window = lcp -> lcd_input_window = + lcp -> lcd_last_transmitted_pr = 0; + m = lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_RESET); + m -> m_pkthdr.len = m -> m_len += 2; + mtod (m, struct x25_packet *) -> packet_data = 0; + mtod (m, octet *)[4] = diagnostic; + pk_output (lcp); + +} + +/* + * This procedure frees all data queued for output or delivery on a + * virtual circuit. + */ + +pk_flush (lcp) +register struct pklcd *lcp; +{ + register struct socket *so; + + if (lcp -> lcd_template) + m_freem (lcp -> lcd_template); + + if (lcp -> lcd_cps) { + m_freem (lcp -> lcd_cps); + lcp -> lcd_cps = 0; + } + if (lcp -> lcd_facilities) { + m_freem (lcp -> lcd_facilities); + lcp -> lcd_facilities = 0; + } + if (so = lcp -> lcd_so) + sbflush (&so -> so_snd); + else + sbflush (&lcp -> lcd_sb); +} + +/* + * This procedure handles all local protocol procedure errors. + */ + +pk_procerror (error, lcp, errstr, diagnostic) +register struct pklcd *lcp; +char *errstr; +{ + + pk_message (lcp -> lcd_lcn, lcp -> lcd_pkp -> pk_xcp, errstr); + + switch (error) { + case CLEAR: + if (lcp -> lcd_so) { + lcp -> lcd_so -> so_error = ECONNABORTED; + soisdisconnecting (lcp -> lcd_so); + } + pk_clear (lcp, diagnostic, 1); + break; + + case RESET: + pk_reset (lcp, diagnostic); + } +} + +/* + * This procedure is called during the DATA TRANSFER state to check + * and process the P(R) values received in the DATA, RR OR RNR + * packets. + */ + +pk_ack (lcp, pr) +struct pklcd *lcp; +unsigned pr; +{ + register struct socket *so = lcp -> lcd_so; + + if (lcp -> lcd_output_window == pr) + return (PACKET_OK); + if (lcp -> lcd_output_window < lcp -> lcd_ssn) { + if (pr < lcp -> lcd_output_window || pr > lcp -> lcd_ssn) { + pk_procerror (RESET, lcp, + "p(r) flow control error", 2); + return (ERROR_PACKET); + } + } + else { + if (pr < lcp -> lcd_output_window && pr > lcp -> lcd_ssn) { + pk_procerror (RESET, lcp, + "p(r) flow control error #2", 2); + return (ERROR_PACKET); + } + } + + lcp -> lcd_output_window = pr; /* Rotate window. */ + if (lcp -> lcd_window_condition == TRUE) + lcp -> lcd_window_condition = FALSE; + + if (so && sb_notify (&(so -> so_snd))) + sowwakeup (so); + + return (PACKET_OK); +} + +/* + * This procedure decodes the X.25 level 3 packet returning a + * code to be used in switchs or arrays. + */ + +pk_decode (xp) +register struct x25_packet *xp; +{ + register int type; + + if (X25GBITS(xp -> bits, fmt_identifier) != 1) + return (INVALID_PACKET); +#ifdef ancient_history + /* + * Make sure that the logical channel group number is 0. + * This restriction may be removed at some later date. + */ + if (xp -> lc_group_number != 0) + return (INVALID_PACKET); +#endif + /* + * Test for data packet first. + */ + if (!(xp -> packet_type & DATA_PACKET_DESIGNATOR)) + return (DATA); + + /* + * Test if flow control packet (RR or RNR). + */ + if (!(xp -> packet_type & RR_OR_RNR_PACKET_DESIGNATOR)) + switch (xp -> packet_type & 0x1f) { + case X25_RR: + return (RR); + case X25_RNR: + return (RNR); + case X25_REJECT: + return (REJECT); + } + + /* + * Determine the rest of the packet types. + */ + switch (xp -> packet_type) { + case X25_CALL: + type = CALL; + break; + + case X25_CALL_ACCEPTED: + type = CALL_ACCEPTED; + break; + + case X25_CLEAR: + type = CLEAR; + break; + + case X25_CLEAR_CONFIRM: + type = CLEAR_CONF; + break; + + case X25_INTERRUPT: + type = INTERRUPT; + break; + + case X25_INTERRUPT_CONFIRM: + type = INTERRUPT_CONF; + break; + + case X25_RESET: + type = RESET; + break; + + case X25_RESET_CONFIRM: + type = RESET_CONF; + break; + + case X25_RESTART: + type = RESTART; + break; + + case X25_RESTART_CONFIRM: + type = RESTART_CONF; + break; + + case X25_DIAGNOSTIC: + type = DIAG_TYPE; + break; + + default: + type = INVALID_PACKET; + } + return (type); +} + +/* + * A restart packet has been received. Print out the reason + * for the restart. + */ + +pk_restartcause (pkp, xp) +struct pkcb *pkp; +register struct x25_packet *xp; +{ + register struct x25config *xcp = pkp -> pk_xcp; + register int lcn = LCN(xp); + + switch (xp -> packet_data) { + case X25_RESTART_LOCAL_PROCEDURE_ERROR: + pk_message (lcn, xcp, "restart: local procedure error"); + break; + + case X25_RESTART_NETWORK_CONGESTION: + pk_message (lcn, xcp, "restart: network congestion"); + break; + + case X25_RESTART_NETWORK_OPERATIONAL: + pk_message (lcn, xcp, "restart: network operational"); + break; + + default: + pk_message (lcn, xcp, "restart: unknown cause"); + } +} + +#define MAXRESETCAUSE 7 + +int Reset_cause[] = { + EXRESET, EXROUT, 0, EXRRPE, 0, EXRLPE, 0, EXRNCG +}; + +/* + * A reset packet has arrived. Return the cause to the user. + */ + +pk_resetcause (pkp, xp) +struct pkcb *pkp; +register struct x25_packet *xp; +{ + register struct pklcd *lcp = + pkp -> pk_chan[LCN(xp)]; + register int code = xp -> packet_data; + + if (code > MAXRESETCAUSE) + code = 7; /* EXRNCG */ + + pk_message (LCN(xp), lcp -> lcd_pkp, "reset code 0x%x, diagnostic 0x%x", + xp -> packet_data, 4[(u_char *)xp]); + + if (lcp -> lcd_so) + lcp -> lcd_so -> so_error = Reset_cause[code]; +} + +#define MAXCLEARCAUSE 25 + +int Clear_cause[] = { + EXCLEAR, EXCBUSY, 0, EXCINV, 0, EXCNCG, 0, + 0, 0, EXCOUT, 0, EXCAB, 0, EXCNOB, 0, 0, 0, EXCRPE, + 0, EXCLPE, 0, 0, 0, 0, 0, EXCRRC +}; + +/* + * A clear packet has arrived. Return the cause to the user. + */ + +pk_clearcause (pkp, xp) +struct pkcb *pkp; +register struct x25_packet *xp; +{ + register struct pklcd *lcp = + pkp -> pk_chan[LCN(xp)]; + register int code = xp -> packet_data; + + if (code > MAXCLEARCAUSE) + code = 5; /* EXRNCG */ + if (lcp -> lcd_so) + lcp -> lcd_so -> so_error = Clear_cause[code]; +} + +char * +format_ntn (xcp) +register struct x25config *xcp; +{ + + return (xcp -> xc_addr.x25_addr); +} + +/* VARARGS1 */ +pk_message (lcn, xcp, fmt, a1, a2, a3, a4, a5, a6) +struct x25config *xcp; +char *fmt; +{ + + if (lcn) + if (!PQEMPTY) + printf ("X.25(%s): lcn %d: ", format_ntn (xcp), lcn); + else + printf ("X.25: lcn %d: ", lcn); + else + if (!PQEMPTY) + printf ("X.25(%s): ", format_ntn (xcp)); + else + printf ("X.25: "); + + printf (fmt, a1, a2, a3, a4, a5, a6); + printf ("\n"); +} + +pk_fragment (lcp, m0, qbit, mbit, wait) +struct mbuf *m0; +register struct pklcd *lcp; +{ + register struct mbuf *m = m0; + register struct x25_packet *xp; + register struct sockbuf *sb; + struct mbuf *head = 0, *next, **mp = &head, *m_split (); + int totlen, psize = 1 << (lcp -> lcd_packetsize); + + if (m == 0) + return 0; + if (m -> m_flags & M_PKTHDR == 0) + panic ("pk_fragment"); + totlen = m -> m_pkthdr.len; + m -> m_act = 0; + sb = lcp -> lcd_so ? &lcp -> lcd_so -> so_snd : & lcp -> lcd_sb; + do { + if (totlen > psize) { + if ((next = m_split (m, psize, wait)) == 0) + goto abort; + totlen -= psize; + } else + next = 0; + M_PREPEND(m, PKHEADERLN, wait); + if (m == 0) + goto abort; + *mp = m; + mp = & m -> m_act; + *mp = 0; + xp = mtod (m, struct x25_packet *); + 0[(char *)xp] = 0; + if (qbit) + X25SBITS(xp -> bits, q_bit, 1); + if (lcp -> lcd_flags & X25_DBIT) + X25SBITS(xp -> bits, d_bit, 1); + X25SBITS(xp -> bits, fmt_identifier, 1); + xp -> packet_type = X25_DATA; + SET_LCN(xp, lcp -> lcd_lcn); + if (next || (mbit && (totlen == psize || + (lcp -> lcd_flags & X25_DBIT)))) + SMBIT(xp, 1); + } while (m = next); + for (m = head; m; m = next) { + next = m -> m_act; + m -> m_act = 0; + sbappendrecord (sb, m); + } + return 0; +abort: + if (wait) + panic ("pk_fragment null mbuf after wait"); + if (next) + m_freem (next); + for (m = head; m; m = next) { + next = m -> m_act; + m_freem (m); + } + return ENOBUFS; +} diff --git a/sys/netccitt/pk_timer.c b/sys/netccitt/pk_timer.c new file mode 100644 index 00000000000..c078e23b652 --- /dev/null +++ b/sys/netccitt/pk_timer.c @@ -0,0 +1,128 @@ +/* $NetBSD: pk_timer.c,v 1.5 1994/06/29 06:37:40 cgd Exp $ */ + +/* + * Copyright (c) Computing Centre, University of British Columbia, 1984 + * Copyright (C) Computer Science Department IV, + * University of Erlangen-Nuremberg, Germany, 1990, 1992 + * Copyright (c) 1990, 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_timer.c 8.1 (Berkeley) 6/10/93 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/protosw.h> +#include <sys/socketvar.h> +#include <sys/errno.h> + +#include <net/if.h> + +#include <netccitt/x25.h> +#include <netccitt/pk.h> +#include <netccitt/pk_var.h> + +/* + * Various timer values. They can be adjusted + * by patching the binary with adb if necessary. + */ +int pk_t20 = 18 * PR_SLOWHZ; /* restart timer */ +int pk_t21 = 20 * PR_SLOWHZ; /* call timer */ +/* XXX pk_t22 is never used */ +int pk_t22 = 18 * PR_SLOWHZ; /* reset timer */ +int pk_t23 = 18 * PR_SLOWHZ; /* clear timer */ + +pk_timer () +{ + register struct pkcb *pkp; + register struct pklcd *lcp, **pp; + register int lcns_jammed, cant_restart; + + FOR_ALL_PKCBS(pkp) { + switch (pkp -> pk_state) { + case DTE_SENT_RESTART: + lcp = pkp -> pk_chan[0]; + /* + * If restart failures are common, a link level + * reset should be initiated here. + */ + if (lcp -> lcd_timer && --lcp -> lcd_timer == 0) { + pk_message (0, pkp -> pk_xcp, + "packet level restart failed"); + pkp -> pk_state = DTE_WAITING; + } + break; + + case DTE_READY: + lcns_jammed = cant_restart = 0; + for (pp = &pkp -> pk_chan[1]; pp <= &pkp -> pk_chan[pkp -> pk_maxlcn]; pp++) { + if ((lcp = *pp) == 0) + continue; + switch (lcp -> lcd_state) { + case SENT_CALL: + if (--lcp -> lcd_timer == 0) { + if (lcp -> lcd_so) + lcp -> lcd_so -> so_error = ETIMEDOUT; + pk_clear (lcp, 49, 1); + } + break; + + case SENT_CLEAR: + if (lcp -> lcd_retry >= 3) + lcns_jammed++; + else + if (--lcp -> lcd_timer == 0) + pk_clear (lcp, 50, 1); + break; + + case DATA_TRANSFER: /* lcn active */ + cant_restart++; + break; + + case LCN_ZOMBIE: /* zombie state */ + pk_freelcd (lcp); + break; + } + } + if (lcns_jammed > pkp -> pk_maxlcn / 2 && cant_restart == 0) { + pk_message (0, pkp -> pk_xcp, "%d lcns jammed: attempting restart", lcns_jammed); + pk_restart (pkp, 0); + } + } + } +} diff --git a/sys/netccitt/pk_usrreq.c b/sys/netccitt/pk_usrreq.c new file mode 100644 index 00000000000..fcf57003437 --- /dev/null +++ b/sys/netccitt/pk_usrreq.c @@ -0,0 +1,603 @@ +/* $NetBSD: pk_usrreq.c,v 1.9 1995/06/13 05:38:58 mycroft 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. 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 + */ + +#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, (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) { + 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; +u_long 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.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) { + 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)); + 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 */ + 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); +} diff --git a/sys/netccitt/pk_var.h b/sys/netccitt/pk_var.h new file mode 100644 index 00000000000..2b87135c0c3 --- /dev/null +++ b/sys/netccitt/pk_var.h @@ -0,0 +1,232 @@ +/* $NetBSD: pk_var.h,v 1.7 1995/06/13 09:07:37 mycroft Exp $ */ + +/* + * Copyright (c) Computing Centre, University of British Columbia, 1985 + * Copyright (C) Computer Science Department IV, + * University of Erlangen-Nuremberg, Germany, 1990, 1991, 1992 + * Copyright (c) 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_var.h 8.1 (Berkeley) 6/10/93 + */ + +/* + * + * X.25 Logical Channel Descriptor + * + */ + +struct pklcd { + struct pklcd_q { + struct pklcd_q *q_forw; /* debugging chain */ + struct pklcd_q *q_back; /* debugging chain */ + } lcd_q; + int (*lcd_upper)(); /* switch to socket vs datagram vs ...*/ + caddr_t lcd_upnext; /* reference for lcd_upper() */ + int (*lcd_send)(); /* if X.25 front end, direct connect */ + caddr_t lcd_downnext; /* reference for lcd_send() */ + short lcd_lcn; /* Logical channel number */ + short lcd_state; /* Logical Channel state */ + short lcd_timer; /* Various timer values */ + short lcd_dg_timer; /* to reclaim idle datagram circuits */ + bool lcd_intrconf_pending; /* Interrupt confirmation pending */ + octet lcd_intrdata; /* Octet of incoming intr data */ + char lcd_retry; /* Timer retry count */ + char lcd_rsn; /* Seq no of last received packet */ + char lcd_ssn; /* Seq no of next packet to send */ + char lcd_output_window; /* Output flow control window */ + char lcd_input_window; /* Input flow control window */ + char lcd_last_transmitted_pr;/* Last Pr value transmitted */ + bool lcd_rnr_condition; /* Remote in busy condition */ + bool lcd_window_condition; /* Output window size exceeded */ + bool lcd_reset_condition; /* True, if waiting reset confirm */ + bool lcd_rxrnr_condition; /* True, if we have sent rnr */ + char lcd_packetsize; /* Maximum packet size */ + char lcd_windowsize; /* Window size - both directions */ + octet lcd_closed_user_group; /* Closed user group specification */ + char lcd_flags; /* copy of sockaddr_x25 op_flags */ + struct mbuf *lcd_facilities; /* user supplied facilities for cr */ + struct mbuf *lcd_template; /* Address of response packet */ + struct socket *lcd_so; /* Socket addr for connection */ + struct sockaddr_x25 *lcd_craddr;/* Calling address pointer */ + struct sockaddr_x25 *lcd_ceaddr;/* Called address pointer */ + time_t lcd_stime; /* time circuit established */ + long lcd_txcnt; /* Data packet transmit count */ + long lcd_rxcnt; /* Data packet receive count */ + short lcd_intrcnt; /* Interrupt packet transmit count */ + struct pklcd *lcd_listen; /* Next lcd on listen queue */ + struct pkcb *lcd_pkp; /* Network this lcd is attached to */ + struct mbuf *lcd_cps; /* Complete Packet Sequence reassembly*/ + long lcd_cpsmax; /* Max length for CPS */ + struct sockaddr_x25 lcd_faddr; /* Remote Address (Calling) */ + struct sockaddr_x25 lcd_laddr; /* Local Address (Called) */ + struct sockbuf lcd_sb; /* alternate for datagram service */ +}; + +/* + * Per network information, allocated dynamically + * when a new network is configured. + */ + +struct pkcb { + struct pkcb_q { + struct pkcb_q *q_forw; + struct pkcb_q *q_backw; + } pk_q; + short pk_state; /* packet level status */ + short pk_maxlcn; /* local copy of xc_maxlcn */ + int (*pk_lloutput) (); /* link level output procedure */ + caddr_t (*pk_llctlinput) (); /* link level ctloutput procedure */ + caddr_t pk_llnext; /* handle for next level down */ + struct x25config *pk_xcp; /* network specific configuration */ + struct x25_ifaddr *pk_ia; /* backpointer to ifaddr */ + struct pklcd **pk_chan; /* actual size == xc_maxlcn+1 */ + short pk_dxerole; /* DXE role of PLE over LLC2 */ + short pk_restartcolls; /* counting RESTART collisions til resolved */ + struct rtentry *pk_rt; /* back pointer to route */ + struct rtentry *pk_llrt; /* pointer to reverse mapping */ + u_short pk_refcount; /* ref count */ +}; + +#define FOR_ALL_PKCBS(p) for((p) = (struct pkcb *)(pkcb_q.q_forw); \ + (pkcb_q.q_forw != &pkcb_q) && ((struct pkcb_q *)(p) != &pkcb_q); \ + (p) = (struct pkcb *)((p) -> pk_q.q_forw)) + +#define PQEMPTY (pkcb_q.q_forw == &pkcb_q) + +/* + * Interface address, x25 version. Exactly one of these structures is + * allocated for each interface with an x25 address. + * + * The ifaddr structure conatins the protocol-independent part + * of the structure, and is assumed to be first. + */ +struct x25_ifaddr { + struct ifaddr ia_ifa; /* protocol-independent info */ +#define ia_ifp ia_ifa.ifa_ifp +#define ia_flags ia_ifa.ifa_flags + struct x25config ia_xc; /* network specific configuration */ + struct pkcb *ia_pkcb; +#define ia_maxlcn ia_xc.xc_maxlcn + int (*ia_start) (); /* connect, confirm method */ + struct sockaddr_x25 ia_dstaddr; /* reserve space for route dst */ +}; + +/* + * ``Link-Level'' extension to Routing Entry for upper level + * packet switching via X.25 virtual circuits. + */ +struct llinfo_x25 { + LIST_ENTRY(llinfo_x25) lx_list; + struct rtentry *lx_rt; /* back pointer to route */ + struct pklcd *lx_lcd; /* local connection block */ + struct x25_ifaddr *lx_ia; /* may not be same as rt_ifa */ + int lx_state; /* can't trust lcd->lcd_state */ + int lx_flags; + int lx_timer; /* for idle timeout */ + int lx_family; /* for dispatch */ +}; + +/* States for lx_state */ +#define LXS_NEWBORN 0 +#define LXS_RESOLVING 1 +#define LXS_FREE 2 +#define LXS_CONNECTING 3 +#define LXS_CONNECTED 4 +#define LXS_DISCONNECTING 5 +#define LXS_LISTENING 6 + +/* flags */ +#define LXF_VALID 0x1 /* Circuit is live, etc. */ +#define LXF_RTHELD 0x2 /* this lcb references rtentry */ +#define LXF_LISTEN 0x4 /* accepting incoming calls */ + +/* + * Definitions for accessing bitfields/bitslices inside X.25 structs + */ + + +struct x25bitslice { + unsigned int bs_mask; + unsigned int bs_shift; +}; + +#define calling_addrlen 0 +#define called_addrlen 1 +#define q_bit 2 +#define d_bit 3 +#define fmt_identifier 4 +#define lc_group_number 1 +#define p_r 5 +#define m_bit 6 +#define p_s 7 +#define zilch 8 + +#define X25GBITS(Arg, Index) (((Arg) & x25_bitslice[(Index)].bs_mask) >> x25_bitslice[(Index)].bs_shift) +#define X25SBITS(Arg, Index, Val) (Arg) |= (((Val) << x25_bitslice[(Index)].bs_shift) & x25_bitslice[(Index)].bs_mask) +#define X25CSBITS(Arg, Index, Val) (Arg) = (((Val) << x25_bitslice[(Index)].bs_shift) & x25_bitslice[(Index)].bs_mask) + +extern struct x25bitslice x25_bitslice[]; + + +#define ISOFIFTTYPE(i,t) ((i)->if_type == (t)) +#define ISISO8802(i) ((ISOFIFTTYPE(i, IFT_ETHER) || \ + ISOFIFTTYPE(i, IFT_ISO88023) || \ + ISOFIFTTYPE(i, IFT_ISO88024) || \ + ISOFIFTTYPE(i, IFT_ISO88025) || \ + ISOFIFTTYPE(i, IFT_ISO88026) || \ + ISOFIFTTYPE(i, IFT_P10) || \ + ISOFIFTTYPE(i, IFT_P80) || \ + ISOFIFTTYPE(i, IFT_FDDI))) + +/* + * miscellenous debugging info + */ +struct mbuf_cache { + int mbc_size; + int mbc_num; + int mbc_oldsize; + struct mbuf **mbc_cache; +}; + +#if defined(_KERNEL) && defined(CCITT) +extern struct pkcb_q pkcb_q; +struct pklcd *pk_listenhead; +struct pklcd *pk_attach(); + +extern char *pk_name[], *pk_state[]; +int pk_t20, pk_t21, pk_t22, pk_t23; +#endif diff --git a/sys/netccitt/x25.h b/sys/netccitt/x25.h new file mode 100644 index 00000000000..b63545d964c --- /dev/null +++ b/sys/netccitt/x25.h @@ -0,0 +1,159 @@ +/* $NetBSD: x25.h,v 1.6 1995/03/26 20:33:46 jtc Exp $ */ + +/* + * Copyright (c) University of British Columbia, 1984 + * Copyright (c) 1990, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * University of Erlangen-Nuremberg, Germany, 1992 + * + * 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. + * + * @(#)x25.h 8.1 (Berkeley) 6/10/93 + */ + +#ifdef _KERNEL +#define PRC_IFUP 3 +#define PRC_LINKUP 4 +#define PRC_LINKDOWN 5 +#define PRC_LINKRESET 6 +#define PRC_LINKDONTCOPY 7 +#ifndef PRC_DISCONNECT_REQUEST +#define PRC_DISCONNECT_REQUEST 10 +#endif +#endif + +#define CCITTPROTO_HDLC 1 +#define CCITTPROTO_X25 2 /* packet level protocol */ +#define IEEEPROTO_802LLC 3 /* doesn't belong here */ + +#define HDLCPROTO_LAP 1 +#define HDLCPROTO_LAPB 2 +#define HDLCPROTO_UNSET 3 +#define HDLCPROTO_LAPD 4 + +/* socket options */ +#define PK_ACCTFILE 1 /* use level = CCITTPROTO_X25 */ +#define PK_FACILITIES 2 /* use level = CCITTPROTO_X25 */ +#define PK_RTATTACH 3 /* use level = CCITTPROTO_X25 */ +#define PK_PRLISTEN 4 /* use level = CCITTPROTO_X25 */ + +#define MAX_FACILITIES 109 /* maximum size for facilities */ + +/* + * X.25 Socket address structure. It contains the X.121 or variation of + * X.121, facilities information, higher level protocol value (first four + * bytes of the User Data field), and the last 12 characters of the User + * Data field. + */ + +struct x25_sockaddr { /* obsolete - use sockaddr_x25 */ + short xaddr_len; /* Length of xaddr_addr. */ + u_char xaddr_addr[15]; /* Network dependent or X.121 address. */ + u_char xaddr_facilities; /* Facilities information. */ +#define XS_REVERSE_CHARGE 0x01 +#define XS_HIPRIO 0x02 + u_char xaddr_proto[4]; /* Protocol ID (4 bytes of user data). */ + u_char xaddr_userdata[12]; /* Remaining User data field. */ +}; + +/* + * X.25 Socket address structure. It contains the network id, X.121 + * address, facilities information, higher level protocol value (first four + * bytes of the User Data field), and up to 12 characters of User Data. + */ + +struct sockaddr_x25 { + u_char x25_len; + u_char x25_family; /* must be AF_CCITT */ + short x25_net; /* network id code (usually a dnic) */ + char x25_addr[16]; /* X.121 address (null terminated) */ + struct x25opts { + char op_flags; /* miscellaneous options */ + /* pk_var.h defines other lcd_flags */ +#define X25_REVERSE_CHARGE 0x01 /* remote DTE pays for call */ +#define X25_DBIT 0x02 /* not yet supported */ +#define X25_MQBIT 0x04 /* prepend M&Q bit status byte to packet data */ +#define X25_OLDSOCKADDR 0x08 /* uses old sockaddr structure */ +#define X25_DG_CIRCUIT 0x10 /* lcd_flag: used for datagrams */ +#define X25_DG_ROUTING 0x20 /* lcd_flag: peer addr not yet known */ +#define X25_MBS_HOLD 0x40 /* lcd_flag: collect m-bit sequences */ + char op_psize; /* requested packet size */ +#define X25_PS128 7 +#define X25_PS256 8 +#define X25_PS512 9 + char op_wsize; /* window size (1 .. 7) */ + char op_speed; /* throughput class */ + } x25_opts; + short x25_udlen; /* user data field length */ + char x25_udata[16]; /* user data field */ +}; + +/* + * network configuration info + * this structure must be 16 bytes long + */ + +struct x25config { + struct sockaddr_x25 xc_addr; + /* link level parameters */ + u_short xc_lproto:4, /* link level protocol eg. CCITTPROTO_HDLC */ + xc_lptype:4, /* protocol type eg. HDLCPROTO_LAPB */ + xc_ltrace:1, /* link level tracing flag */ + xc_lwsize:7; /* link level window size */ + u_short xc_lxidxchg:1, /* link level XID exchange flag - NOT YET */ + /* packet level parameters */ + xc_rsvd1:2, + xc_pwsize:3, /* default window size */ + xc_psize:4, /* default packet size 7=128, 8=256, ... */ + xc_type:3, /* network type */ +#define X25_1976 0 +#define X25_1980 1 +#define X25_1984 2 +#define X25_DDN 3 +#define X25_BASIC 4 + xc_ptrace:1, /* packet level tracing flag */ + xc_nodnic:1, /* remove our dnic when calling on net */ + xc_prepnd0:1; /* prepend 0 when making offnet calls */ + u_short xc_maxlcn; /* max logical channels */ + u_short xc_dg_idletimo; /* timeout for idle datagram circuits. */ +}; + +#ifdef IFNAMSIZ +struct ifreq_x25 { + char ifr_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + struct x25config ifr_xc; +}; +#define SIOCSIFCONF_X25 _IOW('i', 12, struct ifreq_x25) /* set ifnet config */ +#define SIOCGIFCONF_X25 _IOWR('i',13, struct ifreq_x25) /* get ifnet config */ +#endif diff --git a/sys/netccitt/x25acct.h b/sys/netccitt/x25acct.h new file mode 100644 index 00000000000..465df012f6c --- /dev/null +++ b/sys/netccitt/x25acct.h @@ -0,0 +1,72 @@ +/* $NetBSD: x25acct.h,v 1.5 1994/06/29 06:37:46 cgd Exp $ */ + +/* + * Copyright (c) University of British Columbia, 1984 + * Copyright (c) 1990, 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. + * + * 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. + * + * @(#)x25acct.h 8.1 (Berkeley) 6/10/93 + */ + +/* + * Format of X.25 accounting record written + * to X25ACCTF whenever a circuit is closed. + */ + +#ifdef waterloo +#define X25ACCTF "/usr/adm/logs/x25acct" +#else +#define X25ACCTF "/usr/adm/x25acct" +#endif + +struct x25acct { + time_t x25acct_stime; /* start time */ +#ifdef waterloo + u_long x25acct_etime; /* elapsed time (seconds) */ +#else + u_short x25acct_etime; /* elapsed time (seconds) */ +#endif + short x25acct_uid; /* user id */ + short x25acct_net; /* network id */ + u_short x25acct_psize:4, /* packet size */ + x25acct_addrlen:4, /* x25acct_addr length */ + x25acct_revcharge:1, /* reverse charging */ + x25acct_callin:1, /* incoming call */ + x25acct_unused:6; + char x25acct_addr[8]; /* remote DTE address (in bcd) */ + char x25acct_udata[4]; /* protocol id */ + long x25acct_txcnt; /* packets transmitted */ + long x25acct_rxcnt; /* packets received */ +}; diff --git a/sys/netccitt/x25err.h b/sys/netccitt/x25err.h new file mode 100644 index 00000000000..a7da105cd20 --- /dev/null +++ b/sys/netccitt/x25err.h @@ -0,0 +1,66 @@ +/* $NetBSD: x25err.h,v 1.5 1994/06/29 06:37:47 cgd Exp $ */ + +/* + * Copyright (c) University of British Columbia, 1984 + * Copyright (c) 1990, 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. + * + * 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. + * + * @(#)x25err.h 8.1 (Berkeley) 6/10/93 + */ + +/* + * + * X.25 Reset and Clear errors and diagnostics. These values are + * returned in the u_error field of the u structure. + * + */ + +#define EXRESET 100 /* Reset: call reset */ +#define EXROUT 101 /* Reset: out of order */ +#define EXRRPE 102 /* Reset: remote procedure error */ +#define EXRLPE 103 /* Reset: local procedure error */ +#define EXRNCG 104 /* Reset: network congestion */ + +#define EXCLEAR 110 /* Clear: call cleared */ +#define EXCBUSY 111 /* Clear: number busy */ +#define EXCOUT 112 /* Clear: out of order */ +#define EXCRPE 113 /* Clear: remote procedure error */ +#define EXCRRC 114 /* Clear: collect call refused */ +#define EXCINV 115 /* Clear: invalid call */ +#define EXCAB 116 /* Clear: access barred */ +#define EXCLPE 117 /* Clear: local procedure error */ +#define EXCNCG 118 /* Clear: network congestion */ +#define EXCNOB 119 /* Clear: not obtainable */ + |