/* $NetBSD: llc_subr.c,v 1.19 2004/04/22 01:01:40 matt Exp $ */ /* * 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. 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 */ /* * Copyright (c) 1990, 1991, 1992 * Dirk Husemann, Computer Science Department IV, * University of Erlangen-Nuremberg, Germany. * * 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 __KERNEL_RCSID(0, "$NetBSD: llc_subr.c,v 1.19 2004/04/22 01:01:40 matt Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * 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 */ int sdl_cmp(sdl_a, sdl_b) 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 */ void sdl_copy(sdl_f, sdl_t) 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 */ void sdl_swapaddr(sdl_a, sdl_b) 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(ifp) struct ifnet *ifp; { 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 */ int sdl_checkaddrif(ifp, sdl_c) struct ifnet *ifp; struct sockaddr_dl *sdl_c; { 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 */ int sdl_setaddrif(ifp, mac_addr, dlsap_addr, mac_len, sdl_to) struct ifnet *ifp; u_char *mac_addr; u_char dlsap_addr; u_char mac_len; struct sockaddr_dl *sdl_to; { 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 */ int sdl_sethdrif(ifp, mac_src, dlsap_src, mac_dst, dlsap_dst, mac_len, sdlhdr_to) 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(ifp, af, sap, llconf) struct ifnet *ifp; u_char af; u_char sap; struct dllconfig* llconf; { const 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|M_ZERO); sapinfo = (struct npaidbentry *) sirt->rt_llinfo; if (sapinfo) { /* * 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 = pp->pr_ctlinput; return (sapinfo); } return 0; } /* * Get sapinfo for SAP address and interface */ struct npaidbentry * llc_getsapinfo(sap, ifp) u_char sap; struct ifnet *ifp; { struct sockaddr_dl *ifdl_addr; struct sockaddr_dl sdl_addr; struct rtentry *sirt; u_char saploc; USES_AF_LINK_RTS; ifdl_addr = sdl_getaddrif(ifp); sdl_copy(ifdl_addr, &sdl_addr); saploc = LLSAPLOC(&sdl_addr, ifp); sdl_addr.sdl_data[saploc] = sap; sdl_addr.sdl_alen++; if ((sirt = rtalloc1((struct sockaddr *) & sdl_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(linkp, seqn) struct llc_linkcb *linkp; short seqn; { int 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]. * * [Preceding 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(linkp, frame, frame_kind, cmdrsp, pollfinal) 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(linkp, frame, frame_kind, cmdrsp, pollfinal) 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(linkp, frame, frame_kind, cmdrsp, pollfinal) 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(linkp, frame, frame_kind, cmdrsp, pollfinal) 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(linkp, frame, frame_kind, cmdrsp, pollfinal) 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(linkp, frame, frame_kind, cmdrsp, pollfinal) 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(linkp, frame, frame_kind, cmdrsp, pollfinal) 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(linkp, frame, frame_kind, cmdrsp, pollfinal) 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(linkp, frame, frame_kind, cmdrsp, pollfinal) 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(linkp, frame, frame_kind, cmdrsp, pollfinal) 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:{ int p = LLC_GETFLAG(linkp,P); 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:{ int p = LLC_GETFLAG(linkp,P); 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:{ int p = LLC_GETFLAG(linkp,P); 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:{ int p = LLC_GETFLAG(linkp,P); 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:{ int p = LLC_GETFLAG(linkp,P); 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(linkp, frame, frame_kind, cmdrsp, pollfinal) 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:{ int p = LLC_GETFLAG(linkp,P); 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:{ int p = LLC_GETFLAG(linkp,P); 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:{ int p = LLC_GETFLAG(linkp,P); 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:{ #if 0 int p = LLC_GETFLAG(linkp,P); #endif 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:{ int p = LLC_GETFLAG(linkp,P); 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(linkp, frame, frame_kind, cmdrsp, pollfinal) 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:{ int p = LLC_GETFLAG(linkp,P); 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:{ int p = LLC_GETFLAG(linkp,P); 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:{ int p = LLC_GETFLAG(linkp,P); 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:{ int p = LLC_GETFLAG(linkp,P); 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:{ int p = LLC_GETFLAG(linkp,P); 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(linkp, frame, frame_kind, cmdrsp, pollfinal) 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:{ #if 0 int p = LLC_GETFLAG(linkp,P); #endif 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:{ #if 0 int p = LLC_GETFLAG(linkp,P); #endif 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:{ #if 0 int p = LLC_GETFLAG(linkp,P); #endif 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:{ #if 0 int p = LLC_GETFLAG(linkp,P); #endif 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(linkp, frame, frame_kind, cmdrsp, pollfinal) 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:{ #if 0 int p = LLC_GETFLAG(linkp,P); #endif 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:{ #if 0 int p = LLC_GETFLAG(linkp,P); #endif 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:{ #if 0 int p = LLC_GETFLAG(linkp,P); #endif 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:{ #if 0 int p = LLC_GETFLAG(linkp,P); #endif 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 acknowledgement 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(linkp, frame, frame_kind, cmdrsp, pollfinal) 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:{ 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:{ 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:{ 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:{ 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(linkp, frame, frame_kind, cmdrsp, pollfinal) struct llc_linkcb *linkp; struct llc *frame; int frame_kind; int cmdrsp; int pollfinal; { 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. */ void 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(linkp) struct llc_linkcb *linkp; { struct mbuf *mptr = (struct mbuf *) 0; struct mbuf *anchor = (struct mbuf *) 0; 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(dst, ifp, nlrt, nlnext, llrt) 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_NOWAIT|M_ZERO); if (nlinkp == 0) return (NULL); /* 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_NOWAIT|M_ZERO); if (nlinkp->llcl_output_buffers == 0) { FREE(nlinkp, M_PCB); return (NULL); } /* 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 */ void llc_dellink(linkp) struct llc_linkcb *linkp; { struct mbuf *m; struct mbuf *n; struct npaidbentry *sapinfo = linkp->llcl_sapinfo; int 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_nextpkt; 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); } int llc_decode(frame, linkp) struct llc *frame; struct llc_linkcb *linkp; { 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(linkp) struct llc_linkcb *linkp; { 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 __STRING(s) char *timer_names[] = {"ACK", "P", "BUSY", "REJ", "AGE"}; char * llc_getstatename(linkp) 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(linkp, message) struct llc_linkcb *linkp; const char *message; { int i; /* print interface */ printf("if %s\n", linkp->llcl_if->if_xname); /* 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,F), 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(linkp, level, message) struct llc_linkcb *linkp; int level; const char *message; { if (linkp->llcl_sapinfo->si_trace && level > llc_tracelevel) llc_link_dump(linkp, message); }