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.3 (Berkeley) 07/19/94 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_ISOIFADDR(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 isop->isop_laddr->siso_tlen = 2; 206 IFDEBUG(D_ISO) 207 printf("iso_pcbbind noname\n"); 208 ENDDEBUG 209 do { 210 if (head->isop_lport++ < ISO_PORT_RESERVED || 211 head->isop_lport > ISO_PORT_USERRESERVED) 212 head->isop_lport = ISO_PORT_RESERVED; 213 suf.s = htons(head->isop_lport); 214 cp[0] = suf.data[0]; 215 cp[1] = suf.data[1]; 216 } while (iso_pcblookup(head, 0, (caddr_t)0, isop->isop_laddr)); 217 } 218 IFDEBUG(D_ISO) 219 printf("iso_pcbbind returns 0, suf 0x%x\n", suf); 220 ENDDEBUG 221 return 0; 222 } 223 /* 224 * FUNCTION: iso_pcbconnect 225 * 226 * PURPOSE: Make the isopcb (isop) look like it's connected. 227 * In other words, give it the peer address given in 228 * the mbuf * (nam). Make sure such a combination 229 * of local, peer addresses doesn't already exist 230 * for this protocol. Internet mentality prevails here, 231 * wherein a src,dst pair uniquely identifies a connection. 232 * Both net address and port must be specified in argument 233 * (nam). 234 * If we don't have a local address for this socket yet, 235 * we pick one by calling iso_pcbbind(). 236 * 237 * RETURNS: errno E* or 0 if ok. 238 * 239 * SIDE EFFECTS: Looks up a route, which may cause one to be left 240 * in the isopcb. 241 * 242 * NOTES: 243 */ 244 int 245 iso_pcbconnect(isop, nam) 246 register struct isopcb *isop; 247 struct mbuf *nam; 248 { 249 register struct sockaddr_iso *siso = mtod(nam, struct sockaddr_iso *); 250 int local_zero, error = 0; 251 struct iso_ifaddr *ia; 252 253 IFDEBUG(D_ISO) 254 printf("iso_pcbconnect(isop 0x%x sock 0x%x nam 0x%x", 255 isop, isop->isop_socket, nam); 256 printf("nam->m_len 0x%x), addr:\n", nam->m_len); 257 dump_isoaddr(siso); 258 ENDDEBUG 259 if (nam->m_len < siso->siso_len) 260 return EINVAL; 261 if (siso->siso_family != AF_ISO) 262 return EAFNOSUPPORT; 263 if (siso->siso_nlen == 0) { 264 if (ia = iso_ifaddr) { 265 int nlen = ia->ia_addr.siso_nlen; 266 ovbcopy(TSEL(siso), nlen + TSEL(siso), 267 siso->siso_plen + siso->siso_tlen + siso->siso_slen); 268 bcopy((caddr_t)&ia->ia_addr.siso_addr, 269 (caddr_t)&siso->siso_addr, nlen + 1); 270 /* includes siso->siso_nlen = nlen; */ 271 } else 272 return EADDRNOTAVAIL; 273 } 274 /* 275 * Local zero means either not bound, or bound to a TSEL, but no 276 * particular local interface. So, if we want to send somebody 277 * we need to choose a return address. 278 */ 279 local_zero = 280 ((isop->isop_laddr == 0) || (isop->isop_laddr->siso_nlen == 0)); 281 if (local_zero) { 282 int flags; 283 284 IFDEBUG(D_ISO) 285 printf("iso_pcbconnect localzero 1\n"); 286 ENDDEBUG 287 /* 288 * If route is known or can be allocated now, 289 * our src addr is taken from the i/f, else punt. 290 */ 291 flags = isop->isop_socket->so_options & SO_DONTROUTE; 292 if (error = clnp_route(&siso->siso_addr, &isop->isop_route, flags, 293 (struct sockaddr **)0, &ia)) 294 return error; 295 IFDEBUG(D_ISO) 296 printf("iso_pcbconnect localzero 2, ro->ro_rt 0x%x", 297 isop->isop_route.ro_rt); 298 printf(" ia 0x%x\n", ia); 299 ENDDEBUG 300 } 301 IFDEBUG(D_ISO) 302 printf("in iso_pcbconnect before lookup isop 0x%x isop->sock 0x%x\n", 303 isop, isop->isop_socket); 304 ENDDEBUG 305 if (local_zero) { 306 int nlen, tlen, totlen; caddr_t oldtsel, newtsel; 307 siso = isop->isop_laddr; 308 if (siso == 0 || siso->siso_tlen == 0) 309 (void)iso_pcbbind(isop, (struct mbuf *)0); 310 /* 311 * Here we have problem of squezeing in a definite network address 312 * into an existing sockaddr_iso, which in fact may not have room 313 * for it. This gets messy. 314 */ 315 siso = isop->isop_laddr; 316 oldtsel = TSEL(siso); 317 tlen = siso->siso_tlen; 318 nlen = ia->ia_addr.siso_nlen; 319 totlen = tlen + nlen + _offsetof(struct sockaddr_iso, siso_data[0]); 320 if ((siso == &isop->isop_sladdr) && 321 (totlen > sizeof(isop->isop_sladdr))) { 322 struct mbuf *m = m_get(MT_SONAME, M_DONTWAIT); 323 if (m == 0) 324 return ENOBUFS; 325 m->m_len = totlen; 326 isop->isop_laddr = siso = mtod(m, struct sockaddr_iso *); 327 } 328 siso->siso_nlen = ia->ia_addr.siso_nlen; 329 newtsel = TSEL(siso); 330 ovbcopy(oldtsel, newtsel, tlen); 331 bcopy(ia->ia_addr.siso_data, siso->siso_data, nlen); 332 siso->siso_tlen = tlen; 333 siso->siso_family = AF_ISO; 334 siso->siso_len = totlen; 335 siso = mtod(nam, struct sockaddr_iso *); 336 } 337 IFDEBUG(D_ISO) 338 printf("in iso_pcbconnect before bcopy isop 0x%x isop->sock 0x%x\n", 339 isop, isop->isop_socket); 340 ENDDEBUG 341 /* 342 * If we had to allocate space to a previous big foreign address, 343 * and for some reason we didn't free it, we reuse it knowing 344 * that is going to be big enough, as sockaddrs are delivered in 345 * 128 byte mbufs. 346 * If the foreign address is small enough, we use default space; 347 * otherwise, we grab an mbuf to copy into. 348 */ 349 if (isop->isop_faddr == 0 || isop->isop_faddr == &isop->isop_sfaddr) { 350 if (siso->siso_len <= sizeof(isop->isop_sfaddr)) 351 isop->isop_faddr = &isop->isop_sfaddr; 352 else { 353 struct mbuf *m = m_get(MT_SONAME, M_DONTWAIT); 354 if (m == 0) 355 return ENOBUFS; 356 isop->isop_faddr = mtod(m, struct sockaddr_iso *); 357 } 358 } 359 bcopy((caddr_t)siso, (caddr_t)isop->isop_faddr, siso->siso_len); 360 IFDEBUG(D_ISO) 361 printf("in iso_pcbconnect after bcopy isop 0x%x isop->sock 0x%x\n", 362 isop, isop->isop_socket); 363 printf("iso_pcbconnect connected to addr:\n"); 364 dump_isoaddr(isop->isop_faddr); 365 printf("iso_pcbconnect end: src addr:\n"); 366 dump_isoaddr(isop->isop_laddr); 367 ENDDEBUG 368 return 0; 369 } 370 371 /* 372 * FUNCTION: iso_pcbdisconnect() 373 * 374 * PURPOSE: washes away the peer address info so the socket 375 * appears to be disconnected. 376 * If there's no file descriptor associated with the socket 377 * it detaches the pcb. 378 * 379 * RETURNS: Nada. 380 * 381 * SIDE EFFECTS: May detach the pcb. 382 * 383 * NOTES: 384 */ 385 void 386 iso_pcbdisconnect(isop) 387 struct isopcb *isop; 388 { 389 void iso_pcbdetach(); 390 register struct sockaddr_iso *siso; 391 392 IFDEBUG(D_ISO) 393 printf("iso_pcbdisconnect(isop 0x%x)\n", isop); 394 ENDDEBUG 395 /* 396 * Preserver binding infnormation if already bound. 397 */ 398 if ((siso = isop->isop_laddr) && siso->siso_nlen && siso->siso_tlen) { 399 caddr_t otsel = TSEL(siso); 400 siso->siso_nlen = 0; 401 ovbcopy(otsel, TSEL(siso), siso->siso_tlen); 402 } 403 if (isop->isop_faddr && isop->isop_faddr != &isop->isop_sfaddr) 404 m_freem(dtom(isop->isop_faddr)); 405 isop->isop_faddr = 0; 406 if (isop->isop_socket->so_state & SS_NOFDREF) 407 iso_pcbdetach(isop); 408 } 409 410 /* 411 * FUNCTION: iso_pcbdetach 412 * 413 * PURPOSE: detach the pcb at *(isop) from it's socket and free 414 * the mbufs associated with the pcb.. 415 * Dequeues (isop) from its head. 416 * 417 * RETURNS: Nada. 418 * 419 * SIDE EFFECTS: 420 * 421 * NOTES: 422 */ 423 void 424 iso_pcbdetach(isop) 425 struct isopcb *isop; 426 { 427 struct socket *so = isop->isop_socket; 428 429 IFDEBUG(D_ISO) 430 printf("iso_pcbdetach(isop 0x%x socket 0x%x so 0x%x)\n", 431 isop, isop->isop_socket, so); 432 ENDDEBUG 433 #ifdef TPCONS 434 if (isop->isop_chan) { 435 register struct pklcd *lcp = (struct pklcd *)isop->isop_chan; 436 if (--isop->isop_refcnt > 0) 437 return; 438 if (lcp && lcp->lcd_state == DATA_TRANSFER) { 439 lcp->lcd_upper = 0; 440 lcp->lcd_upnext = 0; 441 pk_disconnect(lcp); 442 } 443 isop->isop_chan = 0; 444 } 445 #endif 446 if (so) { /* in the x.25 domain, we sometimes have no socket */ 447 so->so_pcb = 0; 448 sofree(so); 449 } 450 IFDEBUG(D_ISO) 451 printf("iso_pcbdetach 2 \n"); 452 ENDDEBUG 453 if (isop->isop_options) 454 (void)m_free(isop->isop_options); 455 IFDEBUG(D_ISO) 456 printf("iso_pcbdetach 3 \n"); 457 ENDDEBUG 458 if (isop->isop_route.ro_rt) 459 rtfree(isop->isop_route.ro_rt); 460 IFDEBUG(D_ISO) 461 printf("iso_pcbdetach 3.1\n"); 462 ENDDEBUG 463 if (isop->isop_clnpcache != NULL) { 464 struct clnp_cache *clcp = 465 mtod(isop->isop_clnpcache, struct clnp_cache *); 466 IFDEBUG(D_ISO) 467 printf("iso_pcbdetach 3.2: clcp 0x%x freeing clc_hdr x%x\n", 468 clcp, clcp->clc_hdr); 469 ENDDEBUG 470 if (clcp->clc_hdr != NULL) 471 m_free(clcp->clc_hdr); 472 IFDEBUG(D_ISO) 473 printf("iso_pcbdetach 3.3: freeing cache x%x\n", 474 isop->isop_clnpcache); 475 ENDDEBUG 476 m_free(isop->isop_clnpcache); 477 } 478 IFDEBUG(D_ISO) 479 printf("iso_pcbdetach 4 \n"); 480 ENDDEBUG 481 remque(isop); 482 IFDEBUG(D_ISO) 483 printf("iso_pcbdetach 5 \n"); 484 ENDDEBUG 485 if (isop->isop_laddr && (isop->isop_laddr != &isop->isop_sladdr)) 486 m_freem(dtom(isop->isop_laddr)); 487 free((caddr_t)isop, M_PCB); 488 } 489 490 491 /* 492 * FUNCTION: iso_pcbnotify 493 * 494 * PURPOSE: notify all connections in this protocol's queue (head) 495 * that have peer address (dst) of the problem (errno) 496 * by calling (notify) on the connections' isopcbs. 497 * 498 * RETURNS: Rien. 499 * 500 * SIDE EFFECTS: 501 * 502 * NOTES: (notify) is called at splimp! 503 */ 504 void 505 iso_pcbnotify(head, siso, errno, notify) 506 struct isopcb *head; 507 register struct sockaddr_iso *siso; 508 int errno, (*notify)(); 509 { 510 register struct isopcb *isop; 511 int s = splimp(); 512 513 IFDEBUG(D_ISO) 514 printf("iso_pcbnotify(head 0x%x, notify 0x%x) dst:\n", head, notify); 515 ENDDEBUG 516 for (isop = head->isop_next; isop != head; isop = isop->isop_next) { 517 if (isop->isop_socket == 0 || isop->isop_faddr == 0 || 518 !SAME_ISOADDR(siso, isop->isop_faddr)) { 519 IFDEBUG(D_ISO) 520 printf("iso_pcbnotify: CONTINUE isop 0x%x, sock 0x%x\n" , 521 isop, isop->isop_socket); 522 printf("addrmatch cmp'd with (0x%x):\n", isop->isop_faddr); 523 dump_isoaddr(isop->isop_faddr); 524 ENDDEBUG 525 continue; 526 } 527 if (errno) 528 isop->isop_socket->so_error = errno; 529 if (notify) 530 (*notify)(isop); 531 } 532 splx(s); 533 IFDEBUG(D_ISO) 534 printf("END OF iso_pcbnotify\n" ); 535 ENDDEBUG 536 } 537 538 539 /* 540 * FUNCTION: iso_pcblookup 541 * 542 * PURPOSE: looks for a given combination of (faddr), (fport), 543 * (lport), (laddr) in the queue named by (head). 544 * Argument (flags) is ignored. 545 * 546 * RETURNS: ptr to the isopcb if it finds a connection matching 547 * these arguments, o.w. returns zero. 548 * 549 * SIDE EFFECTS: 550 * 551 * NOTES: 552 */ 553 struct isopcb * 554 iso_pcblookup(head, fportlen, fport, laddr) 555 struct isopcb *head; 556 register struct sockaddr_iso *laddr; 557 caddr_t fport; 558 int fportlen; 559 { 560 register struct isopcb *isop; 561 register caddr_t lp = TSEL(laddr); 562 unsigned int llen = laddr->siso_tlen; 563 564 IFDEBUG(D_ISO) 565 printf("iso_pcblookup(head 0x%x laddr 0x%x fport 0x%x)\n", 566 head, laddr, fport); 567 ENDDEBUG 568 for (isop = head->isop_next; isop != head; isop = isop->isop_next) { 569 if (isop->isop_laddr == 0 || isop->isop_laddr == laddr) 570 continue; 571 if (isop->isop_laddr->siso_tlen != llen) 572 continue; 573 if (bcmp(lp, TSEL(isop->isop_laddr), llen)) 574 continue; 575 if (fportlen && isop->isop_faddr && 576 bcmp(fport, TSEL(isop->isop_faddr), (unsigned)fportlen)) 577 continue; 578 /* PHASE2 579 * addrmatch1 should be iso_addrmatch(a, b, mask) 580 * where mask is taken from isop->isop_laddrmask (new field) 581 * isop_lnetmask will also be available in isop 582 if (laddr != &zeroiso_addr && 583 !iso_addrmatch1(laddr, &(isop->isop_laddr.siso_addr))) 584 continue; 585 */ 586 if (laddr->siso_nlen && (!SAME_ISOADDR(laddr, isop->isop_laddr))) 587 continue; 588 return (isop); 589 } 590 return (struct isopcb *)0; 591 } 592 #endif /* ISO */ 593