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