/* $OpenBSD: raw_ip.c,v 1.14 1999/01/08 21:51:23 provos Exp $ */ /* $NetBSD: raw_ip.c,v 1.25 1996/02/18 18:58:33 christos Exp $ */ /* * Copyright (c) 1982, 1986, 1988, 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. * * @(#)raw_ip.c 8.2 (Berkeley) 1/4/94 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef IPSEC extern int check_ipsec_policy __P((struct inpcb *, u_int32_t)); #endif #include struct inpcbtable rawcbtable; /* * Nominal space allocated to a raw ip socket. */ #define RIPSNDQ 8192 #define RIPRCVQ 8192 /* * Raw interface to IP protocol. */ /* * Initialize raw connection block q. */ void rip_init() { in_pcbinit(&rawcbtable, 1); } struct sockaddr_in ripsrc = { sizeof(ripsrc), AF_INET }; /* * Setup generic address and protocol structures * for raw_input routine, then pass them along with * mbuf chain. */ void #if __STDC__ rip_input(struct mbuf *m, ...) #else rip_input(m, va_alist) struct mbuf *m; va_dcl #endif { register struct ip *ip = mtod(m, struct ip *); register struct inpcb *inp; struct socket *last = 0; ripsrc.sin_addr = ip->ip_src; for (inp = rawcbtable.inpt_queue.cqh_first; inp != (struct inpcb *)&rawcbtable.inpt_queue; inp = inp->inp_queue.cqe_next) { if (inp->inp_ip.ip_p && inp->inp_ip.ip_p != ip->ip_p) continue; if (inp->inp_laddr.s_addr && inp->inp_laddr.s_addr != ip->ip_dst.s_addr) continue; if (inp->inp_faddr.s_addr && inp->inp_faddr.s_addr != ip->ip_src.s_addr) continue; if (last) { struct mbuf *n; if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) { if (sbappendaddr(&last->so_rcv, sintosa(&ripsrc), n, (struct mbuf *)0) == 0) /* should notify about lost packet */ m_freem(n); else sorwakeup(last); } } last = inp->inp_socket; } if (last) { if (sbappendaddr(&last->so_rcv, sintosa(&ripsrc), m, (struct mbuf *)0) == 0) m_freem(m); else sorwakeup(last); } else { m_freem(m); ipstat.ips_noproto++; ipstat.ips_delivered--; } } /* * Generate IP header and pass packet to ip_output. * Tack on options user may have setup with control call. */ int #if __STDC__ rip_output(struct mbuf *m, ...) #else rip_output(m, va_alist) struct mbuf *m; va_dcl #endif { struct socket *so; u_long dst; register struct ip *ip; register struct inpcb *inp; int flags; va_list ap; va_start(ap, m); so = va_arg(ap, struct socket *); dst = va_arg(ap, u_long); va_end(ap); inp = sotoinpcb(so); flags = (so->so_options & SO_DONTROUTE) | IP_ALLOWBROADCAST; /* * If the user handed us a complete IP packet, use it. * Otherwise, allocate an mbuf for a header and fill it in. */ if ((inp->inp_flags & INP_HDRINCL) == 0) { if ((m->m_pkthdr.len + sizeof(struct ip)) > IP_MAXPACKET) { m_freem(m); return (EMSGSIZE); } M_PREPEND(m, sizeof(struct ip), M_WAIT); ip = mtod(m, struct ip *); ip->ip_tos = 0; ip->ip_off = 0; ip->ip_p = inp->inp_ip.ip_p; ip->ip_len = m->m_pkthdr.len; ip->ip_src = inp->inp_laddr; ip->ip_dst.s_addr = dst; ip->ip_ttl = MAXTTL; } else { if (m->m_pkthdr.len > IP_MAXPACKET) { m_freem(m); return (EMSGSIZE); } ip = mtod(m, struct ip *); NTOHS(ip->ip_len); NTOHS(ip->ip_off); /* * don't allow both user specified and setsockopt options, * and don't allow packet length sizes that will crash */ if ((ip->ip_hl != (sizeof (*ip) >> 2) && inp->inp_options) || ip->ip_len > m->m_pkthdr.len || ip->ip_len < ip->ip_hl << 2) { m_freem(m); return (EINVAL); } if (ip->ip_id == 0) { ip->ip_id = ip_randomid(); HTONS(ip->ip_id); } /* XXX prevent ip_output from overwriting header fields */ flags |= IP_RAWOUTPUT; ipstat.ips_rawout++; } return (ip_output(m, inp->inp_options, &inp->inp_route, flags, inp->inp_moptions, inp)); } /* * Raw IP socket option processing. */ int rip_ctloutput(op, so, level, optname, m) int op; struct socket *so; int level, optname; struct mbuf **m; { register struct inpcb *inp = sotoinpcb(so); register int error; if (level != IPPROTO_IP) { if (op == PRCO_SETOPT && *m) (void) m_free(*m); return (EINVAL); } switch (optname) { case IP_HDRINCL: error = 0; if (op == PRCO_SETOPT) { if (*m == 0 || (*m)->m_len < sizeof (int)) error = EINVAL; else if (*mtod(*m, int *)) inp->inp_flags |= INP_HDRINCL; else inp->inp_flags &= ~INP_HDRINCL; if (*m) (void)m_free(*m); } else { *m = m_get(M_WAIT, M_SOOPTS); (*m)->m_len = sizeof(int); *mtod(*m, int *) = inp->inp_flags & INP_HDRINCL; } return (error); case MRT_INIT: case MRT_DONE: case MRT_ADD_VIF: case MRT_DEL_VIF: case MRT_ADD_MFC: case MRT_DEL_MFC: case MRT_VERSION: case MRT_ASSERT: #ifdef MROUTING switch (op) { case PRCO_SETOPT: error = ip_mrouter_set(optname, so, m); break; case PRCO_GETOPT: error = ip_mrouter_get(optname, so, m); break; default: error = EINVAL; break; } return (error); #else if (op == PRCO_SETOPT && *m) m_free(*m); return (EOPNOTSUPP); #endif } return (ip_ctloutput(op, so, level, optname, m)); } u_long rip_sendspace = RIPSNDQ; u_long rip_recvspace = RIPRCVQ; /*ARGSUSED*/ int rip_usrreq(so, req, m, nam, control) register struct socket *so; int req; struct mbuf *m, *nam, *control; { register int error = 0; register struct inpcb *inp = sotoinpcb(so); #ifdef MROUTING extern struct socket *ip_mrouter; #endif if (req == PRU_CONTROL) return (in_control(so, (u_long)m, (caddr_t)nam, (struct ifnet *)control)); if (inp == NULL && req != PRU_ATTACH) { error = EINVAL; goto release; } switch (req) { case PRU_ATTACH: if (inp) panic("rip_attach"); if ((so->so_state & SS_PRIV) == 0) { error = EACCES; break; } if ((error = soreserve(so, rip_sendspace, rip_recvspace)) || (error = in_pcballoc(so, &rawcbtable))) break; inp = (struct inpcb *)so->so_pcb; inp->inp_ip.ip_p = (long)nam; break; case PRU_DISCONNECT: if ((so->so_state & SS_ISCONNECTED) == 0) { error = ENOTCONN; break; } /* FALLTHROUGH */ case PRU_ABORT: soisdisconnected(so); /* FALLTHROUGH */ case PRU_DETACH: if (inp == 0) panic("rip_detach"); #ifdef MROUTING if (so == ip_mrouter) ip_mrouter_done(); #endif in_pcbdetach(inp); break; case PRU_BIND: { struct sockaddr_in *addr = mtod(nam, struct sockaddr_in *); if (nam->m_len != sizeof(*addr)) { error = EINVAL; break; } if ((ifnet.tqh_first == 0) || ((addr->sin_family != AF_INET) && (addr->sin_family != AF_IMPLINK)) || (addr->sin_addr.s_addr && ifa_ifwithaddr(sintosa(addr)) == 0)) { error = EADDRNOTAVAIL; break; } inp->inp_laddr = addr->sin_addr; break; } case PRU_CONNECT: { struct sockaddr_in *addr = mtod(nam, struct sockaddr_in *); if (nam->m_len != sizeof(*addr)) { error = EINVAL; break; } if (ifnet.tqh_first == 0) { error = EADDRNOTAVAIL; break; } if ((addr->sin_family != AF_INET) && (addr->sin_family != AF_IMPLINK)) { error = EAFNOSUPPORT; break; } inp->inp_faddr = addr->sin_addr; soisconnected(so); break; } case PRU_CONNECT2: error = EOPNOTSUPP; break; /* * Mark the connection as being incapable of further input. */ case PRU_SHUTDOWN: socantsendmore(so); break; /* * Ship a packet out. The appropriate raw output * routine handles any massaging necessary. */ case PRU_SEND: { register u_int32_t dst; if (so->so_state & SS_ISCONNECTED) { if (nam) { error = EISCONN; break; } dst = inp->inp_faddr.s_addr; } else { if (nam == NULL) { error = ENOTCONN; break; } dst = mtod(nam, struct sockaddr_in *)->sin_addr.s_addr; } #ifdef IPSEC if (!(error = check_ipsec_policy(inp, dst))) #endif error = rip_output(m, so, dst); m = NULL; break; } case PRU_SENSE: /* * stat: don't bother with a blocksize. */ return (0); /* * Not supported. */ case PRU_RCVOOB: case PRU_RCVD: case PRU_LISTEN: case PRU_ACCEPT: case PRU_SENDOOB: error = EOPNOTSUPP; break; case PRU_SOCKADDR: in_setsockaddr(inp, nam); break; case PRU_PEERADDR: in_setpeeraddr(inp, nam); break; default: panic("rip_usrreq"); } release: if (m != NULL) m_freem(m); return (error); }