/* $OpenBSD: idp_usrreq.c,v 1.4 1997/01/18 17:31:02 briggs Exp $ */ /* $NetBSD: idp_usrreq.c,v 1.9 1996/02/13 22:13:43 christos Exp $ */ /* * Copyright (c) 1984, 1985, 1986, 1987, 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. * * @(#)idp_usrreq.c 8.1 (Berkeley) 6/10/93 */ #include <sys/param.h> #include <sys/systm.h> #include <sys/malloc.h> #include <sys/mbuf.h> #include <sys/protosw.h> #include <sys/socket.h> #include <sys/socketvar.h> #include <sys/errno.h> #include <sys/stat.h> #include <net/if.h> #include <net/route.h> #include <netns/ns.h> #include <netns/ns_pcb.h> #include <netns/ns_if.h> #include <netns/ns_var.h> #include <netns/idp.h> #include <netns/idp_var.h> #include <netns/ns_error.h> #include <machine/stdarg.h> /* * IDP protocol implementation. */ /* * This may also be called for raw listeners. */ void #if __STDC__ idp_input(struct mbuf *m, ...) #else idp_input(m, va_alist) struct mbuf *m; va_dcl #endif { register struct nspcb *nsp; register struct idp *idp = mtod(m, struct idp *); struct ifnet *ifp = m->m_pkthdr.rcvif; struct sockaddr_ns idp_ns; va_list ap; va_start(ap, m); nsp = va_arg(ap, struct nspcb *); va_end(ap); if (nsp == NULL) panic("No nspcb"); /* * Construct sockaddr format source address. * Stuff source address and datagram in user buffer. */ bzero(&idp_ns, sizeof(idp_ns)); idp_ns.sns_len = sizeof(idp_ns); idp_ns.sns_family = AF_NS; idp_ns.sns_addr = idp->idp_sna; if (ns_neteqnn(idp->idp_sna.x_net, ns_zeronet) && ifp) { register struct ifaddr *ifa; for (ifa = ifp->if_addrlist.tqh_first; ifa != 0; ifa = ifa->ifa_list.tqe_next) { if (ifa->ifa_addr->sa_family == AF_NS) { idp_ns.sns_addr.x_net = IA_SNS(ifa)->sns_addr.x_net; break; } } } nsp->nsp_rpt = idp->idp_pt; if ( ! (nsp->nsp_flags & NSP_RAWIN) ) { m->m_len -= sizeof (struct idp); m->m_pkthdr.len -= sizeof (struct idp); m->m_data += sizeof (struct idp); } if (sbappendaddr(&nsp->nsp_socket->so_rcv, snstosa(&idp_ns), m, (struct mbuf *)0) == 0) goto bad; sorwakeup(nsp->nsp_socket); return; bad: m_freem(m); } void idp_abort(nsp) struct nspcb *nsp; { struct socket *so = nsp->nsp_socket; ns_pcbdisconnect(nsp); soisdisconnected(so); } /* * Drop connection, reporting * the specified error. */ void idp_drop(nsp, errno) register struct nspcb *nsp; int errno; { struct socket *so = nsp->nsp_socket; #if 0 /* * someday, in the xerox world * we will generate error protocol packets * announcing that the socket has gone away. */ if (TCPS_HAVERCVDSYN(tp->t_state)) { tp->t_state = TCPS_CLOSED; (void) tcp_output(tp); } #endif so->so_error = errno; ns_pcbdisconnect(nsp); soisdisconnected(so); } int noIdpRoute; int #if __STDC__ idp_output(struct mbuf *m0, ...) #else idp_output(m0, va_alist) struct mbuf *m0; va_dcl #endif { struct nspcb *nsp; register struct mbuf *m; register struct idp *idp; register struct socket *so; register int len = 0; register struct route *ro; struct mbuf *mprev = NULL; extern int idpcksum; va_list ap; va_start(ap, m0); nsp = va_arg(ap, struct nspcb *); va_end(ap); /* * Calculate data length. */ for (m = m0; m; m = m->m_next) { mprev = m; len += m->m_len; } /* * Make sure packet is actually of even length. */ if (len & 1) { m = mprev; if ((m->m_flags & M_EXT) == 0 && (m->m_len + m->m_data < &m->m_dat[MLEN])) { m->m_len++; } else { struct mbuf *m1 = m_get(M_DONTWAIT, MT_DATA); if (m1 == 0) { m_freem(m0); return (ENOBUFS); } m1->m_len = 1; * mtod(m1, char *) = 0; m->m_next = m1; } m0->m_pkthdr.len++; } /* * Fill in mbuf with extended IDP header * and addresses and length put into network format. */ m = m0; if (nsp->nsp_flags & NSP_RAWOUT) { idp = mtod(m, struct idp *); } else { M_PREPEND(m, sizeof (struct idp), M_DONTWAIT); if (m == 0) return (ENOBUFS); idp = mtod(m, struct idp *); idp->idp_tc = 0; idp->idp_pt = nsp->nsp_dpt; idp->idp_sna = nsp->nsp_laddr; idp->idp_dna = nsp->nsp_faddr; len += sizeof (struct idp); } idp->idp_len = htons((u_short)len); if (idpcksum) { idp->idp_sum = 0; len = ((len - 1) | 1) + 1; idp->idp_sum = ns_cksum(m, len); } else idp->idp_sum = 0xffff; /* * Output datagram. */ so = nsp->nsp_socket; if (so->so_options & SO_DONTROUTE) return (ns_output(m, (struct route *)0, (so->so_options & SO_BROADCAST) | NS_ROUTETOIF)); /* * Use cached route for previous datagram if * possible. If the previous net was the same * and the interface was a broadcast medium, or * if the previous destination was identical, * then we are ok. * * NB: We don't handle broadcasts because that * would require 3 subroutine calls. */ ro = &nsp->nsp_route; #ifdef ancient_history /* * I think that this will all be handled in ns_pcbconnect! */ if (ro->ro_rt) { if(ns_neteq(nsp->nsp_lastdst, idp->idp_dna)) { /* * This assumes we have no GH type routes */ if (ro->ro_rt->rt_flags & RTF_HOST) { if (!ns_hosteq(nsp->nsp_lastdst, idp->idp_dna)) goto re_route; } if ((ro->ro_rt->rt_flags & RTF_GATEWAY) == 0) { register struct ns_addr *dst = &satons_addr(ro->ro_dst); dst->x_host = idp->idp_dna.x_host; } /* * Otherwise, we go through the same gateway * and dst is already set up. */ } else { re_route: RTFREE(ro->ro_rt); ro->ro_rt = (struct rtentry *)0; } } nsp->nsp_lastdst = idp->idp_dna; #endif /* ancient_history */ if (noIdpRoute) ro = 0; return (ns_output(m, ro, so->so_options & SO_BROADCAST)); } /* ARGSUSED */ int idp_ctloutput(req, so, level, name, value) int req, level; struct socket *so; int name; struct mbuf **value; { register struct mbuf *m; struct nspcb *nsp = sotonspcb(so); int mask, error = 0; extern long ns_pexseq; if (nsp == NULL) return (EINVAL); switch (req) { case PRCO_GETOPT: if (value==NULL) return (EINVAL); m = m_get(M_DONTWAIT, MT_DATA); if (m==NULL) return (ENOBUFS); switch (name) { case SO_ALL_PACKETS: mask = NSP_ALL_PACKETS; goto get_flags; case SO_HEADERS_ON_INPUT: mask = NSP_RAWIN; goto get_flags; case SO_HEADERS_ON_OUTPUT: mask = NSP_RAWOUT; get_flags: m->m_len = sizeof(short); *mtod(m, short *) = nsp->nsp_flags & mask; break; case SO_DEFAULT_HEADERS: m->m_len = sizeof(struct idp); { register struct idp *idp = mtod(m, struct idp *); idp->idp_len = 0; idp->idp_sum = 0; idp->idp_tc = 0; idp->idp_pt = nsp->nsp_dpt; idp->idp_dna = nsp->nsp_faddr; idp->idp_sna = nsp->nsp_laddr; } break; case SO_SEQNO: m->m_len = sizeof(long); *mtod(m, long *) = ns_pexseq++; break; default: error = EINVAL; } *value = m; break; case PRCO_SETOPT: switch (name) { int *ok; case SO_ALL_PACKETS: mask = NSP_ALL_PACKETS; goto set_head; case SO_HEADERS_ON_INPUT: mask = NSP_RAWIN; goto set_head; case SO_HEADERS_ON_OUTPUT: mask = NSP_RAWOUT; set_head: if (value && *value) { ok = mtod(*value, int *); if (*ok) nsp->nsp_flags |= mask; else nsp->nsp_flags &= ~mask; } else error = EINVAL; break; case SO_DEFAULT_HEADERS: { register struct idp *idp = mtod(*value, struct idp *); nsp->nsp_dpt = idp->idp_pt; } break; #ifdef NSIP case SO_NSIP_ROUTE: error = nsip_route(*value); break; #endif /* NSIP */ default: error = EINVAL; } if (value && *value) m_freem(*value); break; } return (error); } /*ARGSUSED*/ int idp_usrreq(so, req, m, nam, control) struct socket *so; int req; struct mbuf *m, *nam, *control; { struct nspcb *nsp = sotonspcb(so); int error = 0; if (req == PRU_CONTROL) return (ns_control(so, (long)m, (caddr_t)nam, (struct ifnet *)control)); if (control && control->m_len) { error = EINVAL; goto release; } if (nsp == NULL && req != PRU_ATTACH) { error = EINVAL; goto release; } switch (req) { case PRU_ATTACH: if (nsp != NULL) { error = EINVAL; break; } error = ns_pcballoc(so, &nspcb); if (error) break; error = soreserve(so, (u_long) 2048, (u_long) 2048); if (error) break; break; case PRU_DETACH: if (nsp == NULL) { error = ENOTCONN; break; } ns_pcbdetach(nsp); break; case PRU_BIND: error = ns_pcbbind(nsp, nam); break; case PRU_LISTEN: error = EOPNOTSUPP; break; case PRU_CONNECT: if (!ns_nullhost(nsp->nsp_faddr)) { error = EISCONN; break; } error = ns_pcbconnect(nsp, nam); if (error == 0) soisconnected(so); break; case PRU_CONNECT2: error = EOPNOTSUPP; break; case PRU_ACCEPT: error = EOPNOTSUPP; break; case PRU_DISCONNECT: if (ns_nullhost(nsp->nsp_faddr)) { error = ENOTCONN; break; } ns_pcbdisconnect(nsp); soisdisconnected(so); break; case PRU_SHUTDOWN: socantsendmore(so); break; case PRU_SEND: { struct ns_addr laddr; int s = 0; if (nam) { laddr = nsp->nsp_laddr; if (!ns_nullhost(nsp->nsp_faddr)) { error = EISCONN; break; } /* * Must block input while temporarily connected. */ s = splsoftnet(); error = ns_pcbconnect(nsp, nam); if (error) { splx(s); break; } } else { if (ns_nullhost(nsp->nsp_faddr)) { error = ENOTCONN; break; } } error = idp_output(m, nsp); m = NULL; if (nam) { ns_pcbdisconnect(nsp); splx(s); nsp->nsp_laddr.x_host = laddr.x_host; nsp->nsp_laddr.x_port = laddr.x_port; } } break; case PRU_ABORT: ns_pcbdetach(nsp); sofree(so); soisdisconnected(so); break; case PRU_SOCKADDR: ns_setsockaddr(nsp, nam); break; case PRU_PEERADDR: ns_setpeeraddr(nsp, nam); break; case PRU_SENSE: /* * stat: don't bother with a blocksize. */ return (0); case PRU_SENDOOB: case PRU_FASTTIMO: case PRU_SLOWTIMO: case PRU_PROTORCV: case PRU_PROTOSEND: error = EOPNOTSUPP; break; case PRU_CONTROL: case PRU_RCVD: case PRU_RCVOOB: return (EOPNOTSUPP); /* do not free mbuf's */ default: panic("idp_usrreq"); } release: if (control != NULL) m_freem(control); if (m != NULL) m_freem(m); return (error); } /*ARGSUSED*/ int idp_raw_usrreq(so, req, m, nam, control) struct socket *so; int req; struct mbuf *m, *nam, *control; { int error = 0; struct nspcb *nsp = sotonspcb(so); extern struct nspcb nsrawpcb; switch (req) { case PRU_ATTACH: if (!(so->so_state & SS_PRIV) || (nsp != NULL)) { error = EINVAL; break; } error = ns_pcballoc(so, &nsrawpcb); if (error) break; error = soreserve(so, (u_long) 2048, (u_long) 2048); if (error) break; nsp = sotonspcb(so); nsp->nsp_faddr.x_host = ns_broadhost; nsp->nsp_flags = NSP_RAWIN | NSP_RAWOUT; break; default: error = idp_usrreq(so, req, m, nam, control); } return (error); }