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