157026Ssklower /*
257026Ssklower * Copyright (C) Dirk Husemann, Computer Science Department IV,
357026Ssklower * University of Erlangen-Nuremberg, Germany, 1990, 1991, 1992
4*63216Sbostic * Copyright (c) 1992, 1993
5*63216Sbostic * The Regents of the University of California. All rights reserved.
657026Ssklower *
757026Ssklower * This code is derived from software contributed to Berkeley by
857026Ssklower * Dirk Husemann and the Computer Science Department (IV) of
957026Ssklower * the University of Erlangen-Nuremberg, Germany.
1057026Ssklower *
1157026Ssklower * %sccs.include.redist.c%
1257026Ssklower *
13*63216Sbostic * @(#)llc_input.c 8.1 (Berkeley) 06/10/93
1457026Ssklower */
1557026Ssklower
1657026Ssklower #include <sys/param.h>
1757026Ssklower #include <sys/systm.h>
1857026Ssklower #include <sys/mbuf.h>
1957026Ssklower #include <sys/domain.h>
2057026Ssklower #include <sys/socket.h>
2157026Ssklower #include <sys/protosw.h>
2257026Ssklower #include <sys/errno.h>
2357026Ssklower #include <sys/time.h>
2457026Ssklower #include <sys/kernel.h>
2557026Ssklower
2657026Ssklower #include <net/if.h>
2757026Ssklower #include <net/if_dl.h>
2857026Ssklower #include <net/if_llc.h>
2957026Ssklower #include <net/route.h>
3057026Ssklower
3157026Ssklower #include <netccitt/dll.h>
3257026Ssklower #include <netccitt/llc_var.h>
3357026Ssklower
3457026Ssklower /*
3557026Ssklower * This module implements LLC as specified by ISO 8802-2.
3657026Ssklower */
3757026Ssklower
3857026Ssklower
3957026Ssklower /*
4057026Ssklower * llcintr() handles all LLC frames (except ISO CLNS ones for the time being)
4157026Ssklower * and tries to pass them on to the appropriate network layer entity.
4257026Ssklower */
4357026Ssklower void
llcintr()4457026Ssklower llcintr()
4557026Ssklower {
4657026Ssklower register struct mbuf *m;
4757026Ssklower register int i;
4857026Ssklower register int frame_kind;
4957026Ssklower register u_char cmdrsp;
5057026Ssklower struct llc_linkcb *linkp;
5157026Ssklower struct rtentry *sirt;
5257026Ssklower struct npaidbentry *sapinfo;
5357026Ssklower struct sdl_hdr *sdlhdr;
5457026Ssklower struct llc *frame;
5557026Ssklower char *c;
5657026Ssklower long expected_len;
5757026Ssklower
5857026Ssklower struct ifnet *ifp;
5957026Ssklower struct rtentry *llrt;
6057026Ssklower struct rtentry *nlrt;
6157026Ssklower
6257026Ssklower for (;;) {
6357026Ssklower i = splimp();
6457026Ssklower IF_DEQUEUE(&llcintrq, m);
6557026Ssklower splx(i);
6657026Ssklower if (m == 0)
6757026Ssklower break;
6857026Ssklower #ifdef DIAGNOSTIC
6957026Ssklower if ((m->m_flags & M_PKTHDR) == 0)
7057026Ssklower panic("llcintr no HDR");
7157026Ssklower #endif
7257026Ssklower /*
7357026Ssklower * Get ifp this packet was received on
7457026Ssklower */
7557026Ssklower ifp = m->m_pkthdr.rcvif;
7657026Ssklower
7757026Ssklower sdlhdr = mtod(m, struct sdl_hdr *);
7857026Ssklower
7957026Ssklower /*
8057026Ssklower * [Copied from net/ip_input.c]
8157026Ssklower *
8257026Ssklower * Check that the amount of data in the buffers is
8357026Ssklower * at least as much as the LLC header tells us.
8457026Ssklower * Trim mbufs if longer than expected.
8557026Ssklower * Drop packets if shorter than we think they are.
8657026Ssklower *
8757026Ssklower * Layout of mbuf chain at this point:
8857026Ssklower *
8957026Ssklower * +-------------------------------+----+ -\
9057026Ssklower * | sockaddr_dl src - sdlhdr_src | 20 | \
9157026Ssklower * +-------------------------------+----+ |
9257026Ssklower * | sockaddr_dl dst - sdlhdr_dst | 20 | > sizeof(struct sdl_hdr) == 44
9357026Ssklower * +-------------------------------+----+ |
9457026Ssklower * | LLC frame len - sdlhdr_len | 04 | /
9557026Ssklower * +-------------------------------+----+ -/
9657026Ssklower * /
9757026Ssklower * | m_next
9857026Ssklower * \
9957026Ssklower * +----------------------------+----+ -\
10057026Ssklower * | llc DSAP | 01 | \
10157026Ssklower * +----------------------------+----+ |
10257026Ssklower * | llc SSAP | 01 | |
10357026Ssklower * +----------------------------+----+ > sdlhdr_len
10457026Ssklower * | llc control | 01 | |
10557026Ssklower * +----------------------------+----+ |
10657026Ssklower * | ... | | /
10757026Ssklower * -/
10857026Ssklower *
10957026Ssklower * Thus the we expect to have exactly
11057026Ssklower * (sdlhdr->sdlhdr_len+sizeof(struct sdl_hdr)) in the mbuf chain
11157026Ssklower */
11257026Ssklower expected_len = sdlhdr->sdlhdr_len + sizeof(struct sdl_hdr);
11357026Ssklower
11457026Ssklower if (m->m_pkthdr.len < expected_len) {
11557026Ssklower m_freem(m);
11657026Ssklower continue;
11757026Ssklower }
11857026Ssklower if (m->m_pkthdr.len > expected_len) {
11957026Ssklower if (m->m_len == m->m_pkthdr.len) {
12057026Ssklower m->m_len = expected_len;
12157026Ssklower m->m_pkthdr.len = expected_len;
12257026Ssklower } else
12357026Ssklower m_adj(m, expected_len - m->m_pkthdr.len);
12457026Ssklower }
12557026Ssklower
12657026Ssklower /*
12757026Ssklower * Get llc header
12857026Ssklower */
12957026Ssklower if (m->m_len > sizeof(struct sdl_hdr))
13057026Ssklower frame = mtod((struct mbuf *)((struct sdl_hdr*)(m+1)),
13157026Ssklower struct llc *);
13257026Ssklower else frame = mtod(m->m_next, struct llc *);
13357026Ssklower if (frame == (struct llc *) NULL)
13457026Ssklower panic("llcintr no llc header");
13557026Ssklower
13657026Ssklower /*
13757026Ssklower * Now check for bogus I/S frame, i.e. those with a control
13857026Ssklower * field telling us they're an I/S frame yet their length
13957026Ssklower * is less than the established I/S frame length (DSAP + SSAP +
14057026Ssklower * control + N(R)&P/F = 4) --- we drop those suckers
14157026Ssklower */
14257026Ssklower if (((frame->llc_control & 0x03) != 0x03)
14357026Ssklower && ((expected_len - sizeof(struct sdl_hdr)) < LLC_ISFRAMELEN)) {
14457026Ssklower m_freem(m);
14557026Ssklower printf("llc: hurz error\n");
14657026Ssklower continue;
14757026Ssklower }
14857026Ssklower
14957026Ssklower /*
15057026Ssklower * Get link control block for the addressed link connection.
15157026Ssklower * If there is none we take care of it later on.
15257026Ssklower */
15357026Ssklower cmdrsp = (frame->llc_ssap & 0x01);
15457026Ssklower frame->llc_ssap &= ~0x01;
15561656Ssklower if (llrt = rtalloc1((struct sockaddr *)&sdlhdr->sdlhdr_src, 0))
15657026Ssklower llrt->rt_refcnt--;
15757026Ssklower #ifdef notyet
15857026Ssklower else llrt = npaidb_enter(&sdlhdr->sdlhdr_src, 0, 0, 0);
15957026Ssklower #endif /* notyet */
16057026Ssklower else {
16157026Ssklower /*
16257026Ssklower * We cannot do anything currently here as we
16357026Ssklower * don't `know' this link --- drop it
16457026Ssklower */
16557026Ssklower m_freem(m);
16657026Ssklower continue;
16757026Ssklower }
16857026Ssklower linkp = ((struct npaidbentry *)(llrt->rt_llinfo))->np_link;
16957026Ssklower nlrt = ((struct npaidbentry *)(llrt->rt_llinfo))->np_rt;
17057026Ssklower
17157026Ssklower /*
17257026Ssklower * If the link is not existing right now, we can try and look up
17357026Ssklower * the SAP info block.
17457026Ssklower */
17557026Ssklower if ((linkp == 0) && frame->llc_ssap)
17657026Ssklower sapinfo = llc_getsapinfo(frame->llc_dsap, ifp);
17757026Ssklower
17857026Ssklower /*
17957026Ssklower * Handle XID and TEST frames
18057026Ssklower * XID: if DLSAP == 0, return type-of-services
18157026Ssklower * window-0
18257026Ssklower * DLSAP-0
18357026Ssklower * format-identifier-?
18457026Ssklower * if DLSAP != 0, locate sapcb and return
18557026Ssklower * type-of-services
18657026Ssklower * SAP-window
18757026Ssklower * format-identifier-?
18857026Ssklower * TEST: swap (snpah_dst, snpah_src) and return frame
18957026Ssklower *
19057026Ssklower * Also toggle the CMD/RESP bit
19157026Ssklower *
19257026Ssklower * Is this behaviour correct? Check ISO 8802-2 (90)!
19357026Ssklower */
19457026Ssklower frame_kind = llc_decode(frame, (struct llc_linkcb *)0);
19557026Ssklower switch(frame_kind) {
19657026Ssklower case LLCFT_XID:
19757026Ssklower if (linkp || sapinfo) {
19857026Ssklower if (linkp)
19957026Ssklower frame->llc_window = linkp->llcl_window;
20057026Ssklower else frame->llc_window = sapinfo->si_window;
20157026Ssklower frame->llc_fid = 9; /* XXX */
20257026Ssklower frame->llc_class = sapinfo->si_class;
20357026Ssklower frame->llc_ssap = frame->llc_dsap;
20457026Ssklower } else {
20557026Ssklower frame->llc_window = 0;
20657026Ssklower frame->llc_fid = 9;
20757026Ssklower frame->llc_class = 1;
20857026Ssklower frame->llc_dsap = frame->llc_ssap = 0;
20957026Ssklower }
21057026Ssklower
21157026Ssklower /* fall thru to */
21257026Ssklower case LLCFT_TEST:
21357026Ssklower sdl_swapaddr(&(mtod(m, struct sdl_hdr *)->sdlhdr_dst),
21457026Ssklower &(mtod(m, struct sdl_hdr *)->sdlhdr_src));
21557026Ssklower
21657026Ssklower /* Now set the CMD/RESP bit */
21757026Ssklower frame->llc_ssap |= (cmdrsp == 0x0 ? 0x1 : 0x0);
21857026Ssklower
21957026Ssklower /* Ship it out again */
22057026Ssklower (*ifp->if_output)(ifp, m,
22157026Ssklower (struct sockaddr *) &(mtod(m, struct sdl_hdr *)->sdlhdr_dst),
22257026Ssklower (struct rtentry *) 0);
22357026Ssklower continue;
22457026Ssklower }
22557026Ssklower
22657026Ssklower /*
22757026Ssklower * Create link control block in case it is not existing
22857026Ssklower */
22957026Ssklower if (linkp == 0 && sapinfo) {
23057026Ssklower if ((linkp = llc_newlink(&sdlhdr->sdlhdr_src, ifp, nlrt,
23157026Ssklower (nlrt == 0) ? 0 : nlrt->rt_llinfo,
23257026Ssklower llrt)) == 0) {
23357026Ssklower printf("llcintr: couldn't create new link\n");
23457026Ssklower m_freem(m);
23557026Ssklower continue;
23657026Ssklower }
23757026Ssklower ((struct npaidbentry *)llrt->rt_llinfo)->np_link = linkp;
23857026Ssklower } else if (linkp == 0) {
23957026Ssklower /* The link is not known to us, drop the frame and continue */
24057026Ssklower m_freem(m);
24157026Ssklower continue;
24257026Ssklower }
24357026Ssklower
24457026Ssklower /*
24557026Ssklower * Drop SNPA header and get rid of empty mbuf at the
24657026Ssklower * front of the mbuf chain (I don't like 'em)
24757026Ssklower */
24857026Ssklower m_adj(m, sizeof(struct sdl_hdr));
24957026Ssklower /*
25057026Ssklower * LLC_UFRAMELEN is sufficient, m_pullup() will pull up
25157026Ssklower * the min(m->m_len, maxprotohdr_len [=40]) thus doing
25257026Ssklower * the trick ...
25357026Ssklower */
25457026Ssklower if ((m = m_pullup(m, LLC_UFRAMELEN)))
25557026Ssklower /*
25657026Ssklower * Pass it on thru the elements of procedure
25757026Ssklower */
25857026Ssklower llc_input(linkp, m, cmdrsp);
25957026Ssklower }
26057026Ssklower return;
26157026Ssklower }
26257026Ssklower
26357026Ssklower /*
26457026Ssklower * llc_input() --- We deal with the various incoming frames here.
26557026Ssklower * Basically we (indirectly) call the appropriate
26657026Ssklower * state handler function that's pointed to by
26757026Ssklower * llcl_statehandler.
26857026Ssklower *
26957026Ssklower * The statehandler returns an action code ---
27057026Ssklower * further actions like
27157026Ssklower * o notify network layer
27257026Ssklower * o block further sending
27357026Ssklower * o deblock link
27457026Ssklower * o ...
27557026Ssklower * are then enacted accordingly.
27657026Ssklower */
llc_input(struct llc_linkcb * linkp,struct mbuf * m,u_char cmdrsp)27757026Ssklower llc_input(struct llc_linkcb *linkp, struct mbuf *m, u_char cmdrsp)
27857026Ssklower {
27957026Ssklower int frame_kind;
28057026Ssklower int pollfinal;
28157026Ssklower int action = 0;
28257026Ssklower struct llc *frame;
28357026Ssklower struct ifnet *ifp = linkp->llcl_if;
28457026Ssklower
28557026Ssklower if ((frame = mtod(m, struct llc *)) == (struct llc *) 0) {
28657026Ssklower m_freem(m);
28757026Ssklower return 0;
28857026Ssklower }
28957026Ssklower pollfinal = ((frame->llc_control & 0x03) == 0x03) ?
29057026Ssklower LLCGBITS(frame->llc_control, u_pf) :
29157026Ssklower LLCGBITS(frame->llc_control_ext, s_pf);
29257026Ssklower
29357026Ssklower /*
29457026Ssklower * first decode the frame
29557026Ssklower */
29657026Ssklower frame_kind = llc_decode(frame, linkp);
29757026Ssklower
29857026Ssklower switch (action = llc_statehandler(linkp, frame, frame_kind, cmdrsp,
29957026Ssklower pollfinal)) {
30057026Ssklower case LLC_DATA_INDICATION:
30157026Ssklower m_adj(m, LLC_ISFRAMELEN);
30257026Ssklower if (m = m_pullup(m, NLHDRSIZEGUESS)) {
30357026Ssklower m->m_pkthdr.rcvif = (struct ifnet *)linkp->llcl_nlnext;
30457026Ssklower (*linkp->llcl_sapinfo->si_input)(m);
30557026Ssklower }
30657026Ssklower break;
30757026Ssklower }
30857026Ssklower
30957026Ssklower /* release mbuf if not an info frame */
31057026Ssklower if (action != LLC_DATA_INDICATION && m)
31157026Ssklower m_freem(m);
31257026Ssklower
31357026Ssklower /* try to get frames out ... */
31457026Ssklower llc_start(linkp);
31557026Ssklower
31657026Ssklower return 0;
31757026Ssklower }
31857026Ssklower
31957026Ssklower /*
32057026Ssklower * This routine is called by configuration setup. It sets up a station control
32157026Ssklower * block and notifies all registered upper level protocols.
32257026Ssklower */
32357026Ssklower caddr_t
llc_ctlinput(int prc,struct sockaddr * addr,caddr_t info)32457026Ssklower llc_ctlinput(int prc, struct sockaddr *addr, caddr_t info)
32557026Ssklower {
32657026Ssklower struct ifnet *ifp;
32757026Ssklower struct ifaddr *ifa;
32857026Ssklower struct dll_ctlinfo *ctlinfo = (struct dll_ctlinfo *)info;
32957026Ssklower u_char sap;
33057026Ssklower struct dllconfig *config;
33157026Ssklower caddr_t pcb;
33257026Ssklower struct rtentry *nlrt;
33357026Ssklower struct rtentry *llrt;
33457026Ssklower struct llc_linkcb *linkp;
33557026Ssklower register int i;
33657026Ssklower
33757026Ssklower /* info must point to something valid at all times */
33857026Ssklower if (info == 0)
33957026Ssklower return 0;
34057026Ssklower
34157026Ssklower if (prc == PRC_IFUP || prc == PRC_IFDOWN) {
34257026Ssklower /* we use either this set ... */
34357026Ssklower ifa = ifa_ifwithaddr(addr);
34457026Ssklower ifp = ifa ? ifa->ifa_ifp : 0;
34557026Ssklower if (ifp == 0)
34657026Ssklower return 0;
34757026Ssklower
34857026Ssklower sap = ctlinfo->dlcti_lsap;
34957026Ssklower config = ctlinfo->dlcti_cfg;
35057026Ssklower pcb = (caddr_t) 0;
35157026Ssklower nlrt = (struct rtentry *) 0;
35257026Ssklower } else {
35357026Ssklower /* or this one */
35457026Ssklower sap = 0;
35557026Ssklower config = (struct dllconfig *) 0;
35657026Ssklower pcb = ctlinfo->dlcti_pcb;
35757026Ssklower nlrt = ctlinfo->dlcti_rt;
35857026Ssklower
35957026Ssklower if ((llrt = rtalloc1(nlrt->rt_gateway, 0)))
36057026Ssklower llrt->rt_refcnt--;
36157026Ssklower else return 0;
36257026Ssklower
36357026Ssklower linkp = ((struct npaidbentry *)llrt->rt_llinfo)->np_link;
36457026Ssklower }
36557026Ssklower
36657026Ssklower switch (prc) {
36757026Ssklower case PRC_IFUP:
36857026Ssklower (void) llc_setsapinfo(ifp, addr->sa_family, sap, config);
36957026Ssklower return 0;
37057026Ssklower
37157026Ssklower case PRC_IFDOWN: {
37257026Ssklower register struct llc_linkcb *linkp;
37357026Ssklower register struct llc_linkcb *nlinkp;
37457026Ssklower register int i;
37557026Ssklower
37657026Ssklower /*
37757026Ssklower * All links are accessible over the doubly linked list llccb_q
37857026Ssklower */
37957026Ssklower if (!LQEMPTY) {
38057026Ssklower /*
38157026Ssklower * A for-loop is not that great an idea as the linkp
38257026Ssklower * will get deleted by llc_timer()
38357026Ssklower */
38457026Ssklower linkp = LQFIRST;
38557026Ssklower while (LQVALID(linkp)) {
38657026Ssklower nlinkp = LQNEXT(linkp);
38757026Ssklower if (linkp->llcl_if = ifp) {
38857026Ssklower i = splimp();
38957026Ssklower (void)llc_statehandler(linkp, (struct llc *)0,
39057026Ssklower NL_DISCONNECT_REQUEST,
39157026Ssklower 0, 1);
39257026Ssklower splx(i);
39357026Ssklower }
39457026Ssklower linkp = nlinkp;
39557026Ssklower }
39657026Ssklower }
39757026Ssklower }
39857026Ssklower
39957026Ssklower case PRC_CONNECT_REQUEST:
40057026Ssklower if (linkp == 0) {
40157026Ssklower if ((linkp = llc_newlink((struct sockaddr_dl *) nlrt->rt_gateway,
40257026Ssklower nlrt->rt_ifp, nlrt,
40357026Ssklower pcb, llrt)) == 0)
40457026Ssklower return (0);
40557026Ssklower ((struct npaidbentry *)llrt->rt_llinfo)->np_link = linkp;
40657026Ssklower i = splimp();
40757026Ssklower (void)llc_statehandler(linkp, (struct llc *) 0,
40857026Ssklower NL_CONNECT_REQUEST, 0, 1);
40957026Ssklower splx(i);
41057026Ssklower }
41157026Ssklower return ((caddr_t)linkp);
41257026Ssklower
41357026Ssklower case PRC_DISCONNECT_REQUEST:
41457026Ssklower if (linkp == 0)
41557026Ssklower panic("no link control block!");
41657026Ssklower
41757026Ssklower i = splimp();
41857026Ssklower (void)llc_statehandler(linkp, (struct llc *) 0,
41957026Ssklower NL_DISCONNECT_REQUEST, 0, 1);
42057026Ssklower splx(i);
42157026Ssklower
42257026Ssklower /*
42357026Ssklower * The actual removal of the link control block is done by the
42457026Ssklower * cleaning neutrum (i.e. llc_timer()).
42557026Ssklower */
42657026Ssklower break;
42757026Ssklower
42857026Ssklower case PRC_RESET_REQUEST:
42957026Ssklower if (linkp == 0)
43057026Ssklower panic("no link control block!");
43157026Ssklower
43257026Ssklower i = splimp();
43357026Ssklower (void)llc_statehandler(linkp, (struct llc *) 0,
43457026Ssklower NL_RESET_REQUEST, 0, 1);
43557026Ssklower splx(i);
43657026Ssklower
43757026Ssklower break;
43857026Ssklower
43957026Ssklower }
44057026Ssklower
44157026Ssklower return 0;
44257026Ssklower }
443