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