mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-18 10:35:55 +00:00
470 lines
13 KiB
C
470 lines
13 KiB
C
/*
|
|
* 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
|
|
* $Id$
|
|
*/
|
|
|
|
#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;
|
|
}
|