diff options
Diffstat (limited to 'sys/netiso/tp_inet.c')
-rw-r--r-- | sys/netiso/tp_inet.c | 688 |
1 files changed, 688 insertions, 0 deletions
diff --git a/sys/netiso/tp_inet.c b/sys/netiso/tp_inet.c new file mode 100644 index 0000000..fb01371 --- /dev/null +++ b/sys/netiso/tp_inet.c @@ -0,0 +1,688 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tp_inet.c 8.1 (Berkeley) 6/10/93 + */ + +/*********************************************************** + 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 + */ +/* + * ARGO TP + * $Header: tp_inet.c,v 5.3 88/11/18 17:27:29 nhall Exp $ + * $Source: /usr/argo/sys/netiso/RCS/tp_inet.c,v $ + * + * Here is where you find the inet-dependent code. We've tried + * keep all net-level and (primarily) address-family-dependent stuff + * out of the tp source, and everthing here is reached indirectly + * through a switch table (struct nl_protosw *) tpcb->tp_nlproto + * (see tp_pcb.c). + * The routines here are: + * in_getsufx: gets transport suffix out of an inpcb structure. + * in_putsufx: put transport suffix into an inpcb structure. + * in_putnetaddr: put a whole net addr into an inpcb. + * in_getnetaddr: get a whole net addr from an inpcb. + * in_cmpnetaddr: compare a whole net addr from an isopcb. + * in_recycle_suffix: clear suffix for reuse in inpcb + * tpip_mtu: figure out what size tpdu to use + * tpip_input: take a pkt from ip, strip off its ip header, give to tp + * tpip_output_dg: package a pkt for ip given 2 addresses & some data + * tpip_output: package a pkt for ip given an inpcb & some data + */ + +#ifdef INET + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/mbuf.h> +#include <sys/errno.h> +#include <sys/time.h> + +#include <net/if.h> + +#include <netiso/tp_param.h> +#include <netiso/argo_debug.h> +#include <netiso/tp_stat.h> +#include <netiso/tp_ip.h> +#include <netiso/tp_pcb.h> +#include <netiso/tp_trace.h> +#include <netiso/tp_stat.h> +#include <netiso/tp_tpdu.h> +#include <netinet/in_var.h> + +#ifndef ISO +#include <netiso/iso_chksum.c> +#endif + +/* + * NAME: in_getsufx() + + * CALLED FROM: pr_usrreq() on PRU_BIND, + * PRU_CONNECT, PRU_ACCEPT, and PRU_PEERADDR + * + * FUNCTION, ARGUMENTS, and RETURN VALUE: + * Get a transport suffix from an inpcb structure (inp). + * The argument (which) takes the value TP_LOCAL or TP_FOREIGN. + * + * RETURNS: internet port / transport suffix + * (CAST TO AN INT) + * + * SIDE EFFECTS: + * + * NOTES: + */ +in_getsufx(inp, lenp, data_out, which) + struct inpcb *inp; + u_short *lenp; + caddr_t data_out; + int which; +{ + *lenp = sizeof(u_short); + switch (which) { + case TP_LOCAL: + *(u_short *)data_out = inp->inp_lport; + return; + + case TP_FOREIGN: + *(u_short *)data_out = inp->inp_fport; + } + +} + +/* + * NAME: in_putsufx() + * + * CALLED FROM: tp_newsocket(); i.e., when a connection + * is being established by an incoming CR_TPDU. + * + * FUNCTION, ARGUMENTS: + * Put a transport suffix (found in name) into an inpcb structure (inp). + * The argument (which) takes the value TP_LOCAL or TP_FOREIGN. + * + * RETURNS: Nada + * + * SIDE EFFECTS: + * + * NOTES: + */ +/*ARGSUSED*/ +void +in_putsufx(inp, sufxloc, sufxlen, which) + struct inpcb *inp; + caddr_t sufxloc; + int which; +{ + if (which == TP_FOREIGN) { + bcopy(sufxloc, (caddr_t)&inp->inp_fport, sizeof(inp->inp_fport)); + } +} + +/* + * NAME: in_recycle_tsuffix() + * + * CALLED FROM: tp.trans whenever we go into REFWAIT state. + * + * FUNCTION and ARGUMENT: + * Called when a ref is frozen, to allow the suffix to be reused. + * (inp) is the net level pcb. + * + * RETURNS: Nada + * + * SIDE EFFECTS: + * + * NOTES: This really shouldn't have to be done in a NET level pcb + * but... for the internet world that just the way it is done in BSD... + * The alternative is to have the port unusable until the reference + * timer goes off. + */ +void +in_recycle_tsuffix(inp) + struct inpcb *inp; +{ + inp->inp_fport = inp->inp_lport = 0; +} + +/* + * NAME: in_putnetaddr() + * + * CALLED FROM: + * tp_newsocket(); i.e., when a connection is being established by an + * incoming CR_TPDU. + * + * FUNCTION and ARGUMENTS: + * Copy a whole net addr from a struct sockaddr (name). + * into an inpcb (inp). + * The argument (which) takes values TP_LOCAL or TP_FOREIGN + * + * RETURNS: Nada + * + * SIDE EFFECTS: + * + * NOTES: + */ +void +in_putnetaddr(inp, name, which) + register struct inpcb *inp; + struct sockaddr_in *name; + int which; +{ + switch (which) { + case TP_LOCAL: + bcopy((caddr_t)&name->sin_addr, + (caddr_t)&inp->inp_laddr, sizeof(struct in_addr)); + /* won't work if the dst address (name) is INADDR_ANY */ + + break; + case TP_FOREIGN: + if( name != (struct sockaddr_in *)0 ) { + bcopy((caddr_t)&name->sin_addr, + (caddr_t)&inp->inp_faddr, sizeof(struct in_addr)); + } + } +} + +/* + * NAME: in_putnetaddr() + * + * CALLED FROM: + * tp_input() when a connection is being established by an + * incoming CR_TPDU, and considered for interception. + * + * FUNCTION and ARGUMENTS: + * Compare a whole net addr from a struct sockaddr (name), + * with that implicitly stored in an inpcb (inp). + * The argument (which) takes values TP_LOCAL or TP_FOREIGN + * + * RETURNS: Nada + * + * SIDE EFFECTS: + * + * NOTES: + */ +in_cmpnetaddr(inp, name, which) + register struct inpcb *inp; + register struct sockaddr_in *name; + int which; +{ + if (which == TP_LOCAL) { + if (name->sin_port && name->sin_port != inp->inp_lport) + return 0; + return (name->sin_addr.s_addr == inp->inp_laddr.s_addr); + } + if (name->sin_port && name->sin_port != inp->inp_fport) + return 0; + return (name->sin_addr.s_addr == inp->inp_faddr.s_addr); +} + +/* + * NAME: in_getnetaddr() + * + * CALLED FROM: + * pr_usrreq() PRU_SOCKADDR, PRU_ACCEPT, PRU_PEERADDR + * FUNCTION and ARGUMENTS: + * Copy a whole net addr from an inpcb (inp) into + * an mbuf (name); + * The argument (which) takes values TP_LOCAL or TP_FOREIGN. + * + * RETURNS: Nada + * + * SIDE EFFECTS: + * + * NOTES: + */ + +void +in_getnetaddr( inp, name, which) + register struct mbuf *name; + struct inpcb *inp; + int which; +{ + register struct sockaddr_in *sin = mtod(name, struct sockaddr_in *); + bzero((caddr_t)sin, sizeof(*sin)); + switch (which) { + case TP_LOCAL: + sin->sin_addr = inp->inp_laddr; + sin->sin_port = inp->inp_lport; + break; + case TP_FOREIGN: + sin->sin_addr = inp->inp_faddr; + sin->sin_port = inp->inp_fport; + break; + default: + return; + } + name->m_len = sin->sin_len = sizeof (*sin); + sin->sin_family = AF_INET; +} + +/* + * NAME: tpip_mtu() + * + * CALLED FROM: + * tp_route_to() on incoming CR, CC, and pr_usrreq() for PRU_CONNECT + * + * FUNCTION, ARGUMENTS, and RETURN VALUE: + * + * Perform subnetwork dependent part of determining MTU information. + * It appears that setting a double pointer to the rtentry associated with + * the destination, and returning the header size for the network protocol + * suffices. + * + * SIDE EFFECTS: + * Sets tp_routep pointer in pcb. + * + * NOTES: + */ + +tpip_mtu(tpcb) +register struct tp_pcb *tpcb; +{ + struct inpcb *inp = (struct inpcb *)tpcb->tp_npcb; + + IFDEBUG(D_CONN) + printf("tpip_mtu(tpcb)\n", tpcb); + printf("tpip_mtu routing to addr 0x%x\n", inp->inp_faddr.s_addr); + ENDDEBUG + tpcb->tp_routep = &(inp->inp_route.ro_rt); + return (sizeof (struct ip)); + +} + +/* + * NAME: tpip_output() + * + * CALLED FROM: tp_emit() + * + * FUNCTION and ARGUMENTS: + * Take a packet(m0) from tp and package it so that ip will accept it. + * This means prepending space for the ip header and filling in a few + * of the fields. + * inp is the inpcb structure; datalen is the length of the data in the + * mbuf string m0. + * RETURNS: + * whatever (E*) is returned form the net layer output routine. + * + * SIDE EFFECTS: + * + * NOTES: + */ + +int +tpip_output(inp, m0, datalen, nochksum) + struct inpcb *inp; + struct mbuf *m0; + int datalen; + int nochksum; +{ + return tpip_output_dg( &inp->inp_laddr, &inp->inp_faddr, m0, datalen, + &inp->inp_route, nochksum); +} + +/* + * NAME: tpip_output_dg() + * + * CALLED FROM: tp_error_emit() + * + * FUNCTION and ARGUMENTS: + * This is a copy of tpip_output that takes the addresses + * instead of a pcb. It's used by the tp_error_emit, when we + * don't have an in_pcb with which to call the normal output rtn. + * + * RETURNS: ENOBUFS or whatever (E*) is + * returned form the net layer output routine. + * + * SIDE EFFECTS: + * + * NOTES: + */ + +/*ARGSUSED*/ +int +tpip_output_dg(laddr, faddr, m0, datalen, ro, nochksum) + struct in_addr *laddr, *faddr; + struct mbuf *m0; + int datalen; + struct route *ro; + int nochksum; +{ + register struct mbuf *m; + register struct ip *ip; + int error; + + IFDEBUG(D_EMIT) + printf("tpip_output_dg datalen 0x%x m0 0x%x\n", datalen, m0); + ENDDEBUG + + + MGETHDR(m, M_DONTWAIT, TPMT_IPHDR); + if (m == 0) { + error = ENOBUFS; + goto bad; + } + m->m_next = m0; + MH_ALIGN(m, sizeof(struct ip)); + m->m_len = sizeof(struct ip); + + ip = mtod(m, struct ip *); + bzero((caddr_t)ip, sizeof *ip); + + ip->ip_p = IPPROTO_TP; + m->m_pkthdr.len = ip->ip_len = sizeof(struct ip) + datalen; + ip->ip_ttl = MAXTTL; + /* don't know why you need to set ttl; + * overlay doesn't even make this available + */ + + ip->ip_src = *laddr; + ip->ip_dst = *faddr; + + IncStat(ts_tpdu_sent); + IFDEBUG(D_EMIT) + dump_mbuf(m, "tpip_output_dg before ip_output\n"); + ENDDEBUG + + error = ip_output(m, (struct mbuf *)0, ro, IP_ALLOWBROADCAST, NULL); + + IFDEBUG(D_EMIT) + printf("tpip_output_dg after ip_output\n"); + ENDDEBUG + + return error; + +bad: + m_freem(m); + IncStat(ts_send_drop); + return error; +} + +/* + * NAME: tpip_input() + * + * CALLED FROM: + * ip's input routine, indirectly through the protosw. + * + * FUNCTION and ARGUMENTS: + * Take a packet (m) from ip, strip off the ip header and give it to tp + * + * RETURNS: No return value. + * + * SIDE EFFECTS: + * + * NOTES: + */ +ProtoHook +tpip_input(m, iplen) + struct mbuf *m; + int iplen; +{ + struct sockaddr_in src, dst; + register struct ip *ip; + int s = splnet(), hdrlen; + + IncStat(ts_pkt_rcvd); + + /* + * IP layer has already pulled up the IP header, + * but the first byte after the IP header may not be there, + * e.g. if you came in via loopback, so you have to do an + * m_pullup to before you can even look to see how much you + * really need. The good news is that m_pullup will round + * up to almost the next mbuf's worth. + */ + + + if((m = m_pullup(m, iplen + 1)) == MNULL) + goto discard; + CHANGE_MTYPE(m, TPMT_DATA); + + /* + * Now pull up the whole tp header: + * Unfortunately, there may be IP options to skip past so we + * just fetch it as an unsigned char. + */ + hdrlen = iplen + 1 + mtod(m, u_char *)[iplen]; + + if( m->m_len < hdrlen ) { + if((m = m_pullup(m, hdrlen)) == MNULL){ + IFDEBUG(D_TPINPUT) + printf("tp_input, pullup 2!\n"); + ENDDEBUG + goto discard; + } + } + /* + * cannot use tp_inputprep() here 'cause you don't + * have quite the same situation + */ + + IFDEBUG(D_TPINPUT) + dump_mbuf(m, "after tpip_input both pullups"); + ENDDEBUG + /* + * m_pullup may have returned a different mbuf + */ + ip = mtod(m, struct ip *); + + /* + * drop the ip header from the front of the mbuf + * this is necessary for the tp checksum + */ + m->m_len -= iplen; + m->m_data += iplen; + + src.sin_addr = *(struct in_addr *)&(ip->ip_src); + src.sin_family = AF_INET; + src.sin_len = sizeof(src); + dst.sin_addr = *(struct in_addr *)&(ip->ip_dst); + dst.sin_family = AF_INET; + dst.sin_len = sizeof(dst); + + (void) tp_input(m, (struct sockaddr *)&src, (struct sockaddr *)&dst, + 0, tpip_output_dg, 0); + return 0; + +discard: + IFDEBUG(D_TPINPUT) + printf("tpip_input DISCARD\n"); + ENDDEBUG + IFTRACE(D_TPINPUT) + tptrace(TPPTmisc, "tpip_input DISCARD m", m,0,0,0); + ENDTRACE + m_freem(m); + IncStat(ts_recv_drop); + splx(s); + return 0; +} + + +#include <sys/protosw.h> +#include <netinet/ip_icmp.h> + +extern void tp_quench(); +/* + * NAME: tpin_quench() + * + * CALLED FROM: tpip_ctlinput() + * + * FUNCTION and ARGUMENTS: find the tpcb pointer and pass it to tp_quench + * + * RETURNS: Nada + * + * SIDE EFFECTS: + * + * NOTES: + */ + +void +tpin_quench(inp) + struct inpcb *inp; +{ + tp_quench((struct tp_pcb *)inp->inp_socket->so_pcb, PRC_QUENCH); +} + +/* + * NAME: tpip_ctlinput() + * + * CALLED FROM: + * The network layer through the protosw table. + * + * FUNCTION and ARGUMENTS: + * When clnp gets an ICMP msg this gets called. + * It either returns an error status to the user or + * causes all connections on this address to be aborted + * by calling the appropriate xx_notify() routine. + * (cmd) is the type of ICMP error. + * (sa) the address of the sender + * + * RETURNS: Nothing + * + * SIDE EFFECTS: + * + * NOTES: + */ +ProtoHook +tpip_ctlinput(cmd, sin) + int cmd; + struct sockaddr_in *sin; +{ + extern u_char inetctlerrmap[]; + extern struct in_addr zeroin_addr; + void tp_quench __P((struct inpcb *,int)); + void tpin_abort __P((struct inpcb *,int)); + + if (sin->sin_family != AF_INET && sin->sin_family != AF_IMPLINK) + return 0; + if (sin->sin_addr.s_addr == INADDR_ANY) + return 0; + if (cmd < 0 || cmd > PRC_NCMDS) + return 0; + switch (cmd) { + + case PRC_QUENCH: + in_pcbnotify(&tp_inpcb, (struct sockaddr *)sin, 0, + zeroin_addr, 0, cmd, tp_quench); + break; + + case PRC_ROUTEDEAD: + case PRC_HOSTUNREACH: + case PRC_UNREACH_NET: + case PRC_IFDOWN: + case PRC_HOSTDEAD: + in_pcbnotify(&tp_inpcb, (struct sockaddr *)sin, 0, + zeroin_addr, 0, cmd, in_rtchange); + break; + + default: + /* + case PRC_MSGSIZE: + case PRC_UNREACH_HOST: + case PRC_UNREACH_PROTOCOL: + case PRC_UNREACH_PORT: + case PRC_UNREACH_NEEDFRAG: + case PRC_UNREACH_SRCFAIL: + case PRC_REDIRECT_NET: + case PRC_REDIRECT_HOST: + case PRC_REDIRECT_TOSNET: + case PRC_REDIRECT_TOSHOST: + case PRC_TIMXCEED_INTRANS: + case PRC_TIMXCEED_REASS: + case PRC_PARAMPROB: + */ + in_pcbnotify(&tp_inpcb, (struct sockaddr *)sin, 0, + zeroin_addr, 0, cmd, tpin_abort); + } + return 0; +} + +/* + * NAME: tpin_abort() + * + * CALLED FROM: + * xxx_notify() from tp_ctlinput() when + * net level gets some ICMP-equiv. type event. + * + * FUNCTION and ARGUMENTS: + * Cause the connection to be aborted with some sort of error + * reason indicating that the network layer caused the abort. + * Fakes an ER TPDU so we can go through the driver. + * + * RETURNS: Nothing + * + * SIDE EFFECTS: + * + * NOTES: + */ + +ProtoHook +tpin_abort(inp) + struct inpcb *inp; +{ + struct tp_event e; + + e.ev_number = ER_TPDU; + e.ATTR(ER_TPDU).e_reason = ENETRESET; + (void) tp_driver((struct tp_pcb *)inp->inp_ppcb, &e); + return 0; +} + +#ifdef ARGO_DEBUG +dump_inaddr(addr) + register struct sockaddr_in *addr; +{ + printf("INET: port 0x%x; addr 0x%x\n", addr->sin_port, addr->sin_addr); +} +#endif /* ARGO_DEBUG */ +#endif /* INET */ |