aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Herbert <therbert@google.com>2014-06-04 20:20:29 -0400
committerDavid S. Miller <davem@davemloft.net>2014-06-05 01:46:39 -0400
commit359a0ea9875ef4f32c8425bbe1ae348e1fd2ed2a (patch)
treeca8567d5204c6b891dcf5d3e012ceb4a4f027c09
parent4749c09c37030ccdc44aecebe0f71b02a377fc14 (diff)
vxlan: Add support for UDP checksums (v4 sending, v6 zero csums)
Added VXLAN link configuration for sending UDP checksums, and allowing TX and RX of UDP6 checksums. Also, call common iptunnel_handle_offloads and added GSO support for checksums. Signed-off-by: Tom Herbert <therbert@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/vxlan.c120
-rw-r--r--include/net/vxlan.h12
-rw-r--r--include/uapi/linux/if_link.h3
-rw-r--r--net/openvswitch/vport-vxlan.c2
4 files changed, 74 insertions, 63 deletions
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index e68c8eb4ea8e..4e2caaf8b5da 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -135,7 +135,7 @@ struct vxlan_dev {
135 __u16 port_max; 135 __u16 port_max;
136 __u8 tos; /* TOS override */ 136 __u8 tos; /* TOS override */
137 __u8 ttl; 137 __u8 ttl;
138 u32 flags; /* VXLAN_F_* below */ 138 u32 flags; /* VXLAN_F_* in vxlan.h */
139 139
140 struct work_struct sock_work; 140 struct work_struct sock_work;
141 struct work_struct igmp_join; 141 struct work_struct igmp_join;
@@ -150,13 +150,6 @@ struct vxlan_dev {
150 struct hlist_head fdb_head[FDB_HASH_SIZE]; 150 struct hlist_head fdb_head[FDB_HASH_SIZE];
151}; 151};
152 152
153#define VXLAN_F_LEARN 0x01
154#define VXLAN_F_PROXY 0x02
155#define VXLAN_F_RSC 0x04
156#define VXLAN_F_L2MISS 0x08
157#define VXLAN_F_L3MISS 0x10
158#define VXLAN_F_IPV6 0x20 /* internal flag */
159
160/* salt for hash table */ 153/* salt for hash table */
161static u32 vxlan_salt __read_mostly; 154static u32 vxlan_salt __read_mostly;
162static struct workqueue_struct *vxlan_wq; 155static struct workqueue_struct *vxlan_wq;
@@ -1601,18 +1594,11 @@ __be16 vxlan_src_port(__u16 port_min, __u16 port_max, struct sk_buff *skb)
1601} 1594}
1602EXPORT_SYMBOL_GPL(vxlan_src_port); 1595EXPORT_SYMBOL_GPL(vxlan_src_port);
1603 1596
1604static int handle_offloads(struct sk_buff *skb) 1597static inline struct sk_buff *vxlan_handle_offloads(struct sk_buff *skb,
1598 bool udp_csum)
1605{ 1599{
1606 if (skb_is_gso(skb)) { 1600 int type = udp_csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL;
1607 int err = skb_unclone(skb, GFP_ATOMIC); 1601 return iptunnel_handle_offloads(skb, udp_csum, type);
1608 if (unlikely(err))
1609 return err;
1610
1611 skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL;
1612 } else if (skb->ip_summed != CHECKSUM_PARTIAL)
1613 skb->ip_summed = CHECKSUM_NONE;
1614
1615 return 0;
1616} 1602}
1617 1603
1618#if IS_ENABLED(CONFIG_IPV6) 1604#if IS_ENABLED(CONFIG_IPV6)
@@ -1629,10 +1615,9 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs,
1629 int min_headroom; 1615 int min_headroom;
1630 int err; 1616 int err;
1631 1617
1632 if (!skb->encapsulation) { 1618 skb = vxlan_handle_offloads(skb, !udp_get_no_check6_tx(vs->sock->sk));
1633 skb_reset_inner_headers(skb); 1619 if (IS_ERR(skb))
1634 skb->encapsulation = 1; 1620 return -EINVAL;
1635 }
1636 1621
1637 skb_scrub_packet(skb, xnet); 1622 skb_scrub_packet(skb, xnet);
1638 1623
@@ -1666,27 +1651,14 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs,
1666 uh->source = src_port; 1651 uh->source = src_port;
1667 1652
1668 uh->len = htons(skb->len); 1653 uh->len = htons(skb->len);
1669 uh->check = 0;
1670 1654
1671 memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); 1655 memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
1672 IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED | 1656 IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
1673 IPSKB_REROUTED); 1657 IPSKB_REROUTED);
1674 skb_dst_set(skb, dst); 1658 skb_dst_set(skb, dst);
1675 1659
1676 if (!skb_is_gso(skb) && !(dst->dev->features & NETIF_F_IPV6_CSUM)) { 1660 udp6_set_csum(udp_get_no_check6_tx(vs->sock->sk), skb,
1677 __wsum csum = skb_checksum(skb, 0, skb->len, 0); 1661 saddr, daddr, skb->len);
1678 skb->ip_summed = CHECKSUM_UNNECESSARY;
1679 uh->check = csum_ipv6_magic(saddr, daddr, skb->len,
1680 IPPROTO_UDP, csum);
1681 if (uh->check == 0)
1682 uh->check = CSUM_MANGLED_0;
1683 } else {
1684 skb->ip_summed = CHECKSUM_PARTIAL;
1685 skb->csum_start = skb_transport_header(skb) - skb->head;
1686 skb->csum_offset = offsetof(struct udphdr, check);
1687 uh->check = ~csum_ipv6_magic(saddr, daddr,
1688 skb->len, IPPROTO_UDP, 0);
1689 }
1690 1662
1691 __skb_push(skb, sizeof(*ip6h)); 1663 __skb_push(skb, sizeof(*ip6h));
1692 skb_reset_network_header(skb); 1664 skb_reset_network_header(skb);
@@ -1702,10 +1674,6 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs,
1702 ip6h->daddr = *daddr; 1674 ip6h->daddr = *daddr;
1703 ip6h->saddr = *saddr; 1675 ip6h->saddr = *saddr;
1704 1676
1705 err = handle_offloads(skb);
1706 if (err)
1707 return err;
1708
1709 ip6tunnel_xmit(skb, dev); 1677 ip6tunnel_xmit(skb, dev);
1710 return 0; 1678 return 0;
1711} 1679}
@@ -1721,10 +1689,9 @@ int vxlan_xmit_skb(struct vxlan_sock *vs,
1721 int min_headroom; 1689 int min_headroom;
1722 int err; 1690 int err;
1723 1691
1724 if (!skb->encapsulation) { 1692 skb = vxlan_handle_offloads(skb, !vs->sock->sk->sk_no_check_tx);
1725 skb_reset_inner_headers(skb); 1693 if (IS_ERR(skb))
1726 skb->encapsulation = 1; 1694 return -EINVAL;
1727 }
1728 1695
1729 min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len 1696 min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len
1730 + VXLAN_HLEN + sizeof(struct iphdr) 1697 + VXLAN_HLEN + sizeof(struct iphdr)
@@ -1756,11 +1723,9 @@ int vxlan_xmit_skb(struct vxlan_sock *vs,
1756 uh->source = src_port; 1723 uh->source = src_port;
1757 1724
1758 uh->len = htons(skb->len); 1725 uh->len = htons(skb->len);
1759 uh->check = 0;
1760 1726
1761 err = handle_offloads(skb); 1727 udp_set_csum(vs->sock->sk->sk_no_check_tx, skb,
1762 if (err) 1728 src, dst, skb->len);
1763 return err;
1764 1729
1765 return iptunnel_xmit(vs->sock->sk, rt, skb, src, dst, IPPROTO_UDP, 1730 return iptunnel_xmit(vs->sock->sk, rt, skb, src, dst, IPPROTO_UDP,
1766 tos, ttl, df, xnet); 1731 tos, ttl, df, xnet);
@@ -2405,7 +2370,7 @@ static void vxlan_del_work(struct work_struct *work)
2405 * could be used for both IPv4 and IPv6 communications, but 2370 * could be used for both IPv4 and IPv6 communications, but
2406 * users may set bindv6only=1. 2371 * users may set bindv6only=1.
2407 */ 2372 */
2408static struct socket *create_v6_sock(struct net *net, __be16 port) 2373static struct socket *create_v6_sock(struct net *net, __be16 port, u32 flags)
2409{ 2374{
2410 struct sock *sk; 2375 struct sock *sk;
2411 struct socket *sock; 2376 struct socket *sock;
@@ -2442,18 +2407,25 @@ static struct socket *create_v6_sock(struct net *net, __be16 port)
2442 2407
2443 /* Disable multicast loopback */ 2408 /* Disable multicast loopback */
2444 inet_sk(sk)->mc_loop = 0; 2409 inet_sk(sk)->mc_loop = 0;
2410
2411 if (flags & VXLAN_F_UDP_ZERO_CSUM6_TX)
2412 udp_set_no_check6_tx(sk, true);
2413
2414 if (flags & VXLAN_F_UDP_ZERO_CSUM6_RX)
2415 udp_set_no_check6_rx(sk, true);
2416
2445 return sock; 2417 return sock;
2446} 2418}
2447 2419
2448#else 2420#else
2449 2421
2450static struct socket *create_v6_sock(struct net *net, __be16 port) 2422static struct socket *create_v6_sock(struct net *net, __be16 port, u32 flags)
2451{ 2423{
2452 return ERR_PTR(-EPFNOSUPPORT); 2424 return ERR_PTR(-EPFNOSUPPORT);
2453} 2425}
2454#endif 2426#endif
2455 2427
2456static struct socket *create_v4_sock(struct net *net, __be16 port) 2428static struct socket *create_v4_sock(struct net *net, __be16 port, u32 flags)
2457{ 2429{
2458 struct sock *sk; 2430 struct sock *sk;
2459 struct socket *sock; 2431 struct socket *sock;
@@ -2486,18 +2458,24 @@ static struct socket *create_v4_sock(struct net *net, __be16 port)
2486 2458
2487 /* Disable multicast loopback */ 2459 /* Disable multicast loopback */
2488 inet_sk(sk)->mc_loop = 0; 2460 inet_sk(sk)->mc_loop = 0;
2461
2462 if (!(flags & VXLAN_F_UDP_CSUM))
2463 sock->sk->sk_no_check_tx = 1;
2464
2489 return sock; 2465 return sock;
2490} 2466}
2491 2467
2492/* Create new listen socket if needed */ 2468/* Create new listen socket if needed */
2493static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port, 2469static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port,
2494 vxlan_rcv_t *rcv, void *data, bool ipv6) 2470 vxlan_rcv_t *rcv, void *data,
2471 u32 flags)
2495{ 2472{
2496 struct vxlan_net *vn = net_generic(net, vxlan_net_id); 2473 struct vxlan_net *vn = net_generic(net, vxlan_net_id);
2497 struct vxlan_sock *vs; 2474 struct vxlan_sock *vs;
2498 struct socket *sock; 2475 struct socket *sock;
2499 struct sock *sk; 2476 struct sock *sk;
2500 unsigned int h; 2477 unsigned int h;
2478 bool ipv6 = !!(flags & VXLAN_F_IPV6);
2501 2479
2502 vs = kzalloc(sizeof(*vs), GFP_KERNEL); 2480 vs = kzalloc(sizeof(*vs), GFP_KERNEL);
2503 if (!vs) 2481 if (!vs)
@@ -2509,9 +2487,9 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port,
2509 INIT_WORK(&vs->del_work, vxlan_del_work); 2487 INIT_WORK(&vs->del_work, vxlan_del_work);
2510 2488
2511 if (ipv6) 2489 if (ipv6)
2512 sock = create_v6_sock(net, port); 2490 sock = create_v6_sock(net, port, flags);
2513 else 2491 else
2514 sock = create_v4_sock(net, port); 2492 sock = create_v4_sock(net, port, flags);
2515 if (IS_ERR(sock)) { 2493 if (IS_ERR(sock)) {
2516 kfree(vs); 2494 kfree(vs);
2517 return ERR_CAST(sock); 2495 return ERR_CAST(sock);
@@ -2549,12 +2527,12 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port,
2549 2527
2550struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port, 2528struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port,
2551 vxlan_rcv_t *rcv, void *data, 2529 vxlan_rcv_t *rcv, void *data,
2552 bool no_share, bool ipv6) 2530 bool no_share, u32 flags)
2553{ 2531{
2554 struct vxlan_net *vn = net_generic(net, vxlan_net_id); 2532 struct vxlan_net *vn = net_generic(net, vxlan_net_id);
2555 struct vxlan_sock *vs; 2533 struct vxlan_sock *vs;
2556 2534
2557 vs = vxlan_socket_create(net, port, rcv, data, ipv6); 2535 vs = vxlan_socket_create(net, port, rcv, data, flags);
2558 if (!IS_ERR(vs)) 2536 if (!IS_ERR(vs))
2559 return vs; 2537 return vs;
2560 2538
@@ -2587,7 +2565,7 @@ static void vxlan_sock_work(struct work_struct *work)
2587 __be16 port = vxlan->dst_port; 2565 __be16 port = vxlan->dst_port;
2588 struct vxlan_sock *nvs; 2566 struct vxlan_sock *nvs;
2589 2567
2590 nvs = vxlan_sock_add(net, port, vxlan_rcv, NULL, false, vxlan->flags & VXLAN_F_IPV6); 2568 nvs = vxlan_sock_add(net, port, vxlan_rcv, NULL, false, vxlan->flags);
2591 spin_lock(&vn->sock_lock); 2569 spin_lock(&vn->sock_lock);
2592 if (!IS_ERR(nvs)) 2570 if (!IS_ERR(nvs))
2593 vxlan_vs_add_dev(nvs, vxlan); 2571 vxlan_vs_add_dev(nvs, vxlan);
@@ -2711,6 +2689,17 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
2711 if (data[IFLA_VXLAN_PORT]) 2689 if (data[IFLA_VXLAN_PORT])
2712 vxlan->dst_port = nla_get_be16(data[IFLA_VXLAN_PORT]); 2690 vxlan->dst_port = nla_get_be16(data[IFLA_VXLAN_PORT]);
2713 2691
2692 if (data[IFLA_VXLAN_UDP_CSUM] && nla_get_u8(data[IFLA_VXLAN_UDP_CSUM]))
2693 vxlan->flags |= VXLAN_F_UDP_CSUM;
2694
2695 if (data[IFLA_VXLAN_UDP_ZERO_CSUM6_TX] &&
2696 nla_get_u8(data[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]))
2697 vxlan->flags |= VXLAN_F_UDP_ZERO_CSUM6_TX;
2698
2699 if (data[IFLA_VXLAN_UDP_ZERO_CSUM6_RX] &&
2700 nla_get_u8(data[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]))
2701 vxlan->flags |= VXLAN_F_UDP_ZERO_CSUM6_RX;
2702
2714 if (vxlan_find_vni(net, vni, vxlan->dst_port)) { 2703 if (vxlan_find_vni(net, vni, vxlan->dst_port)) {
2715 pr_info("duplicate VNI %u\n", vni); 2704 pr_info("duplicate VNI %u\n", vni);
2716 return -EEXIST; 2705 return -EEXIST;
@@ -2774,7 +2763,10 @@ static size_t vxlan_get_size(const struct net_device *dev)
2774 nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_AGEING */ 2763 nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_AGEING */
2775 nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_LIMIT */ 2764 nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_LIMIT */
2776 nla_total_size(sizeof(struct ifla_vxlan_port_range)) + 2765 nla_total_size(sizeof(struct ifla_vxlan_port_range)) +
2777 nla_total_size(sizeof(__be16))+ /* IFLA_VXLAN_PORT */ 2766 nla_total_size(sizeof(__be16)) + /* IFLA_VXLAN_PORT */
2767 nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_UDP_CSUM */
2768 nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_UDP_ZERO_CSUM6_TX */
2769 nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_UDP_ZERO_CSUM6_RX */
2778 0; 2770 0;
2779} 2771}
2780 2772
@@ -2834,7 +2826,13 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
2834 !!(vxlan->flags & VXLAN_F_L3MISS)) || 2826 !!(vxlan->flags & VXLAN_F_L3MISS)) ||
2835 nla_put_u32(skb, IFLA_VXLAN_AGEING, vxlan->age_interval) || 2827 nla_put_u32(skb, IFLA_VXLAN_AGEING, vxlan->age_interval) ||
2836 nla_put_u32(skb, IFLA_VXLAN_LIMIT, vxlan->addrmax) || 2828 nla_put_u32(skb, IFLA_VXLAN_LIMIT, vxlan->addrmax) ||
2837 nla_put_be16(skb, IFLA_VXLAN_PORT, vxlan->dst_port)) 2829 nla_put_be16(skb, IFLA_VXLAN_PORT, vxlan->dst_port) ||
2830 nla_put_u8(skb, IFLA_VXLAN_UDP_CSUM,
2831 !!(vxlan->flags & VXLAN_F_UDP_CSUM)) ||
2832 nla_put_u8(skb, IFLA_VXLAN_UDP_ZERO_CSUM6_TX,
2833 !!(vxlan->flags & VXLAN_F_UDP_ZERO_CSUM6_TX)) ||
2834 nla_put_u8(skb, IFLA_VXLAN_UDP_ZERO_CSUM6_RX,
2835 !!(vxlan->flags & VXLAN_F_UDP_ZERO_CSUM6_RX)))
2838 goto nla_put_failure; 2836 goto nla_put_failure;
2839 2837
2840 if (nla_put(skb, IFLA_VXLAN_PORT_RANGE, sizeof(ports), &ports)) 2838 if (nla_put(skb, IFLA_VXLAN_PORT_RANGE, sizeof(ports), &ports))
diff --git a/include/net/vxlan.h b/include/net/vxlan.h
index 7bb4084b1bd0..12196ce661d9 100644
--- a/include/net/vxlan.h
+++ b/include/net/vxlan.h
@@ -24,9 +24,19 @@ struct vxlan_sock {
24 struct udp_offload udp_offloads; 24 struct udp_offload udp_offloads;
25}; 25};
26 26
27#define VXLAN_F_LEARN 0x01
28#define VXLAN_F_PROXY 0x02
29#define VXLAN_F_RSC 0x04
30#define VXLAN_F_L2MISS 0x08
31#define VXLAN_F_L3MISS 0x10
32#define VXLAN_F_IPV6 0x20
33#define VXLAN_F_UDP_CSUM 0x40
34#define VXLAN_F_UDP_ZERO_CSUM6_TX 0x80
35#define VXLAN_F_UDP_ZERO_CSUM6_RX 0x100
36
27struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port, 37struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port,
28 vxlan_rcv_t *rcv, void *data, 38 vxlan_rcv_t *rcv, void *data,
29 bool no_share, bool ipv6); 39 bool no_share, u32 flags);
30 40
31void vxlan_sock_release(struct vxlan_sock *vs); 41void vxlan_sock_release(struct vxlan_sock *vs);
32 42
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 622e7910b8cc..b38534895db5 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -319,6 +319,9 @@ enum {
319 IFLA_VXLAN_PORT, /* destination port */ 319 IFLA_VXLAN_PORT, /* destination port */
320 IFLA_VXLAN_GROUP6, 320 IFLA_VXLAN_GROUP6,
321 IFLA_VXLAN_LOCAL6, 321 IFLA_VXLAN_LOCAL6,
322 IFLA_VXLAN_UDP_CSUM,
323 IFLA_VXLAN_UDP_ZERO_CSUM6_TX,
324 IFLA_VXLAN_UDP_ZERO_CSUM6_RX,
322 __IFLA_VXLAN_MAX 325 __IFLA_VXLAN_MAX
323}; 326};
324#define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1) 327#define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1)
diff --git a/net/openvswitch/vport-vxlan.c b/net/openvswitch/vport-vxlan.c
index a93efa3f64c3..0edbd95c60e7 100644
--- a/net/openvswitch/vport-vxlan.c
+++ b/net/openvswitch/vport-vxlan.c
@@ -122,7 +122,7 @@ static struct vport *vxlan_tnl_create(const struct vport_parms *parms)
122 vxlan_port = vxlan_vport(vport); 122 vxlan_port = vxlan_vport(vport);
123 strncpy(vxlan_port->name, parms->name, IFNAMSIZ); 123 strncpy(vxlan_port->name, parms->name, IFNAMSIZ);
124 124
125 vs = vxlan_sock_add(net, htons(dst_port), vxlan_rcv, vport, true, false); 125 vs = vxlan_sock_add(net, htons(dst_port), vxlan_rcv, vport, true, 0);
126 if (IS_ERR(vs)) { 126 if (IS_ERR(vs)) {
127 ovs_vport_free(vport); 127 ovs_vport_free(vport);
128 return (void *)vs; 128 return (void *)vs;