summaryrefslogtreecommitdiff
path: root/sys/netccitt
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
commitd6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch)
treeece253b876159b39c620e62b6c9b1174642e070e /sys/netccitt
initial import of NetBSD tree
Diffstat (limited to 'sys/netccitt')
-rw-r--r--sys/netccitt/README.hdlc52
-rw-r--r--sys/netccitt/README.packet38
-rw-r--r--sys/netccitt/ccitt_proto.c95
-rw-r--r--sys/netccitt/dll.h85
-rw-r--r--sys/netccitt/hd_debug.c214
-rw-r--r--sys/netccitt/hd_input.c671
-rw-r--r--sys/netccitt/hd_output.c249
-rw-r--r--sys/netccitt/hd_subr.c393
-rw-r--r--sys/netccitt/hd_timer.c149
-rw-r--r--sys/netccitt/hd_var.h109
-rw-r--r--sys/netccitt/hdlc.h158
-rw-r--r--sys/netccitt/if_x25subr.c803
-rw-r--r--sys/netccitt/llc_input.c470
-rw-r--r--sys/netccitt/llc_output.c306
-rw-r--r--sys/netccitt/llc_subr.c2360
-rw-r--r--sys/netccitt/llc_timer.c182
-rw-r--r--sys/netccitt/llc_var.h661
-rw-r--r--sys/netccitt/pk.h209
-rw-r--r--sys/netccitt/pk_acct.c147
-rw-r--r--sys/netccitt/pk_debug.c142
-rw-r--r--sys/netccitt/pk_input.c1121
-rw-r--r--sys/netccitt/pk_llcsubr.c371
-rw-r--r--sys/netccitt/pk_output.c218
-rw-r--r--sys/netccitt/pk_subr.c1194
-rw-r--r--sys/netccitt/pk_timer.c128
-rw-r--r--sys/netccitt/pk_usrreq.c603
-rw-r--r--sys/netccitt/pk_var.h232
-rw-r--r--sys/netccitt/x25.h159
-rw-r--r--sys/netccitt/x25acct.h72
-rw-r--r--sys/netccitt/x25err.h66
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 */
+