Loading include/linux/skbuff.h +1 −0 Original line number Diff line number Diff line Loading @@ -1297,6 +1297,7 @@ extern void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len); extern void skb_release_data(struct sk_buff *skb); extern struct sk_buff *skb_segment(struct sk_buff *skb, int sg); static inline void *skb_header_pointer(const struct sk_buff *skb, int offset, int len, void *buffer) Loading include/net/protocol.h +1 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ struct net_protocol { int (*handler)(struct sk_buff *skb); void (*err_handler)(struct sk_buff *skb, u32 info); struct sk_buff *(*gso_segment)(struct sk_buff *skb, int sg); int no_policy; }; Loading include/net/tcp.h +2 −0 Original line number Diff line number Diff line Loading @@ -1086,6 +1086,8 @@ extern struct request_sock_ops tcp_request_sock_ops; extern int tcp_v4_destroy_sock(struct sock *sk); extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg); #ifdef CONFIG_PROC_FS extern int tcp4_proc_init(void); extern void tcp4_proc_exit(void); Loading net/core/skbuff.c +126 −0 Original line number Diff line number Diff line Loading @@ -1842,6 +1842,132 @@ unsigned char *skb_pull_rcsum(struct sk_buff *skb, unsigned int len) EXPORT_SYMBOL_GPL(skb_pull_rcsum); /** * skb_segment - Perform protocol segmentation on skb. * @skb: buffer to segment * @sg: whether scatter-gather can be used for generated segments * * This function performs segmentation on the given skb. It returns * the segment at the given position. It returns NULL if there are * no more segments to generate, or when an error is encountered. */ struct sk_buff *skb_segment(struct sk_buff *skb, int sg) { struct sk_buff *segs = NULL; struct sk_buff *tail = NULL; unsigned int mss = skb_shinfo(skb)->gso_size; unsigned int doffset = skb->data - skb->mac.raw; unsigned int offset = doffset; unsigned int headroom; unsigned int len; int nfrags = skb_shinfo(skb)->nr_frags; int err = -ENOMEM; int i = 0; int pos; __skb_push(skb, doffset); headroom = skb_headroom(skb); pos = skb_headlen(skb); do { struct sk_buff *nskb; skb_frag_t *frag; int hsize, nsize; int k; int size; len = skb->len - offset; if (len > mss) len = mss; hsize = skb_headlen(skb) - offset; if (hsize < 0) hsize = 0; nsize = hsize + doffset; if (nsize > len + doffset || !sg) nsize = len + doffset; nskb = alloc_skb(nsize + headroom, GFP_ATOMIC); if (unlikely(!nskb)) goto err; if (segs) tail->next = nskb; else segs = nskb; tail = nskb; nskb->dev = skb->dev; nskb->priority = skb->priority; nskb->protocol = skb->protocol; nskb->dst = dst_clone(skb->dst); memcpy(nskb->cb, skb->cb, sizeof(skb->cb)); nskb->pkt_type = skb->pkt_type; nskb->mac_len = skb->mac_len; skb_reserve(nskb, headroom); nskb->mac.raw = nskb->data; nskb->nh.raw = nskb->data + skb->mac_len; nskb->h.raw = nskb->nh.raw + (skb->h.raw - skb->nh.raw); memcpy(skb_put(nskb, doffset), skb->data, doffset); if (!sg) { nskb->csum = skb_copy_and_csum_bits(skb, offset, skb_put(nskb, len), len, 0); continue; } frag = skb_shinfo(nskb)->frags; k = 0; nskb->ip_summed = CHECKSUM_HW; nskb->csum = skb->csum; memcpy(skb_put(nskb, hsize), skb->data + offset, hsize); while (pos < offset + len) { BUG_ON(i >= nfrags); *frag = skb_shinfo(skb)->frags[i]; get_page(frag->page); size = frag->size; if (pos < offset) { frag->page_offset += offset - pos; frag->size -= offset - pos; } k++; if (pos + size <= offset + len) { i++; pos += size; } else { frag->size -= pos + size - (offset + len); break; } frag++; } skb_shinfo(nskb)->nr_frags = k; nskb->data_len = len - hsize; nskb->len += nskb->data_len; nskb->truesize += nskb->data_len; } while ((offset += len) < skb->len); return segs; err: while ((skb = segs)) { segs = skb->next; kfree(skb); } return ERR_PTR(err); } EXPORT_SYMBOL_GPL(skb_segment); void __init skb_init(void) { skbuff_head_cache = kmem_cache_create("skbuff_head_cache", Loading net/ipv4/af_inet.c +51 −0 Original line number Diff line number Diff line Loading @@ -68,6 +68,7 @@ */ #include <linux/config.h> #include <linux/err.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> Loading Loading @@ -1096,6 +1097,54 @@ int inet_sk_rebuild_header(struct sock *sk) EXPORT_SYMBOL(inet_sk_rebuild_header); static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int sg) { struct sk_buff *segs = ERR_PTR(-EINVAL); struct iphdr *iph; struct net_protocol *ops; int proto; int ihl; int id; if (!pskb_may_pull(skb, sizeof(*iph))) goto out; iph = skb->nh.iph; ihl = iph->ihl * 4; if (ihl < sizeof(*iph)) goto out; if (!pskb_may_pull(skb, ihl)) goto out; skb->h.raw = __skb_pull(skb, ihl); iph = skb->nh.iph; id = ntohs(iph->id); proto = iph->protocol & (MAX_INET_PROTOS - 1); segs = ERR_PTR(-EPROTONOSUPPORT); rcu_read_lock(); ops = rcu_dereference(inet_protos[proto]); if (ops && ops->gso_segment) segs = ops->gso_segment(skb, sg); rcu_read_unlock(); if (IS_ERR(segs)) goto out; skb = segs; do { iph = skb->nh.iph; iph->id = htons(id++); iph->tot_len = htons(skb->len - skb->mac_len); iph->check = 0; iph->check = ip_fast_csum(skb->nh.raw, iph->ihl); } while ((skb = skb->next)); out: return segs; } #ifdef CONFIG_IP_MULTICAST static struct net_protocol igmp_protocol = { .handler = igmp_rcv, Loading @@ -1105,6 +1154,7 @@ static struct net_protocol igmp_protocol = { static struct net_protocol tcp_protocol = { .handler = tcp_v4_rcv, .err_handler = tcp_v4_err, .gso_segment = tcp_tso_segment, .no_policy = 1, }; Loading Loading @@ -1150,6 +1200,7 @@ static int ipv4_proc_init(void); static struct packet_type ip_packet_type = { .type = __constant_htons(ETH_P_IP), .func = ip_rcv, .gso_segment = inet_gso_segment, }; static int __init inet_init(void) Loading Loading
include/linux/skbuff.h +1 −0 Original line number Diff line number Diff line Loading @@ -1297,6 +1297,7 @@ extern void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len); extern void skb_release_data(struct sk_buff *skb); extern struct sk_buff *skb_segment(struct sk_buff *skb, int sg); static inline void *skb_header_pointer(const struct sk_buff *skb, int offset, int len, void *buffer) Loading
include/net/protocol.h +1 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ struct net_protocol { int (*handler)(struct sk_buff *skb); void (*err_handler)(struct sk_buff *skb, u32 info); struct sk_buff *(*gso_segment)(struct sk_buff *skb, int sg); int no_policy; }; Loading
include/net/tcp.h +2 −0 Original line number Diff line number Diff line Loading @@ -1086,6 +1086,8 @@ extern struct request_sock_ops tcp_request_sock_ops; extern int tcp_v4_destroy_sock(struct sock *sk); extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg); #ifdef CONFIG_PROC_FS extern int tcp4_proc_init(void); extern void tcp4_proc_exit(void); Loading
net/core/skbuff.c +126 −0 Original line number Diff line number Diff line Loading @@ -1842,6 +1842,132 @@ unsigned char *skb_pull_rcsum(struct sk_buff *skb, unsigned int len) EXPORT_SYMBOL_GPL(skb_pull_rcsum); /** * skb_segment - Perform protocol segmentation on skb. * @skb: buffer to segment * @sg: whether scatter-gather can be used for generated segments * * This function performs segmentation on the given skb. It returns * the segment at the given position. It returns NULL if there are * no more segments to generate, or when an error is encountered. */ struct sk_buff *skb_segment(struct sk_buff *skb, int sg) { struct sk_buff *segs = NULL; struct sk_buff *tail = NULL; unsigned int mss = skb_shinfo(skb)->gso_size; unsigned int doffset = skb->data - skb->mac.raw; unsigned int offset = doffset; unsigned int headroom; unsigned int len; int nfrags = skb_shinfo(skb)->nr_frags; int err = -ENOMEM; int i = 0; int pos; __skb_push(skb, doffset); headroom = skb_headroom(skb); pos = skb_headlen(skb); do { struct sk_buff *nskb; skb_frag_t *frag; int hsize, nsize; int k; int size; len = skb->len - offset; if (len > mss) len = mss; hsize = skb_headlen(skb) - offset; if (hsize < 0) hsize = 0; nsize = hsize + doffset; if (nsize > len + doffset || !sg) nsize = len + doffset; nskb = alloc_skb(nsize + headroom, GFP_ATOMIC); if (unlikely(!nskb)) goto err; if (segs) tail->next = nskb; else segs = nskb; tail = nskb; nskb->dev = skb->dev; nskb->priority = skb->priority; nskb->protocol = skb->protocol; nskb->dst = dst_clone(skb->dst); memcpy(nskb->cb, skb->cb, sizeof(skb->cb)); nskb->pkt_type = skb->pkt_type; nskb->mac_len = skb->mac_len; skb_reserve(nskb, headroom); nskb->mac.raw = nskb->data; nskb->nh.raw = nskb->data + skb->mac_len; nskb->h.raw = nskb->nh.raw + (skb->h.raw - skb->nh.raw); memcpy(skb_put(nskb, doffset), skb->data, doffset); if (!sg) { nskb->csum = skb_copy_and_csum_bits(skb, offset, skb_put(nskb, len), len, 0); continue; } frag = skb_shinfo(nskb)->frags; k = 0; nskb->ip_summed = CHECKSUM_HW; nskb->csum = skb->csum; memcpy(skb_put(nskb, hsize), skb->data + offset, hsize); while (pos < offset + len) { BUG_ON(i >= nfrags); *frag = skb_shinfo(skb)->frags[i]; get_page(frag->page); size = frag->size; if (pos < offset) { frag->page_offset += offset - pos; frag->size -= offset - pos; } k++; if (pos + size <= offset + len) { i++; pos += size; } else { frag->size -= pos + size - (offset + len); break; } frag++; } skb_shinfo(nskb)->nr_frags = k; nskb->data_len = len - hsize; nskb->len += nskb->data_len; nskb->truesize += nskb->data_len; } while ((offset += len) < skb->len); return segs; err: while ((skb = segs)) { segs = skb->next; kfree(skb); } return ERR_PTR(err); } EXPORT_SYMBOL_GPL(skb_segment); void __init skb_init(void) { skbuff_head_cache = kmem_cache_create("skbuff_head_cache", Loading
net/ipv4/af_inet.c +51 −0 Original line number Diff line number Diff line Loading @@ -68,6 +68,7 @@ */ #include <linux/config.h> #include <linux/err.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> Loading Loading @@ -1096,6 +1097,54 @@ int inet_sk_rebuild_header(struct sock *sk) EXPORT_SYMBOL(inet_sk_rebuild_header); static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int sg) { struct sk_buff *segs = ERR_PTR(-EINVAL); struct iphdr *iph; struct net_protocol *ops; int proto; int ihl; int id; if (!pskb_may_pull(skb, sizeof(*iph))) goto out; iph = skb->nh.iph; ihl = iph->ihl * 4; if (ihl < sizeof(*iph)) goto out; if (!pskb_may_pull(skb, ihl)) goto out; skb->h.raw = __skb_pull(skb, ihl); iph = skb->nh.iph; id = ntohs(iph->id); proto = iph->protocol & (MAX_INET_PROTOS - 1); segs = ERR_PTR(-EPROTONOSUPPORT); rcu_read_lock(); ops = rcu_dereference(inet_protos[proto]); if (ops && ops->gso_segment) segs = ops->gso_segment(skb, sg); rcu_read_unlock(); if (IS_ERR(segs)) goto out; skb = segs; do { iph = skb->nh.iph; iph->id = htons(id++); iph->tot_len = htons(skb->len - skb->mac_len); iph->check = 0; iph->check = ip_fast_csum(skb->nh.raw, iph->ihl); } while ((skb = skb->next)); out: return segs; } #ifdef CONFIG_IP_MULTICAST static struct net_protocol igmp_protocol = { .handler = igmp_rcv, Loading @@ -1105,6 +1154,7 @@ static struct net_protocol igmp_protocol = { static struct net_protocol tcp_protocol = { .handler = tcp_v4_rcv, .err_handler = tcp_v4_err, .gso_segment = tcp_tso_segment, .no_policy = 1, }; Loading Loading @@ -1150,6 +1200,7 @@ static int ipv4_proc_init(void); static struct packet_type ip_packet_type = { .type = __constant_htons(ETH_P_IP), .func = ip_rcv, .gso_segment = inet_gso_segment, }; static int __init inet_init(void) Loading