/* $OpenBSD: llc_input.c,v 1.6 2004/01/03 14:08:53 espie Exp $ */ /* $NetBSD: llc_input.c,v 1.3 1996/02/13 22:04:44 christos 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. 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * 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() { struct mbuf *m; int i; int frame_kind; u_char cmdrsp; struct llc_linkcb *linkp; struct npaidbentry *sapinfo = NULL; struct sdl_hdr *sdlhdr; struct llc *frame; 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; llrt = rtalloc1((struct sockaddr *)&sdlhdr->sdlhdr_src, 0); if (llrt) 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(m, linkp, 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. */ int llc_input(struct mbuf *m, ...) { int frame_kind; int pollfinal; int action = 0; struct llc *frame; struct llc_linkcb *linkp; u_int cmdrsp; va_list ap; va_start(ap, m); linkp = va_arg(ap, struct llc_linkcb *); cmdrsp = va_arg(ap, u_int); va_end(ap); 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)) != NULL) { m->m_pkthdr.rcvif = (struct ifnet *)linkp->llcl_nlnext; (*linkp->llcl_sapinfo->si_input)(m, NULL, NULL, NULL); } 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. */ void * llc_ctlinput(prc, addr, info) int prc; struct sockaddr *addr; void *info; { struct ifnet *ifp = NULL; 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 = NULL; struct llc_linkcb *linkp = NULL; 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: { struct llc_linkcb *linkp; struct llc_linkcb *nlinkp; 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) != NULL) { 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; }