Skip to content

Commit

Permalink
Merge branch 'inet_csums'
Browse files Browse the repository at this point in the history
Tom Herbert says:

====================
net: Support checksum in UDP

This patch series adds support for using checksums in UDP tunnels. With
this it is possible that two or more checksums may be set within the
same packet and we would like to do that efficiently.

This series also creates some new helper functions to be used by various
tunnel protocol implementations.

v2: Fixed indentation in tcp_v6_send_check arguments.
v3: Move udp_set_csum and udp6_set_csum to be not inlined
    Also have this functions call with a nocheck boolean argument
    instead of passing a sock structure.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Jun 5, 2014
2 parents 6579867 + 359a0ea commit 00d115f
Show file tree
Hide file tree
Showing 22 changed files with 272 additions and 148 deletions.
120 changes: 59 additions & 61 deletions drivers/net/vxlan.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ struct vxlan_dev {
__u16 port_max;
__u8 tos; /* TOS override */
__u8 ttl;
u32 flags; /* VXLAN_F_* below */
u32 flags; /* VXLAN_F_* in vxlan.h */

struct work_struct sock_work;
struct work_struct igmp_join;
Expand All @@ -150,13 +150,6 @@ struct vxlan_dev {
struct hlist_head fdb_head[FDB_HASH_SIZE];
};

#define VXLAN_F_LEARN 0x01
#define VXLAN_F_PROXY 0x02
#define VXLAN_F_RSC 0x04
#define VXLAN_F_L2MISS 0x08
#define VXLAN_F_L3MISS 0x10
#define VXLAN_F_IPV6 0x20 /* internal flag */

/* salt for hash table */
static u32 vxlan_salt __read_mostly;
static struct workqueue_struct *vxlan_wq;
Expand Down Expand Up @@ -1601,18 +1594,11 @@ __be16 vxlan_src_port(__u16 port_min, __u16 port_max, struct sk_buff *skb)
}
EXPORT_SYMBOL_GPL(vxlan_src_port);

static int handle_offloads(struct sk_buff *skb)
static inline struct sk_buff *vxlan_handle_offloads(struct sk_buff *skb,
bool udp_csum)
{
if (skb_is_gso(skb)) {
int err = skb_unclone(skb, GFP_ATOMIC);
if (unlikely(err))
return err;

skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL;
} else if (skb->ip_summed != CHECKSUM_PARTIAL)
skb->ip_summed = CHECKSUM_NONE;

return 0;
int type = udp_csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL;
return iptunnel_handle_offloads(skb, udp_csum, type);
}

#if IS_ENABLED(CONFIG_IPV6)
Expand All @@ -1629,10 +1615,9 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs,
int min_headroom;
int err;

if (!skb->encapsulation) {
skb_reset_inner_headers(skb);
skb->encapsulation = 1;
}
skb = vxlan_handle_offloads(skb, !udp_get_no_check6_tx(vs->sock->sk));
if (IS_ERR(skb))
return -EINVAL;

skb_scrub_packet(skb, xnet);

Expand Down Expand Up @@ -1666,27 +1651,14 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs,
uh->source = src_port;

uh->len = htons(skb->len);
uh->check = 0;

memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
IPSKB_REROUTED);
skb_dst_set(skb, dst);

if (!skb_is_gso(skb) && !(dst->dev->features & NETIF_F_IPV6_CSUM)) {
__wsum csum = skb_checksum(skb, 0, skb->len, 0);
skb->ip_summed = CHECKSUM_UNNECESSARY;
uh->check = csum_ipv6_magic(saddr, daddr, skb->len,
IPPROTO_UDP, csum);
if (uh->check == 0)
uh->check = CSUM_MANGLED_0;
} else {
skb->ip_summed = CHECKSUM_PARTIAL;
skb->csum_start = skb_transport_header(skb) - skb->head;
skb->csum_offset = offsetof(struct udphdr, check);
uh->check = ~csum_ipv6_magic(saddr, daddr,
skb->len, IPPROTO_UDP, 0);
}
udp6_set_csum(udp_get_no_check6_tx(vs->sock->sk), skb,
saddr, daddr, skb->len);

__skb_push(skb, sizeof(*ip6h));
skb_reset_network_header(skb);
Expand All @@ -1702,10 +1674,6 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs,
ip6h->daddr = *daddr;
ip6h->saddr = *saddr;

err = handle_offloads(skb);
if (err)
return err;

ip6tunnel_xmit(skb, dev);
return 0;
}
Expand All @@ -1721,10 +1689,9 @@ int vxlan_xmit_skb(struct vxlan_sock *vs,
int min_headroom;
int err;

if (!skb->encapsulation) {
skb_reset_inner_headers(skb);
skb->encapsulation = 1;
}
skb = vxlan_handle_offloads(skb, !vs->sock->sk->sk_no_check_tx);
if (IS_ERR(skb))
return -EINVAL;

min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len
+ VXLAN_HLEN + sizeof(struct iphdr)
Expand Down Expand Up @@ -1756,11 +1723,9 @@ int vxlan_xmit_skb(struct vxlan_sock *vs,
uh->source = src_port;

uh->len = htons(skb->len);
uh->check = 0;

err = handle_offloads(skb);
if (err)
return err;
udp_set_csum(vs->sock->sk->sk_no_check_tx, skb,
src, dst, skb->len);

return iptunnel_xmit(vs->sock->sk, rt, skb, src, dst, IPPROTO_UDP,
tos, ttl, df, xnet);
Expand Down Expand Up @@ -2405,7 +2370,7 @@ static void vxlan_del_work(struct work_struct *work)
* could be used for both IPv4 and IPv6 communications, but
* users may set bindv6only=1.
*/
static struct socket *create_v6_sock(struct net *net, __be16 port)
static struct socket *create_v6_sock(struct net *net, __be16 port, u32 flags)
{
struct sock *sk;
struct socket *sock;
Expand Down Expand Up @@ -2442,18 +2407,25 @@ static struct socket *create_v6_sock(struct net *net, __be16 port)

/* Disable multicast loopback */
inet_sk(sk)->mc_loop = 0;

if (flags & VXLAN_F_UDP_ZERO_CSUM6_TX)
udp_set_no_check6_tx(sk, true);

if (flags & VXLAN_F_UDP_ZERO_CSUM6_RX)
udp_set_no_check6_rx(sk, true);

return sock;
}

#else

static struct socket *create_v6_sock(struct net *net, __be16 port)
static struct socket *create_v6_sock(struct net *net, __be16 port, u32 flags)
{
return ERR_PTR(-EPFNOSUPPORT);
}
#endif

static struct socket *create_v4_sock(struct net *net, __be16 port)
static struct socket *create_v4_sock(struct net *net, __be16 port, u32 flags)
{
struct sock *sk;
struct socket *sock;
Expand Down Expand Up @@ -2486,18 +2458,24 @@ static struct socket *create_v4_sock(struct net *net, __be16 port)

/* Disable multicast loopback */
inet_sk(sk)->mc_loop = 0;

if (!(flags & VXLAN_F_UDP_CSUM))
sock->sk->sk_no_check_tx = 1;

return sock;
}

/* Create new listen socket if needed */
static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port,
vxlan_rcv_t *rcv, void *data, bool ipv6)
vxlan_rcv_t *rcv, void *data,
u32 flags)
{
struct vxlan_net *vn = net_generic(net, vxlan_net_id);
struct vxlan_sock *vs;
struct socket *sock;
struct sock *sk;
unsigned int h;
bool ipv6 = !!(flags & VXLAN_F_IPV6);

vs = kzalloc(sizeof(*vs), GFP_KERNEL);
if (!vs)
Expand All @@ -2509,9 +2487,9 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port,
INIT_WORK(&vs->del_work, vxlan_del_work);

if (ipv6)
sock = create_v6_sock(net, port);
sock = create_v6_sock(net, port, flags);
else
sock = create_v4_sock(net, port);
sock = create_v4_sock(net, port, flags);
if (IS_ERR(sock)) {
kfree(vs);
return ERR_CAST(sock);
Expand Down Expand Up @@ -2549,12 +2527,12 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port,

struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port,
vxlan_rcv_t *rcv, void *data,
bool no_share, bool ipv6)
bool no_share, u32 flags)
{
struct vxlan_net *vn = net_generic(net, vxlan_net_id);
struct vxlan_sock *vs;

vs = vxlan_socket_create(net, port, rcv, data, ipv6);
vs = vxlan_socket_create(net, port, rcv, data, flags);
if (!IS_ERR(vs))
return vs;

Expand Down Expand Up @@ -2587,7 +2565,7 @@ static void vxlan_sock_work(struct work_struct *work)
__be16 port = vxlan->dst_port;
struct vxlan_sock *nvs;

nvs = vxlan_sock_add(net, port, vxlan_rcv, NULL, false, vxlan->flags & VXLAN_F_IPV6);
nvs = vxlan_sock_add(net, port, vxlan_rcv, NULL, false, vxlan->flags);
spin_lock(&vn->sock_lock);
if (!IS_ERR(nvs))
vxlan_vs_add_dev(nvs, vxlan);
Expand Down Expand Up @@ -2711,6 +2689,17 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
if (data[IFLA_VXLAN_PORT])
vxlan->dst_port = nla_get_be16(data[IFLA_VXLAN_PORT]);

if (data[IFLA_VXLAN_UDP_CSUM] && nla_get_u8(data[IFLA_VXLAN_UDP_CSUM]))
vxlan->flags |= VXLAN_F_UDP_CSUM;

if (data[IFLA_VXLAN_UDP_ZERO_CSUM6_TX] &&
nla_get_u8(data[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]))
vxlan->flags |= VXLAN_F_UDP_ZERO_CSUM6_TX;

if (data[IFLA_VXLAN_UDP_ZERO_CSUM6_RX] &&
nla_get_u8(data[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]))
vxlan->flags |= VXLAN_F_UDP_ZERO_CSUM6_RX;

if (vxlan_find_vni(net, vni, vxlan->dst_port)) {
pr_info("duplicate VNI %u\n", vni);
return -EEXIST;
Expand Down Expand Up @@ -2774,7 +2763,10 @@ static size_t vxlan_get_size(const struct net_device *dev)
nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_AGEING */
nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_LIMIT */
nla_total_size(sizeof(struct ifla_vxlan_port_range)) +
nla_total_size(sizeof(__be16))+ /* IFLA_VXLAN_PORT */
nla_total_size(sizeof(__be16)) + /* IFLA_VXLAN_PORT */
nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_UDP_CSUM */
nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_UDP_ZERO_CSUM6_TX */
nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_UDP_ZERO_CSUM6_RX */
0;
}

Expand Down Expand Up @@ -2834,7 +2826,13 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
!!(vxlan->flags & VXLAN_F_L3MISS)) ||
nla_put_u32(skb, IFLA_VXLAN_AGEING, vxlan->age_interval) ||
nla_put_u32(skb, IFLA_VXLAN_LIMIT, vxlan->addrmax) ||
nla_put_be16(skb, IFLA_VXLAN_PORT, vxlan->dst_port))
nla_put_be16(skb, IFLA_VXLAN_PORT, vxlan->dst_port) ||
nla_put_u8(skb, IFLA_VXLAN_UDP_CSUM,
!!(vxlan->flags & VXLAN_F_UDP_CSUM)) ||
nla_put_u8(skb, IFLA_VXLAN_UDP_ZERO_CSUM6_TX,
!!(vxlan->flags & VXLAN_F_UDP_ZERO_CSUM6_TX)) ||
nla_put_u8(skb, IFLA_VXLAN_UDP_ZERO_CSUM6_RX,
!!(vxlan->flags & VXLAN_F_UDP_ZERO_CSUM6_RX)))
goto nla_put_failure;

if (nla_put(skb, IFLA_VXLAN_PORT_RANGE, sizeof(ports), &ports))
Expand Down
3 changes: 3 additions & 0 deletions include/linux/netdev_features.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,11 @@ enum {
NETIF_F_TSO6_BIT, /* ... TCPv6 segmentation */
NETIF_F_FSO_BIT, /* ... FCoE segmentation */
NETIF_F_GSO_GRE_BIT, /* ... GRE with TSO */
NETIF_F_GSO_GRE_CSUM_BIT, /* ... GRE with csum with TSO */
NETIF_F_GSO_IPIP_BIT, /* ... IPIP tunnel with TSO */
NETIF_F_GSO_SIT_BIT, /* ... SIT tunnel with TSO */
NETIF_F_GSO_UDP_TUNNEL_BIT, /* ... UDP TUNNEL with TSO */
NETIF_F_GSO_UDP_TUNNEL_CSUM_BIT,/* ... UDP TUNNEL with TSO & CSUM */
NETIF_F_GSO_MPLS_BIT, /* ... MPLS segmentation */
/**/NETIF_F_GSO_LAST = /* last bit, see GSO_MASK */
NETIF_F_GSO_MPLS_BIT,
Expand Down Expand Up @@ -111,6 +113,7 @@ enum {
#define NETIF_F_RXFCS __NETIF_F(RXFCS)
#define NETIF_F_RXALL __NETIF_F(RXALL)
#define NETIF_F_GSO_GRE __NETIF_F(GSO_GRE)
#define NETIF_F_GSO_GRE_CSUM __NETIF_F(GSO_GRE_CSUM)
#define NETIF_F_GSO_IPIP __NETIF_F(GSO_IPIP)
#define NETIF_F_GSO_SIT __NETIF_F(GSO_SIT)
#define NETIF_F_GSO_UDP_TUNNEL __NETIF_F(GSO_UDP_TUNNEL)
Expand Down
30 changes: 29 additions & 1 deletion include/linux/skbuff.h
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,10 @@ enum {
SKB_GSO_UDP_TUNNEL = 1 << 9,

SKB_GSO_MPLS = 1 << 10,

SKB_GSO_UDP_TUNNEL_CSUM = 1 << 11,

SKB_GSO_GRE_CSUM = 1 << 12,
};

#if BITS_PER_LONG > 32
Expand Down Expand Up @@ -567,7 +571,8 @@ struct sk_buff {
* headers if needed
*/
__u8 encapsulation:1;
/* 6/8 bit hole (depending on ndisc_nodetype presence) */
__u8 encap_hdr_csum:1;
/* 5/7 bit hole (depending on ndisc_nodetype presence) */
kmemcheck_bitfield_end(flags2);

#if defined CONFIG_NET_DMA || defined CONFIG_NET_RX_BUSY_POLL
Expand Down Expand Up @@ -2988,6 +2993,7 @@ static inline struct sec_path *skb_sec_path(struct sk_buff *skb)
struct skb_gso_cb {
int mac_offset;
int encap_level;
__u16 csum_start;
};
#define SKB_GSO_CB(skb) ((struct skb_gso_cb *)(skb)->cb)

Expand All @@ -3012,6 +3018,28 @@ static inline int gso_pskb_expand_head(struct sk_buff *skb, int extra)
return 0;
}

/* Compute the checksum for a gso segment. First compute the checksum value
* from the start of transport header to SKB_GSO_CB(skb)->csum_start, and
* then add in skb->csum (checksum from csum_start to end of packet).
* skb->csum and csum_start are then updated to reflect the checksum of the
* resultant packet starting from the transport header-- the resultant checksum
* is in the res argument (i.e. normally zero or ~ of checksum of a pseudo
* header.
*/
static inline __sum16 gso_make_checksum(struct sk_buff *skb, __wsum res)
{
int plen = SKB_GSO_CB(skb)->csum_start - skb_headroom(skb) -
skb_transport_offset(skb);
__u16 csum;

csum = csum_fold(csum_partial(skb_transport_header(skb),
plen, skb->csum));
skb->csum = res;
SKB_GSO_CB(skb)->csum_start -= plen;

return csum;
}

static inline bool skb_is_gso(const struct sk_buff *skb)
{
return skb_shinfo(skb)->gso_size;
Expand Down
5 changes: 3 additions & 2 deletions include/net/gre.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,10 @@ void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
int hdr_len);

static inline struct sk_buff *gre_handle_offloads(struct sk_buff *skb,
bool gre_csum)
bool csum)
{
return iptunnel_handle_offloads(skb, gre_csum, SKB_GSO_GRE);
return iptunnel_handle_offloads(skb, csum,
csum ? SKB_GSO_GRE_CSUM : SKB_GSO_GRE);
}


Expand Down
12 changes: 12 additions & 0 deletions include/net/ip6_checksum.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,17 @@ static inline void tcp_v6_send_check(struct sock *sk, struct sk_buff *skb)
}
#endif

static inline __sum16 udp_v6_check(int len,
const struct in6_addr *saddr,
const struct in6_addr *daddr,
__wsum base)
{
return csum_ipv6_magic(saddr, daddr, len, IPPROTO_UDP, base);
}

void udp6_set_csum(bool nocheck, struct sk_buff *skb,
const struct in6_addr *saddr,
const struct in6_addr *daddr, int len);

int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, int proto);
#endif
9 changes: 9 additions & 0 deletions include/net/udp.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,15 @@ static inline __wsum udp_csum(struct sk_buff *skb)
return csum;
}

static inline __sum16 udp_v4_check(int len, __be32 saddr,
__be32 daddr, __wsum base)
{
return csum_tcpudp_magic(saddr, daddr, len, IPPROTO_UDP, base);
}

void udp_set_csum(bool nocheck, struct sk_buff *skb,
__be32 saddr, __be32 daddr, int len);

/* hash routines shared between UDPv4/6 and UDP-Litev4/6 */
static inline void udp_lib_hash(struct sock *sk)
{
Expand Down
Loading

0 comments on commit 00d115f

Please sign in to comment.