Skip to content

Commit 0feca61

Browse files
ummakynesdavem330
authored andcommitted
net: ipv6: add skbuff fraglist splitter
This patch adds the skbuff fraglist split iterator. This API provides an iterator to transform the fraglist into single skbuff objects, it consists of: * ip6_fraglist_init(), that initializes the internal state of the fraglist iterator. * ip6_fraglist_prepare(), that restores the IPv6 header on the fragment. * ip6_fraglist_next(), that retrieves the fragment from the fraglist and updates the internal state of the iterator to point to the next fragment in the fraglist. The ip6_fraglist_iter object stores the internal state of the iterator. This code has been extracted from ip6_fragment(). Symbols are also exported to allow to reuse this iterator from the bridge codepath to build its own refragmentation routine by reusing the existing codebase. Signed-off-by: Pablo Neira Ayuso <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent c8b17be commit 0feca61

File tree

2 files changed

+102
-55
lines changed

2 files changed

+102
-55
lines changed

include/net/ipv6.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,31 @@ struct frag_hdr {
154154
#define IP6_MF 0x0001
155155
#define IP6_OFFSET 0xFFF8
156156

157+
struct ip6_fraglist_iter {
158+
struct ipv6hdr *tmp_hdr;
159+
struct sk_buff *frag_list;
160+
struct sk_buff *frag;
161+
int offset;
162+
unsigned int hlen;
163+
__be32 frag_id;
164+
u8 nexthdr;
165+
};
166+
167+
int ip6_fraglist_init(struct sk_buff *skb, unsigned int hlen, u8 *prevhdr,
168+
u8 nexthdr, __be32 frag_id,
169+
struct ip6_fraglist_iter *iter);
170+
void ip6_fraglist_prepare(struct sk_buff *skb, struct ip6_fraglist_iter *iter);
171+
172+
static inline struct sk_buff *ip6_fraglist_next(struct ip6_fraglist_iter *iter)
173+
{
174+
struct sk_buff *skb = iter->frag;
175+
176+
iter->frag = skb->next;
177+
skb_mark_not_on_list(skb);
178+
179+
return skb;
180+
}
181+
157182
#define IP6_REPLY_MARK(net, mark) \
158183
((net)->ipv6.sysctl.fwmark_reflect ? (mark) : 0)
159184

net/ipv6/ip6_output.c

Lines changed: 77 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -592,14 +592,80 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from)
592592
skb_copy_secmark(to, from);
593593
}
594594

595+
int ip6_fraglist_init(struct sk_buff *skb, unsigned int hlen, u8 *prevhdr,
596+
u8 nexthdr, __be32 frag_id,
597+
struct ip6_fraglist_iter *iter)
598+
{
599+
unsigned int first_len;
600+
struct frag_hdr *fh;
601+
602+
/* BUILD HEADER */
603+
*prevhdr = NEXTHDR_FRAGMENT;
604+
iter->tmp_hdr = kmemdup(skb_network_header(skb), hlen, GFP_ATOMIC);
605+
if (!iter->tmp_hdr)
606+
return -ENOMEM;
607+
608+
iter->frag_list = skb_shinfo(skb)->frag_list;
609+
iter->frag = iter->frag_list;
610+
skb_frag_list_init(skb);
611+
612+
iter->offset = 0;
613+
iter->hlen = hlen;
614+
iter->frag_id = frag_id;
615+
iter->nexthdr = nexthdr;
616+
617+
__skb_pull(skb, hlen);
618+
fh = __skb_push(skb, sizeof(struct frag_hdr));
619+
__skb_push(skb, hlen);
620+
skb_reset_network_header(skb);
621+
memcpy(skb_network_header(skb), iter->tmp_hdr, hlen);
622+
623+
fh->nexthdr = nexthdr;
624+
fh->reserved = 0;
625+
fh->frag_off = htons(IP6_MF);
626+
fh->identification = frag_id;
627+
628+
first_len = skb_pagelen(skb);
629+
skb->data_len = first_len - skb_headlen(skb);
630+
skb->len = first_len;
631+
ipv6_hdr(skb)->payload_len = htons(first_len - sizeof(struct ipv6hdr));
632+
633+
return 0;
634+
}
635+
EXPORT_SYMBOL(ip6_fraglist_init);
636+
637+
void ip6_fraglist_prepare(struct sk_buff *skb,
638+
struct ip6_fraglist_iter *iter)
639+
{
640+
struct sk_buff *frag = iter->frag;
641+
unsigned int hlen = iter->hlen;
642+
struct frag_hdr *fh;
643+
644+
frag->ip_summed = CHECKSUM_NONE;
645+
skb_reset_transport_header(frag);
646+
fh = __skb_push(frag, sizeof(struct frag_hdr));
647+
__skb_push(frag, hlen);
648+
skb_reset_network_header(frag);
649+
memcpy(skb_network_header(frag), iter->tmp_hdr, hlen);
650+
iter->offset += skb->len - hlen - sizeof(struct frag_hdr);
651+
fh->nexthdr = iter->nexthdr;
652+
fh->reserved = 0;
653+
fh->frag_off = htons(iter->offset);
654+
if (frag->next)
655+
fh->frag_off |= htons(IP6_MF);
656+
fh->identification = iter->frag_id;
657+
ipv6_hdr(frag)->payload_len = htons(frag->len - sizeof(struct ipv6hdr));
658+
ip6_copy_metadata(frag, skb);
659+
}
660+
EXPORT_SYMBOL(ip6_fraglist_prepare);
661+
595662
int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
596663
int (*output)(struct net *, struct sock *, struct sk_buff *))
597664
{
598665
struct sk_buff *frag;
599666
struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
600667
struct ipv6_pinfo *np = skb->sk && !dev_recursion_level() ?
601668
inet6_sk(skb->sk) : NULL;
602-
struct ipv6hdr *tmp_hdr;
603669
struct frag_hdr *fh;
604670
unsigned int mtu, hlen, left, len, nexthdr_offset;
605671
int hroom, troom;
@@ -651,6 +717,7 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
651717
hroom = LL_RESERVED_SPACE(rt->dst.dev);
652718
if (skb_has_frag_list(skb)) {
653719
unsigned int first_len = skb_pagelen(skb);
720+
struct ip6_fraglist_iter iter;
654721
struct sk_buff *frag2;
655722

656723
if (first_len - hlen > mtu ||
@@ -678,82 +745,37 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
678745
skb->truesize -= frag->truesize;
679746
}
680747

681-
err = 0;
682-
offset = 0;
683-
/* BUILD HEADER */
684-
685-
*prevhdr = NEXTHDR_FRAGMENT;
686-
tmp_hdr = kmemdup(skb_network_header(skb), hlen, GFP_ATOMIC);
687-
if (!tmp_hdr) {
688-
err = -ENOMEM;
748+
err = ip6_fraglist_init(skb, hlen, prevhdr, nexthdr, frag_id,
749+
&iter);
750+
if (err < 0)
689751
goto fail;
690-
}
691-
frag = skb_shinfo(skb)->frag_list;
692-
skb_frag_list_init(skb);
693-
694-
__skb_pull(skb, hlen);
695-
fh = __skb_push(skb, sizeof(struct frag_hdr));
696-
__skb_push(skb, hlen);
697-
skb_reset_network_header(skb);
698-
memcpy(skb_network_header(skb), tmp_hdr, hlen);
699-
700-
fh->nexthdr = nexthdr;
701-
fh->reserved = 0;
702-
fh->frag_off = htons(IP6_MF);
703-
fh->identification = frag_id;
704-
705-
first_len = skb_pagelen(skb);
706-
skb->data_len = first_len - skb_headlen(skb);
707-
skb->len = first_len;
708-
ipv6_hdr(skb)->payload_len = htons(first_len -
709-
sizeof(struct ipv6hdr));
710752

711753
for (;;) {
712754
/* Prepare header of the next frame,
713755
* before previous one went down. */
714-
if (frag) {
715-
frag->ip_summed = CHECKSUM_NONE;
716-
skb_reset_transport_header(frag);
717-
fh = __skb_push(frag, sizeof(struct frag_hdr));
718-
__skb_push(frag, hlen);
719-
skb_reset_network_header(frag);
720-
memcpy(skb_network_header(frag), tmp_hdr,
721-
hlen);
722-
offset += skb->len - hlen - sizeof(struct frag_hdr);
723-
fh->nexthdr = nexthdr;
724-
fh->reserved = 0;
725-
fh->frag_off = htons(offset);
726-
if (frag->next)
727-
fh->frag_off |= htons(IP6_MF);
728-
fh->identification = frag_id;
729-
ipv6_hdr(frag)->payload_len =
730-
htons(frag->len -
731-
sizeof(struct ipv6hdr));
732-
ip6_copy_metadata(frag, skb);
733-
}
756+
if (iter.frag)
757+
ip6_fraglist_prepare(skb, &iter);
734758

735759
err = output(net, sk, skb);
736760
if (!err)
737761
IP6_INC_STATS(net, ip6_dst_idev(&rt->dst),
738762
IPSTATS_MIB_FRAGCREATES);
739763

740-
if (err || !frag)
764+
if (err || !iter.frag)
741765
break;
742766

743-
skb = frag;
744-
frag = skb->next;
745-
skb_mark_not_on_list(skb);
767+
skb = ip6_fraglist_next(&iter);
746768
}
747769

748-
kfree(tmp_hdr);
770+
kfree(iter.tmp_hdr);
749771

750772
if (err == 0) {
751773
IP6_INC_STATS(net, ip6_dst_idev(&rt->dst),
752774
IPSTATS_MIB_FRAGOKS);
753775
return 0;
754776
}
755777

756-
kfree_skb_list(frag);
778+
kfree_skb_list(iter.frag_list);
757779

758780
IP6_INC_STATS(net, ip6_dst_idev(&rt->dst),
759781
IPSTATS_MIB_FRAGFAILS);

0 commit comments

Comments
 (0)