Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet/ip_divert.c
Context not available. | |||||
#ifdef SCTP | #ifdef SCTP | ||||
#include <netinet/sctp_crc32.h> | #include <netinet/sctp_crc32.h> | ||||
#endif | #endif | ||||
#include <netinet/ip_divert.h> | |||||
#include <net/route.h> | |||||
#include <security/mac/mac_framework.h> | #include <security/mac/mac_framework.h> | ||||
/* | /* | ||||
Context not available. | |||||
* the packet filter) and information on the matching filter rule for | * the packet filter) and information on the matching filter rule for | ||||
* subsequent reinjection. The divert_port is used to put the packet | * subsequent reinjection. The divert_port is used to put the packet | ||||
* on the corresponding divert socket, while the rule number is passed | * on the corresponding divert socket, while the rule number is passed | ||||
* up (at least partially) as the sin_port in the struct sockaddr. | * up as the sdiv_port in the struct sockaddr. | ||||
* | * | ||||
* Packets written to the divert socket carry in sin_addr a | * Packets written to the divert socket carry in sdiv_addr a | ||||
* destination address, and in sin_port the number of the filter rule | * destination address, and in sdiv_port the number of the filter rule | ||||
* after which to continue processing. | * after which to continue processing. | ||||
* If the destination address is INADDR_ANY, the packet is treated as | * If the destination address is INADDR_ANY, the packet is treated as | ||||
* as outgoing and sent to ip_output(); otherwise it is treated as | * as outgoing and sent to ip_output(); otherwise it is treated as | ||||
* incoming and sent to ip_input(). | * incoming and sent to ip_input(). sdiv_ifnam and sdiv_fib are set up | ||||
* Further, sin_zero carries some information on the interface, | * with the interface name the packet came in on, c.q. the FIB number on the | ||||
* which can be used in the reinject -- see comments in the code. | * packet. | ||||
* | * | ||||
* On reinjection, processing in ip_input() and ip_output() | * On reinjection, processing in ip_input() and ip_output() | ||||
* will be exactly the same as for the original packet, except that | * will be exactly the same as for the original packet, except that | ||||
* packet filter processing will start at the rule number after the one | * packet filter processing will start at the rule number after the one | ||||
* written in the sin_port (ipfw does not allow a rule #0, so sin_port=0 | * written in the sdiv_port (ipfw does not allow a rule #0, so sdiv_port=0 | ||||
* will apply the entire ruleset to the packet). | * will apply the entire ruleset to the packet). sdiv_fib is set on the | ||||
* outgoing packet. | |||||
*/ | */ | ||||
/* Internal variables. */ | /* Internal variables. */ | ||||
Context not available. | |||||
static eventhandler_tag ip_divert_event_tag; | static eventhandler_tag ip_divert_event_tag; | ||||
static int div_output_inbound(int fmaily, struct socket *so, struct mbuf *m, | static int div_output_inbound(int fmaily, struct socket *so, struct mbuf *m, | ||||
struct sockaddr_in *sin); | struct sockaddr_div *sdiv); | ||||
static int div_output_outbound(int family, struct socket *so, struct mbuf *m); | static int div_output_outbound(int family, struct socket *so, struct mbuf *m); | ||||
/* | /* | ||||
Context not available. | |||||
struct inpcb *inp; | struct inpcb *inp; | ||||
struct socket *sa; | struct socket *sa; | ||||
u_int16_t nport; | u_int16_t nport; | ||||
struct sockaddr_in divsrc; | struct sockaddr_div divsrc; | ||||
struct m_tag *mtag; | struct m_tag *mtag; | ||||
NET_EPOCH_ASSERT(); | NET_EPOCH_ASSERT(); | ||||
Context not available. | |||||
} | } | ||||
#endif | #endif | ||||
bzero(&divsrc, sizeof(divsrc)); | bzero(&divsrc, sizeof(divsrc)); | ||||
divsrc.sin_len = sizeof(divsrc); | divsrc.sdiv_len = sizeof(divsrc); | ||||
melifaro: How do we know if the caller supports old or new format? Abi has to be preserved.
Probably some… | |||||
divsrc.sin_family = AF_INET; | divsrc.sdiv_family = AF_INET; | ||||
/* record matching rule, in host format */ | /* record matching rule, in host byte order */ | ||||
divsrc.sin_port = ((struct ipfw_rule_ref *)(mtag+1))->rulenum; | divsrc.sdiv_port = ((struct ipfw_rule_ref *)(mtag+1))->rulenum; | ||||
/* record fib for packet */ | |||||
divsrc.sdiv_fib = M_GETFIB(m); | |||||
/* | /* | ||||
* Record receive interface address, if any. | * Record receive interface address, if any. | ||||
* But only for incoming packets. | * But only for incoming packets. | ||||
Context not available. | |||||
CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { | CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { | ||||
if (ifa->ifa_addr->sa_family != AF_INET) | if (ifa->ifa_addr->sa_family != AF_INET) | ||||
continue; | continue; | ||||
divsrc.sin_addr = | divsrc.sdiv_addr = | ||||
((struct sockaddr_in *) ifa->ifa_addr)->sin_addr; | ((struct sockaddr_in *) ifa->ifa_addr)->sin_addr; | ||||
break; | break; | ||||
} | } | ||||
Context not available. | |||||
*/ | */ | ||||
if (m->m_pkthdr.rcvif) { | if (m->m_pkthdr.rcvif) { | ||||
/* | /* | ||||
* Hide the actual interface name in there in the | |||||
* sin_zero array. XXX This needs to be moved to a | |||||
* different sockaddr type for divert, e.g. | |||||
* sockaddr_div with multiple fields like | |||||
* sockaddr_dl. Presently we have only 7 bytes | |||||
* but that will do for now as most interfaces | |||||
* are 4 or less + 2 or less bytes for unit. | |||||
* There is probably a faster way of doing this, | * There is probably a faster way of doing this, | ||||
* possibly taking it from the sockaddr_dl on the iface. | * possibly taking it from the sockaddr_dl on the iface. | ||||
* This solves the problem of a P2P link and a LAN interface | * This solves the problem of a P2P link and a LAN interface | ||||
Context not available. | |||||
* this iface name will come along for the ride. | * this iface name will come along for the ride. | ||||
* (see div_output for the other half of this.) | * (see div_output for the other half of this.) | ||||
*/ | */ | ||||
strlcpy(divsrc.sin_zero, m->m_pkthdr.rcvif->if_xname, | strlcpy(divsrc.sdiv_ifnam, m->m_pkthdr.rcvif->if_xname, | ||||
sizeof(divsrc.sin_zero)); | sizeof(divsrc.sdiv_ifnam)); | ||||
} | } | ||||
/* Put packet on socket queue, if any */ | /* Put packet on socket queue, if any */ | ||||
Context not available. | |||||
* the interface with that address. | * the interface with that address. | ||||
*/ | */ | ||||
static int | static int | ||||
div_output(struct socket *so, struct mbuf *m, struct sockaddr_in *sin, | div_output(struct socket *so, struct mbuf *m, struct sockaddr_div *sdiv, | ||||
struct mbuf *control) | struct mbuf *control) | ||||
{ | { | ||||
struct epoch_tracker et; | struct epoch_tracker et; | ||||
Context not available. | |||||
int error, family; | int error, family; | ||||
/* | /* | ||||
* An mbuf may hasn't come from userland, but we pretend | * An mbuf may not have come from userland, but we pretend that it has. | ||||
* that it has. | |||||
*/ | */ | ||||
m->m_pkthdr.rcvif = NULL; | m->m_pkthdr.rcvif = NULL; | ||||
m->m_nextpkt = NULL; | m->m_nextpkt = NULL; | ||||
Context not available. | |||||
dt = (struct ipfw_rule_ref *)(mtag+1); | dt = (struct ipfw_rule_ref *)(mtag+1); | ||||
/* Loopback avoidance and state recovery */ | /* Loopback avoidance and state recovery */ | ||||
if (sin) { | if (sdiv) { | ||||
int i; | int i; | ||||
/* set the starting point. We provide a non-zero slot, | /* set the starting point. We provide a non-zero slot, | ||||
Context not available. | |||||
*/ | */ | ||||
dt->slot = 1; /* dummy, chain_id is invalid */ | dt->slot = 1; /* dummy, chain_id is invalid */ | ||||
dt->chain_id = 0; | dt->chain_id = 0; | ||||
dt->rulenum = sin->sin_port+1; /* host format ? */ | dt->rulenum = sdiv->sdiv_port+1; /* host byte order */ | ||||
melifaroUnsubmitted Not Done Inline ActionsWe need to think about compatibility. Can probably distinguish between old/new by the sin_size. melifaro: We need to think about compatibility. Can probably distinguish between old/new by the sin_size. | |||||
dt->rule_id = 0; | dt->rule_id = 0; | ||||
/* XXX: broken for IPv6 */ | /* XXX: broken for IPv6 */ | ||||
/* | /* | ||||
* Find receive interface with the given name, stuffed | * Find receive interface with the given name. The name is user | ||||
* (if it exists) in the sin_zero[] field. | * supplied data so don't trust its size or that it is zero | ||||
* The name is user supplied data so don't trust its size | * terminated. | ||||
* or that it is zero terminated. | |||||
*/ | */ | ||||
for (i = 0; i < sizeof(sin->sin_zero) && sin->sin_zero[i]; i++) | for (i = 0; i < sizeof(sdiv->sdiv_ifnam) && sdiv->sdiv_ifnam[i]; i++) | ||||
; | ; | ||||
if ( i > 0 && i < sizeof(sin->sin_zero)) | if (i > 0 && i < sizeof(sdiv->sdiv_ifnam)) | ||||
m->m_pkthdr.rcvif = ifunit(sin->sin_zero); | m->m_pkthdr.rcvif = ifunit(sdiv->sdiv_ifnam); | ||||
} | } | ||||
ip = mtod(m, struct ip *); | ip = mtod(m, struct ip *); | ||||
Context not available. | |||||
/* Reinject packet into the system as incoming or outgoing */ | /* Reinject packet into the system as incoming or outgoing */ | ||||
NET_EPOCH_ENTER(et); | NET_EPOCH_ENTER(et); | ||||
if (!sin || sin->sin_addr.s_addr == 0) { | if (!sdiv || sdiv->sdiv_addr.s_addr == 0) { | ||||
dt->info |= IPFW_IS_DIVERT | IPFW_INFO_OUT; | dt->info |= IPFW_IS_DIVERT | IPFW_INFO_OUT; | ||||
error = div_output_outbound(family, so, m); | error = div_output_outbound(family, so, m); | ||||
} else { | } else { | ||||
dt->info |= IPFW_IS_DIVERT | IPFW_INFO_IN; | dt->info |= IPFW_IS_DIVERT | IPFW_INFO_IN; | ||||
error = div_output_inbound(family, so, m, sin); | error = div_output_inbound(family, so, m, sdiv); | ||||
} | } | ||||
NET_EPOCH_EXIT(et); | NET_EPOCH_EXIT(et); | ||||
Context not available. | |||||
*/ | */ | ||||
static int | static int | ||||
div_output_inbound(int family, struct socket *so, struct mbuf *m, | div_output_inbound(int family, struct socket *so, struct mbuf *m, | ||||
struct sockaddr_in *sin) | struct sockaddr_div *sdiv) | ||||
{ | { | ||||
const struct ip *ip; | const struct ip *ip; | ||||
struct ifaddr *ifa; | struct ifaddr *ifa; | ||||
Context not available. | |||||
* there are no distractions for ifa_ifwithaddr. | * there are no distractions for ifa_ifwithaddr. | ||||
*/ | */ | ||||
/* XXX: broken for IPv6 */ | /* XXX: broken for IPv6 */ | ||||
bzero(sin->sin_zero, sizeof(sin->sin_zero)); | bzero(sdiv->sdiv_ifnam, sizeof(sdiv->sdiv_ifnam)); | ||||
sin->sin_port = 0; | sdiv->sdiv_port = 0; | ||||
ifa = ifa_ifwithaddr((struct sockaddr *) sin); | ifa = ifa_ifwithaddr((struct sockaddr *) sdiv); | ||||
if (ifa == NULL) | if (ifa == NULL) | ||||
return (EADDRNOTAVAIL); | return (EADDRNOTAVAIL); | ||||
m->m_pkthdr.rcvif = ifa->ifa_ifp; | m->m_pkthdr.rcvif = ifa->ifa_ifp; | ||||
} | } | ||||
#ifdef MAC | #ifdef MAC | ||||
mac_socket_create_mbuf(so, m); | mac_socket_create_mbuf(so, m); | ||||
#endif | #endif | ||||
Context not available. | |||||
* and in_pcbbind requires a valid address. Since divert | * and in_pcbbind requires a valid address. Since divert | ||||
* sockets don't we need to make sure the address is | * sockets don't we need to make sure the address is | ||||
* filled in properly. | * filled in properly. | ||||
* XXX -- divert should not be abusing in_pcbind | * XXX -- divert should not be abusing in_pcbbind | ||||
* and should probably have its own family. | * and should probably have its own family. | ||||
*/ | */ | ||||
if (nam->sa_family != AF_INET) | if (nam->sa_family != AF_INET) | ||||
Context not available. | |||||
} | } | ||||
/* Send packet */ | /* Send packet */ | ||||
return div_output(so, m, (struct sockaddr_in *)nam, control); | return div_output(so, m, (struct sockaddr_div *)nam, control); | ||||
} | } | ||||
static void | static void | ||||
Context not available. |
How do we know if the caller supports old or new format? Abi has to be preserved.
Probably some setsockopt() or similar is needed..