1 /*********************************************************** 2 Copyright IBM Corporation 1987 3 4 All Rights Reserved 5 6 Permission to use, copy, modify, and distribute this software and its 7 documentation for any purpose and without fee is hereby granted, 8 provided that the above copyright notice appear in all copies and that 9 both that copyright notice and this permission notice appear in 10 supporting documentation, and that the name of IBM not be 11 used in advertising or publicity pertaining to distribution of the 12 software without specific, written prior permission. 13 14 IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 15 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 16 IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 17 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 18 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 19 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 20 SOFTWARE. 21 22 ******************************************************************/ 23 24 /* 25 * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison 26 */ 27 /* 28 * $Header: iso_pcb.c,v 4.5 88/06/29 14:59:56 hagens Exp $ 29 * $Source: /usr/argo/sys/netiso/RCS/iso_pcb.c,v $ 30 * @(#)iso_pcb.c 7.8 (Berkeley) 04/26/91 31 * 32 * Iso address family net-layer(s) pcb stuff. NEH 1/29/87 33 */ 34 35 #ifndef lint 36 static char *rcsid = "$Header: iso_pcb.c,v 4.5 88/06/29 14:59:56 hagens Exp $"; 37 #endif 38 39 #ifdef ISO 40 41 #include "param.h" 42 #include "systm.h" 43 #include "user.h" 44 #include "mbuf.h" 45 #include "socket.h" 46 #include "socketvar.h" 47 #include "argo_debug.h" 48 #include "iso.h" 49 #include "clnp.h" 50 #include "../netinet/in_systm.h" 51 #include "../net/if.h" 52 #include "../net/route.h" 53 #include "iso_pcb.h" 54 #include "iso_var.h" 55 #include "protosw.h" 56 57 #ifdef TPCONS 58 #include "../netccitt/x25.h" 59 #include "../netccitt/pk.h" 60 #include "../netccitt/pk_var.h" 61 #endif 62 63 #define PCBNULL (struct isopcb *)0 64 struct iso_addr zeroiso_addr = { 65 0 66 }; 67 68 69 /* 70 * FUNCTION: iso_pcballoc 71 * 72 * PURPOSE: creates an isopcb structure in an mbuf, 73 * with socket (so), and 74 * puts it in the queue with head (head) 75 * 76 * RETURNS: 0 if OK, ENOBUFS if can't alloc the necessary mbuf 77 */ 78 int 79 iso_pcballoc(so, head) 80 struct socket *so; 81 struct isopcb *head; 82 { 83 register struct isopcb *isop; 84 85 IFDEBUG(D_ISO) 86 printf("iso_pcballoc(so 0x%x)\n", so); 87 ENDDEBUG 88 MALLOC(isop, struct isopcb *, sizeof(*isop), M_PCB, M_NOWAIT); 89 if (isop == NULL) 90 return ENOBUFS; 91 bzero((caddr_t)isop, sizeof(*isop)); 92 isop->isop_head = head; 93 isop->isop_socket = so; 94 insque(isop, head); 95 if (so) 96 so->so_pcb = (caddr_t)isop; 97 return 0; 98 } 99 100 /* 101 * FUNCTION: iso_pcbbind 102 * 103 * PURPOSE: binds the address given in *(nam) to the socket 104 * specified by the isopcb in *(isop) 105 * If the given address is zero, it makes sure the 106 * address isn't already in use and if it's got a network 107 * portion, we look for an interface with that network 108 * address. If the address given is zero, we allocate 109 * a port and stuff it in the (nam) structure. 110 * 111 * RETURNS: errno E* or 0 if ok. 112 * 113 * SIDE EFFECTS: increments head->isop_lport if it allocates a port # 114 * 115 * NOTES: 116 */ 117 #define satosiso(sa) ((struct sockaddr_iso *)(sa)) 118 int 119 iso_pcbbind(isop, nam) 120 register struct isopcb *isop; 121 struct mbuf *nam; 122 { 123 register struct isopcb *head = isop->isop_head; 124 register struct sockaddr_iso *siso; 125 struct iso_ifaddr *ia; 126 union { 127 char data[2]; 128 u_short s; 129 } suf; 130 131 IFDEBUG(D_ISO) 132 printf("iso_pcbbind(isop 0x%x, nam 0x%x)\n", isop, nam); 133 ENDDEBUG 134 suf.s = 0; 135 if (iso_ifaddr == 0) /* any interfaces attached? */ 136 return EADDRNOTAVAIL; 137 if (isop->isop_laddr) /* already bound */ 138 return EADDRINUSE; 139 if(nam == (struct mbuf *)0) { 140 isop->isop_laddr = &isop->isop_sladdr; 141 isop->isop_sladdr.siso_len = sizeof(struct sockaddr_iso); 142 isop->isop_sladdr.siso_family = AF_ISO; 143 isop->isop_sladdr.siso_tlen = 2; 144 isop->isop_sladdr.siso_nlen = 0; 145 isop->isop_sladdr.siso_slen = 0; 146 isop->isop_sladdr.siso_plen = 0; 147 goto noname; 148 } 149 siso = mtod(nam, struct sockaddr_iso *); 150 IFDEBUG(D_ISO) 151 printf("iso_pcbbind(name len 0x%x)\n", nam->m_len); 152 printf("The address is %s\n", clnp_iso_addrp(&siso->siso_addr)); 153 ENDDEBUG 154 /* 155 * We would like sort of length check but since some OSI addrs 156 * do not have fixed length, we can't really do much. 157 * The ONLY thing we can say is that an osi addr has to have 158 * at LEAST an afi and one more byte and had better fit into 159 * a struct iso_addr. 160 * However, in fact the size of the whole thing is a struct 161 * sockaddr_iso, so probably this is what we should check for. 162 */ 163 if( (nam->m_len < 2) || (nam->m_len < siso->siso_len)) { 164 return ENAMETOOLONG; 165 } 166 if (siso->siso_tlen) { 167 register char *cp = TSEL(siso); 168 suf.data[0] = cp[0]; 169 suf.data[1] = cp[1]; 170 } 171 if (siso->siso_nlen) { 172 /* non-zero net addr- better match one of our interfaces */ 173 IFDEBUG(D_ISO) 174 printf("iso_pcbbind: bind to NOT zeroisoaddr\n"); 175 ENDDEBUG 176 for (ia = iso_ifaddr; ia; ia = ia->ia_next) 177 if (SAME_ISOADDR(siso, &ia->ia_addr)) 178 break; 179 if (ia == 0) 180 return EADDRNOTAVAIL; 181 } 182 if (siso->siso_len <= sizeof (isop->isop_sladdr)) { 183 isop->isop_laddr = &isop->isop_sladdr; 184 } else { 185 if ((nam = m_copy(nam, 0, (int)M_COPYALL)) == 0) 186 return ENOBUFS; 187 isop->isop_laddr = mtod(nam, struct sockaddr_iso *); 188 } 189 bcopy((caddr_t)siso, (caddr_t)isop->isop_laddr, siso->siso_len); 190 if (suf.s || siso->siso_tlen != 2) { 191 if((suf.s < ISO_PORT_RESERVED) && (siso->siso_tlen <= 2) && 192 (u.u_uid != 0)) 193 return EACCES; 194 if ((isop->isop_socket->so_options & SO_REUSEADDR) == 0 && 195 iso_pcblookup(head, 0, (caddr_t)0, isop->isop_laddr)) 196 return EADDRINUSE; 197 } else { 198 register char *cp; 199 noname: 200 cp = TSEL(isop->isop_laddr); 201 IFDEBUG(D_ISO) 202 printf("iso_pcbbind noname\n"); 203 ENDDEBUG 204 do { 205 if (head->isop_lport++ < ISO_PORT_RESERVED || 206 head->isop_lport > ISO_PORT_USERRESERVED) 207 head->isop_lport = ISO_PORT_RESERVED; 208 suf.s = head->isop_lport; 209 cp[0] = suf.data[0]; 210 cp[1] = suf.data[1]; 211 } while (iso_pcblookup(head, 0, (caddr_t)0, isop->isop_laddr)); 212 } 213 IFDEBUG(D_ISO) 214 printf("iso_pcbbind returns 0, suf 0x%x\n", suf); 215 ENDDEBUG 216 return 0; 217 } 218 /* 219 * FUNCTION: iso_pcbconnect 220 * 221 * PURPOSE: Make the isopcb (isop) look like it's connected. 222 * In other words, give it the peer address given in 223 * the mbuf * (nam). Make sure such a combination 224 * of local, peer addresses doesn't already exist 225 * for this protocol. Internet mentality prevails here, 226 * wherein a src,dst pair uniquely identifies a connection. 227 * Both net address and port must be specified in argument 228 * (nam). 229 * If we don't have a local address for this socket yet, 230 * we pick one by calling iso_pcbbind(). 231 * 232 * RETURNS: errno E* or 0 if ok. 233 * 234 * SIDE EFFECTS: Looks up a route, which may cause one to be left 235 * in the isopcb. 236 * 237 * NOTES: 238 */ 239 int 240 iso_pcbconnect(isop, nam) 241 register struct isopcb *isop; 242 struct mbuf *nam; 243 { 244 register struct sockaddr_iso *siso = mtod(nam, struct sockaddr_iso *); 245 int local_zero, error = 0; 246 struct iso_ifaddr *ia; 247 248 IFDEBUG(D_ISO) 249 printf("iso_pcbconnect(isop 0x%x sock 0x%x nam 0x%x", 250 isop, isop->isop_socket, nam); 251 printf("nam->m_len 0x%x), addr:\n", nam->m_len); 252 dump_isoaddr(siso); 253 ENDDEBUG 254 if (nam->m_len < siso->siso_len) 255 return EINVAL; 256 if (siso->siso_family != AF_ISO) 257 return EAFNOSUPPORT; 258 if (siso->siso_nlen == 0) { 259 if (ia = iso_ifaddr) { 260 int nlen = ia->ia_addr.siso_nlen; 261 ovbcopy(TSEL(siso), nlen + TSEL(siso), 262 siso->siso_plen + siso->siso_tlen + siso->siso_slen); 263 bcopy((caddr_t)&ia->ia_addr.siso_addr, 264 (caddr_t)&siso->siso_addr, nlen + 1); 265 /* includes siso->siso_nlen = nlen; */ 266 } else 267 return EADDRNOTAVAIL; 268 } 269 /* 270 * Local zero means either not bound, or bound to a TSEL, but no 271 * particular local interface. So, if we want to send somebody 272 * we need to choose a return address. 273 */ 274 local_zero = 275 ((isop->isop_laddr == 0) || (isop->isop_laddr->siso_nlen == 0)); 276 if (local_zero) { 277 int flags; 278 279 IFDEBUG(D_ISO) 280 printf("iso_pcbconnect localzero 1\n"); 281 ENDDEBUG 282 /* 283 * If route is known or can be allocated now, 284 * our src addr is taken from the i/f, else punt. 285 */ 286 flags = isop->isop_socket->so_options & SO_DONTROUTE; 287 if (error = clnp_route(&siso->siso_addr, &isop->isop_route, flags, 288 (struct sockaddr **)0, &ia)) 289 return error; 290 IFDEBUG(D_ISO) 291 printf("iso_pcbconnect localzero 2, ro->ro_rt 0x%x", 292 isop->isop_route.ro_rt); 293 printf(" ia 0x%x\n", ia); 294 ENDDEBUG 295 } 296 IFDEBUG(D_ISO) 297 printf("in iso_pcbconnect before lookup isop 0x%x isop->sock 0x%x\n", 298 isop, isop->isop_socket); 299 ENDDEBUG 300 if (local_zero) { 301 int nlen, tlen, totlen; caddr_t oldtsel, newtsel; 302 siso = isop->isop_laddr; 303 if (siso == 0 || siso->siso_tlen == 0) 304 (void)iso_pcbbind(isop, (struct mbuf *)0); 305 /* 306 * Here we have problem of squezeing in a definite network address 307 * into an existing sockaddr_iso, which in fact may not have room 308 * for it. This gets messy. 309 */ 310 siso = isop->isop_laddr; 311 oldtsel = TSEL(siso); 312 tlen = siso->siso_tlen; 313 nlen = ia->ia_addr.siso_nlen; 314 totlen = tlen + nlen + _offsetof(struct sockaddr_iso, siso_data[0]); 315 if ((siso == &isop->isop_sladdr) && 316 (totlen > sizeof(isop->isop_sladdr))) { 317 struct mbuf *m = m_get(MT_SONAME, M_DONTWAIT); 318 if (m == 0) 319 return ENOBUFS; 320 m->m_len = totlen; 321 isop->isop_laddr = siso = mtod(m, struct sockaddr_iso *); 322 } 323 siso->siso_nlen = ia->ia_addr.siso_nlen; 324 newtsel = TSEL(siso); 325 ovbcopy(oldtsel, newtsel, tlen); 326 bcopy(ia->ia_addr.siso_data, siso->siso_data, nlen); 327 siso->siso_tlen = tlen; 328 siso->siso_family = AF_ISO; 329 siso->siso_len = totlen; 330 siso = mtod(nam, struct sockaddr_iso *); 331 } 332 IFDEBUG(D_ISO) 333 printf("in iso_pcbconnect before bcopy isop 0x%x isop->sock 0x%x\n", 334 isop, isop->isop_socket); 335 ENDDEBUG 336 /* 337 * If we had to allocate space to a previous big foreign address, 338 * and for some reason we didn't free it, we reuse it knowing 339 * that is going to be big enough, as sockaddrs are delivered in 340 * 128 byte mbufs. 341 * If the foreign address is small enough, we use default space; 342 * otherwise, we grab an mbuf to copy into. 343 */ 344 if (isop->isop_faddr == 0 || isop->isop_faddr == &isop->isop_sfaddr) { 345 if (siso->siso_len <= sizeof(isop->isop_sfaddr)) 346 isop->isop_faddr = &isop->isop_sfaddr; 347 else { 348 struct mbuf *m = m_get(MT_SONAME, M_DONTWAIT); 349 if (m == 0) 350 return ENOBUFS; 351 isop->isop_faddr = mtod(m, struct sockaddr_iso *); 352 } 353 } 354 bcopy((caddr_t)siso, (caddr_t)isop->isop_faddr, siso->siso_len); 355 IFDEBUG(D_ISO) 356 printf("in iso_pcbconnect after bcopy isop 0x%x isop->sock 0x%x\n", 357 isop, isop->isop_socket); 358 printf("iso_pcbconnect connected to addr:\n"); 359 dump_isoaddr(isop->isop_faddr); 360 printf("iso_pcbconnect end: src addr:\n"); 361 dump_isoaddr(isop->isop_laddr); 362 ENDDEBUG 363 return 0; 364 } 365 366 /* 367 * FUNCTION: iso_pcbdisconnect() 368 * 369 * PURPOSE: washes away the peer address info so the socket 370 * appears to be disconnected. 371 * If there's no file descriptor associated with the socket 372 * it detaches the pcb. 373 * 374 * RETURNS: Nada. 375 * 376 * SIDE EFFECTS: May detach the pcb. 377 * 378 * NOTES: 379 */ 380 void 381 iso_pcbdisconnect(isop) 382 struct isopcb *isop; 383 { 384 void iso_pcbdetach(); 385 register struct sockaddr_iso *siso; 386 387 IFDEBUG(D_ISO) 388 printf("iso_pcbdisconnect(isop 0x%x)\n", isop); 389 ENDDEBUG 390 /* 391 * Preserver binding infnormation if already bound. 392 */ 393 if ((siso = isop->isop_laddr) && siso->siso_nlen && siso->siso_tlen) { 394 caddr_t otsel = TSEL(siso); 395 siso->siso_nlen = 0; 396 ovbcopy(otsel, TSEL(siso), siso->siso_tlen); 397 } 398 if (isop->isop_faddr && isop->isop_faddr != &isop->isop_sfaddr) 399 m_freem(dtom(isop->isop_faddr)); 400 isop->isop_faddr = 0; 401 if (isop->isop_socket->so_state & SS_NOFDREF) 402 iso_pcbdetach(isop); 403 } 404 405 /* 406 * FUNCTION: iso_pcbdetach 407 * 408 * PURPOSE: detach the pcb at *(isop) from it's socket and free 409 * the mbufs associated with the pcb.. 410 * Dequeues (isop) from its head. 411 * 412 * RETURNS: Nada. 413 * 414 * SIDE EFFECTS: 415 * 416 * NOTES: 417 */ 418 void 419 iso_pcbdetach(isop) 420 struct isopcb *isop; 421 { 422 struct socket *so = isop->isop_socket; 423 424 IFDEBUG(D_ISO) 425 printf("iso_pcbdetach(isop 0x%x socket 0x%x so 0x%x)\n", 426 isop, isop->isop_socket, so); 427 ENDDEBUG 428 #ifdef TPCONS 429 if (isop->isop_refcnt) { 430 register struct pklcd *lcp = (struct pklcd *)isop->isop_chan; 431 if (--isop->isop_refcnt > 0) 432 return; 433 if (lcp && lcp->lcd_state == DATA_TRANSFER) 434 pk_disconnect(lcp); 435 isop->isop_chan = 0; 436 } 437 #endif 438 if (so) { /* in the x.25 domain, we sometimes have no socket */ 439 so->so_pcb = 0; 440 sofree(so); 441 } 442 IFDEBUG(D_ISO) 443 printf("iso_pcbdetach 2 \n"); 444 ENDDEBUG 445 if (isop->isop_options) 446 (void)m_free(isop->isop_options); 447 IFDEBUG(D_ISO) 448 printf("iso_pcbdetach 3 \n"); 449 ENDDEBUG 450 if (isop->isop_route.ro_rt) 451 rtfree(isop->isop_route.ro_rt); 452 IFDEBUG(D_ISO) 453 printf("iso_pcbdetach 3.1\n"); 454 ENDDEBUG 455 if (isop->isop_clnpcache != NULL) { 456 struct clnp_cache *clcp = 457 mtod(isop->isop_clnpcache, struct clnp_cache *); 458 IFDEBUG(D_ISO) 459 printf("iso_pcbdetach 3.2: clcp 0x%x freeing clc_hdr x%x\n", 460 clcp, clcp->clc_hdr); 461 ENDDEBUG 462 if (clcp->clc_hdr != NULL) 463 m_free(clcp->clc_hdr); 464 IFDEBUG(D_ISO) 465 printf("iso_pcbdetach 3.3: freeing cache x%x\n", 466 isop->isop_clnpcache); 467 ENDDEBUG 468 m_free(isop->isop_clnpcache); 469 } 470 IFDEBUG(D_ISO) 471 printf("iso_pcbdetach 4 \n"); 472 ENDDEBUG 473 remque(isop); 474 IFDEBUG(D_ISO) 475 printf("iso_pcbdetach 5 \n"); 476 ENDDEBUG 477 if (isop->isop_laddr && (isop->isop_laddr != &isop->isop_sladdr)) 478 m_freem(dtom(isop->isop_laddr)); 479 free((caddr_t)isop, M_PCB); 480 } 481 482 483 /* 484 * FUNCTION: iso_pcbnotify 485 * 486 * PURPOSE: notify all connections in this protocol's queue (head) 487 * that have peer address (dst) of the problem (errno) 488 * by calling (notify) on the connections' isopcbs. 489 * 490 * RETURNS: Rien. 491 * 492 * SIDE EFFECTS: 493 * 494 * NOTES: (notify) is called at splimp! 495 */ 496 void 497 iso_pcbnotify(head, siso, errno, notify) 498 struct isopcb *head; 499 register struct sockaddr_iso *siso; 500 int errno, (*notify)(); 501 { 502 register struct isopcb *isop; 503 int s = splimp(); 504 505 IFDEBUG(D_ISO) 506 printf("iso_pcbnotify(head 0x%x, notify 0x%x) dst:\n", head, notify); 507 ENDDEBUG 508 for (isop = head->isop_next; isop != head; isop = isop->isop_next) { 509 if (isop->isop_socket == 0 || isop->isop_faddr == 0 || 510 !SAME_ISOADDR(siso, isop->isop_faddr)) { 511 IFDEBUG(D_ISO) 512 printf("iso_pcbnotify: CONTINUE isop 0x%x, sock 0x%x\n" , 513 isop, isop->isop_socket); 514 printf("addrmatch cmp'd with (0x%x):\n", isop->isop_faddr); 515 dump_isoaddr(isop->isop_faddr); 516 ENDDEBUG 517 continue; 518 } 519 if (errno) 520 isop->isop_socket->so_error = errno; 521 if (notify) 522 (*notify)(isop); 523 } 524 splx(s); 525 IFDEBUG(D_ISO) 526 printf("END OF iso_pcbnotify\n" ); 527 ENDDEBUG 528 } 529 530 531 /* 532 * FUNCTION: iso_pcblookup 533 * 534 * PURPOSE: looks for a given combination of (faddr), (fport), 535 * (lport), (laddr) in the queue named by (head). 536 * Argument (flags) is ignored. 537 * 538 * RETURNS: ptr to the isopcb if it finds a connection matching 539 * these arguments, o.w. returns zero. 540 * 541 * SIDE EFFECTS: 542 * 543 * NOTES: 544 */ 545 struct isopcb * 546 iso_pcblookup(head, fportlen, fport, laddr) 547 struct isopcb *head; 548 register struct sockaddr_iso *laddr; 549 caddr_t fport; 550 int fportlen; 551 { 552 register struct isopcb *isop; 553 register caddr_t lp = TSEL(laddr); 554 unsigned int llen = laddr->siso_tlen; 555 556 IFDEBUG(D_ISO) 557 printf("iso_pcblookup(head 0x%x laddr 0x%x fport 0x%x)\n", 558 head, laddr, fport); 559 ENDDEBUG 560 for (isop = head->isop_next; isop != head; isop = isop->isop_next) { 561 if (isop->isop_laddr == 0 || isop->isop_laddr == laddr) 562 continue; 563 if (isop->isop_laddr->siso_tlen != llen) 564 continue; 565 if (bcmp(lp, TSEL(isop->isop_laddr), llen)) 566 continue; 567 if (fportlen && isop->isop_faddr && 568 bcmp(fport, TSEL(isop->isop_faddr), (unsigned)fportlen)) 569 continue; 570 /* PHASE2 571 * addrmatch1 should be iso_addrmatch(a, b, mask) 572 * where mask is taken from isop->isop_laddrmask (new field) 573 * isop_lnetmask will also be available in isop 574 if (laddr != &zeroiso_addr && 575 !iso_addrmatch1(laddr, &(isop->isop_laddr.siso_addr))) 576 continue; 577 */ 578 if (laddr->siso_nlen && (!SAME_ISOADDR(laddr, isop->isop_laddr))) 579 continue; 580 return (isop); 581 } 582 return (struct isopcb *)0; 583 } 584 #endif ISO 585