History

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.

Tom Herbert committed 12 years ago

Commit 359a0ea1 parent 4749c094 files · +74 −63Browse files

Unified · Split

4 files
  1. drivers/net/vxlan.c +59 −61
  2. include/net/vxlan.h +11 −1
  3. include/uapi/linux/if_link.h +3 −0
  4. net/openvswitch/vport-vxlan.c +1 −1
drivers/net/vxlan.c+59−61
@@ -135,7 +135,7 @@ struct vxlan_dev { · 134 unmodified lines
135135 __u16 port_max;
136136 __u8 tos; /* TOS override */
137137 __u8 ttl;
138− u32 flags; /* VXLAN_F_* below */
138+ u32 flags; /* VXLAN_F_* in vxlan.h */
139139
140140 struct work_struct sock_work;
141141 struct work_struct igmp_join;
@@ -150,13 +150,6 @@ struct vxlan_dev { · 8 unmodified lines
150150 struct hlist_head fdb_head[FDB_HASH_SIZE];
151151 };
152152
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−
160153 /* salt for hash table */
161154 static u32 vxlan_salt __read_mostly;
162155 static struct workqueue_struct *vxlan_wq;
@@ -1601,18 +1594,11 @@ __be16 vxlan_src_port(__u16 port_min, __u16 port_max, struct sk_buff *skb) · 1438 unmodified lines
16011594 }
16021595 EXPORT_SYMBOL_GPL(vxlan_src_port);
16031596
1604−static int handle_offloads(struct sk_buff *skb)
1597+static inline struct sk_buff *vxlan_handle_offloads(struct sk_buff *skb,
1598+ bool udp_csum)
16051599 {
1606− if (skb_is_gso(skb)) {
1607− int err = skb_unclone(skb, GFP_ATOMIC);
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;
1600+ int type = udp_csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL;
1601+ return iptunnel_handle_offloads(skb, udp_csum, type);
16161602 }
16171603
16181604 #if IS_ENABLED(CONFIG_IPV6)
@@ -1629,10 +1615,9 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs, · 10 unmodified lines
16291615 int min_headroom;
16301616 int err;
16311617
1632− if (!skb->encapsulation) {
1633− skb_reset_inner_headers(skb);
1634− skb->encapsulation = 1;
1635− }
1618+ skb = vxlan_handle_offloads(skb, !udp_get_no_check6_tx(vs->sock->sk));
1619+ if (IS_ERR(skb))
1620+ return -EINVAL;
16361621
16371622 skb_scrub_packet(skb, xnet);
16381623
@@ -1666,27 +1651,14 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs, · 27 unmodified lines
16661651 uh->source = src_port;
16671652
16681653 uh->len = htons(skb->len);
1669− uh->check = 0;
16701654
16711655 memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
16721656 IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
16731657 IPSKB_REROUTED);
16741658 skb_dst_set(skb, dst);
16751659
1676− if (!skb_is_gso(skb) && !(dst->dev->features & NETIF_F_IPV6_CSUM)) {
1677− __wsum csum = skb_checksum(skb, 0, skb->len, 0);
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− }
1660+ udp6_set_csum(udp_get_no_check6_tx(vs->sock->sk), skb,
1661+ saddr, daddr, skb->len);
16901662
16911663 __skb_push(skb, sizeof(*ip6h));
16921664 skb_reset_network_header(skb);
@@ -1702,10 +1674,6 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs, · 9 unmodified lines
17021674 ip6h->daddr = *daddr;
17031675 ip6h->saddr = *saddr;
17041676
1705− err = handle_offloads(skb);
1706− if (err)
1707− return err;
1708−
17091677 ip6tunnel_xmit(skb, dev);
17101678 return 0;
17111679 }
@@ -1721,10 +1689,9 @@ int vxlan_xmit_skb(struct vxlan_sock *vs, · 9 unmodified lines
17211689 int min_headroom;
17221690 int err;
17231691
1724− if (!skb->encapsulation) {
1725− skb_reset_inner_headers(skb);
1726− skb->encapsulation = 1;
1727− }
1692+ skb = vxlan_handle_offloads(skb, !vs->sock->sk->sk_no_check_tx);
1693+ if (IS_ERR(skb))
1694+ return -EINVAL;
17281695
17291696 min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len
17301697 + VXLAN_HLEN + sizeof(struct iphdr)
@@ -1756,11 +1723,9 @@ int vxlan_xmit_skb(struct vxlan_sock *vs, · 25 unmodified lines
17561723 uh->source = src_port;
17571724
17581725 uh->len = htons(skb->len);
1759− uh->check = 0;
17601726
1761− err = handle_offloads(skb);
1762− if (err)
1763− return err;
1727+ udp_set_csum(vs->sock->sk->sk_no_check_tx, skb,
1728+ src, dst, skb->len);
17641729
17651730 return iptunnel_xmit(vs->sock->sk, rt, skb, src, dst, IPPROTO_UDP,
17661731 tos, ttl, df, xnet);
@@ -2405,7 +2370,7 @@ static void vxlan_del_work(struct work_struct *work) · 638 unmodified lines
24052370 * could be used for both IPv4 and IPv6 communications, but
24062371 * users may set bindv6only=1.
24072372 */
2408−static struct socket *create_v6_sock(struct net *net, __be16 port)
2373+static struct socket *create_v6_sock(struct net *net, __be16 port, u32 flags)
24092374 {
24102375 struct sock *sk;
24112376 struct socket *sock;
@@ -2442,18 +2407,25 @@ static struct socket *create_v6_sock(struct net *net, __be16 port) · 30 unmodified lines
24422407
24432408 /* Disable multicast loopback */
24442409 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+
24452417 return sock;
24462418 }
24472419
24482420 #else
24492421
2450−static struct socket *create_v6_sock(struct net *net, __be16 port)
2422+static struct socket *create_v6_sock(struct net *net, __be16 port, u32 flags)
24512423 {
24522424 return ERR_PTR(-EPFNOSUPPORT);
24532425 }
24542426 #endif
24552427
2456−static struct socket *create_v4_sock(struct net *net, __be16 port)
2428+static struct socket *create_v4_sock(struct net *net, __be16 port, u32 flags)
24572429 {
24582430 struct sock *sk;
24592431 struct socket *sock;
@@ -2486,18 +2458,24 @@ static struct socket *create_v4_sock(struct net *net, __be16 port) · 26 unmodified lines
24862458
24872459 /* Disable multicast loopback */
24882460 inet_sk(sk)->mc_loop = 0;
2461+
2462+ if (!(flags & VXLAN_F_UDP_CSUM))
2463+ sock->sk->sk_no_check_tx = 1;
2464+
24892465 return sock;
24902466 }
24912467
24922468 /* Create new listen socket if needed */
24932469 static 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)
24952472 {
24962473 struct vxlan_net *vn = net_generic(net, vxlan_net_id);
24972474 struct vxlan_sock *vs;
24982475 struct socket *sock;
24992476 struct sock *sk;
25002477 unsigned int h;
2478+ bool ipv6 = !!(flags & VXLAN_F_IPV6);
25012479
25022480 vs = kzalloc(sizeof(*vs), GFP_KERNEL);
25032481 if (!vs)
@@ -2509,9 +2487,9 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port, · 5 unmodified lines
25092487 INIT_WORK(&vs->del_work, vxlan_del_work);
25102488
25112489 if (ipv6)
2512− sock = create_v6_sock(net, port);
2490+ sock = create_v6_sock(net, port, flags);
25132491 else
2514− sock = create_v4_sock(net, port);
2492+ sock = create_v4_sock(net, port, flags);
25152493 if (IS_ERR(sock)) {
25162494 kfree(vs);
25172495 return ERR_CAST(sock);
@@ -2549,12 +2527,12 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port, · 31 unmodified lines
25492527
25502528 struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port,
25512529 vxlan_rcv_t *rcv, void *data,
2552− bool no_share, bool ipv6)
2530+ bool no_share, u32 flags)
25532531 {
25542532 struct vxlan_net *vn = net_generic(net, vxlan_net_id);
25552533 struct vxlan_sock *vs;
25562534
2557− vs = vxlan_socket_create(net, port, rcv, data, ipv6);
2535+ vs = vxlan_socket_create(net, port, rcv, data, flags);
25582536 if (!IS_ERR(vs))
25592537 return vs;
25602538
@@ -2587,7 +2565,7 @@ static void vxlan_sock_work(struct work_struct *work) · 26 unmodified lines
25872565 __be16 port = vxlan->dst_port;
25882566 struct vxlan_sock *nvs;
25892567
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);
25912569 spin_lock(&vn->sock_lock);
25922570 if (!IS_ERR(nvs))
25932571 vxlan_vs_add_dev(nvs, vxlan);
@@ -2711,6 +2689,17 @@ static int vxlan_newlink(struct net *net, struct net_device *dev, · 117 unmodified lines
27112689 if (data[IFLA_VXLAN_PORT])
27122690 vxlan->dst_port = nla_get_be16(data[IFLA_VXLAN_PORT]);
27132691
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+
27142703 if (vxlan_find_vni(net, vni, vxlan->dst_port)) {
27152704 pr_info("duplicate VNI %u\n", vni);
27162705 return -EEXIST;
@@ -2774,7 +2763,10 @@ static size_t vxlan_get_size(const struct net_device *dev) · 57 unmodified lines
27742763 nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_AGEING */
27752764 nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_LIMIT */
27762765 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 */
27782770 0;
27792771 }
27802772
@@ -2834,7 +2826,13 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev) · 53 unmodified lines
28342826 !!(vxlan->flags & VXLAN_F_L3MISS)) ||
28352827 nla_put_u32(skb, IFLA_VXLAN_AGEING, vxlan->age_interval) ||
28362828 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)))
28382836 goto nla_put_failure;
28392837
28402838 if (nla_put(skb, IFLA_VXLAN_PORT_RANGE, sizeof(ports), &ports))
include/net/vxlan.h+11−1
@@ -24,9 +24,19 @@ struct vxlan_sock { · 23 unmodified lines
2424 struct udp_offload udp_offloads;
2525 };
2626
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+
2737 struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port,
2838 vxlan_rcv_t *rcv, void *data,
29− bool no_share, bool ipv6);
39+ bool no_share, u32 flags);
3040
3141 void vxlan_sock_release(struct vxlan_sock *vs);
3242
include/uapi/linux/if_link.h+3−0
@@ -319,6 +319,9 @@ enum { · 318 unmodified lines
319319 IFLA_VXLAN_PORT, /* destination port */
320320 IFLA_VXLAN_GROUP6,
321321 IFLA_VXLAN_LOCAL6,
322+ IFLA_VXLAN_UDP_CSUM,
323+ IFLA_VXLAN_UDP_ZERO_CSUM6_TX,
324+ IFLA_VXLAN_UDP_ZERO_CSUM6_RX,
322325 __IFLA_VXLAN_MAX
323326 };
324327 #define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1)
net/openvswitch/vport-vxlan.c+1−1
@@ -122,7 +122,7 @@ static struct vport *vxlan_tnl_create(const struct vport_parms *parms) · 121 unmodified lines
122122 vxlan_port = vxlan_vport(vport);
123123 strncpy(vxlan_port->name, parms->name, IFNAMSIZ);
124124
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);
126126 if (IS_ERR(vs)) {
127127 ovs_vport_free(vport);
128128 return (void *)vs;