Skip to content

Commit

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

====================
udp: UDP tunnel enhancements

- Add udp_sock_create in new helper module udp_tunnel. Tunnel
  implementations call this function to create listener UDP ports.
- Make vxlan and l2tp call udp_sock_create.
- Move udp_tunnel_segment into udp_offload.c.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Jul 14, 2014
2 parents 39b1c29 + 155e010 commit 64bbca3
Show file tree
Hide file tree
Showing 10 changed files with 266 additions and 226 deletions.
1 change: 1 addition & 0 deletions drivers/net/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ config VXLAN
tristate "Virtual eXtensible Local Area Network (VXLAN)"
depends on INET
select NET_IP_TUNNEL
select NET_UDP_TUNNEL
---help---
This allows one to create vxlan virtual interfaces that provide
Layer 2 Networks over Layer 3 Networks. VXLAN is often used
Expand Down
115 changes: 24 additions & 91 deletions drivers/net/vxlan.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <net/ip_tunnels.h>
#include <net/icmp.h>
#include <net/udp.h>
#include <net/udp_tunnel.h>
#include <net/rtnetlink.h>
#include <net/route.h>
#include <net/dsfield.h>
Expand Down Expand Up @@ -2339,102 +2340,37 @@ static void vxlan_del_work(struct work_struct *work)
kfree_rcu(vs, rcu);
}

#if IS_ENABLED(CONFIG_IPV6)
/* Create UDP socket for encapsulation receive. AF_INET6 socket
* 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, u32 flags)
static struct socket *vxlan_create_sock(struct net *net, bool ipv6,
__be16 port, u32 flags)
{
struct sock *sk;
struct socket *sock;
struct sockaddr_in6 vxlan_addr = {
.sin6_family = AF_INET6,
.sin6_port = port,
};
int rc, val = 1;

rc = sock_create_kern(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, &sock);
if (rc < 0) {
pr_debug("UDPv6 socket create failed\n");
return ERR_PTR(rc);
}

/* Put in proper namespace */
sk = sock->sk;
sk_change_net(sk, net);

kernel_setsockopt(sock, SOL_IPV6, IPV6_V6ONLY,
(char *)&val, sizeof(val));
rc = kernel_bind(sock, (struct sockaddr *)&vxlan_addr,
sizeof(struct sockaddr_in6));
if (rc < 0) {
pr_debug("bind for UDPv6 socket %pI6:%u (%d)\n",
&vxlan_addr.sin6_addr, ntohs(vxlan_addr.sin6_port), rc);
sk_release_kernel(sk);
return ERR_PTR(rc);
}
/* At this point, IPv6 module should have been loaded in
* sock_create_kern().
*/
BUG_ON(!ipv6_stub);

/* 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
struct udp_port_cfg udp_conf;
int err;

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

static struct socket *create_v4_sock(struct net *net, __be16 port, u32 flags)
{
struct sock *sk;
struct socket *sock;
struct sockaddr_in vxlan_addr = {
.sin_family = AF_INET,
.sin_addr.s_addr = htonl(INADDR_ANY),
.sin_port = port,
};
int rc;

/* Create UDP socket for encapsulation receive. */
rc = sock_create_kern(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock);
if (rc < 0) {
pr_debug("UDP socket create failed\n");
return ERR_PTR(rc);
if (ipv6) {
udp_conf.family = AF_INET6;
udp_conf.use_udp6_tx_checksums =
!!(flags & VXLAN_F_UDP_ZERO_CSUM6_TX);
udp_conf.use_udp6_rx_checksums =
!!(flags & VXLAN_F_UDP_ZERO_CSUM6_RX);
} else {
udp_conf.family = AF_INET;
udp_conf.local_ip.s_addr = INADDR_ANY;
udp_conf.use_udp_checksums =
!!(flags & VXLAN_F_UDP_CSUM);
}

/* Put in proper namespace */
sk = sock->sk;
sk_change_net(sk, net);
udp_conf.local_udp_port = port;

rc = kernel_bind(sock, (struct sockaddr *) &vxlan_addr,
sizeof(vxlan_addr));
if (rc < 0) {
pr_debug("bind for UDP socket %pI4:%u (%d)\n",
&vxlan_addr.sin_addr, ntohs(vxlan_addr.sin_port), rc);
sk_release_kernel(sk);
return ERR_PTR(rc);
}
/* Open UDP socket */
err = udp_sock_create(net, &udp_conf, &sock);
if (err < 0)
return ERR_PTR(err);

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

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

return sock;
}
Expand All @@ -2460,10 +2396,7 @@ 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, flags);
else
sock = create_v4_sock(net, port, flags);
sock = vxlan_create_sock(net, ipv6, port, flags);
if (IS_ERR(sock)) {
kfree(vs);
return ERR_CAST(sock);
Expand Down
32 changes: 32 additions & 0 deletions include/net/udp_tunnel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef __NET_UDP_TUNNEL_H
#define __NET_UDP_TUNNEL_H

struct udp_port_cfg {
u8 family;

/* Used only for kernel-created sockets */
union {
struct in_addr local_ip;
#if IS_ENABLED(CONFIG_IPV6)
struct in6_addr local_ip6;
#endif
};

union {
struct in_addr peer_ip;
#if IS_ENABLED(CONFIG_IPV6)
struct in6_addr peer_ip6;
#endif
};

__be16 local_udp_port;
__be16 peer_udp_port;
unsigned int use_udp_checksums:1,
use_udp6_tx_checksums:1,
use_udp6_rx_checksums:1;
};

int udp_sock_create(struct net *net, struct udp_port_cfg *cfg,
struct socket **sockp);

#endif
4 changes: 4 additions & 0 deletions net/ipv4/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,10 @@ config NET_IPVTI
the notion of a secure tunnel for IPSEC and then use routing protocol
on top.

config NET_UDP_TUNNEL
tristate
default n

config INET_AH
tristate "IP: AH transformation"
select XFRM_ALGO
Expand Down
1 change: 1 addition & 0 deletions net/ipv4/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ obj-$(CONFIG_NET_IPIP) += ipip.o
gre-y := gre_demux.o
obj-$(CONFIG_NET_IPGRE_DEMUX) += gre.o
obj-$(CONFIG_NET_IPGRE) += ip_gre.o
obj-$(CONFIG_NET_UDP_TUNNEL) += udp_tunnel.o
obj-$(CONFIG_NET_IPVTI) += ip_vti.o
obj-$(CONFIG_SYN_COOKIES) += syncookies.o
obj-$(CONFIG_INET_AH) += ah4.o
Expand Down
76 changes: 0 additions & 76 deletions net/ipv4/udp.c
Original file line number Diff line number Diff line change
Expand Up @@ -2522,79 +2522,3 @@ void __init udp_init(void)
sysctl_udp_rmem_min = SK_MEM_QUANTUM;
sysctl_udp_wmem_min = SK_MEM_QUANTUM;
}

struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
netdev_features_t features)
{
struct sk_buff *segs = ERR_PTR(-EINVAL);
u16 mac_offset = skb->mac_header;
int mac_len = skb->mac_len;
int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb);
__be16 protocol = skb->protocol;
netdev_features_t enc_features;
int udp_offset, outer_hlen;
unsigned int oldlen;
bool need_csum;

oldlen = (u16)~skb->len;

if (unlikely(!pskb_may_pull(skb, tnl_hlen)))
goto out;

skb->encapsulation = 0;
__skb_pull(skb, tnl_hlen);
skb_reset_mac_header(skb);
skb_set_network_header(skb, skb_inner_network_offset(skb));
skb->mac_len = skb_inner_network_offset(skb);
skb->protocol = htons(ETH_P_TEB);

need_csum = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM);
if (need_csum)
skb->encap_hdr_csum = 1;

/* segment inner packet. */
enc_features = skb->dev->hw_enc_features & netif_skb_features(skb);
segs = skb_mac_gso_segment(skb, enc_features);
if (!segs || IS_ERR(segs)) {
skb_gso_error_unwind(skb, protocol, tnl_hlen, mac_offset,
mac_len);
goto out;
}

outer_hlen = skb_tnl_header_len(skb);
udp_offset = outer_hlen - tnl_hlen;
skb = segs;
do {
struct udphdr *uh;
int len;

skb_reset_inner_headers(skb);
skb->encapsulation = 1;

skb->mac_len = mac_len;

skb_push(skb, outer_hlen);
skb_reset_mac_header(skb);
skb_set_network_header(skb, mac_len);
skb_set_transport_header(skb, udp_offset);
len = skb->len - udp_offset;
uh = udp_hdr(skb);
uh->len = htons(len);

if (need_csum) {
__be32 delta = htonl(oldlen + len);

uh->check = ~csum_fold((__force __wsum)
((__force u32)uh->check +
(__force u32)delta));
uh->check = gso_make_checksum(skb, ~uh->check);

if (uh->check == 0)
uh->check = CSUM_MANGLED_0;
}

skb->protocol = protocol;
} while ((skb = skb->next));
out:
return segs;
}
76 changes: 76 additions & 0 deletions net/ipv4/udp_offload.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,82 @@ static int udp4_ufo_send_check(struct sk_buff *skb)
return 0;
}

struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
netdev_features_t features)
{
struct sk_buff *segs = ERR_PTR(-EINVAL);
u16 mac_offset = skb->mac_header;
int mac_len = skb->mac_len;
int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb);
__be16 protocol = skb->protocol;
netdev_features_t enc_features;
int udp_offset, outer_hlen;
unsigned int oldlen;
bool need_csum;

oldlen = (u16)~skb->len;

if (unlikely(!pskb_may_pull(skb, tnl_hlen)))
goto out;

skb->encapsulation = 0;
__skb_pull(skb, tnl_hlen);
skb_reset_mac_header(skb);
skb_set_network_header(skb, skb_inner_network_offset(skb));
skb->mac_len = skb_inner_network_offset(skb);
skb->protocol = htons(ETH_P_TEB);

need_csum = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM);
if (need_csum)
skb->encap_hdr_csum = 1;

/* segment inner packet. */
enc_features = skb->dev->hw_enc_features & netif_skb_features(skb);
segs = skb_mac_gso_segment(skb, enc_features);
if (!segs || IS_ERR(segs)) {
skb_gso_error_unwind(skb, protocol, tnl_hlen, mac_offset,
mac_len);
goto out;
}

outer_hlen = skb_tnl_header_len(skb);
udp_offset = outer_hlen - tnl_hlen;
skb = segs;
do {
struct udphdr *uh;
int len;

skb_reset_inner_headers(skb);
skb->encapsulation = 1;

skb->mac_len = mac_len;

skb_push(skb, outer_hlen);
skb_reset_mac_header(skb);
skb_set_network_header(skb, mac_len);
skb_set_transport_header(skb, udp_offset);
len = skb->len - udp_offset;
uh = udp_hdr(skb);
uh->len = htons(len);

if (need_csum) {
__be32 delta = htonl(oldlen + len);

uh->check = ~csum_fold((__force __wsum)
((__force u32)uh->check +
(__force u32)delta));
uh->check = gso_make_checksum(skb, ~uh->check);

if (uh->check == 0)
uh->check = CSUM_MANGLED_0;
}

skb->protocol = protocol;
} while ((skb = skb->next));
out:
return segs;
}

static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
netdev_features_t features)
{
Expand Down
Loading

0 comments on commit 64bbca3

Please sign in to comment.