xref: /csrg-svn/sys/netccitt/llc_input.c (revision 63216)
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