/*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * %sccs.include.redist.c% * * @(#)iso_pcb.c 8.3 (Berkeley) 07/19/94 */ /*********************************************************** Copyright IBM Corporation 1987 All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of IBM not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ******************************************************************/ /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ /* * $Header: iso_pcb.c,v 4.5 88/06/29 14:59:56 hagens Exp $ * $Source: /usr/argo/sys/netiso/RCS/iso_pcb.c,v $ * * Iso address family net-layer(s) pcb stuff. NEH 1/29/87 */ #ifdef ISO #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef TPCONS #include #include #include #endif #define PCBNULL (struct isopcb *)0 struct iso_addr zeroiso_addr = { 0 }; /* * FUNCTION: iso_pcballoc * * PURPOSE: creates an isopcb structure in an mbuf, * with socket (so), and * puts it in the queue with head (head) * * RETURNS: 0 if OK, ENOBUFS if can't alloc the necessary mbuf */ int iso_pcballoc(so, head) struct socket *so; struct isopcb *head; { register struct isopcb *isop; IFDEBUG(D_ISO) printf("iso_pcballoc(so 0x%x)\n", so); ENDDEBUG MALLOC(isop, struct isopcb *, sizeof(*isop), M_PCB, M_NOWAIT); if (isop == NULL) return ENOBUFS; bzero((caddr_t)isop, sizeof(*isop)); isop->isop_head = head; isop->isop_socket = so; insque(isop, head); if (so) so->so_pcb = (caddr_t)isop; return 0; } /* * FUNCTION: iso_pcbbind * * PURPOSE: binds the address given in *(nam) to the socket * specified by the isopcb in *(isop) * If the given address is zero, it makes sure the * address isn't already in use and if it's got a network * portion, we look for an interface with that network * address. If the address given is zero, we allocate * a port and stuff it in the (nam) structure. * * RETURNS: errno E* or 0 if ok. * * SIDE EFFECTS: increments head->isop_lport if it allocates a port # * * NOTES: */ #define satosiso(sa) ((struct sockaddr_iso *)(sa)) int iso_pcbbind(isop, nam) register struct isopcb *isop; struct mbuf *nam; { register struct isopcb *head = isop->isop_head; register struct sockaddr_iso *siso; struct iso_ifaddr *ia; union { char data[2]; u_short s; } suf; IFDEBUG(D_ISO) printf("iso_pcbbind(isop 0x%x, nam 0x%x)\n", isop, nam); ENDDEBUG suf.s = 0; if (iso_ifaddr == 0) /* any interfaces attached? */ return EADDRNOTAVAIL; if (isop->isop_laddr) /* already bound */ return EADDRINUSE; if(nam == (struct mbuf *)0) { isop->isop_laddr = &isop->isop_sladdr; isop->isop_sladdr.siso_len = sizeof(struct sockaddr_iso); isop->isop_sladdr.siso_family = AF_ISO; isop->isop_sladdr.siso_tlen = 2; isop->isop_sladdr.siso_nlen = 0; isop->isop_sladdr.siso_slen = 0; isop->isop_sladdr.siso_plen = 0; goto noname; } siso = mtod(nam, struct sockaddr_iso *); IFDEBUG(D_ISO) printf("iso_pcbbind(name len 0x%x)\n", nam->m_len); printf("The address is %s\n", clnp_iso_addrp(&siso->siso_addr)); ENDDEBUG /* * We would like sort of length check but since some OSI addrs * do not have fixed length, we can't really do much. * The ONLY thing we can say is that an osi addr has to have * at LEAST an afi and one more byte and had better fit into * a struct iso_addr. * However, in fact the size of the whole thing is a struct * sockaddr_iso, so probably this is what we should check for. */ if( (nam->m_len < 2) || (nam->m_len < siso->siso_len)) { return ENAMETOOLONG; } if (siso->siso_nlen) { /* non-zero net addr- better match one of our interfaces */ IFDEBUG(D_ISO) printf("iso_pcbbind: bind to NOT zeroisoaddr\n"); ENDDEBUG for (ia = iso_ifaddr; ia; ia = ia->ia_next) if (SAME_ISOIFADDR(siso, &ia->ia_addr)) break; if (ia == 0) return EADDRNOTAVAIL; } if (siso->siso_len <= sizeof (isop->isop_sladdr)) { isop->isop_laddr = &isop->isop_sladdr; } else { if ((nam = m_copy(nam, 0, (int)M_COPYALL)) == 0) return ENOBUFS; isop->isop_laddr = mtod(nam, struct sockaddr_iso *); } bcopy((caddr_t)siso, (caddr_t)isop->isop_laddr, siso->siso_len); if (siso->siso_tlen == 0) goto noname; if ((isop->isop_socket->so_options & SO_REUSEADDR) == 0 && iso_pcblookup(head, 0, (caddr_t)0, isop->isop_laddr)) return EADDRINUSE; if (siso->siso_tlen <= 2) { bcopy(TSEL(siso), suf.data, sizeof(suf.data)); suf.s = ntohs(suf.s); if((suf.s < ISO_PORT_RESERVED) && (isop->isop_socket->so_state && SS_PRIV) == 0) return EACCES; } else { register char *cp; noname: cp = TSEL(isop->isop_laddr); isop->isop_laddr->siso_tlen = 2; IFDEBUG(D_ISO) printf("iso_pcbbind noname\n"); ENDDEBUG do { if (head->isop_lport++ < ISO_PORT_RESERVED || head->isop_lport > ISO_PORT_USERRESERVED) head->isop_lport = ISO_PORT_RESERVED; suf.s = htons(head->isop_lport); cp[0] = suf.data[0]; cp[1] = suf.data[1]; } while (iso_pcblookup(head, 0, (caddr_t)0, isop->isop_laddr)); } IFDEBUG(D_ISO) printf("iso_pcbbind returns 0, suf 0x%x\n", suf); ENDDEBUG return 0; } /* * FUNCTION: iso_pcbconnect * * PURPOSE: Make the isopcb (isop) look like it's connected. * In other words, give it the peer address given in * the mbuf * (nam). Make sure such a combination * of local, peer addresses doesn't already exist * for this protocol. Internet mentality prevails here, * wherein a src,dst pair uniquely identifies a connection. * Both net address and port must be specified in argument * (nam). * If we don't have a local address for this socket yet, * we pick one by calling iso_pcbbind(). * * RETURNS: errno E* or 0 if ok. * * SIDE EFFECTS: Looks up a route, which may cause one to be left * in the isopcb. * * NOTES: */ int iso_pcbconnect(isop, nam) register struct isopcb *isop; struct mbuf *nam; { register struct sockaddr_iso *siso = mtod(nam, struct sockaddr_iso *); int local_zero, error = 0; struct iso_ifaddr *ia; IFDEBUG(D_ISO) printf("iso_pcbconnect(isop 0x%x sock 0x%x nam 0x%x", isop, isop->isop_socket, nam); printf("nam->m_len 0x%x), addr:\n", nam->m_len); dump_isoaddr(siso); ENDDEBUG if (nam->m_len < siso->siso_len) return EINVAL; if (siso->siso_family != AF_ISO) return EAFNOSUPPORT; if (siso->siso_nlen == 0) { if (ia = iso_ifaddr) { int nlen = ia->ia_addr.siso_nlen; ovbcopy(TSEL(siso), nlen + TSEL(siso), siso->siso_plen + siso->siso_tlen + siso->siso_slen); bcopy((caddr_t)&ia->ia_addr.siso_addr, (caddr_t)&siso->siso_addr, nlen + 1); /* includes siso->siso_nlen = nlen; */ } else return EADDRNOTAVAIL; } /* * Local zero means either not bound, or bound to a TSEL, but no * particular local interface. So, if we want to send somebody * we need to choose a return address. */ local_zero = ((isop->isop_laddr == 0) || (isop->isop_laddr->siso_nlen == 0)); if (local_zero) { int flags; IFDEBUG(D_ISO) printf("iso_pcbconnect localzero 1\n"); ENDDEBUG /* * If route is known or can be allocated now, * our src addr is taken from the i/f, else punt. */ flags = isop->isop_socket->so_options & SO_DONTROUTE; if (error = clnp_route(&siso->siso_addr, &isop->isop_route, flags, (struct sockaddr **)0, &ia)) return error; IFDEBUG(D_ISO) printf("iso_pcbconnect localzero 2, ro->ro_rt 0x%x", isop->isop_route.ro_rt); printf(" ia 0x%x\n", ia); ENDDEBUG } IFDEBUG(D_ISO) printf("in iso_pcbconnect before lookup isop 0x%x isop->sock 0x%x\n", isop, isop->isop_socket); ENDDEBUG if (local_zero) { int nlen, tlen, totlen; caddr_t oldtsel, newtsel; siso = isop->isop_laddr; if (siso == 0 || siso->siso_tlen == 0) (void)iso_pcbbind(isop, (struct mbuf *)0); /* * Here we have problem of squezeing in a definite network address * into an existing sockaddr_iso, which in fact may not have room * for it. This gets messy. */ siso = isop->isop_laddr; oldtsel = TSEL(siso); tlen = siso->siso_tlen; nlen = ia->ia_addr.siso_nlen; totlen = tlen + nlen + _offsetof(struct sockaddr_iso, siso_data[0]); if ((siso == &isop->isop_sladdr) && (totlen > sizeof(isop->isop_sladdr))) { struct mbuf *m = m_get(MT_SONAME, M_DONTWAIT); if (m == 0) return ENOBUFS; m->m_len = totlen; isop->isop_laddr = siso = mtod(m, struct sockaddr_iso *); } siso->siso_nlen = ia->ia_addr.siso_nlen; newtsel = TSEL(siso); ovbcopy(oldtsel, newtsel, tlen); bcopy(ia->ia_addr.siso_data, siso->siso_data, nlen); siso->siso_tlen = tlen; siso->siso_family = AF_ISO; siso->siso_len = totlen; siso = mtod(nam, struct sockaddr_iso *); } IFDEBUG(D_ISO) printf("in iso_pcbconnect before bcopy isop 0x%x isop->sock 0x%x\n", isop, isop->isop_socket); ENDDEBUG /* * If we had to allocate space to a previous big foreign address, * and for some reason we didn't free it, we reuse it knowing * that is going to be big enough, as sockaddrs are delivered in * 128 byte mbufs. * If the foreign address is small enough, we use default space; * otherwise, we grab an mbuf to copy into. */ if (isop->isop_faddr == 0 || isop->isop_faddr == &isop->isop_sfaddr) { if (siso->siso_len <= sizeof(isop->isop_sfaddr)) isop->isop_faddr = &isop->isop_sfaddr; else { struct mbuf *m = m_get(MT_SONAME, M_DONTWAIT); if (m == 0) return ENOBUFS; isop->isop_faddr = mtod(m, struct sockaddr_iso *); } } bcopy((caddr_t)siso, (caddr_t)isop->isop_faddr, siso->siso_len); IFDEBUG(D_ISO) printf("in iso_pcbconnect after bcopy isop 0x%x isop->sock 0x%x\n", isop, isop->isop_socket); printf("iso_pcbconnect connected to addr:\n"); dump_isoaddr(isop->isop_faddr); printf("iso_pcbconnect end: src addr:\n"); dump_isoaddr(isop->isop_laddr); ENDDEBUG return 0; } /* * FUNCTION: iso_pcbdisconnect() * * PURPOSE: washes away the peer address info so the socket * appears to be disconnected. * If there's no file descriptor associated with the socket * it detaches the pcb. * * RETURNS: Nada. * * SIDE EFFECTS: May detach the pcb. * * NOTES: */ void iso_pcbdisconnect(isop) struct isopcb *isop; { void iso_pcbdetach(); register struct sockaddr_iso *siso; IFDEBUG(D_ISO) printf("iso_pcbdisconnect(isop 0x%x)\n", isop); ENDDEBUG /* * Preserver binding infnormation if already bound. */ if ((siso = isop->isop_laddr) && siso->siso_nlen && siso->siso_tlen) { caddr_t otsel = TSEL(siso); siso->siso_nlen = 0; ovbcopy(otsel, TSEL(siso), siso->siso_tlen); } if (isop->isop_faddr && isop->isop_faddr != &isop->isop_sfaddr) m_freem(dtom(isop->isop_faddr)); isop->isop_faddr = 0; if (isop->isop_socket->so_state & SS_NOFDREF) iso_pcbdetach(isop); } /* * FUNCTION: iso_pcbdetach * * PURPOSE: detach the pcb at *(isop) from it's socket and free * the mbufs associated with the pcb.. * Dequeues (isop) from its head. * * RETURNS: Nada. * * SIDE EFFECTS: * * NOTES: */ void iso_pcbdetach(isop) struct isopcb *isop; { struct socket *so = isop->isop_socket; IFDEBUG(D_ISO) printf("iso_pcbdetach(isop 0x%x socket 0x%x so 0x%x)\n", isop, isop->isop_socket, so); ENDDEBUG #ifdef TPCONS if (isop->isop_chan) { register struct pklcd *lcp = (struct pklcd *)isop->isop_chan; if (--isop->isop_refcnt > 0) return; if (lcp && lcp->lcd_state == DATA_TRANSFER) { lcp->lcd_upper = 0; lcp->lcd_upnext = 0; pk_disconnect(lcp); } isop->isop_chan = 0; } #endif if (so) { /* in the x.25 domain, we sometimes have no socket */ so->so_pcb = 0; sofree(so); } IFDEBUG(D_ISO) printf("iso_pcbdetach 2 \n"); ENDDEBUG if (isop->isop_options) (void)m_free(isop->isop_options); IFDEBUG(D_ISO) printf("iso_pcbdetach 3 \n"); ENDDEBUG if (isop->isop_route.ro_rt) rtfree(isop->isop_route.ro_rt); IFDEBUG(D_ISO) printf("iso_pcbdetach 3.1\n"); ENDDEBUG if (isop->isop_clnpcache != NULL) { struct clnp_cache *clcp = mtod(isop->isop_clnpcache, struct clnp_cache *); IFDEBUG(D_ISO) printf("iso_pcbdetach 3.2: clcp 0x%x freeing clc_hdr x%x\n", clcp, clcp->clc_hdr); ENDDEBUG if (clcp->clc_hdr != NULL) m_free(clcp->clc_hdr); IFDEBUG(D_ISO) printf("iso_pcbdetach 3.3: freeing cache x%x\n", isop->isop_clnpcache); ENDDEBUG m_free(isop->isop_clnpcache); } IFDEBUG(D_ISO) printf("iso_pcbdetach 4 \n"); ENDDEBUG remque(isop); IFDEBUG(D_ISO) printf("iso_pcbdetach 5 \n"); ENDDEBUG if (isop->isop_laddr && (isop->isop_laddr != &isop->isop_sladdr)) m_freem(dtom(isop->isop_laddr)); free((caddr_t)isop, M_PCB); } /* * FUNCTION: iso_pcbnotify * * PURPOSE: notify all connections in this protocol's queue (head) * that have peer address (dst) of the problem (errno) * by calling (notify) on the connections' isopcbs. * * RETURNS: Rien. * * SIDE EFFECTS: * * NOTES: (notify) is called at splimp! */ void iso_pcbnotify(head, siso, errno, notify) struct isopcb *head; register struct sockaddr_iso *siso; int errno, (*notify)(); { register struct isopcb *isop; int s = splimp(); IFDEBUG(D_ISO) printf("iso_pcbnotify(head 0x%x, notify 0x%x) dst:\n", head, notify); ENDDEBUG for (isop = head->isop_next; isop != head; isop = isop->isop_next) { if (isop->isop_socket == 0 || isop->isop_faddr == 0 || !SAME_ISOADDR(siso, isop->isop_faddr)) { IFDEBUG(D_ISO) printf("iso_pcbnotify: CONTINUE isop 0x%x, sock 0x%x\n" , isop, isop->isop_socket); printf("addrmatch cmp'd with (0x%x):\n", isop->isop_faddr); dump_isoaddr(isop->isop_faddr); ENDDEBUG continue; } if (errno) isop->isop_socket->so_error = errno; if (notify) (*notify)(isop); } splx(s); IFDEBUG(D_ISO) printf("END OF iso_pcbnotify\n" ); ENDDEBUG } /* * FUNCTION: iso_pcblookup * * PURPOSE: looks for a given combination of (faddr), (fport), * (lport), (laddr) in the queue named by (head). * Argument (flags) is ignored. * * RETURNS: ptr to the isopcb if it finds a connection matching * these arguments, o.w. returns zero. * * SIDE EFFECTS: * * NOTES: */ struct isopcb * iso_pcblookup(head, fportlen, fport, laddr) struct isopcb *head; register struct sockaddr_iso *laddr; caddr_t fport; int fportlen; { register struct isopcb *isop; register caddr_t lp = TSEL(laddr); unsigned int llen = laddr->siso_tlen; IFDEBUG(D_ISO) printf("iso_pcblookup(head 0x%x laddr 0x%x fport 0x%x)\n", head, laddr, fport); ENDDEBUG for (isop = head->isop_next; isop != head; isop = isop->isop_next) { if (isop->isop_laddr == 0 || isop->isop_laddr == laddr) continue; if (isop->isop_laddr->siso_tlen != llen) continue; if (bcmp(lp, TSEL(isop->isop_laddr), llen)) continue; if (fportlen && isop->isop_faddr && bcmp(fport, TSEL(isop->isop_faddr), (unsigned)fportlen)) continue; /* PHASE2 * addrmatch1 should be iso_addrmatch(a, b, mask) * where mask is taken from isop->isop_laddrmask (new field) * isop_lnetmask will also be available in isop if (laddr != &zeroiso_addr && !iso_addrmatch1(laddr, &(isop->isop_laddr.siso_addr))) continue; */ if (laddr->siso_nlen && (!SAME_ISOADDR(laddr, isop->isop_laddr))) continue; return (isop); } return (struct isopcb *)0; } #endif /* ISO */