diff options
Diffstat (limited to 'sys/netccitt/llc_input.c')
-rw-r--r-- | sys/netccitt/llc_input.c | 468 |
1 files changed, 468 insertions, 0 deletions
diff --git a/sys/netccitt/llc_input.c b/sys/netccitt/llc_input.c new file mode 100644 index 0000000..7a01973 --- /dev/null +++ b/sys/netccitt/llc_input.c @@ -0,0 +1,468 @@ +/* + * 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; +} |