Commit 56a6f4d0 authored by Arnaldo Carvalho de Melo's avatar Arnaldo Carvalho de Melo Committed by Arnaldo Carvalho de Melo

[NET] generalise tcp_add_data, skb_split and tcp_copy_to_page

Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@conectiva.com.br>
parent 090ee8ec
......@@ -22,6 +22,10 @@ struct sock_extended_err
#ifdef __KERNEL__
#include <linux/config.h>
#include <net/ip.h>
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
#include <linux/ipv6.h>
#endif
#define SKB_EXT_ERR(skb) ((struct sock_exterr_skb *) ((skb)->cb))
......
......@@ -27,6 +27,7 @@
#include <linux/highmem.h>
#include <linux/poll.h>
#include <linux/net.h>
#include <net/checksum.h>
#define HAVE_ALLOC_SKB /* For the drivers to know */
#define HAVE_ALIGNABLE_SKB /* Ditto 8) */
......@@ -971,6 +972,27 @@ static inline struct sk_buff *skb_padto(struct sk_buff *skb, unsigned int len)
return skb_pad(skb, len-size);
}
static inline int skb_add_data(struct sk_buff *skb,
char __user *from, int copy)
{
const int off = skb->len;
if (skb->ip_summed == CHECKSUM_NONE) {
int err = 0;
unsigned int csum = csum_and_copy_from_user(from,
skb_put(skb, copy),
copy, 0, &err);
if (!err) {
skb->csum = csum_block_add(skb->csum, csum, off);
return 0;
}
} else if (!copy_from_user(skb_put(skb, copy), from, copy))
return 0;
__skb_trim(skb, off);
return -EFAULT;
}
/**
* skb_linearize - convert paged skb to linear one
* @skb: buffer to linarize
......@@ -1034,6 +1056,8 @@ extern unsigned int skb_copy_and_csum_bits(const struct sk_buff *skb,
int offset, u8 *to, int len,
unsigned int csum);
extern void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to);
extern void skb_split(struct sk_buff *skb,
struct sk_buff *skb1, const u32 len);
extern void skb_init(void);
extern void skb_add_mtu(int mtu);
......
......@@ -16,83 +16,15 @@
* 2 of the License, or (at your option) any later version.
*/
/*
* Fixes:
*
* Ralf Baechle : generic ipv6 checksum
* <ralf@waldorf-gmbh.de>
*/
#ifndef _CHECKSUM_H
#define _CHECKSUM_H
#include <linux/errno.h>
#include <asm/types.h>
#include <asm/byteorder.h>
#include <net/ip.h>
#include <linux/in6.h>
#include <asm/uaccess.h>
#include <asm/checksum.h>
#ifndef _HAVE_ARCH_IPV6_CSUM
static __inline__ unsigned short int csum_ipv6_magic(struct in6_addr *saddr,
struct in6_addr *daddr,
__u16 len,
unsigned short proto,
unsigned int csum)
{
int carry;
__u32 ulen;
__u32 uproto;
csum += saddr->s6_addr32[0];
carry = (csum < saddr->s6_addr32[0]);
csum += carry;
csum += saddr->s6_addr32[1];
carry = (csum < saddr->s6_addr32[1]);
csum += carry;
csum += saddr->s6_addr32[2];
carry = (csum < saddr->s6_addr32[2]);
csum += carry;
csum += saddr->s6_addr32[3];
carry = (csum < saddr->s6_addr32[3]);
csum += carry;
csum += daddr->s6_addr32[0];
carry = (csum < daddr->s6_addr32[0]);
csum += carry;
csum += daddr->s6_addr32[1];
carry = (csum < daddr->s6_addr32[1]);
csum += carry;
csum += daddr->s6_addr32[2];
carry = (csum < daddr->s6_addr32[2]);
csum += carry;
csum += daddr->s6_addr32[3];
carry = (csum < daddr->s6_addr32[3]);
csum += carry;
ulen = htonl((__u32) len);
csum += ulen;
carry = (csum < ulen);
csum += carry;
uproto = htonl(proto);
csum += uproto;
carry = (csum < uproto);
csum += carry;
return csum_fold(csum);
}
#endif
#ifndef _HAVE_ARCH_COPY_AND_CSUM_FROM_USER
static inline
unsigned int csum_and_copy_from_user (const char __user *src, char *dst,
......
......@@ -37,7 +37,7 @@
#include <net/snmp.h>
#endif
#include <net/sock.h> /* struct sock */
struct sock;
struct inet_skb_parm
{
......
/*
* INET An implementation of the TCP/IP protocol suite for the LINUX
* operating system. INET is implemented using the BSD Socket
* interface as the means of communication with the user level.
*
* Checksumming functions for IPv6
*
* Authors: Jorge Cwik, <jorge@laser.satlink.net>
* Arnt Gulbrandsen, <agulbra@nvg.unit.no>
* Borrows very liberally from tcp.c and ip.c, see those
* files for more names.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
/*
* Fixes:
*
* Ralf Baechle : generic ipv6 checksum
* <ralf@waldorf-gmbh.de>
*/
#ifndef _CHECKSUM_IPV6_H
#define _CHECKSUM_IPV6_H
#include <asm/types.h>
#include <asm/byteorder.h>
#include <net/ip.h>
#include <asm/checksum.h>
#include <linux/in6.h>
#ifndef _HAVE_ARCH_IPV6_CSUM
static __inline__ unsigned short int csum_ipv6_magic(struct in6_addr *saddr,
struct in6_addr *daddr,
__u16 len,
unsigned short proto,
unsigned int csum)
{
int carry;
__u32 ulen;
__u32 uproto;
csum += saddr->s6_addr32[0];
carry = (csum < saddr->s6_addr32[0]);
csum += carry;
csum += saddr->s6_addr32[1];
carry = (csum < saddr->s6_addr32[1]);
csum += carry;
csum += saddr->s6_addr32[2];
carry = (csum < saddr->s6_addr32[2]);
csum += carry;
csum += saddr->s6_addr32[3];
carry = (csum < saddr->s6_addr32[3]);
csum += carry;
csum += daddr->s6_addr32[0];
carry = (csum < daddr->s6_addr32[0]);
csum += carry;
csum += daddr->s6_addr32[1];
carry = (csum < daddr->s6_addr32[1]);
csum += carry;
csum += daddr->s6_addr32[2];
carry = (csum < daddr->s6_addr32[2]);
csum += carry;
csum += daddr->s6_addr32[3];
carry = (csum < daddr->s6_addr32[3]);
csum += carry;
ulen = htonl((__u32) len);
csum += ulen;
carry = (csum < ulen);
csum += carry;
uproto = htonl(proto);
csum += uproto;
carry = (csum < uproto);
csum += carry;
return csum_fold(csum);
}
#endif
#endif
......@@ -53,6 +53,7 @@
#include <asm/atomic.h>
#include <net/dst.h>
#include <net/checksum.h>
/*
* This structure really needs to be cleaned up.
......@@ -923,6 +924,29 @@ static inline void sk_charge_skb(struct sock *sk, struct sk_buff *skb)
sk->sk_forward_alloc -= skb->truesize;
}
static inline int skb_copy_to_page(struct sock *sk, char __user *from,
struct sk_buff *skb, struct page *page,
int off, int copy)
{
if (skb->ip_summed == CHECKSUM_NONE) {
int err = 0;
unsigned int csum = csum_and_copy_from_user(from,
page_address(page) + off,
copy, 0, &err);
if (err)
return err;
skb->csum = csum_block_add(skb->csum, csum, skb->len);
} else if (copy_from_user(page_address(page) + off, from, copy))
return -EFAULT;
skb->len += copy;
skb->data_len += copy;
skb->truesize += copy;
sk->sk_wmem_queued += copy;
sk->sk_forward_alloc -= copy;
return 0;
}
/*
* Queue a received datagram if it will fit. Stream and sequenced
* protocols can't normally use this as they need to fit buffers in
......
......@@ -33,6 +33,7 @@
#include <net/checksum.h>
#include <net/sock.h>
#include <net/snmp.h>
#include <net/ip.h>
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
#include <linux/ipv6.h>
#endif
......
......@@ -1263,6 +1263,81 @@ void skb_add_mtu(int mtu)
}
#endif
static void inline skb_split_inside_header(struct sk_buff *skb,
struct sk_buff* skb1,
const u32 len, const int pos)
{
int i;
memcpy(skb_put(skb1, pos - len), skb->data + len, pos - len);
/* And move data appendix as is. */
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
skb_shinfo(skb1)->frags[i] = skb_shinfo(skb)->frags[i];
skb_shinfo(skb1)->nr_frags = skb_shinfo(skb)->nr_frags;
skb_shinfo(skb)->nr_frags = 0;
skb1->data_len = skb->data_len;
skb1->len += skb1->data_len;
skb->data_len = 0;
skb->len = len;
skb->tail = skb->data + len;
}
static void inline skb_split_no_header(struct sk_buff *skb,
struct sk_buff* skb1,
const u32 len, int pos)
{
int i, k = 0;
const int nfrags = skb_shinfo(skb)->nr_frags;
skb_shinfo(skb)->nr_frags = 0;
skb1->len = skb1->data_len = skb->len - len;
skb->len = len;
skb->data_len = len - pos;
for (i = 0; i < nfrags; i++) {
int size = skb_shinfo(skb)->frags[i].size;
if (pos + size > len) {
skb_shinfo(skb1)->frags[k] = skb_shinfo(skb)->frags[i];
if (pos < len) {
/* Split frag.
* We have to variants in this case:
* 1. Move all the frag to the second
* part, if it is possible. F.e.
* this approach is mandatory for TUX,
* where splitting is expensive.
* 2. Split is accurately. We make this.
*/
get_page(skb_shinfo(skb)->frags[i].page);
skb_shinfo(skb1)->frags[0].page_offset += len - pos;
skb_shinfo(skb1)->frags[0].size -= len - pos;
skb_shinfo(skb)->frags[i].size = len - pos;
skb_shinfo(skb)->nr_frags++;
}
k++;
} else
skb_shinfo(skb)->nr_frags++;
pos += size;
}
skb_shinfo(skb1)->nr_frags = k;
}
/**
* skb_split - Split fragmented skb to two parts at length len.
*/
void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len)
{
int pos = skb_headlen(skb);
if (len < pos) /* Split line is inside header. */
skb_split_inside_header(skb, skb1, len, pos);
else /* Second chunk has no header, nothing to copy. */
skb_split_no_header(skb, skb1, len, pos);
}
void __init skb_init(void)
{
skbuff_head_cache = kmem_cache_create("skbuff_head_cache",
......@@ -1300,3 +1375,4 @@ EXPORT_SYMBOL(skb_queue_head);
EXPORT_SYMBOL(skb_queue_tail);
EXPORT_SYMBOL(skb_unlink);
EXPORT_SYMBOL(skb_append);
EXPORT_SYMBOL(skb_split);
......@@ -28,6 +28,7 @@
#include <linux/proc_fs.h>
#include <linux/vmalloc.h>
#include <net/checksum.h>
#include <net/ip.h>
#include <linux/stddef.h>
#include <linux/sysctl.h>
#include <linux/slab.h>
......
......@@ -24,6 +24,7 @@
#include <linux/sysctl.h>
#endif
#include <net/checksum.h>
#include <net/ip.h>
#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_conntrack_lock)
#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_conntrack_lock)
......
......@@ -22,6 +22,7 @@
#include <linux/udp.h>
#include <linux/tcp.h>
#include <net/checksum.h>
#include <net/ip.h>
#include <linux/timer.h>
#include <linux/netdevice.h>
#include <linux/if.h>
......
......@@ -27,6 +27,7 @@
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/proc_fs.h>
#include <net/ip.h>
#include <net/checksum.h>
#include <linux/spinlock.h>
......
......@@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/netfilter.h>
#include <net/protocol.h>
#include <net/ip.h>
#include <net/checksum.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv4/ip_nat_rule.h>
......
......@@ -948,53 +948,6 @@ ssize_t tcp_sendpage(struct socket *sock, struct page *page, int offset,
#define TCP_PAGE(sk) (inet_sk(sk)->sndmsg_page)
#define TCP_OFF(sk) (inet_sk(sk)->sndmsg_off)
static inline int tcp_copy_to_page(struct sock *sk, char __user *from,
struct sk_buff *skb, struct page *page,
int off, int copy)
{
int err = 0;
unsigned int csum;
if (skb->ip_summed == CHECKSUM_NONE) {
csum = csum_and_copy_from_user(from, page_address(page) + off,
copy, 0, &err);
if (err) return err;
skb->csum = csum_block_add(skb->csum, csum, skb->len);
} else {
if (copy_from_user(page_address(page) + off, from, copy))
return -EFAULT;
}
skb->len += copy;
skb->data_len += copy;
skb->truesize += copy;
sk->sk_wmem_queued += copy;
sk->sk_forward_alloc -= copy;
return 0;
}
static inline int skb_add_data(struct sk_buff *skb, char __user *from, int copy)
{
int err = 0;
unsigned int csum;
int off = skb->len;
if (skb->ip_summed == CHECKSUM_NONE) {
csum = csum_and_copy_from_user(from, skb_put(skb, copy),
copy, 0, &err);
if (!err) {
skb->csum = csum_block_add(skb->csum, csum, off);
return 0;
}
} else {
if (!copy_from_user(skb_put(skb, copy), from, copy))
return 0;
}
__skb_trim(skb, off);
return -EFAULT;
}
static inline int select_size(struct sock *sk, struct tcp_opt *tp)
{
int tmp = tp->mss_cache_std;
......@@ -1138,7 +1091,7 @@ int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
/* Time to copy data. We are close to
* the end! */
err = tcp_copy_to_page(sk, from, skb, page,
err = skb_copy_to_page(sk, from, skb, page,
off, copy);
if (err) {
/* If this page was new, give it to the
......
......@@ -354,70 +354,6 @@ void tcp_push_one(struct sock *sk, unsigned cur_mss)
}
}
/* Split fragmented skb to two parts at length len. */
static void skb_split(struct sk_buff *skb, struct sk_buff *skb1, u32 len)
{
int i;
int pos = skb_headlen(skb);
if (len < pos) {
/* Split line is inside header. */
memcpy(skb_put(skb1, pos-len), skb->data + len, pos-len);
/* And move data appendix as is. */
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
skb_shinfo(skb1)->frags[i] = skb_shinfo(skb)->frags[i];
skb_shinfo(skb1)->nr_frags = skb_shinfo(skb)->nr_frags;
skb_shinfo(skb)->nr_frags = 0;
skb1->data_len = skb->data_len;
skb1->len += skb1->data_len;
skb->data_len = 0;
skb->len = len;
skb->tail = skb->data+len;
} else {
int k = 0;
int nfrags = skb_shinfo(skb)->nr_frags;
/* Second chunk has no header, nothing to copy. */
skb_shinfo(skb)->nr_frags = 0;
skb1->len = skb1->data_len = skb->len - len;
skb->len = len;
skb->data_len = len - pos;
for (i=0; i<nfrags; i++) {
int size = skb_shinfo(skb)->frags[i].size;
if (pos + size > len) {
skb_shinfo(skb1)->frags[k] = skb_shinfo(skb)->frags[i];
if (pos < len) {
/* Split frag.
* We have to variants in this case:
* 1. Move all the frag to the second
* part, if it is possible. F.e.
* this approach is mandatory for TUX,
* where splitting is expensive.
* 2. Split is accurately. We make this.
*/
get_page(skb_shinfo(skb)->frags[i].page);
skb_shinfo(skb1)->frags[0].page_offset += (len-pos);
skb_shinfo(skb1)->frags[0].size -= (len-pos);
skb_shinfo(skb)->frags[i].size = len-pos;
skb_shinfo(skb)->nr_frags++;
}
k++;
} else {
skb_shinfo(skb)->nr_frags++;
}
pos += size;
}
skb_shinfo(skb1)->nr_frags = k;
}
}
/* Function to create two new TCP segments. Shrinks the given segment
* to the specified size and appends a new segment with the rest of the
* packet to the list. This won't be called frequently, I hope.
......
......@@ -55,7 +55,7 @@
#include <net/sock.h>
#include <net/ipv6.h>
#include <net/checksum.h>
#include <net/ip6_checksum.h>
#include <net/protocol.h>
#include <net/raw.h>
#include <net/rawv6.h>
......
......@@ -60,7 +60,7 @@
#include <net/addrconf.h>
#include <net/ip6_route.h>
#include <net/checksum.h>
#include <net/ip6_checksum.h>
/* Set to 3 to get tracing... */
#define MCAST_DEBUG 2
......
......@@ -77,7 +77,7 @@
#include <net/icmp.h>
#include <net/flow.h>
#include <net/checksum.h>
#include <net/ip6_checksum.h>
#include <linux/proc_fs.h>
#include <linux/netfilter.h>
......
......@@ -35,6 +35,7 @@
#include <asm/uaccess.h>
#include <asm/ioctls.h>
#include <net/ip.h>
#include <net/sock.h>
#include <net/snmp.h>
......@@ -42,6 +43,7 @@
#include <net/ndisc.h>
#include <net/protocol.h>
#include <net/ip6_route.h>
#include <net/ip6_checksum.h>
#include <net/addrconf.h>
#include <net/transp_v6.h>
#include <net/udp.h>
......
......@@ -51,6 +51,7 @@
#include <net/transp_v6.h>
#include <net/addrconf.h>
#include <net/ip6_route.h>
#include <net/ip6_checksum.h>
#include <net/inet_ecn.h>
#include <net/protocol.h>
#include <net/xfrm.h>
......
......@@ -51,7 +51,7 @@
#include <net/udp.h>
#include <net/inet_common.h>
#include <net/checksum.h>
#include <net/ip6_checksum.h>
#include <net/xfrm.h>
#include <linux/proc_fs.h>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment