| version 1.12, 1994/02/10 18:46:05 |
version 1.13, 1994/05/13 06:06:30 |
|
|
| /* |
/* |
| * Copyright (c) 1982, 1986, 1988 Regents of the University of California. |
* Copyright (c) 1982, 1986, 1988, 1993 |
| * All rights reserved. |
* The Regents of the University of California. All rights reserved. |
| * |
* |
| * Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
* modification, are permitted provided that the following conditions |
|
|
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
* SUCH DAMAGE. |
| * |
* |
| * from: @(#)raw_ip.c 7.8 (Berkeley) 7/25/90 |
* from: @(#)raw_ip.c 8.2 (Berkeley) 1/4/94 |
| * $Id$ |
* $Id$ |
| */ |
*/ |
| |
|
|
|
| #include <sys/protosw.h> |
#include <sys/protosw.h> |
| #include <sys/socketvar.h> |
#include <sys/socketvar.h> |
| #include <sys/errno.h> |
#include <sys/errno.h> |
| |
#include <sys/systm.h> |
| |
|
| #include <net/if.h> |
#include <net/if.h> |
| #include <net/route.h> |
#include <net/route.h> |
| #include <net/raw_cb.h> |
|
| |
|
| #include <netinet/in.h> |
#include <netinet/in.h> |
| #include <netinet/in_systm.h> |
#include <netinet/in_systm.h> |
| #include <netinet/ip.h> |
#include <netinet/ip.h> |
| #include <netinet/ip_var.h> |
#include <netinet/ip_var.h> |
| #include <netinet/in_pcb.h> |
|
| #include <netinet/ip_mroute.h> |
#include <netinet/ip_mroute.h> |
| |
#include <netinet/in_pcb.h> |
| |
|
| |
struct inpcb rawinpcb; |
| |
|
| |
/* |
| |
* Nominal space allocated to a raw ip socket. |
| |
*/ |
| |
#define RIPSNDQ 8192 |
| |
#define RIPRCVQ 8192 |
| |
|
| /* |
/* |
| * Raw interface to IP protocol. |
* Raw interface to IP protocol. |
| */ |
*/ |
| struct sockaddr_in ripdst = { sizeof(ripdst), AF_INET }; |
|
| struct sockaddr_in ripsrc = { sizeof(ripsrc), AF_INET }; |
|
| struct sockproto ripproto = { PF_INET }; |
|
| |
|
| /* |
/* |
| |
* Initialize raw connection block q. |
| |
*/ |
| |
void |
| |
rip_init() |
| |
{ |
| |
|
| |
rawinpcb.inp_next = rawinpcb.inp_prev = &rawinpcb; |
| |
} |
| |
|
| |
struct sockaddr_in ripsrc = { sizeof(ripsrc), AF_INET }; |
| |
/* |
| * Setup generic address and protocol structures |
* Setup generic address and protocol structures |
| * for raw_input routine, then pass them along with |
* for raw_input routine, then pass them along with |
| * mbuf chain. |
* mbuf chain. |
|
|
| struct mbuf *m; |
struct mbuf *m; |
| { |
{ |
| register struct ip *ip = mtod(m, struct ip *); |
register struct ip *ip = mtod(m, struct ip *); |
| |
register struct inpcb *inp; |
| |
struct socket *last = 0; |
| |
|
| ripproto.sp_protocol = ip->ip_p; |
|
| ripdst.sin_addr = ip->ip_dst; |
|
| ripsrc.sin_addr = ip->ip_src; |
ripsrc.sin_addr = ip->ip_src; |
| if (raw_input(m, &ripproto, (struct sockaddr *)&ripsrc, |
for (inp = rawinpcb.inp_next; inp != &rawinpcb; inp = inp->inp_next) { |
| (struct sockaddr *)&ripdst) == 0) { |
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)) { |
| |
if (sbappendaddr(&last->so_rcv, |
| |
(struct sockaddr *)&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, (struct sockaddr *)&ripsrc, |
| |
m, (struct mbuf *)0) == 0) |
| |
m_freem(m); |
| |
else |
| |
sorwakeup(last); |
| |
} else { |
| |
m_freem(m); |
| ipstat.ips_noproto++; |
ipstat.ips_noproto++; |
| ipstat.ips_delivered--; |
ipstat.ips_delivered--; |
| } |
} |
|
|
| * Generate IP header and pass packet to ip_output. |
* Generate IP header and pass packet to ip_output. |
| * Tack on options user may have setup with control call. |
* Tack on options user may have setup with control call. |
| */ |
*/ |
| #define satosin(sa) ((struct sockaddr_in *)(sa)) |
|
| int |
int |
| rip_output(m, so) |
rip_output(m, so, dst) |
| register struct mbuf *m; |
register struct mbuf *m; |
| struct socket *so; |
struct socket *so; |
| |
u_long dst; |
| { |
{ |
| register struct ip *ip; |
register struct ip *ip; |
| register struct raw_inpcb *rp = sotorawinpcb(so); |
register struct inpcb *inp = sotoinpcb(so); |
| register struct sockaddr_in *sin; |
|
| struct mbuf *opts; |
struct mbuf *opts; |
| |
int flags = (so->so_options & SO_DONTROUTE) | IP_ALLOWBROADCAST; |
| |
|
| /* |
/* |
| * If the user handed us a complete IP packet, use it. |
* If the user handed us a complete IP packet, use it. |
| * Otherwise, allocate an mbuf for a header and fill it in. |
* Otherwise, allocate an mbuf for a header and fill it in. |
| */ |
*/ |
| if (rp->rinp_flags & RINPF_HDRINCL) { |
if ((inp->inp_flags & INP_HDRINCL) == 0) { |
| ip = mtod(m, struct ip *); |
|
| if (ip->ip_len > m->m_pkthdr.len) |
|
| return EMSGSIZE; |
|
| ip->ip_len = m->m_pkthdr.len; |
|
| opts = NULL; |
|
| } else { |
|
| M_PREPEND(m, sizeof(struct ip), M_WAIT); |
M_PREPEND(m, sizeof(struct ip), M_WAIT); |
| ip = mtod(m, struct ip *); |
ip = mtod(m, struct ip *); |
| ip->ip_tos = 0; |
ip->ip_tos = 0; |
| ip->ip_off = 0; |
ip->ip_off = 0; |
| ip->ip_p = rp->rinp_rcb.rcb_proto.sp_protocol; |
ip->ip_p = inp->inp_ip.ip_p; |
| ip->ip_len = m->m_pkthdr.len; |
ip->ip_len = m->m_pkthdr.len; |
| if (sin = satosin(rp->rinp_rcb.rcb_laddr)) { |
ip->ip_src = inp->inp_laddr; |
| ip->ip_src = sin->sin_addr; |
ip->ip_dst.s_addr = dst; |
| } else |
|
| ip->ip_src.s_addr = 0; |
|
| if (sin = satosin(rp->rinp_rcb.rcb_faddr)) |
|
| ip->ip_dst = sin->sin_addr; |
|
| ip->ip_ttl = MAXTTL; |
ip->ip_ttl = MAXTTL; |
| opts = rp->rinp_options; |
opts = inp->inp_options; |
| |
} else { |
| |
ip = mtod(m, struct ip *); |
| |
if (ip->ip_id == 0) |
| |
ip->ip_id = htons(ip_id++); |
| |
opts = NULL; |
| |
/* XXX prevent ip_output from overwriting header fields */ |
| |
flags |= IP_RAWOUTPUT; |
| |
ipstat.ips_rawout++; |
| } |
} |
| return (ip_output(m, opts, &rp->rinp_route, |
return (ip_output(m, opts, &inp->inp_route, flags, inp->inp_moptions)); |
| (so->so_options & SO_DONTROUTE) | IP_ALLOWBROADCAST, |
|
| rp->rinp_rcb.rcb_moptions)); |
|
| } |
} |
| |
|
| /* |
/* |
| Line 137 rip_ctloutput(op, so, level, optname, m) |
|
| Line 178 rip_ctloutput(op, so, level, optname, m) |
|
| int level, optname; |
int level, optname; |
| struct mbuf **m; |
struct mbuf **m; |
| { |
{ |
| int error = 0; |
register struct inpcb *inp = sotoinpcb(so); |
| register struct raw_inpcb *rp = sotorawinpcb(so); |
register int error; |
| |
|
| if (level != IPPROTO_IP) |
if (level != IPPROTO_IP) |
| error = EINVAL; |
return (EINVAL); |
| else switch (op) { |
|
| |
|
| case PRCO_SETOPT: |
|
| switch (optname) { |
|
| |
|
| case IP_OPTIONS: |
switch (optname) { |
| return (ip_pcbopts(&rp->rinp_options, *m)); |
|
| |
|
| case IP_HDRINCL: |
case IP_HDRINCL: |
| if (m == 0 || *m == 0 || (*m)->m_len < sizeof (int)) { |
if (op == PRCO_SETOPT || op == PRCO_GETOPT) { |
| error = EINVAL; |
if (m == 0 || *m == 0 || (*m)->m_len < sizeof (int)) |
| break; |
return (EINVAL); |
| |
if (op == PRCO_SETOPT) { |
| |
if (*mtod(*m, int *)) |
| |
inp->inp_flags |= INP_HDRINCL; |
| |
else |
| |
inp->inp_flags &= ~INP_HDRINCL; |
| |
(void)m_free(*m); |
| |
} else { |
| |
(*m)->m_len = sizeof (int); |
| |
*mtod(*m, int *) = inp->inp_flags & INP_HDRINCL; |
| } |
} |
| if (*mtod(*m, int *)) |
return (0); |
| rp->rinp_flags |= RINPF_HDRINCL; |
|
| else |
|
| rp->rinp_flags &= ~RINPF_HDRINCL; |
|
| break; |
|
| case IP_MULTICAST_IF: |
|
| case IP_MULTICAST_TTL: |
|
| case IP_MULTICAST_LOOP: |
|
| case IP_ADD_MEMBERSHIP: |
|
| case IP_DROP_MEMBERSHIP: |
|
| error = ip_setmoptions(optname, |
|
| &rp->rinp_rcb.rcb_moptions, *m); |
|
| break; |
|
| default: |
|
| #ifdef MROUTING |
|
| error = ip_mrouter_cmd(optname, so, *m); |
|
| #else |
|
| error = EINVAL; |
|
| #endif |
|
| break; |
|
| } |
} |
| break; |
break; |
| |
|
| case PRCO_GETOPT: |
case DVMRP_INIT: |
| *m = m_get(M_WAIT, MT_SOOPTS); |
case DVMRP_DONE: |
| switch (optname) { |
case DVMRP_ADD_VIF: |
| |
case DVMRP_DEL_VIF: |
| case IP_OPTIONS: |
case DVMRP_ADD_LGRP: |
| if (rp->rinp_options) { |
case DVMRP_DEL_LGRP: |
| (*m)->m_len = rp->rinp_options->m_len; |
case DVMRP_ADD_MRT: |
| bcopy(mtod(rp->rinp_options, caddr_t), |
case DVMRP_DEL_MRT: |
| mtod(*m, caddr_t), (unsigned)(*m)->m_len); |
#ifdef MROUTING |
| } else |
if (op == PRCO_SETOPT) { |
| (*m)->m_len = 0; |
error = ip_mrouter_cmd(optname, so, *m); |
| break; |
if (*m) |
| |
(void)m_free(*m); |
| case IP_HDRINCL: |
} else |
| (*m)->m_len = sizeof (int); |
|
| *mtod(*m, int *) = rp->rinp_flags & RINPF_HDRINCL; |
|
| break; |
|
| case IP_MULTICAST_IF: |
|
| case IP_MULTICAST_TTL: |
|
| case IP_MULTICAST_LOOP: |
|
| case IP_ADD_MEMBERSHIP: |
|
| case IP_DROP_MEMBERSHIP: |
|
| error = ip_getmoptions(optname, |
|
| rp->rinp_rcb.rcb_moptions, m); |
|
| break; |
|
| default: |
|
| error = EINVAL; |
error = EINVAL; |
| m_freem(*m); |
return (error); |
| *m = 0; |
#else |
| break; |
if (op == PRCO_SETOPT && *m) |
| } |
(void)m_free(*m); |
| break; |
return (EOPNOTSUPP); |
| |
#endif |
| } |
} |
| if (op == PRCO_SETOPT && *m) |
return (ip_ctloutput(op, so, level, optname, m)); |
| (void)m_free(*m); |
|
| return (error); |
|
| } |
} |
| |
|
| |
u_long rip_sendspace = RIPSNDQ; |
| |
u_long rip_recvspace = RIPRCVQ; |
| |
|
| /*ARGSUSED*/ |
/*ARGSUSED*/ |
| int |
int |
| rip_usrreq(so, req, m, nam, control) |
rip_usrreq(so, req, m, nam, control) |
| Line 224 rip_usrreq(so, req, m, nam, control) |
|
| Line 240 rip_usrreq(so, req, m, nam, control) |
|
| struct mbuf *m, *nam, *control; |
struct mbuf *m, *nam, *control; |
| { |
{ |
| register int error = 0; |
register int error = 0; |
| register struct raw_inpcb *rp = sotorawinpcb(so); |
register struct inpcb *inp = sotoinpcb(so); |
| #if defined(MROUTING) |
#ifdef MROUTING |
| extern struct socket *ip_mrouter; |
extern struct socket *ip_mrouter; |
| #endif |
#endif |
| switch (req) { |
switch (req) { |
| |
|
| case PRU_ATTACH: |
case PRU_ATTACH: |
| if (rp) |
if (inp) |
| panic("rip_attach"); |
panic("rip_attach"); |
| MALLOC(rp, struct raw_inpcb *, sizeof *rp, M_PCB, M_WAITOK); |
if ((so->so_state & SS_PRIV) == 0) { |
| if (rp == 0) |
error = EACCES; |
| return (ENOBUFS); |
break; |
| bzero((caddr_t)rp, sizeof *rp); |
} |
| so->so_pcb = (caddr_t)rp; |
if ((error = soreserve(so, rip_sendspace, rip_recvspace)) || |
| |
(error = in_pcballoc(so, &rawinpcb))) |
| |
break; |
| |
inp = (struct inpcb *)so->so_pcb; |
| |
inp->inp_ip.ip_p = (int)nam; |
| break; |
break; |
| |
|
| |
case PRU_DISCONNECT: |
| |
if ((so->so_state & SS_ISCONNECTED) == 0) { |
| |
error = ENOTCONN; |
| |
break; |
| |
} |
| |
/* FALLTHROUGH */ |
| |
case PRU_ABORT: |
| |
soisdisconnected(so); |
| |
/* FALLTHROUGH */ |
| case PRU_DETACH: |
case PRU_DETACH: |
| if (rp == 0) |
if (inp == 0) |
| panic("rip_detach"); |
panic("rip_detach"); |
| #if defined(MROUTING) |
#ifdef MROUTING |
| if (so == ip_mrouter) |
if (so == ip_mrouter) |
| ip_mrouter_done(); |
ip_mrouter_done(); |
| #endif |
#endif |
| if (rp->rinp_options) |
in_pcbdetach(inp); |
| m_freem(rp->rinp_options); |
|
| if (rp->rinp_rcb.rcb_moptions) |
|
| ip_freemoptions(rp->rinp_rcb.rcb_moptions); |
|
| if (rp->rinp_route.ro_rt) |
|
| RTFREE(rp->rinp_route.ro_rt); |
|
| if (rp->rinp_rcb.rcb_laddr) |
|
| rp->rinp_rcb.rcb_laddr = 0; |
|
| break; |
break; |
| |
|
| case PRU_BIND: |
case PRU_BIND: |
| { |
{ |
| struct sockaddr_in *addr = mtod(nam, struct sockaddr_in *); |
struct sockaddr_in *addr = mtod(nam, struct sockaddr_in *); |
| |
|
| if (nam->m_len != sizeof(*addr)) |
if (nam->m_len != sizeof(*addr)) { |
| return (EINVAL); |
error = EINVAL; |
| |
break; |
| |
} |
| if ((ifnet == 0) || |
if ((ifnet == 0) || |
| ((addr->sin_family != AF_INET) && |
((addr->sin_family != AF_INET) && |
| (addr->sin_family != AF_IMPLINK)) || |
(addr->sin_family != AF_IMPLINK)) || |
| (addr->sin_addr.s_addr && |
(addr->sin_addr.s_addr && |
| ifa_ifwithaddr((struct sockaddr *)addr) == 0)) |
ifa_ifwithaddr((struct sockaddr *)addr) == 0)) { |
| return (EADDRNOTAVAIL); |
error = EADDRNOTAVAIL; |
| rp->rinp_rcb.rcb_laddr = (struct sockaddr *)&rp->rinp_laddr; |
break; |
| rp->rinp_laddr = *addr; |
} |
| return (0); |
inp->inp_laddr = addr->sin_addr; |
| |
break; |
| } |
} |
| case PRU_CONNECT: |
case PRU_CONNECT: |
| { |
{ |
| struct sockaddr_in *addr = mtod(nam, struct sockaddr_in *); |
struct sockaddr_in *addr = mtod(nam, struct sockaddr_in *); |
| |
|
| if (nam->m_len != sizeof(*addr)) |
if (nam->m_len != sizeof(*addr)) { |
| return (EINVAL); |
error = EINVAL; |
| if (ifnet == 0) |
break; |
| return (EADDRNOTAVAIL); |
} |
| |
if (ifnet == 0) { |
| |
error = EADDRNOTAVAIL; |
| |
break; |
| |
} |
| if ((addr->sin_family != AF_INET) && |
if ((addr->sin_family != AF_INET) && |
| (addr->sin_family != AF_IMPLINK)) |
(addr->sin_family != AF_IMPLINK)) { |
| return (EAFNOSUPPORT); |
error = EAFNOSUPPORT; |
| rp->rinp_rcb.rcb_faddr = (struct sockaddr *)&rp->rinp_faddr; |
break; |
| rp->rinp_faddr = *addr; |
} |
| |
inp->inp_faddr = addr->sin_addr; |
| soisconnected(so); |
soisconnected(so); |
| return (0); |
break; |
| } |
} |
| } |
|
| error = raw_usrreq(so, req, m, nam, control); |
|
| |
|
| if (error && (req == PRU_ATTACH) && so->so_pcb) |
case PRU_CONNECT2: |
| free(so->so_pcb, M_PCB); |
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_long 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; |
| |
} |
| |
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"); |
| |
} |
| |
if (m != NULL) |
| |
m_freem(m); |
| return (error); |
return (error); |
| } |
} |