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