aboutsummaryrefslogtreecommitdiff
path: root/net/batman-adv/fragmentation.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/batman-adv/fragmentation.c')
-rw-r--r--net/batman-adv/fragmentation.c41
1 files changed, 27 insertions, 14 deletions
diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
index 9751b207b01f..371f50804fc2 100644
--- a/net/batman-adv/fragmentation.c
+++ b/net/batman-adv/fragmentation.c
@@ -394,9 +394,10 @@ out:
/**
* batadv_frag_create - create a fragment from skb
+ * @net_dev: outgoing device for fragment
* @skb: skb to create fragment from
* @frag_head: header to use in new fragment
- * @mtu: size of new fragment
+ * @fragment_size: size of new fragment
*
* Split the passed skb into two fragments: A new one with size matching the
* passed mtu and the old one with the rest. The new skb contains data from the
@@ -404,22 +405,25 @@ out:
*
* Returns the new fragment, NULL on error.
*/
-static struct sk_buff *batadv_frag_create(struct sk_buff *skb,
+static struct sk_buff *batadv_frag_create(struct net_device *net_dev,
+ struct sk_buff *skb,
struct batadv_frag_packet *frag_head,
- unsigned int mtu)
+ unsigned int fragment_size)
{
+ unsigned int ll_reserved = LL_RESERVED_SPACE(net_dev);
+ unsigned int tailroom = net_dev->needed_tailroom;
struct sk_buff *skb_fragment;
unsigned header_size = sizeof(*frag_head);
- unsigned fragment_size = mtu - header_size;
+ unsigned mtu = fragment_size + header_size;
- skb_fragment = netdev_alloc_skb(NULL, mtu + ETH_HLEN);
+ skb_fragment = dev_alloc_skb(ll_reserved + mtu + tailroom);
if (!skb_fragment)
goto err;
skb->priority = TC_PRIO_CONTROL;
/* Eat the last mtu-bytes of the skb */
- skb_reserve(skb_fragment, header_size + ETH_HLEN);
+ skb_reserve(skb_fragment, ll_reserved + header_size);
skb_split(skb, skb_fragment, skb->len - fragment_size);
/* Add the header */
@@ -442,13 +446,14 @@ bool batadv_frag_send_packet(struct sk_buff *skb,
struct batadv_orig_node *orig_node,
struct batadv_neigh_node *neigh_node)
{
+ struct net_device *net_dev = neigh_node->if_incoming->net_dev;
struct batadv_priv *bat_priv;
struct batadv_hard_iface *primary_if = NULL;
struct batadv_frag_packet frag_header;
struct sk_buff *skb_fragment;
- unsigned mtu = neigh_node->if_incoming->net_dev->mtu;
+ unsigned mtu = net_dev->mtu;
unsigned header_size = sizeof(frag_header);
- unsigned max_fragment_size, max_packet_size;
+ unsigned max_fragment_size, num_fragments;
bool ret = false;
/* To avoid merge and refragmentation at next-hops we never send
@@ -456,10 +461,15 @@ bool batadv_frag_send_packet(struct sk_buff *skb,
*/
mtu = min_t(unsigned, mtu, BATADV_FRAG_MAX_FRAG_SIZE);
max_fragment_size = mtu - header_size;
- max_packet_size = max_fragment_size * BATADV_FRAG_MAX_FRAGMENTS;
+
+ if (skb->len == 0 || max_fragment_size == 0)
+ goto out_err;
+
+ num_fragments = (skb->len - 1) / max_fragment_size + 1;
+ max_fragment_size = (skb->len - 1) / num_fragments + 1;
/* Don't even try to fragment, if we need more than 16 fragments */
- if (skb->len > max_packet_size)
+ if (num_fragments > BATADV_FRAG_MAX_FRAGMENTS)
goto out_err;
bat_priv = orig_node->bat_priv;
@@ -484,7 +494,8 @@ bool batadv_frag_send_packet(struct sk_buff *skb,
if (frag_header.no == BATADV_FRAG_MAX_FRAGMENTS - 1)
goto out_err;
- skb_fragment = batadv_frag_create(skb, &frag_header, mtu);
+ skb_fragment = batadv_frag_create(net_dev, skb, &frag_header,
+ max_fragment_size);
if (!skb_fragment)
goto out_err;
@@ -496,11 +507,13 @@ bool batadv_frag_send_packet(struct sk_buff *skb,
frag_header.no++;
}
- /* Make room for the fragment header. */
- if (batadv_skb_head_push(skb, header_size) < 0 ||
- pskb_expand_head(skb, header_size + ETH_HLEN, 0, GFP_ATOMIC) < 0)
+ /* make sure that there is at least enough head for the fragmentation
+ * and ethernet headers
+ */
+ if (skb_cow_head(skb, ETH_HLEN + header_size) < 0)
goto out_err;
+ skb_push(skb, header_size);
memcpy(skb->data, &frag_header, header_size);
/* Send the last fragment */