Skip to content

Commit 8a6a1f1

Browse files
ummakynesdavem330
authored andcommitted
net: ipv6: split skbuff into fragments transformer
This patch exposes a new API to refragment a skbuff. This allows you to split either a linear skbuff or to force the refragmentation of an existing fraglist using a different mtu. The API consists of: * ip6_frag_init(), that initializes the internal state of the transformer. * ip6_frag_next(), that allows you to fetch the next fragment. This function internally allocates the skbuff that represents the fragment, it pushes the IPv6 header, and it also copies the payload for each fragment. The ip6_frag_state object stores the internal state of the splitter. 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 065ff79 commit 8a6a1f1

File tree

2 files changed

+126
-76
lines changed

2 files changed

+126
-76
lines changed

include/net/ipv6.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,25 @@ static inline struct sk_buff *ip6_fraglist_next(struct ip6_fraglist_iter *iter)
179179
return skb;
180180
}
181181

182+
struct ip6_frag_state {
183+
u8 *prevhdr;
184+
unsigned int hlen;
185+
unsigned int mtu;
186+
unsigned int left;
187+
int offset;
188+
int ptr;
189+
int hroom;
190+
int troom;
191+
__be32 frag_id;
192+
u8 nexthdr;
193+
};
194+
195+
void ip6_frag_init(struct sk_buff *skb, unsigned int hlen, unsigned int mtu,
196+
unsigned short needed_tailroom, int hdr_room, u8 *prevhdr,
197+
u8 nexthdr, __be32 frag_id, struct ip6_frag_state *state);
198+
struct sk_buff *ip6_frag_next(struct sk_buff *skb,
199+
struct ip6_frag_state *state);
200+
182201
#define IP6_REPLY_MARK(net, mark) \
183202
((net)->ipv6.sysctl.fwmark_reflect ? (mark) : 0)
184203

net/ipv6/ip6_output.c

Lines changed: 107 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -659,18 +659,114 @@ void ip6_fraglist_prepare(struct sk_buff *skb,
659659
}
660660
EXPORT_SYMBOL(ip6_fraglist_prepare);
661661

662+
void ip6_frag_init(struct sk_buff *skb, unsigned int hlen, unsigned int mtu,
663+
unsigned short needed_tailroom, int hdr_room, u8 *prevhdr,
664+
u8 nexthdr, __be32 frag_id, struct ip6_frag_state *state)
665+
{
666+
state->prevhdr = prevhdr;
667+
state->nexthdr = nexthdr;
668+
state->frag_id = frag_id;
669+
670+
state->hlen = hlen;
671+
state->mtu = mtu;
672+
673+
state->left = skb->len - hlen; /* Space per frame */
674+
state->ptr = hlen; /* Where to start from */
675+
676+
state->hroom = hdr_room;
677+
state->troom = needed_tailroom;
678+
679+
state->offset = 0;
680+
}
681+
EXPORT_SYMBOL(ip6_frag_init);
682+
683+
struct sk_buff *ip6_frag_next(struct sk_buff *skb, struct ip6_frag_state *state)
684+
{
685+
u8 *prevhdr = state->prevhdr, *fragnexthdr_offset;
686+
struct sk_buff *frag;
687+
struct frag_hdr *fh;
688+
unsigned int len;
689+
690+
len = state->left;
691+
/* IF: it doesn't fit, use 'mtu' - the data space left */
692+
if (len > state->mtu)
693+
len = state->mtu;
694+
/* IF: we are not sending up to and including the packet end
695+
then align the next start on an eight byte boundary */
696+
if (len < state->left)
697+
len &= ~7;
698+
699+
/* Allocate buffer */
700+
frag = alloc_skb(len + state->hlen + sizeof(struct frag_hdr) +
701+
state->hroom + state->troom, GFP_ATOMIC);
702+
if (!frag)
703+
return ERR_PTR(-ENOMEM);
704+
705+
/*
706+
* Set up data on packet
707+
*/
708+
709+
ip6_copy_metadata(frag, skb);
710+
skb_reserve(frag, state->hroom);
711+
skb_put(frag, len + state->hlen + sizeof(struct frag_hdr));
712+
skb_reset_network_header(frag);
713+
fh = (struct frag_hdr *)(skb_network_header(frag) + state->hlen);
714+
frag->transport_header = (frag->network_header + state->hlen +
715+
sizeof(struct frag_hdr));
716+
717+
/*
718+
* Charge the memory for the fragment to any owner
719+
* it might possess
720+
*/
721+
if (skb->sk)
722+
skb_set_owner_w(frag, skb->sk);
723+
724+
/*
725+
* Copy the packet header into the new buffer.
726+
*/
727+
skb_copy_from_linear_data(skb, skb_network_header(frag), state->hlen);
728+
729+
fragnexthdr_offset = skb_network_header(frag);
730+
fragnexthdr_offset += prevhdr - skb_network_header(skb);
731+
*fragnexthdr_offset = NEXTHDR_FRAGMENT;
732+
733+
/*
734+
* Build fragment header.
735+
*/
736+
fh->nexthdr = state->nexthdr;
737+
fh->reserved = 0;
738+
fh->identification = state->frag_id;
739+
740+
/*
741+
* Copy a block of the IP datagram.
742+
*/
743+
BUG_ON(skb_copy_bits(skb, state->ptr, skb_transport_header(frag),
744+
len));
745+
state->left -= len;
746+
747+
fh->frag_off = htons(state->offset);
748+
if (state->left > 0)
749+
fh->frag_off |= htons(IP6_MF);
750+
ipv6_hdr(frag)->payload_len = htons(frag->len - sizeof(struct ipv6hdr));
751+
752+
state->ptr += len;
753+
state->offset += len;
754+
755+
return frag;
756+
}
757+
EXPORT_SYMBOL(ip6_frag_next);
758+
662759
int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
663760
int (*output)(struct net *, struct sock *, struct sk_buff *))
664761
{
665762
struct sk_buff *frag;
666763
struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
667764
struct ipv6_pinfo *np = skb->sk && !dev_recursion_level() ?
668765
inet6_sk(skb->sk) : NULL;
669-
struct frag_hdr *fh;
670-
unsigned int mtu, hlen, left, len, nexthdr_offset;
671-
int hroom, troom;
766+
struct ip6_frag_state state;
767+
unsigned int mtu, hlen, nexthdr_offset;
768+
int hroom, err = 0;
672769
__be32 frag_id;
673-
int ptr, offset = 0, err = 0;
674770
u8 *prevhdr, nexthdr = 0;
675771

676772
err = ip6_find_1stfragopt(skb, &prevhdr);
@@ -792,90 +888,25 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
792888
}
793889

794890
slow_path:
795-
left = skb->len - hlen; /* Space per frame */
796-
ptr = hlen; /* Where to start from */
797-
798891
/*
799892
* Fragment the datagram.
800893
*/
801894

802-
troom = rt->dst.dev->needed_tailroom;
895+
ip6_frag_init(skb, hlen, mtu, rt->dst.dev->needed_tailroom,
896+
LL_RESERVED_SPACE(rt->dst.dev), prevhdr, nexthdr, frag_id,
897+
&state);
803898

804899
/*
805900
* Keep copying data until we run out.
806901
*/
807-
while (left > 0) {
808-
u8 *fragnexthdr_offset;
809-
810-
len = left;
811-
/* IF: it doesn't fit, use 'mtu' - the data space left */
812-
if (len > mtu)
813-
len = mtu;
814-
/* IF: we are not sending up to and including the packet end
815-
then align the next start on an eight byte boundary */
816-
if (len < left) {
817-
len &= ~7;
818-
}
819902

820-
/* Allocate buffer */
821-
frag = alloc_skb(len + hlen + sizeof(struct frag_hdr) +
822-
hroom + troom, GFP_ATOMIC);
823-
if (!frag) {
824-
err = -ENOMEM;
903+
while (state.left > 0) {
904+
frag = ip6_frag_next(skb, &state);
905+
if (IS_ERR(frag)) {
906+
err = PTR_ERR(frag);
825907
goto fail;
826908
}
827909

828-
/*
829-
* Set up data on packet
830-
*/
831-
832-
ip6_copy_metadata(frag, skb);
833-
skb_reserve(frag, hroom);
834-
skb_put(frag, len + hlen + sizeof(struct frag_hdr));
835-
skb_reset_network_header(frag);
836-
fh = (struct frag_hdr *)(skb_network_header(frag) + hlen);
837-
frag->transport_header = (frag->network_header + hlen +
838-
sizeof(struct frag_hdr));
839-
840-
/*
841-
* Charge the memory for the fragment to any owner
842-
* it might possess
843-
*/
844-
if (skb->sk)
845-
skb_set_owner_w(frag, skb->sk);
846-
847-
/*
848-
* Copy the packet header into the new buffer.
849-
*/
850-
skb_copy_from_linear_data(skb, skb_network_header(frag), hlen);
851-
852-
fragnexthdr_offset = skb_network_header(frag);
853-
fragnexthdr_offset += prevhdr - skb_network_header(skb);
854-
*fragnexthdr_offset = NEXTHDR_FRAGMENT;
855-
856-
/*
857-
* Build fragment header.
858-
*/
859-
fh->nexthdr = nexthdr;
860-
fh->reserved = 0;
861-
fh->identification = frag_id;
862-
863-
/*
864-
* Copy a block of the IP datagram.
865-
*/
866-
BUG_ON(skb_copy_bits(skb, ptr, skb_transport_header(frag),
867-
len));
868-
left -= len;
869-
870-
fh->frag_off = htons(offset);
871-
if (left > 0)
872-
fh->frag_off |= htons(IP6_MF);
873-
ipv6_hdr(frag)->payload_len = htons(frag->len -
874-
sizeof(struct ipv6hdr));
875-
876-
ptr += len;
877-
offset += len;
878-
879910
/*
880911
* Put this fragment into the sending queue.
881912
*/

0 commit comments

Comments
 (0)