1 /*
2 * Copyright (C) Dirk Husemann, Computer Science Department IV,
3 * University of Erlangen-Nuremberg, Germany, 1990, 1991, 1992
4 * Copyright (c) 1992, 1993
5 * The Regents of the University of California. 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 8.1 (Berkeley) 06/10/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
llcintr()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 */
llc_input(struct llc_linkcb * linkp,struct mbuf * m,u_char cmdrsp)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
llc_ctlinput(int prc,struct sockaddr * addr,caddr_t info)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