Add possibility to pass IPv6 packets to a divert(4) socket.
Submitted by: sem
This commit is contained in:
parent
c7b111bbcf
commit
b39e872c06
@ -3,6 +3,11 @@
|
|||||||
.PATH: ${.CURDIR}/../../netinet
|
.PATH: ${.CURDIR}/../../netinet
|
||||||
|
|
||||||
KMOD= ipdivert
|
KMOD= ipdivert
|
||||||
SRCS= ip_divert.c
|
SRCS= ip_divert.c opt_inet6.h
|
||||||
|
|
||||||
|
.if !defined(KERNBUILDDIR)
|
||||||
|
opt_inet6.h:
|
||||||
|
echo "#define INET6 1" > ${.TARGET}
|
||||||
|
.endif
|
||||||
|
|
||||||
.include <bsd.kmod.mk>
|
.include <bsd.kmod.mk>
|
||||||
|
@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$");
|
|||||||
#error "IPDIVERT requires INET."
|
#error "IPDIVERT requires INET."
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
#include "opt_inet6.h"
|
||||||
|
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <sys/kernel.h>
|
#include <sys/kernel.h>
|
||||||
@ -62,6 +63,10 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <netinet/in_var.h>
|
#include <netinet/in_var.h>
|
||||||
#include <netinet/ip.h>
|
#include <netinet/ip.h>
|
||||||
#include <netinet/ip_var.h>
|
#include <netinet/ip_var.h>
|
||||||
|
#ifdef INET6
|
||||||
|
#include <netinet/ip6.h>
|
||||||
|
#include <netinet6/ip6_var.h>
|
||||||
|
#endif
|
||||||
#ifdef SCTP
|
#ifdef SCTP
|
||||||
#include <netinet/sctp_crc32.h>
|
#include <netinet/sctp_crc32.h>
|
||||||
#endif
|
#endif
|
||||||
@ -312,10 +317,10 @@ static int
|
|||||||
div_output(struct socket *so, struct mbuf *m, struct sockaddr_in *sin,
|
div_output(struct socket *so, struct mbuf *m, struct sockaddr_in *sin,
|
||||||
struct mbuf *control)
|
struct mbuf *control)
|
||||||
{
|
{
|
||||||
|
struct ip *const ip = mtod(m, struct ip *);
|
||||||
struct m_tag *mtag;
|
struct m_tag *mtag;
|
||||||
struct ipfw_rule_ref *dt;
|
struct ipfw_rule_ref *dt;
|
||||||
int error = 0;
|
int error = 0;
|
||||||
struct mbuf *options;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* An mbuf may hasn't come from userland, but we pretend
|
* An mbuf may hasn't come from userland, but we pretend
|
||||||
@ -367,71 +372,103 @@ div_output(struct socket *so, struct mbuf *m, struct sockaddr_in *sin,
|
|||||||
|
|
||||||
/* Reinject packet into the system as incoming or outgoing */
|
/* Reinject packet into the system as incoming or outgoing */
|
||||||
if (!sin || sin->sin_addr.s_addr == 0) {
|
if (!sin || sin->sin_addr.s_addr == 0) {
|
||||||
struct ip *const ip = mtod(m, struct ip *);
|
struct mbuf *options = NULL;
|
||||||
struct inpcb *inp;
|
struct inpcb *inp;
|
||||||
|
|
||||||
dt->info |= IPFW_IS_DIVERT | IPFW_INFO_OUT;
|
dt->info |= IPFW_IS_DIVERT | IPFW_INFO_OUT;
|
||||||
inp = sotoinpcb(so);
|
inp = sotoinpcb(so);
|
||||||
INP_RLOCK(inp);
|
INP_RLOCK(inp);
|
||||||
/*
|
switch (ip->ip_v) {
|
||||||
* Don't allow both user specified and setsockopt options,
|
case IPVERSION:
|
||||||
* and don't allow packet length sizes that will crash
|
/*
|
||||||
*/
|
* Don't allow both user specified and setsockopt
|
||||||
if (((ip->ip_hl != (sizeof (*ip) >> 2)) && inp->inp_options) ||
|
* options, and don't allow packet length sizes that
|
||||||
((u_short)ntohs(ip->ip_len) > m->m_pkthdr.len)) {
|
* will crash.
|
||||||
error = EINVAL;
|
*/
|
||||||
INP_RUNLOCK(inp);
|
if ((((ip->ip_hl << 2) != sizeof(struct ip)) &&
|
||||||
m_freem(m);
|
inp->inp_options != NULL) ||
|
||||||
} else {
|
((u_short)ntohs(ip->ip_len) > m->m_pkthdr.len)) {
|
||||||
|
error = EINVAL;
|
||||||
|
INP_RUNLOCK(inp);
|
||||||
|
goto cantsend;
|
||||||
|
}
|
||||||
|
|
||||||
/* Convert fields to host order for ip_output() */
|
/* Convert fields to host order for ip_output() */
|
||||||
ip->ip_len = ntohs(ip->ip_len);
|
ip->ip_len = ntohs(ip->ip_len);
|
||||||
ip->ip_off = ntohs(ip->ip_off);
|
ip->ip_off = ntohs(ip->ip_off);
|
||||||
|
break;
|
||||||
|
#ifdef INET6
|
||||||
|
case IPV6_VERSION >> 4:
|
||||||
|
{
|
||||||
|
struct ip6_hdr *const ip6 = mtod(m, struct ip6_hdr *);
|
||||||
|
|
||||||
/* Send packet to output processing */
|
/* Don't allow packet length sizes that will crash */
|
||||||
KMOD_IPSTAT_INC(ips_rawout); /* XXX */
|
if (((u_short)ntohs(ip6->ip6_plen) > m->m_pkthdr.len)) {
|
||||||
|
error = EINVAL;
|
||||||
|
INP_RUNLOCK(inp);
|
||||||
|
goto cantsend;
|
||||||
|
}
|
||||||
|
|
||||||
|
ip6->ip6_plen = ntohs(ip6->ip6_plen);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
error = EINVAL;
|
||||||
|
INP_RUNLOCK(inp);
|
||||||
|
goto cantsend;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send packet to output processing */
|
||||||
|
KMOD_IPSTAT_INC(ips_rawout); /* XXX */
|
||||||
|
|
||||||
#ifdef MAC
|
#ifdef MAC
|
||||||
mac_inpcb_create_mbuf(inp, m);
|
mac_inpcb_create_mbuf(inp, m);
|
||||||
#endif
|
#endif
|
||||||
/*
|
/*
|
||||||
* Get ready to inject the packet into ip_output().
|
* Get ready to inject the packet into ip_output().
|
||||||
* Just in case socket options were specified on the
|
* Just in case socket options were specified on the
|
||||||
* divert socket, we duplicate them. This is done
|
* divert socket, we duplicate them. This is done
|
||||||
* to avoid having to hold the PCB locks over the call
|
* to avoid having to hold the PCB locks over the call
|
||||||
* to ip_output(), as doing this results in a number of
|
* to ip_output(), as doing this results in a number of
|
||||||
* lock ordering complexities.
|
* lock ordering complexities.
|
||||||
*
|
*
|
||||||
* Note that we set the multicast options argument for
|
* Note that we set the multicast options argument for
|
||||||
* ip_output() to NULL since it should be invariant that
|
* ip_output() to NULL since it should be invariant that
|
||||||
* they are not present.
|
* they are not present.
|
||||||
*/
|
*/
|
||||||
KASSERT(inp->inp_moptions == NULL,
|
KASSERT(inp->inp_moptions == NULL,
|
||||||
("multicast options set on a divert socket"));
|
("multicast options set on a divert socket"));
|
||||||
options = NULL;
|
/*
|
||||||
/*
|
* XXXCSJP: It is unclear to me whether or not it makes
|
||||||
* XXXCSJP: It is unclear to me whether or not it makes
|
* sense for divert sockets to have options. However,
|
||||||
* sense for divert sockets to have options. However,
|
* for now we will duplicate them with the INP locks
|
||||||
* for now we will duplicate them with the INP locks
|
* held so we can use them in ip_output() without
|
||||||
* held so we can use them in ip_output() without
|
* requring a reference to the pcb.
|
||||||
* requring a reference to the pcb.
|
*/
|
||||||
*/
|
if (inp->inp_options != NULL) {
|
||||||
if (inp->inp_options != NULL) {
|
options = m_dup(inp->inp_options, M_NOWAIT);
|
||||||
options = m_dup(inp->inp_options, M_DONTWAIT);
|
if (options == NULL) {
|
||||||
if (options == NULL)
|
INP_RUNLOCK(inp);
|
||||||
error = ENOBUFS;
|
error = ENOBUFS;
|
||||||
|
goto cantsend;
|
||||||
}
|
}
|
||||||
INP_RUNLOCK(inp);
|
|
||||||
if (error == ENOBUFS) {
|
|
||||||
m_freem(m);
|
|
||||||
return (error);
|
|
||||||
}
|
|
||||||
error = ip_output(m, options, NULL,
|
|
||||||
((so->so_options & SO_DONTROUTE) ?
|
|
||||||
IP_ROUTETOIF : 0) | IP_ALLOWBROADCAST |
|
|
||||||
IP_RAWOUTPUT, NULL, NULL);
|
|
||||||
if (options != NULL)
|
|
||||||
m_freem(options);
|
|
||||||
}
|
}
|
||||||
|
INP_RUNLOCK(inp);
|
||||||
|
|
||||||
|
switch (ip->ip_v) {
|
||||||
|
case IPVERSION:
|
||||||
|
error = ip_output(m, options, NULL,
|
||||||
|
((so->so_options & SO_DONTROUTE) ? IP_ROUTETOIF : 0)
|
||||||
|
| IP_ALLOWBROADCAST | IP_RAWOUTPUT, NULL, NULL);
|
||||||
|
break;
|
||||||
|
#ifdef INET6
|
||||||
|
case IPV6_VERSION >> 4:
|
||||||
|
error = ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (options != NULL)
|
||||||
|
m_freem(options);
|
||||||
} else {
|
} else {
|
||||||
dt->info |= IPFW_IS_DIVERT | IPFW_INFO_IN;
|
dt->info |= IPFW_IS_DIVERT | IPFW_INFO_IN;
|
||||||
if (m->m_pkthdr.rcvif == NULL) {
|
if (m->m_pkthdr.rcvif == NULL) {
|
||||||
@ -456,14 +493,26 @@ div_output(struct socket *so, struct mbuf *m, struct sockaddr_in *sin,
|
|||||||
mac_socket_create_mbuf(so, m);
|
mac_socket_create_mbuf(so, m);
|
||||||
#endif
|
#endif
|
||||||
/* Send packet to input processing via netisr */
|
/* Send packet to input processing via netisr */
|
||||||
netisr_queue_src(NETISR_IP, (uintptr_t)so, m);
|
switch (ip->ip_v) {
|
||||||
|
case IPVERSION:
|
||||||
|
netisr_queue_src(NETISR_IP, (uintptr_t)so, m);
|
||||||
|
break;
|
||||||
|
#ifdef INET6
|
||||||
|
case IPV6_VERSION >> 4:
|
||||||
|
netisr_queue_src(NETISR_IPV6, (uintptr_t)so, m);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
error = EINVAL;
|
||||||
|
goto cantsend;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return error;
|
return (error);
|
||||||
|
|
||||||
cantsend:
|
cantsend:
|
||||||
m_freem(m);
|
m_freem(m);
|
||||||
return error;
|
return (error);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -58,6 +58,10 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <netinet/ip.h>
|
#include <netinet/ip.h>
|
||||||
#include <netinet/ip_var.h>
|
#include <netinet/ip_var.h>
|
||||||
#include <netinet/ip_fw.h>
|
#include <netinet/ip_fw.h>
|
||||||
|
#ifdef INET6
|
||||||
|
#include <netinet/ip6.h>
|
||||||
|
#include <netinet6/ip6_var.h>
|
||||||
|
#endif
|
||||||
#include <netinet/ipfw/ip_fw_private.h>
|
#include <netinet/ipfw/ip_fw_private.h>
|
||||||
#include <netgraph/ng_ipfw.h>
|
#include <netgraph/ng_ipfw.h>
|
||||||
|
|
||||||
@ -265,7 +269,7 @@ ipfw_divert(struct mbuf **m0, int incoming, struct ipfw_rule_ref *rule,
|
|||||||
* If not tee, consume packet and send it to divert socket.
|
* If not tee, consume packet and send it to divert socket.
|
||||||
*/
|
*/
|
||||||
struct mbuf *clone;
|
struct mbuf *clone;
|
||||||
struct ip *ip;
|
struct ip *ip = mtod(*m0, struct ip *);
|
||||||
struct m_tag *tag;
|
struct m_tag *tag;
|
||||||
|
|
||||||
/* Cloning needed for tee? */
|
/* Cloning needed for tee? */
|
||||||
@ -289,8 +293,9 @@ ipfw_divert(struct mbuf **m0, int incoming, struct ipfw_rule_ref *rule,
|
|||||||
* Note that we now have the 'reass' ipfw option so if we care
|
* Note that we now have the 'reass' ipfw option so if we care
|
||||||
* we can do it before a 'tee'.
|
* we can do it before a 'tee'.
|
||||||
*/
|
*/
|
||||||
ip = mtod(clone, struct ip *);
|
if (!tee) switch (ip->ip_v) {
|
||||||
if (!tee && ntohs(ip->ip_off) & (IP_MF | IP_OFFMASK)) {
|
case IPVERSION:
|
||||||
|
if (ntohs(ip->ip_off) & (IP_MF | IP_OFFMASK)) {
|
||||||
int hlen;
|
int hlen;
|
||||||
struct mbuf *reass;
|
struct mbuf *reass;
|
||||||
|
|
||||||
@ -312,7 +317,26 @@ ipfw_divert(struct mbuf **m0, int incoming, struct ipfw_rule_ref *rule,
|
|||||||
else
|
else
|
||||||
ip->ip_sum = in_cksum(reass, hlen);
|
ip->ip_sum = in_cksum(reass, hlen);
|
||||||
clone = reass;
|
clone = reass;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#ifdef INET6
|
||||||
|
case IPV6_VERSION >> 4:
|
||||||
|
{
|
||||||
|
struct ip6_hdr *const ip6 = mtod(clone, struct ip6_hdr *);
|
||||||
|
|
||||||
|
if (ip6->ip6_nxt == IPPROTO_FRAGMENT) {
|
||||||
|
int nxt, off;
|
||||||
|
|
||||||
|
off = sizeof(struct ip6_hdr);
|
||||||
|
nxt = frag6_input(&clone, &off, 0);
|
||||||
|
if (nxt == IPPROTO_DONE)
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* attach a tag to the packet with the reinject info */
|
/* attach a tag to the packet with the reinject info */
|
||||||
tag = m_tag_alloc(MTAG_IPFW_RULE, 0,
|
tag = m_tag_alloc(MTAG_IPFW_RULE, 0,
|
||||||
sizeof(struct ipfw_rule_ref), M_NOWAIT);
|
sizeof(struct ipfw_rule_ref), M_NOWAIT);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user