Skip to content

Commit 764dd16

Browse files
ummakynesdavem330
authored andcommitted
netfilter: nf_conntrack_bridge: add support for IPv6
br_defrag() and br_fragment() indirections are added in case that IPv6 support comes as a module, to avoid pulling innecessary dependencies in. The new fraglist iterator and fragment transformer APIs are used to implement the refragmentation code. Signed-off-by: Pablo Neira Ayuso <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 3c171f4 commit 764dd16

File tree

3 files changed

+230
-2
lines changed

3 files changed

+230
-2
lines changed

include/linux/netfilter_ipv6.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ struct ip6_rt_info {
1919
};
2020

2121
struct nf_queue_entry;
22+
struct nf_ct_bridge_frag_data;
2223

2324
/*
2425
* Hook functions for ipv6 to allow xt_* modules to be built-in even
@@ -39,6 +40,15 @@ struct nf_ipv6_ops {
3940
int (*fragment)(struct net *net, struct sock *sk, struct sk_buff *skb,
4041
int (*output)(struct net *, struct sock *, struct sk_buff *));
4142
int (*reroute)(struct sk_buff *skb, const struct nf_queue_entry *entry);
43+
#if IS_MODULE(CONFIG_IPV6)
44+
int (*br_defrag)(struct net *net, struct sk_buff *skb, u32 user);
45+
int (*br_fragment)(struct net *net, struct sock *sk,
46+
struct sk_buff *skb,
47+
struct nf_ct_bridge_frag_data *data,
48+
int (*output)(struct net *, struct sock *sk,
49+
const struct nf_ct_bridge_frag_data *data,
50+
struct sk_buff *));
51+
#endif
4252
};
4353

4454
#ifdef CONFIG_NETFILTER
@@ -86,6 +96,46 @@ static inline int nf_ip6_route(struct net *net, struct dst_entry **dst,
8696
#endif
8797
}
8898

99+
static inline int nf_ipv6_br_defrag(struct net *net, struct sk_buff *skb,
100+
u32 user)
101+
{
102+
#if IS_MODULE(CONFIG_IPV6)
103+
const struct nf_ipv6_ops *v6_ops = nf_get_ipv6_ops();
104+
105+
if (!v6_ops)
106+
return 1;
107+
108+
return v6_ops->br_defrag(net, skb, user);
109+
#else
110+
return nf_ct_frag6_gather(net, skb, user);
111+
#endif
112+
}
113+
114+
int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
115+
struct nf_ct_bridge_frag_data *data,
116+
int (*output)(struct net *, struct sock *sk,
117+
const struct nf_ct_bridge_frag_data *data,
118+
struct sk_buff *));
119+
120+
static inline int nf_br_ip6_fragment(struct net *net, struct sock *sk,
121+
struct sk_buff *skb,
122+
struct nf_ct_bridge_frag_data *data,
123+
int (*output)(struct net *, struct sock *sk,
124+
const struct nf_ct_bridge_frag_data *data,
125+
struct sk_buff *))
126+
{
127+
#if IS_MODULE(CONFIG_IPV6)
128+
const struct nf_ipv6_ops *v6_ops = nf_get_ipv6_ops();
129+
130+
if (!v6_ops)
131+
return 1;
132+
133+
return v6_ops->br_fragment(net, sk, skb, data, output);
134+
#else
135+
return br_ip6_fragment(net, sk, skb, data, output);
136+
#endif
137+
}
138+
89139
int ip6_route_me_harder(struct net *net, struct sk_buff *skb);
90140

91141
static inline int nf_ip6_route_me_harder(struct net *net, struct sk_buff *skb)

net/bridge/netfilter/nf_conntrack_bridge.c

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,31 @@ static unsigned int nf_ct_br_defrag4(struct sk_buff *skb,
163163
return NF_STOLEN;
164164
}
165165

166+
static unsigned int nf_ct_br_defrag6(struct sk_buff *skb,
167+
const struct nf_hook_state *state)
168+
{
169+
u16 zone_id = NF_CT_DEFAULT_ZONE_ID;
170+
enum ip_conntrack_info ctinfo;
171+
struct br_input_skb_cb cb;
172+
const struct nf_conn *ct;
173+
int err;
174+
175+
ct = nf_ct_get(skb, &ctinfo);
176+
if (ct)
177+
zone_id = nf_ct_zone_id(nf_ct_zone(ct), CTINFO2DIR(ctinfo));
178+
179+
br_skb_cb_save(skb, &cb, sizeof(struct inet6_skb_parm));
180+
181+
err = nf_ipv6_br_defrag(state->net, skb,
182+
IP_DEFRAG_CONNTRACK_BRIDGE_IN + zone_id);
183+
/* queued */
184+
if (err == -EINPROGRESS)
185+
return NF_STOLEN;
186+
187+
br_skb_cb_restore(skb, &cb, IP6CB(skb)->frag_max_size);
188+
return err == 0 ? NF_ACCEPT : NF_DROP;
189+
}
190+
166191
static int nf_ct_br_ip_check(const struct sk_buff *skb)
167192
{
168193
const struct iphdr *iph;
@@ -177,6 +202,23 @@ static int nf_ct_br_ip_check(const struct sk_buff *skb)
177202
len = ntohs(iph->tot_len);
178203
if (skb->len < nhoff + len ||
179204
len < (iph->ihl * 4))
205+
return -1;
206+
207+
return 0;
208+
}
209+
210+
static int nf_ct_br_ipv6_check(const struct sk_buff *skb)
211+
{
212+
const struct ipv6hdr *hdr;
213+
int nhoff, len;
214+
215+
nhoff = skb_network_offset(skb);
216+
hdr = ipv6_hdr(skb);
217+
if (hdr->version != 6)
218+
return -1;
219+
220+
len = ntohs(hdr->payload_len) + sizeof(struct ipv6hdr) + nhoff;
221+
if (skb->len < len)
180222
return -1;
181223

182224
return 0;
@@ -212,7 +254,19 @@ static unsigned int nf_ct_bridge_pre(void *priv, struct sk_buff *skb,
212254
ret = nf_ct_br_defrag4(skb, &bridge_state);
213255
break;
214256
case htons(ETH_P_IPV6):
215-
/* fall through */
257+
if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
258+
return NF_ACCEPT;
259+
260+
len = sizeof(struct ipv6hdr) + ntohs(ipv6_hdr(skb)->payload_len);
261+
if (pskb_trim_rcsum(skb, len))
262+
return NF_ACCEPT;
263+
264+
if (nf_ct_br_ipv6_check(skb))
265+
return NF_ACCEPT;
266+
267+
bridge_state.pf = NFPROTO_IPV6;
268+
ret = nf_ct_br_defrag6(skb, &bridge_state);
269+
break;
216270
default:
217271
nf_ct_set(skb, NULL, IP_CT_UNTRACKED);
218272
return NF_ACCEPT;
@@ -254,7 +308,8 @@ nf_ct_bridge_refrag(struct sk_buff *skb, const struct nf_hook_state *state,
254308
nf_br_ip_fragment(state->net, state->sk, skb, &data, output);
255309
break;
256310
case htons(ETH_P_IPV6):
257-
return NF_ACCEPT;
311+
nf_br_ip6_fragment(state->net, state->sk, skb, &data, output);
312+
break;
258313
default:
259314
WARN_ON_ONCE(1);
260315
return NF_DROP;

net/ipv6/netfilter.c

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
#include <net/ip6_route.h>
1717
#include <net/xfrm.h>
1818
#include <net/netfilter/nf_queue.h>
19+
#include <net/netfilter/nf_conntrack_bridge.h>
20+
#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
21+
#include "../bridge/br_private.h"
1922

2023
int ip6_route_me_harder(struct net *net, struct sk_buff *skb)
2124
{
@@ -109,6 +112,122 @@ int __nf_ip6_route(struct net *net, struct dst_entry **dst,
109112
}
110113
EXPORT_SYMBOL_GPL(__nf_ip6_route);
111114

115+
int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
116+
struct nf_ct_bridge_frag_data *data,
117+
int (*output)(struct net *, struct sock *sk,
118+
const struct nf_ct_bridge_frag_data *data,
119+
struct sk_buff *))
120+
{
121+
int frag_max_size = BR_INPUT_SKB_CB(skb)->frag_max_size;
122+
struct ip6_frag_state state;
123+
u8 *prevhdr, nexthdr = 0;
124+
unsigned int mtu, hlen;
125+
int hroom, err = 0;
126+
__be32 frag_id;
127+
128+
err = ip6_find_1stfragopt(skb, &prevhdr);
129+
if (err < 0)
130+
goto blackhole;
131+
hlen = err;
132+
nexthdr = *prevhdr;
133+
134+
mtu = skb->dev->mtu;
135+
if (frag_max_size > mtu ||
136+
frag_max_size < IPV6_MIN_MTU)
137+
goto blackhole;
138+
139+
mtu = frag_max_size;
140+
if (mtu < hlen + sizeof(struct frag_hdr) + 8)
141+
goto blackhole;
142+
mtu -= hlen + sizeof(struct frag_hdr);
143+
144+
frag_id = ipv6_select_ident(net, &ipv6_hdr(skb)->daddr,
145+
&ipv6_hdr(skb)->saddr);
146+
147+
if (skb->ip_summed == CHECKSUM_PARTIAL &&
148+
(err = skb_checksum_help(skb)))
149+
goto blackhole;
150+
151+
hroom = LL_RESERVED_SPACE(skb->dev);
152+
if (skb_has_frag_list(skb)) {
153+
unsigned int first_len = skb_pagelen(skb);
154+
struct ip6_fraglist_iter iter;
155+
struct sk_buff *frag2;
156+
157+
if (first_len - hlen > mtu ||
158+
skb_headroom(skb) < (hroom + sizeof(struct frag_hdr)))
159+
goto blackhole;
160+
161+
if (skb_cloned(skb))
162+
goto slow_path;
163+
164+
skb_walk_frags(skb, frag2) {
165+
if (frag2->len > mtu ||
166+
skb_headroom(frag2) < (hlen + hroom + sizeof(struct frag_hdr)))
167+
goto blackhole;
168+
169+
/* Partially cloned skb? */
170+
if (skb_shared(frag2))
171+
goto slow_path;
172+
}
173+
174+
err = ip6_fraglist_init(skb, hlen, prevhdr, nexthdr, frag_id,
175+
&iter);
176+
if (err < 0)
177+
goto blackhole;
178+
179+
for (;;) {
180+
/* Prepare header of the next frame,
181+
* before previous one went down.
182+
*/
183+
if (iter.frag)
184+
ip6_fraglist_prepare(skb, &iter);
185+
186+
err = output(net, sk, data, skb);
187+
if (err || !iter.frag)
188+
break;
189+
190+
skb = ip6_fraglist_next(&iter);
191+
}
192+
193+
kfree(iter.tmp_hdr);
194+
if (!err)
195+
return 0;
196+
197+
kfree_skb_list(iter.frag_list);
198+
return err;
199+
}
200+
slow_path:
201+
/* This is a linearized skbuff, the original geometry is lost for us.
202+
* This may also be a clone skbuff, we could preserve the geometry for
203+
* the copies but probably not worth the effort.
204+
*/
205+
ip6_frag_init(skb, hlen, mtu, skb->dev->needed_tailroom,
206+
LL_RESERVED_SPACE(skb->dev), prevhdr, nexthdr, frag_id,
207+
&state);
208+
209+
while (state.left > 0) {
210+
struct sk_buff *skb2;
211+
212+
skb2 = ip6_frag_next(skb, &state);
213+
if (IS_ERR(skb2)) {
214+
err = PTR_ERR(skb2);
215+
goto blackhole;
216+
}
217+
218+
err = output(net, sk, data, skb2);
219+
if (err)
220+
goto blackhole;
221+
}
222+
consume_skb(skb);
223+
return err;
224+
225+
blackhole:
226+
kfree_skb(skb);
227+
return 0;
228+
}
229+
EXPORT_SYMBOL_GPL(br_ip6_fragment);
230+
112231
static const struct nf_ipv6_ops ipv6ops = {
113232
#if IS_MODULE(CONFIG_IPV6)
114233
.chk_addr = ipv6_chk_addr,
@@ -119,6 +238,10 @@ static const struct nf_ipv6_ops ipv6ops = {
119238
.route_input = ip6_route_input,
120239
.fragment = ip6_fragment,
121240
.reroute = nf_ip6_reroute,
241+
#if IS_MODULE(CONFIG_NF_CONNTRACK_BRIDGE)
242+
.br_defrag = nf_ct_frag6_gather,
243+
.br_fragment = br_ip6_fragment,
244+
#endif
122245
};
123246

124247
int __init ipv6_netfilter_init(void)

0 commit comments

Comments
 (0)