GSO: Packet Offload Callback Function

An actual code to lean how to program a Linux kernel module for GSO (Generic Segmentation Offloading). Basically, a callback function which will perform the segmentation according to a defined type (e.g. MPLS, IPv4 fragmentation, ..) need to be registered in the kernel stack. For this purpose, the data structure packet_offload has to be instantiated in the module, because this is where the callback function will be in.

/**
 *      dev_add_offload - register offload handlers
 *      @po: protocol offload declaration
 *
 *      Add protocol offload handlers to the networking stack. The passed
 *      &proto_offload is linked into kernel lists and may not be freed until
  *      it has been removed from the kernel lists.
  *
  *      This call does not sleep therefore it can not
  *      guarantee all CPU's that are in middle of receiving packets
  *      will see the new offload handlers (until the next received packet).
  */
void dev_add_offload(struct packet_offload *po)
{
        struct list_head *head = &offload_base;
 
         spin_lock(&offload_lock);
         list_add_rcu(&po->list, head);
         spin_unlock(&offload_lock);
}
EXPORT_SYMBOL(dev_add_offload);

I put a code snippet of the actual kernel module for MPLS GSO. This the callback function which performs MPLS segmentation.

static struct sk_buff *mpls_gso_segment(struct sk_buff *skb,
                                        netdev_features_t features)
{
         struct sk_buff *segs = ERR_PTR(-EINVAL);
         netdev_features_t mpls_features;
         __be16 mpls_protocol;
 
         if (unlikely(skb_shinfo(skb)->gso_type &
                                 ~(SKB_GSO_TCPV4 |
                                   SKB_GSO_TCPV6 |
                                   SKB_GSO_UDP |
                                   SKB_GSO_DODGY |
                                   SKB_GSO_TCP_ECN)))
                 goto out;
 
         /* Setup inner SKB. */
         mpls_protocol = skb->protocol;
         skb->protocol = skb->inner_protocol;
 
         /* Push back the mac header that skb_mac_gso_segment() has pulled.
          * It will be re-pulled by the call to skb_mac_gso_segment() below
          */
         __skb_push(skb, skb->mac_len);
 
         /* Segment inner packet. */
         mpls_features = skb->dev->mpls_features & features;
         segs = skb_mac_gso_segment(skb, mpls_features);
 
 
         /* Restore outer protocol. */
         skb->protocol = mpls_protocol;
 
         /* Re-pull the mac header that the call to skb_mac_gso_segment()
          * above pulled.  It will be re-pushed after returning
          * skb_mac_gso_segment(), an indirect caller of this function.
          */
         __skb_pull(skb, skb->data - skb_mac_header(skb));
out:
         return segs;
}

Make a packet_offload variable:

static struct packet_offload mpls_uc_offload __read_mostly = {
         .type = cpu_to_be16(ETH_P_MPLS_UC),
         .priority = 15,
         .callbacks = {
                 .gso_segment    =       mpls_gso_segment,
          },
};

Register the callback in the kernel stack:

dev_add_offload(&mpls_uc_offload);

					
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s