xref: /original-bsd/sys/netccitt/llc_input.c (revision b4971bb3)
1 /*
2  * Copyright (C) Dirk Husemann, Computer Science Department IV,
3  * 		 University of Erlangen-Nuremberg, Germany, 1990, 1991, 1992
4  * Copyright (c) 1992   Regents of the University of California.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Dirk Husemann and the Computer Science Department (IV) of
9  * the University of Erlangen-Nuremberg, Germany.
10  *
11  * %sccs.include.redist.c%
12  *
13  *	@(#)llc_input.c	7.2 (Berkeley) 06/05/93
14  */
15 
16 #include <sys/param.h>
17 #include <sys/systm.h>
18 #include <sys/mbuf.h>
19 #include <sys/domain.h>
20 #include <sys/socket.h>
21 #include <sys/protosw.h>
22 #include <sys/errno.h>
23 #include <sys/time.h>
24 #include <sys/kernel.h>
25 
26 #include <net/if.h>
27 #include <net/if_dl.h>
28 #include <net/if_llc.h>
29 #include <net/route.h>
30 
31 #include <netccitt/dll.h>
32 #include <netccitt/llc_var.h>
33 
34 /*
35  * This module implements LLC as specified by ISO 8802-2.
36  */
37 
38 
39 /*
40  * llcintr() handles all LLC frames (except ISO CLNS ones for the time being)
41  *           and tries to pass them on to the appropriate network layer entity.
42  */
43 void
44 llcintr()
45 {
46 	register struct mbuf *m;
47 	register int i;
48 	register int frame_kind;
49 	register u_char cmdrsp;
50 	struct llc_linkcb *linkp;
51 	struct rtentry *sirt;
52 	struct npaidbentry *sapinfo;
53 	struct sdl_hdr *sdlhdr;
54 	struct llc *frame;
55 	char *c;
56 	long expected_len;
57 
58 	struct ifnet   *ifp;
59 	struct rtentry *llrt;
60 	struct rtentry *nlrt;
61 
62 	for (;;) {
63 		i = splimp();
64 		IF_DEQUEUE(&llcintrq, m);
65 		splx(i);
66 		if (m == 0)
67 			break;
68 #ifdef		DIAGNOSTIC
69 		if ((m->m_flags & M_PKTHDR) == 0)
70 			panic("llcintr no HDR");
71 #endif
72 		/*
73 		 * Get ifp this packet was received on
74 		 */
75 		ifp = m->m_pkthdr.rcvif;
76 
77 		sdlhdr = mtod(m, struct sdl_hdr *);
78 
79 		/*
80 		 * [Copied from net/ip_input.c]
81 		 *
82 		 * Check that the amount of data in the buffers is
83 		 * at least as much as the LLC header tells us.
84 		 * Trim mbufs if longer than expected.
85 		 * Drop packets if shorter than we think they are.
86 		 *
87 		 * Layout of mbuf chain at this point:
88 		 *
89 		 *  +-------------------------------+----+	-\
90                  *  |  sockaddr_dl src - sdlhdr_src | 20 |	  \
91                  *  +-------------------------------+----+	   |
92 		 *  |  sockaddr_dl dst - sdlhdr_dst | 20 |	    > sizeof(struct sdl_hdr) == 44
93 		 *  +-------------------------------+----+	   |
94                  *  |  LLC frame len - sdlhdr_len   | 04 |	  /
95 		 *  +-------------------------------+----+	-/
96 		 * /
97 		 * | m_next
98 		 * \
99                  *  +----------------------------+----+	 -\
100                  *  |  llc DSAP 		 | 01 |	   \
101 		 *  +----------------------------+----+	    |
102                  *  |  llc SSAP 		 | 01 |	    |
103 		 *  +----------------------------+----+	     > sdlhdr_len
104                  *  |  llc control      	 | 01 |	    |
105 		 *  +----------------------------+----+	    |
106 		 *  |  ...                       |    |	   /
107 		 *      			      	 -/
108 		 *
109 		 * Thus the we expect to have exactly
110 		 * (sdlhdr->sdlhdr_len+sizeof(struct sdl_hdr)) in the mbuf chain
111 		 */
112 		expected_len = sdlhdr->sdlhdr_len + sizeof(struct sdl_hdr);
113 
114 		if (m->m_pkthdr.len < expected_len) {
115 			m_freem(m);
116 			continue;
117 		}
118 		if (m->m_pkthdr.len > expected_len) {
119 			if (m->m_len == m->m_pkthdr.len) {
120 				m->m_len = expected_len;
121 				m->m_pkthdr.len = expected_len;
122 			} else
123 				m_adj(m, expected_len - m->m_pkthdr.len);
124 		}
125 
126 		/*
127 		 * Get llc header
128 		 */
129 		if (m->m_len > sizeof(struct sdl_hdr))
130 			frame = mtod((struct mbuf *)((struct sdl_hdr*)(m+1)),
131 			     	     struct llc *);
132 		else frame = mtod(m->m_next, struct llc *);
133 		if (frame == (struct llc *) NULL)
134 			panic("llcintr no llc header");
135 
136 		/*
137 		 * Now check for bogus I/S frame, i.e. those with a control
138 		 * field telling us they're an I/S frame yet their length
139 		 * is less than the established I/S frame length (DSAP + SSAP +
140 		 * control + N(R)&P/F = 4) --- we drop those suckers
141 		 */
142 		if (((frame->llc_control & 0x03) != 0x03)
143 		    && ((expected_len - sizeof(struct sdl_hdr)) < LLC_ISFRAMELEN)) {
144 			m_freem(m);
145 			printf("llc: hurz error\n");
146 			continue;
147 		}
148 
149 		/*
150 		 * Get link control block for the addressed link connection.
151 		 * If there is none we take care of it later on.
152 		 */
153 		cmdrsp = (frame->llc_ssap & 0x01);
154 		frame->llc_ssap &= ~0x01;
155 		if (llrt = rtalloc1((struct sockaddr *)&sdlhdr->sdlhdr_src, 0))
156 			llrt->rt_refcnt--;
157 #ifdef notyet
158 		else llrt = npaidb_enter(&sdlhdr->sdlhdr_src, 0, 0, 0);
159 #endif /* notyet */
160 		else {
161 			/*
162 			 * We cannot do anything currently here as we
163 			 * don't `know' this link --- drop it
164 			 */
165 			m_freem(m);
166 			continue;
167 		}
168 		linkp = ((struct npaidbentry *)(llrt->rt_llinfo))->np_link;
169 		nlrt = ((struct npaidbentry *)(llrt->rt_llinfo))->np_rt;
170 
171 		/*
172 		 * If the link is not existing right now, we can try and look up
173 		 * the SAP info block.
174 		 */
175 		if ((linkp == 0) && frame->llc_ssap)
176 			sapinfo = llc_getsapinfo(frame->llc_dsap, ifp);
177 
178 		/*
179 		 * Handle XID and TEST frames
180 		 * XID:		if DLSAP == 0, return 	type-of-services
181 		 *					window-0
182 		 *					DLSAP-0
183 		 *					format-identifier-?
184 		 * 		if DLSAP != 0, locate sapcb and return
185 		 *					type-of-services
186 		 *					SAP-window
187 		 *					format-identifier-?
188 		 * TEST:	swap (snpah_dst, snpah_src) and return frame
189 		 *
190 		 * Also toggle the CMD/RESP bit
191 		 *
192 		 * Is this behaviour correct? Check ISO 8802-2 (90)!
193 		 */
194 		frame_kind = llc_decode(frame, (struct llc_linkcb *)0);
195 		switch(frame_kind) {
196 		case LLCFT_XID:
197 			if (linkp || sapinfo) {
198 				if (linkp)
199 			   		frame->llc_window = linkp->llcl_window;
200 			   	else frame->llc_window = sapinfo->si_window;
201 			 	frame->llc_fid = 9;			/* XXX */
202 			  	frame->llc_class = sapinfo->si_class;
203 			 	frame->llc_ssap = frame->llc_dsap;
204 			} else {
205 			 	frame->llc_window = 0;
206 			     	frame->llc_fid = 9;
207 				frame->llc_class = 1;
208 				frame->llc_dsap = frame->llc_ssap = 0;
209 			}
210 
211 			/* fall thru to */
212 		case LLCFT_TEST:
213 			sdl_swapaddr(&(mtod(m, struct sdl_hdr *)->sdlhdr_dst),
214 				     &(mtod(m, struct sdl_hdr *)->sdlhdr_src));
215 
216 			/* Now set the CMD/RESP bit */
217 			frame->llc_ssap |= (cmdrsp == 0x0 ? 0x1 : 0x0);
218 
219 			/* Ship it out again */
220 			(*ifp->if_output)(ifp, m,
221 					  (struct sockaddr *) &(mtod(m, struct sdl_hdr *)->sdlhdr_dst),
222 					  (struct rtentry *) 0);
223 			continue;
224 		}
225 
226 		/*
227 		 * Create link control block in case it is not existing
228 		 */
229 		if (linkp == 0 && sapinfo) {
230 			if ((linkp = llc_newlink(&sdlhdr->sdlhdr_src, ifp, nlrt,
231 						     (nlrt == 0) ? 0 : nlrt->rt_llinfo,
232 						     llrt)) == 0) {
233 				printf("llcintr: couldn't create new link\n");
234 				m_freem(m);
235 				continue;
236 			}
237 			((struct npaidbentry *)llrt->rt_llinfo)->np_link = linkp;
238 		} else if (linkp == 0) {
239 			/* The link is not known to us, drop the frame and continue */
240 			m_freem(m);
241 			continue;
242 		}
243 
244 		/*
245 		 * Drop SNPA header and get rid of empty mbuf at the
246 		 * front of the mbuf chain (I don't like 'em)
247 		 */
248 		m_adj(m, sizeof(struct sdl_hdr));
249 		/*
250 		 * LLC_UFRAMELEN is sufficient, m_pullup() will pull up
251 		 * the min(m->m_len, maxprotohdr_len [=40]) thus doing
252 		 * the trick ...
253 		 */
254 		if ((m = m_pullup(m, LLC_UFRAMELEN)))
255 			/*
256 			 * Pass it on thru the elements of procedure
257 			 */
258 			llc_input(linkp, m, cmdrsp);
259 	}
260 	return;
261 }
262 
263 /*
264  * llc_input() --- We deal with the various incoming frames here.
265  *                 Basically we (indirectly) call the appropriate
266  *                 state handler function that's pointed to by
267  *                 llcl_statehandler.
268  *
269  *                 The statehandler returns an action code ---
270  *                 further actions like
271  *                         o notify network layer
272  *                         o block further sending
273  *                         o deblock link
274  *                         o ...
275  *                 are then enacted accordingly.
276  */
277 llc_input(struct llc_linkcb *linkp, struct mbuf *m, u_char cmdrsp)
278 {
279 	int frame_kind;
280 	int pollfinal;
281 	int action = 0;
282 	struct llc *frame;
283 	struct ifnet *ifp = linkp->llcl_if;
284 
285 	if ((frame = mtod(m, struct llc *)) == (struct llc *) 0) {
286 		m_freem(m);
287 		return 0;
288 	}
289 	pollfinal = ((frame->llc_control & 0x03) == 0x03) ?
290 		LLCGBITS(frame->llc_control, u_pf) :
291 			LLCGBITS(frame->llc_control_ext, s_pf);
292 
293 	/*
294 	 * first decode the frame
295 	 */
296 	frame_kind = llc_decode(frame, linkp);
297 
298 	switch (action = llc_statehandler(linkp, frame, frame_kind, cmdrsp,
299 					  pollfinal)) {
300 	case LLC_DATA_INDICATION:
301 		m_adj(m, LLC_ISFRAMELEN);
302 		if (m = m_pullup(m, NLHDRSIZEGUESS)) {
303 			m->m_pkthdr.rcvif = (struct ifnet *)linkp->llcl_nlnext;
304 			(*linkp->llcl_sapinfo->si_input)(m);
305 		}
306 		break;
307 	}
308 
309 	/* release mbuf if not an info frame */
310 	if (action != LLC_DATA_INDICATION && m)
311 		m_freem(m);
312 
313 	/* try to get frames out ... */
314 	llc_start(linkp);
315 
316 	return 0;
317 }
318 
319 /*
320  * This routine is called by configuration setup. It sets up a station control
321  * block and notifies all registered upper level protocols.
322  */
323 caddr_t
324 llc_ctlinput(int prc, struct sockaddr *addr, caddr_t info)
325 {
326 	struct ifnet *ifp;
327 	struct ifaddr *ifa;
328 	struct dll_ctlinfo *ctlinfo = (struct dll_ctlinfo *)info;
329 	u_char sap;
330 	struct dllconfig *config;
331 	caddr_t pcb;
332 	struct rtentry *nlrt;
333 	struct rtentry *llrt;
334 	struct llc_linkcb *linkp;
335 	register int i;
336 
337 	/* info must point to something valid at all times */
338 	if (info == 0)
339 		return 0;
340 
341 	if (prc == PRC_IFUP || prc == PRC_IFDOWN) {
342 		/* we use either this set ... */
343 		ifa = ifa_ifwithaddr(addr);
344 		ifp = ifa ? ifa->ifa_ifp : 0;
345 		if (ifp == 0)
346 			return 0;
347 
348 		sap = ctlinfo->dlcti_lsap;
349 		config = ctlinfo->dlcti_cfg;
350 		pcb = (caddr_t) 0;
351 		nlrt = (struct rtentry *) 0;
352 	} else {
353 		/* or this one */
354 		sap = 0;
355 		config = (struct dllconfig *) 0;
356 		pcb = ctlinfo->dlcti_pcb;
357 		nlrt = ctlinfo->dlcti_rt;
358 
359 		if ((llrt = rtalloc1(nlrt->rt_gateway, 0)))
360 			llrt->rt_refcnt--;
361 		else return 0;
362 
363 		linkp = ((struct npaidbentry *)llrt->rt_llinfo)->np_link;
364 	}
365 
366 	switch (prc) {
367 	case PRC_IFUP:
368 		(void) llc_setsapinfo(ifp, addr->sa_family, sap, config);
369 		return 0;
370 
371 	case PRC_IFDOWN: {
372 		register struct llc_linkcb *linkp;
373 		register struct llc_linkcb *nlinkp;
374 		register int i;
375 
376 		/*
377 		 * All links are accessible over the doubly linked list llccb_q
378 		 */
379 		if (!LQEMPTY) {
380 			/*
381 			 * A for-loop is not that great an idea as the linkp
382 			 * will get deleted by llc_timer()
383 			 */
384 			linkp = LQFIRST;
385 			while (LQVALID(linkp)) {
386 				nlinkp = LQNEXT(linkp);
387 				if (linkp->llcl_if = ifp) {
388 					i = splimp();
389 					(void)llc_statehandler(linkp, (struct llc *)0,
390 							       NL_DISCONNECT_REQUEST,
391 							       0, 1);
392 					splx(i);
393 				}
394 				linkp = nlinkp;
395 			}
396 		}
397 	}
398 
399 	case PRC_CONNECT_REQUEST:
400 		if (linkp == 0) {
401 			if ((linkp = llc_newlink((struct sockaddr_dl *) nlrt->rt_gateway,
402 						 nlrt->rt_ifp, nlrt,
403 						 pcb, llrt)) == 0)
404 				return (0);
405 			((struct npaidbentry *)llrt->rt_llinfo)->np_link = linkp;
406 			i = splimp();
407 			(void)llc_statehandler(linkp, (struct llc *) 0,
408 						NL_CONNECT_REQUEST, 0, 1);
409 			splx(i);
410 		}
411 		return ((caddr_t)linkp);
412 
413 	case PRC_DISCONNECT_REQUEST:
414 		if (linkp == 0)
415 			panic("no link control block!");
416 
417 		i = splimp();
418 		(void)llc_statehandler(linkp, (struct llc *) 0,
419 				       NL_DISCONNECT_REQUEST, 0, 1);
420 		splx(i);
421 
422 		/*
423 		 * The actual removal of the link control block is done by the
424 		 * cleaning neutrum (i.e. llc_timer()).
425 		 */
426 		break;
427 
428 	case PRC_RESET_REQUEST:
429 		if (linkp == 0)
430 			panic("no link control block!");
431 
432 		i = splimp();
433 		(void)llc_statehandler(linkp, (struct llc *) 0,
434 				       NL_RESET_REQUEST, 0, 1);
435 		splx(i);
436 
437 		break;
438 
439 	}
440 
441 	return 0;
442 }
443