Commit 5c27d8d7 authored by Pablo Neira Ayuso's avatar Pablo Neira Ayuso

netfilter: nf_flow_table_offload: add IPv6 support

Add nf_flow_rule_route_ipv6() and use it from the IPv6 and the inet
flowtable type definitions. Rename the nf_flow_rule_route() function to
nf_flow_rule_route_ipv4().

Adjust maximum number of actions, which now becomes 16 to leave
sufficient room for the IPv6 address mangling for NAT.
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 4a766d49
...@@ -163,9 +163,12 @@ void nf_flow_table_offload_flush(struct nf_flowtable *flowtable); ...@@ -163,9 +163,12 @@ void nf_flow_table_offload_flush(struct nf_flowtable *flowtable);
int nf_flow_table_offload_setup(struct nf_flowtable *flowtable, int nf_flow_table_offload_setup(struct nf_flowtable *flowtable,
struct net_device *dev, struct net_device *dev,
enum flow_block_command cmd); enum flow_block_command cmd);
int nf_flow_rule_route(struct net *net, const struct flow_offload *flow, int nf_flow_rule_route_ipv4(struct net *net, const struct flow_offload *flow,
enum flow_offload_tuple_dir dir, enum flow_offload_tuple_dir dir,
struct nf_flow_rule *flow_rule); struct nf_flow_rule *flow_rule);
int nf_flow_rule_route_ipv6(struct net *net, const struct flow_offload *flow,
enum flow_offload_tuple_dir dir,
struct nf_flow_rule *flow_rule);
int nf_flow_table_offload_init(void); int nf_flow_table_offload_init(void);
void nf_flow_table_offload_exit(void); void nf_flow_table_offload_exit(void);
......
...@@ -10,7 +10,7 @@ static struct nf_flowtable_type flowtable_ipv4 = { ...@@ -10,7 +10,7 @@ static struct nf_flowtable_type flowtable_ipv4 = {
.family = NFPROTO_IPV4, .family = NFPROTO_IPV4,
.init = nf_flow_table_init, .init = nf_flow_table_init,
.setup = nf_flow_table_offload_setup, .setup = nf_flow_table_offload_setup,
.action = nf_flow_rule_route, .action = nf_flow_rule_route_ipv4,
.free = nf_flow_table_free, .free = nf_flow_table_free,
.hook = nf_flow_offload_ip_hook, .hook = nf_flow_offload_ip_hook,
.owner = THIS_MODULE, .owner = THIS_MODULE,
......
...@@ -11,7 +11,7 @@ static struct nf_flowtable_type flowtable_ipv6 = { ...@@ -11,7 +11,7 @@ static struct nf_flowtable_type flowtable_ipv6 = {
.family = NFPROTO_IPV6, .family = NFPROTO_IPV6,
.init = nf_flow_table_init, .init = nf_flow_table_init,
.setup = nf_flow_table_offload_setup, .setup = nf_flow_table_offload_setup,
.action = nf_flow_rule_route, .action = nf_flow_rule_route_ipv6,
.free = nf_flow_table_free, .free = nf_flow_table_free,
.hook = nf_flow_offload_ipv6_hook, .hook = nf_flow_offload_ipv6_hook,
.owner = THIS_MODULE, .owner = THIS_MODULE,
......
...@@ -21,11 +21,34 @@ nf_flow_offload_inet_hook(void *priv, struct sk_buff *skb, ...@@ -21,11 +21,34 @@ nf_flow_offload_inet_hook(void *priv, struct sk_buff *skb,
return NF_ACCEPT; return NF_ACCEPT;
} }
static int nf_flow_rule_route_inet(struct net *net,
const struct flow_offload *flow,
enum flow_offload_tuple_dir dir,
struct nf_flow_rule *flow_rule)
{
const struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple;
int err;
switch (flow_tuple->l3proto) {
case NFPROTO_IPV4:
err = nf_flow_rule_route_ipv4(net, flow, dir, flow_rule);
break;
case NFPROTO_IPV6:
err = nf_flow_rule_route_ipv6(net, flow, dir, flow_rule);
break;
default:
err = -1;
break;
}
return err;
}
static struct nf_flowtable_type flowtable_inet = { static struct nf_flowtable_type flowtable_inet = {
.family = NFPROTO_INET, .family = NFPROTO_INET,
.init = nf_flow_table_init, .init = nf_flow_table_init,
.setup = nf_flow_table_offload_setup, .setup = nf_flow_table_offload_setup,
.action = nf_flow_rule_route, .action = nf_flow_rule_route_inet,
.free = nf_flow_table_free, .free = nf_flow_table_free,
.hook = nf_flow_offload_inet_hook, .hook = nf_flow_offload_inet_hook,
.owner = THIS_MODULE, .owner = THIS_MODULE,
......
...@@ -236,6 +236,71 @@ static void flow_offload_ipv4_dnat(struct net *net, ...@@ -236,6 +236,71 @@ static void flow_offload_ipv4_dnat(struct net *net,
(u8 *)&addr, (u8 *)&mask); (u8 *)&addr, (u8 *)&mask);
} }
static void flow_offload_ipv6_mangle(struct nf_flow_rule *flow_rule,
unsigned int offset,
u8 *addr, u8 *mask)
{
struct flow_action_entry *entry;
int i;
for (i = 0; i < sizeof(struct in6_addr) / sizeof(u32); i += sizeof(u32)) {
entry = flow_action_entry_next(flow_rule);
flow_offload_mangle(entry, FLOW_ACT_MANGLE_HDR_TYPE_IP6,
offset + i,
&addr[i], mask);
}
}
static void flow_offload_ipv6_snat(struct net *net,
const struct flow_offload *flow,
enum flow_offload_tuple_dir dir,
struct nf_flow_rule *flow_rule)
{
u32 mask = ~htonl(0xffffffff);
const u8 *addr;
u32 offset;
switch (dir) {
case FLOW_OFFLOAD_DIR_ORIGINAL:
addr = flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_v6.s6_addr;
offset = offsetof(struct ipv6hdr, saddr);
break;
case FLOW_OFFLOAD_DIR_REPLY:
addr = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.src_v6.s6_addr;
offset = offsetof(struct ipv6hdr, daddr);
break;
default:
return;
}
flow_offload_ipv6_mangle(flow_rule, offset, (u8 *)addr, (u8 *)&mask);
}
static void flow_offload_ipv6_dnat(struct net *net,
const struct flow_offload *flow,
enum flow_offload_tuple_dir dir,
struct nf_flow_rule *flow_rule)
{
u32 mask = ~htonl(0xffffffff);
const u8 *addr;
u32 offset;
switch (dir) {
case FLOW_OFFLOAD_DIR_ORIGINAL:
addr = flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.src_v6.s6_addr;
offset = offsetof(struct ipv6hdr, daddr);
break;
case FLOW_OFFLOAD_DIR_REPLY:
addr = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.dst_v6.s6_addr;
offset = offsetof(struct ipv6hdr, saddr);
break;
default:
return;
}
flow_offload_ipv6_mangle(flow_rule, offset, (u8 *)addr, (u8 *)&mask);
}
static int flow_offload_l4proto(const struct flow_offload *flow) static int flow_offload_l4proto(const struct flow_offload *flow)
{ {
u8 protonum = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.l4proto; u8 protonum = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.l4proto;
...@@ -342,9 +407,9 @@ static void flow_offload_redirect(const struct flow_offload *flow, ...@@ -342,9 +407,9 @@ static void flow_offload_redirect(const struct flow_offload *flow,
dev_hold(rt->dst.dev); dev_hold(rt->dst.dev);
} }
int nf_flow_rule_route(struct net *net, const struct flow_offload *flow, int nf_flow_rule_route_ipv4(struct net *net, const struct flow_offload *flow,
enum flow_offload_tuple_dir dir, enum flow_offload_tuple_dir dir,
struct nf_flow_rule *flow_rule) struct nf_flow_rule *flow_rule)
{ {
if (flow_offload_eth_src(net, flow, dir, flow_rule) < 0 || if (flow_offload_eth_src(net, flow, dir, flow_rule) < 0 ||
flow_offload_eth_dst(net, flow, dir, flow_rule) < 0) flow_offload_eth_dst(net, flow, dir, flow_rule) < 0)
...@@ -366,7 +431,32 @@ int nf_flow_rule_route(struct net *net, const struct flow_offload *flow, ...@@ -366,7 +431,32 @@ int nf_flow_rule_route(struct net *net, const struct flow_offload *flow,
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(nf_flow_rule_route); EXPORT_SYMBOL_GPL(nf_flow_rule_route_ipv4);
int nf_flow_rule_route_ipv6(struct net *net, const struct flow_offload *flow,
enum flow_offload_tuple_dir dir,
struct nf_flow_rule *flow_rule)
{
if (flow_offload_eth_src(net, flow, dir, flow_rule) < 0 ||
flow_offload_eth_dst(net, flow, dir, flow_rule) < 0)
return -1;
if (flow->flags & FLOW_OFFLOAD_SNAT) {
flow_offload_ipv6_snat(net, flow, dir, flow_rule);
flow_offload_port_snat(net, flow, dir, flow_rule);
}
if (flow->flags & FLOW_OFFLOAD_DNAT) {
flow_offload_ipv6_dnat(net, flow, dir, flow_rule);
flow_offload_port_dnat(net, flow, dir, flow_rule);
}
flow_offload_redirect(flow, dir, flow_rule);
return 0;
}
EXPORT_SYMBOL_GPL(nf_flow_rule_route_ipv6);
#define NF_FLOW_RULE_ACTION_MAX 16
static struct nf_flow_rule * static struct nf_flow_rule *
nf_flow_offload_rule_alloc(struct net *net, nf_flow_offload_rule_alloc(struct net *net,
...@@ -383,7 +473,7 @@ nf_flow_offload_rule_alloc(struct net *net, ...@@ -383,7 +473,7 @@ nf_flow_offload_rule_alloc(struct net *net,
if (!flow_rule) if (!flow_rule)
goto err_flow; goto err_flow;
flow_rule->rule = flow_rule_alloc(10); flow_rule->rule = flow_rule_alloc(NF_FLOW_RULE_ACTION_MAX);
if (!flow_rule->rule) if (!flow_rule->rule)
goto err_flow_rule; goto err_flow_rule;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment