aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/ip6_output.c7
-rw-r--r--net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c41
-rw-r--r--net/ipv6/netfilter/nf_conntrack_reasm.c19
3 files changed, 53 insertions, 14 deletions
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 5b2d63ed793e..a4f6263fddca 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -493,7 +493,8 @@ int ip6_forward(struct sk_buff *skb)
493 if (mtu < IPV6_MIN_MTU) 493 if (mtu < IPV6_MIN_MTU)
494 mtu = IPV6_MIN_MTU; 494 mtu = IPV6_MIN_MTU;
495 495
496 if (skb->len > mtu && !skb_is_gso(skb)) { 496 if ((!skb->local_df && skb->len > mtu && !skb_is_gso(skb)) ||
497 (IP6CB(skb)->frag_max_size && IP6CB(skb)->frag_max_size > mtu)) {
497 /* Again, force OUTPUT device used as source address */ 498 /* Again, force OUTPUT device used as source address */
498 skb->dev = dst->dev; 499 skb->dev = dst->dev;
499 icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); 500 icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
@@ -636,7 +637,9 @@ int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
636 /* We must not fragment if the socket is set to force MTU discovery 637 /* We must not fragment if the socket is set to force MTU discovery
637 * or if the skb it not generated by a local socket. 638 * or if the skb it not generated by a local socket.
638 */ 639 */
639 if (unlikely(!skb->local_df && skb->len > mtu)) { 640 if (unlikely(!skb->local_df && skb->len > mtu) ||
641 (IP6CB(skb)->frag_max_size &&
642 IP6CB(skb)->frag_max_size > mtu)) {
640 if (skb->sk && dst_allfrag(skb_dst(skb))) 643 if (skb->sk && dst_allfrag(skb_dst(skb)))
641 sk_nocaps_add(skb->sk, NETIF_F_GSO_MASK); 644 sk_nocaps_add(skb->sk, NETIF_F_GSO_MASK);
642 645
diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
index 4794f96cf2e0..521ddca876f8 100644
--- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
+++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
@@ -153,10 +153,10 @@ static unsigned int ipv6_helper(unsigned int hooknum,
153 const struct nf_conn_help *help; 153 const struct nf_conn_help *help;
154 const struct nf_conntrack_helper *helper; 154 const struct nf_conntrack_helper *helper;
155 enum ip_conntrack_info ctinfo; 155 enum ip_conntrack_info ctinfo;
156 unsigned int ret, protoff; 156 unsigned int ret;
157 unsigned int extoff = (u8 *)(ipv6_hdr(skb) + 1) - skb->data; 157 __be16 frag_off;
158 unsigned char pnum = ipv6_hdr(skb)->nexthdr; 158 int protoff;
159 159 u8 nexthdr;
160 160
161 /* This is where we call the helper: as the packet goes out. */ 161 /* This is where we call the helper: as the packet goes out. */
162 ct = nf_ct_get(skb, &ctinfo); 162 ct = nf_ct_get(skb, &ctinfo);
@@ -171,9 +171,10 @@ static unsigned int ipv6_helper(unsigned int hooknum,
171 if (!helper) 171 if (!helper)
172 return NF_ACCEPT; 172 return NF_ACCEPT;
173 173
174 protoff = nf_ct_ipv6_skip_exthdr(skb, extoff, &pnum, 174 nexthdr = ipv6_hdr(skb)->nexthdr;
175 skb->len - extoff); 175 protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr,
176 if (protoff > skb->len || pnum == NEXTHDR_FRAGMENT) { 176 &frag_off);
177 if (protoff < 0 || (frag_off & htons(~0x7)) != 0) {
177 pr_debug("proto header not found\n"); 178 pr_debug("proto header not found\n");
178 return NF_ACCEPT; 179 return NF_ACCEPT;
179 } 180 }
@@ -199,9 +200,14 @@ static unsigned int ipv6_confirm(unsigned int hooknum,
199static unsigned int __ipv6_conntrack_in(struct net *net, 200static unsigned int __ipv6_conntrack_in(struct net *net,
200 unsigned int hooknum, 201 unsigned int hooknum,
201 struct sk_buff *skb, 202 struct sk_buff *skb,
203 const struct net_device *in,
204 const struct net_device *out,
202 int (*okfn)(struct sk_buff *)) 205 int (*okfn)(struct sk_buff *))
203{ 206{
204 struct sk_buff *reasm = skb->nfct_reasm; 207 struct sk_buff *reasm = skb->nfct_reasm;
208 const struct nf_conn_help *help;
209 struct nf_conn *ct;
210 enum ip_conntrack_info ctinfo;
205 211
206 /* This packet is fragmented and has reassembled packet. */ 212 /* This packet is fragmented and has reassembled packet. */
207 if (reasm) { 213 if (reasm) {
@@ -213,6 +219,23 @@ static unsigned int __ipv6_conntrack_in(struct net *net,
213 if (ret != NF_ACCEPT) 219 if (ret != NF_ACCEPT)
214 return ret; 220 return ret;
215 } 221 }
222
223 /* Conntrack helpers need the entire reassembled packet in the
224 * POST_ROUTING hook.
225 */
226 ct = nf_ct_get(reasm, &ctinfo);
227 if (ct != NULL && !nf_ct_is_untracked(ct)) {
228 help = nfct_help(ct);
229 if (help && help->helper) {
230 nf_conntrack_get_reasm(skb);
231 NF_HOOK_THRESH(NFPROTO_IPV6, hooknum, reasm,
232 (struct net_device *)in,
233 (struct net_device *)out,
234 okfn, NF_IP6_PRI_CONNTRACK + 1);
235 return NF_DROP_ERR(-ECANCELED);
236 }
237 }
238
216 nf_conntrack_get(reasm->nfct); 239 nf_conntrack_get(reasm->nfct);
217 skb->nfct = reasm->nfct; 240 skb->nfct = reasm->nfct;
218 skb->nfctinfo = reasm->nfctinfo; 241 skb->nfctinfo = reasm->nfctinfo;
@@ -228,7 +251,7 @@ static unsigned int ipv6_conntrack_in(unsigned int hooknum,
228 const struct net_device *out, 251 const struct net_device *out,
229 int (*okfn)(struct sk_buff *)) 252 int (*okfn)(struct sk_buff *))
230{ 253{
231 return __ipv6_conntrack_in(dev_net(in), hooknum, skb, okfn); 254 return __ipv6_conntrack_in(dev_net(in), hooknum, skb, in, out, okfn);
232} 255}
233 256
234static unsigned int ipv6_conntrack_local(unsigned int hooknum, 257static unsigned int ipv6_conntrack_local(unsigned int hooknum,
@@ -242,7 +265,7 @@ static unsigned int ipv6_conntrack_local(unsigned int hooknum,
242 net_notice_ratelimited("ipv6_conntrack_local: packet too short\n"); 265 net_notice_ratelimited("ipv6_conntrack_local: packet too short\n");
243 return NF_ACCEPT; 266 return NF_ACCEPT;
244 } 267 }
245 return __ipv6_conntrack_in(dev_net(out), hooknum, skb, okfn); 268 return __ipv6_conntrack_in(dev_net(out), hooknum, skb, in, out, okfn);
246} 269}
247 270
248static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = { 271static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = {
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c
index c9c78c2e666b..f94fb3ac2a79 100644
--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
@@ -190,6 +190,7 @@ static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb,
190 const struct frag_hdr *fhdr, int nhoff) 190 const struct frag_hdr *fhdr, int nhoff)
191{ 191{
192 struct sk_buff *prev, *next; 192 struct sk_buff *prev, *next;
193 unsigned int payload_len;
193 int offset, end; 194 int offset, end;
194 195
195 if (fq->q.last_in & INET_FRAG_COMPLETE) { 196 if (fq->q.last_in & INET_FRAG_COMPLETE) {
@@ -197,8 +198,10 @@ static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb,
197 goto err; 198 goto err;
198 } 199 }
199 200
201 payload_len = ntohs(ipv6_hdr(skb)->payload_len);
202
200 offset = ntohs(fhdr->frag_off) & ~0x7; 203 offset = ntohs(fhdr->frag_off) & ~0x7;
201 end = offset + (ntohs(ipv6_hdr(skb)->payload_len) - 204 end = offset + (payload_len -
202 ((u8 *)(fhdr + 1) - (u8 *)(ipv6_hdr(skb) + 1))); 205 ((u8 *)(fhdr + 1) - (u8 *)(ipv6_hdr(skb) + 1)));
203 206
204 if ((unsigned int)end > IPV6_MAXPLEN) { 207 if ((unsigned int)end > IPV6_MAXPLEN) {
@@ -307,6 +310,8 @@ found:
307 skb->dev = NULL; 310 skb->dev = NULL;
308 fq->q.stamp = skb->tstamp; 311 fq->q.stamp = skb->tstamp;
309 fq->q.meat += skb->len; 312 fq->q.meat += skb->len;
313 if (payload_len > fq->q.max_size)
314 fq->q.max_size = payload_len;
310 atomic_add(skb->truesize, &nf_init_frags.mem); 315 atomic_add(skb->truesize, &nf_init_frags.mem);
311 316
312 /* The first fragment. 317 /* The first fragment.
@@ -412,10 +417,12 @@ nf_ct_frag6_reasm(struct nf_ct_frag6_queue *fq, struct net_device *dev)
412 } 417 }
413 atomic_sub(head->truesize, &nf_init_frags.mem); 418 atomic_sub(head->truesize, &nf_init_frags.mem);
414 419
420 head->local_df = 1;
415 head->next = NULL; 421 head->next = NULL;
416 head->dev = dev; 422 head->dev = dev;
417 head->tstamp = fq->q.stamp; 423 head->tstamp = fq->q.stamp;
418 ipv6_hdr(head)->payload_len = htons(payload_len); 424 ipv6_hdr(head)->payload_len = htons(payload_len);
425 IP6CB(head)->frag_max_size = sizeof(struct ipv6hdr) + fq->q.max_size;
419 426
420 /* Yes, and fold redundant checksum back. 8) */ 427 /* Yes, and fold redundant checksum back. 8) */
421 if (head->ip_summed == CHECKSUM_COMPLETE) 428 if (head->ip_summed == CHECKSUM_COMPLETE)
@@ -592,6 +599,7 @@ void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb,
592 int (*okfn)(struct sk_buff *)) 599 int (*okfn)(struct sk_buff *))
593{ 600{
594 struct sk_buff *s, *s2; 601 struct sk_buff *s, *s2;
602 unsigned int ret = 0;
595 603
596 for (s = NFCT_FRAG6_CB(skb)->orig; s;) { 604 for (s = NFCT_FRAG6_CB(skb)->orig; s;) {
597 nf_conntrack_put_reasm(s->nfct_reasm); 605 nf_conntrack_put_reasm(s->nfct_reasm);
@@ -601,8 +609,13 @@ void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb,
601 s2 = s->next; 609 s2 = s->next;
602 s->next = NULL; 610 s->next = NULL;
603 611
604 NF_HOOK_THRESH(NFPROTO_IPV6, hooknum, s, in, out, okfn, 612 if (ret != -ECANCELED)
605 NF_IP6_PRI_CONNTRACK_DEFRAG + 1); 613 ret = NF_HOOK_THRESH(NFPROTO_IPV6, hooknum, s,
614 in, out, okfn,
615 NF_IP6_PRI_CONNTRACK_DEFRAG + 1);
616 else
617 kfree_skb(s);
618
606 s = s2; 619 s = s2;
607 } 620 }
608 nf_conntrack_put_reasm(skb); 621 nf_conntrack_put_reasm(skb);