Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 327774
b: refs/heads/master
c: 4cdd340
h: refs/heads/master
v: v3
  • Loading branch information
Patrick McHardy authored and Pablo Neira Ayuso committed Aug 30, 2012
1 parent 67cf035 commit 2df24e9
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 16 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 590e3f79a21edd2e9857ac3ced25ba6b2a491ef8
refs/heads/master: 4cdd34084d539c758d00c5dc7bf95db2e4f2bc70
1 change: 1 addition & 0 deletions trunk/include/linux/ipv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ struct inet6_skb_parm {
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
__u16 dsthao;
#endif
__u16 frag_max_size;

#define IP6SKB_XFRM_TRANSFORMED 1
#define IP6SKB_FORWARDED 2
Expand Down
7 changes: 5 additions & 2 deletions trunk/net/ipv6/ip6_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,8 @@ int ip6_forward(struct sk_buff *skb)
if (mtu < IPV6_MIN_MTU)
mtu = IPV6_MIN_MTU;

if (skb->len > mtu && !skb_is_gso(skb)) {
if ((!skb->local_df && skb->len > mtu && !skb_is_gso(skb)) ||
(IP6CB(skb)->frag_max_size && IP6CB(skb)->frag_max_size > mtu)) {
/* Again, force OUTPUT device used as source address */
skb->dev = dst->dev;
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
Expand Down Expand Up @@ -636,7 +637,9 @@ int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
/* We must not fragment if the socket is set to force MTU discovery
* or if the skb it not generated by a local socket.
*/
if (unlikely(!skb->local_df && skb->len > mtu)) {
if (unlikely(!skb->local_df && skb->len > mtu) ||
(IP6CB(skb)->frag_max_size &&
IP6CB(skb)->frag_max_size > mtu)) {
if (skb->sk && dst_allfrag(skb_dst(skb)))
sk_nocaps_add(skb->sk, NETIF_F_GSO_MASK);

Expand Down
41 changes: 32 additions & 9 deletions trunk/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,10 @@ static unsigned int ipv6_helper(unsigned int hooknum,
const struct nf_conn_help *help;
const struct nf_conntrack_helper *helper;
enum ip_conntrack_info ctinfo;
unsigned int ret, protoff;
unsigned int extoff = (u8 *)(ipv6_hdr(skb) + 1) - skb->data;
unsigned char pnum = ipv6_hdr(skb)->nexthdr;

unsigned int ret;
__be16 frag_off;
int protoff;
u8 nexthdr;

/* This is where we call the helper: as the packet goes out. */
ct = nf_ct_get(skb, &ctinfo);
Expand All @@ -171,9 +171,10 @@ static unsigned int ipv6_helper(unsigned int hooknum,
if (!helper)
return NF_ACCEPT;

protoff = nf_ct_ipv6_skip_exthdr(skb, extoff, &pnum,
skb->len - extoff);
if (protoff > skb->len || pnum == NEXTHDR_FRAGMENT) {
nexthdr = ipv6_hdr(skb)->nexthdr;
protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr,
&frag_off);
if (protoff < 0 || (frag_off & htons(~0x7)) != 0) {
pr_debug("proto header not found\n");
return NF_ACCEPT;
}
Expand All @@ -199,9 +200,14 @@ static unsigned int ipv6_confirm(unsigned int hooknum,
static unsigned int __ipv6_conntrack_in(struct net *net,
unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct sk_buff *reasm = skb->nfct_reasm;
const struct nf_conn_help *help;
struct nf_conn *ct;
enum ip_conntrack_info ctinfo;

/* This packet is fragmented and has reassembled packet. */
if (reasm) {
Expand All @@ -213,6 +219,23 @@ static unsigned int __ipv6_conntrack_in(struct net *net,
if (ret != NF_ACCEPT)
return ret;
}

/* Conntrack helpers need the entire reassembled packet in the
* POST_ROUTING hook.
*/
ct = nf_ct_get(reasm, &ctinfo);
if (ct != NULL && !nf_ct_is_untracked(ct)) {
help = nfct_help(ct);
if (help && help->helper) {
nf_conntrack_get_reasm(skb);
NF_HOOK_THRESH(NFPROTO_IPV6, hooknum, reasm,
(struct net_device *)in,
(struct net_device *)out,
okfn, NF_IP6_PRI_CONNTRACK + 1);
return NF_DROP_ERR(-ECANCELED);
}
}

nf_conntrack_get(reasm->nfct);
skb->nfct = reasm->nfct;
skb->nfctinfo = reasm->nfctinfo;
Expand All @@ -228,7 +251,7 @@ static unsigned int ipv6_conntrack_in(unsigned int hooknum,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
return __ipv6_conntrack_in(dev_net(in), hooknum, skb, okfn);
return __ipv6_conntrack_in(dev_net(in), hooknum, skb, in, out, okfn);
}

static unsigned int ipv6_conntrack_local(unsigned int hooknum,
Expand All @@ -242,7 +265,7 @@ static unsigned int ipv6_conntrack_local(unsigned int hooknum,
net_notice_ratelimited("ipv6_conntrack_local: packet too short\n");
return NF_ACCEPT;
}
return __ipv6_conntrack_in(dev_net(out), hooknum, skb, okfn);
return __ipv6_conntrack_in(dev_net(out), hooknum, skb, in, out, okfn);
}

static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = {
Expand Down
19 changes: 16 additions & 3 deletions trunk/net/ipv6/netfilter/nf_conntrack_reasm.c
Original file line number Diff line number Diff line change
Expand Up @@ -190,15 +190,18 @@ static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb,
const struct frag_hdr *fhdr, int nhoff)
{
struct sk_buff *prev, *next;
unsigned int payload_len;
int offset, end;

if (fq->q.last_in & INET_FRAG_COMPLETE) {
pr_debug("Already completed\n");
goto err;
}

payload_len = ntohs(ipv6_hdr(skb)->payload_len);

offset = ntohs(fhdr->frag_off) & ~0x7;
end = offset + (ntohs(ipv6_hdr(skb)->payload_len) -
end = offset + (payload_len -
((u8 *)(fhdr + 1) - (u8 *)(ipv6_hdr(skb) + 1)));

if ((unsigned int)end > IPV6_MAXPLEN) {
Expand Down Expand Up @@ -307,6 +310,8 @@ static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb,
skb->dev = NULL;
fq->q.stamp = skb->tstamp;
fq->q.meat += skb->len;
if (payload_len > fq->q.max_size)
fq->q.max_size = payload_len;
atomic_add(skb->truesize, &nf_init_frags.mem);

/* The first fragment.
Expand Down Expand Up @@ -412,10 +417,12 @@ nf_ct_frag6_reasm(struct nf_ct_frag6_queue *fq, struct net_device *dev)
}
atomic_sub(head->truesize, &nf_init_frags.mem);

head->local_df = 1;
head->next = NULL;
head->dev = dev;
head->tstamp = fq->q.stamp;
ipv6_hdr(head)->payload_len = htons(payload_len);
IP6CB(head)->frag_max_size = sizeof(struct ipv6hdr) + fq->q.max_size;

/* Yes, and fold redundant checksum back. 8) */
if (head->ip_summed == CHECKSUM_COMPLETE)
Expand Down Expand Up @@ -592,6 +599,7 @@ void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb,
int (*okfn)(struct sk_buff *))
{
struct sk_buff *s, *s2;
unsigned int ret = 0;

for (s = NFCT_FRAG6_CB(skb)->orig; s;) {
nf_conntrack_put_reasm(s->nfct_reasm);
Expand All @@ -601,8 +609,13 @@ void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb,
s2 = s->next;
s->next = NULL;

NF_HOOK_THRESH(NFPROTO_IPV6, hooknum, s, in, out, okfn,
NF_IP6_PRI_CONNTRACK_DEFRAG + 1);
if (ret != -ECANCELED)
ret = NF_HOOK_THRESH(NFPROTO_IPV6, hooknum, s,
in, out, okfn,
NF_IP6_PRI_CONNTRACK_DEFRAG + 1);
else
kfree_skb(s);

s = s2;
}
nf_conntrack_put_reasm(skb);
Expand Down
9 changes: 8 additions & 1 deletion trunk/net/netfilter/ipvs/ip_vs_xmit.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,14 @@ __ip_vs_dst_check(struct ip_vs_dest *dest, u32 rtos)
static inline bool
__mtu_check_toobig_v6(const struct sk_buff *skb, u32 mtu)
{
if (skb->len > mtu && !skb_is_gso(skb)) {
if (IP6CB(skb)->frag_max_size) {
/* frag_max_size tell us that, this packet have been
* defragmented by netfilter IPv6 conntrack module.
*/
if (IP6CB(skb)->frag_max_size > mtu)
return true; /* largest fragment violate MTU */
}
else if (skb->len > mtu && !skb_is_gso(skb)) {
return true; /* Packet size violate MTU size */
}
return false;
Expand Down

0 comments on commit 2df24e9

Please sign in to comment.