Commit abe492b4 authored by Tom Herbert's avatar Tom Herbert Committed by David S. Miller

geneve: UDP checksum configuration via netlink

Add support to enable and disable UDP checksums via netlink. This is
similar to how VXLAN and GUE allow this. This includes support for
enabling the UDP zero checksum (for both TX and RX).
Signed-off-by: default avatarTom Herbert <tom@herbertland.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 369620a0
......@@ -71,8 +71,14 @@ struct geneve_dev {
__be16 dst_port;
bool collect_md;
struct gro_cells gro_cells;
u32 flags;
};
/* Geneve device flags */
#define GENEVE_F_UDP_CSUM BIT(0)
#define GENEVE_F_UDP_ZERO_CSUM6_TX BIT(1)
#define GENEVE_F_UDP_ZERO_CSUM6_RX BIT(2)
struct geneve_sock {
bool collect_md;
struct list_head list;
......@@ -81,6 +87,7 @@ struct geneve_sock {
int refcnt;
struct udp_offload udp_offloads;
struct hlist_head vni_list[VNI_HASH_SIZE];
u32 flags;
};
static inline __u32 geneve_net_vni_hash(u8 vni[3])
......@@ -343,7 +350,7 @@ static int geneve_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
}
static struct socket *geneve_create_sock(struct net *net, bool ipv6,
__be16 port)
__be16 port, u32 flags)
{
struct socket *sock;
struct udp_port_cfg udp_conf;
......@@ -354,6 +361,8 @@ static struct socket *geneve_create_sock(struct net *net, bool ipv6,
if (ipv6) {
udp_conf.family = AF_INET6;
udp_conf.ipv6_v6only = 1;
udp_conf.use_udp6_rx_checksums =
!(flags & GENEVE_F_UDP_ZERO_CSUM6_RX);
} else {
udp_conf.family = AF_INET;
udp_conf.local_ip.s_addr = htonl(INADDR_ANY);
......@@ -480,7 +489,7 @@ static int geneve_gro_complete(struct sk_buff *skb, int nhoff,
/* Create new listen socket if needed */
static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port,
bool ipv6)
bool ipv6, u32 flags)
{
struct geneve_net *gn = net_generic(net, geneve_net_id);
struct geneve_sock *gs;
......@@ -492,7 +501,7 @@ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port,
if (!gs)
return ERR_PTR(-ENOMEM);
sock = geneve_create_sock(net, ipv6, port);
sock = geneve_create_sock(net, ipv6, port, flags);
if (IS_ERR(sock)) {
kfree(gs);
return ERR_CAST(sock);
......@@ -575,12 +584,13 @@ static int geneve_sock_add(struct geneve_dev *geneve, bool ipv6)
goto out;
}
gs = geneve_socket_create(net, geneve->dst_port, ipv6);
gs = geneve_socket_create(net, geneve->dst_port, ipv6, geneve->flags);
if (IS_ERR(gs))
return PTR_ERR(gs);
out:
gs->collect_md = geneve->collect_md;
gs->flags = geneve->flags;
#if IS_ENABLED(CONFIG_IPV6)
if (ipv6)
geneve->sock6 = gs;
......@@ -642,11 +652,12 @@ static void geneve_build_header(struct genevehdr *geneveh,
static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
__be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
bool csum, bool xnet)
u32 flags, bool xnet)
{
struct genevehdr *gnvh;
int min_headroom;
int err;
bool udp_sum = !!(flags & GENEVE_F_UDP_CSUM);
skb_scrub_packet(skb, xnet);
......@@ -658,7 +669,7 @@ static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
goto free_rt;
}
skb = udp_tunnel_handle_offloads(skb, csum);
skb = udp_tunnel_handle_offloads(skb, udp_sum);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
goto free_rt;
......@@ -678,11 +689,12 @@ static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
#if IS_ENABLED(CONFIG_IPV6)
static int geneve6_build_skb(struct dst_entry *dst, struct sk_buff *skb,
__be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
bool csum, bool xnet)
u32 flags, bool xnet)
{
struct genevehdr *gnvh;
int min_headroom;
int err;
bool udp_sum = !(flags & GENEVE_F_UDP_ZERO_CSUM6_TX);
skb_scrub_packet(skb, xnet);
......@@ -694,7 +706,7 @@ static int geneve6_build_skb(struct dst_entry *dst, struct sk_buff *skb,
goto free_dst;
}
skb = udp_tunnel_handle_offloads(skb, csum);
skb = udp_tunnel_handle_offloads(skb, udp_sum);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
goto free_dst;
......@@ -824,9 +836,9 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
struct flowi4 fl4;
__u8 tos, ttl;
__be16 sport;
bool udp_csum;
__be16 df;
bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
u32 flags = geneve->flags;
if (geneve->collect_md) {
if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) {
......@@ -857,9 +869,13 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
if (key->tun_flags & TUNNEL_GENEVE_OPT)
opts = ip_tunnel_info_opts(info);
udp_csum = !!(key->tun_flags & TUNNEL_CSUM);
if (key->tun_flags & TUNNEL_CSUM)
flags |= GENEVE_F_UDP_CSUM;
else
flags &= ~GENEVE_F_UDP_CSUM;
err = geneve_build_skb(rt, skb, key->tun_flags, vni,
info->options_len, opts, udp_csum, xnet);
info->options_len, opts, flags, xnet);
if (unlikely(err))
goto err;
......@@ -867,9 +883,8 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
ttl = key->ttl;
df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0;
} else {
udp_csum = false;
err = geneve_build_skb(rt, skb, 0, geneve->vni,
0, NULL, udp_csum, xnet);
0, NULL, flags, xnet);
if (unlikely(err))
goto err;
......@@ -883,7 +898,7 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
err = udp_tunnel_xmit_skb(rt, gs4->sock->sk, skb, fl4.saddr, fl4.daddr,
tos, ttl, df, sport, geneve->dst_port,
!net_eq(geneve->net, dev_net(geneve->dev)),
!udp_csum);
!(flags & GENEVE_F_UDP_CSUM));
iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
return NETDEV_TX_OK;
......@@ -912,8 +927,8 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
struct flowi6 fl6;
__u8 prio, ttl;
__be16 sport;
bool udp_csum;
bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
u32 flags = geneve->flags;
if (geneve->collect_md) {
if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) {
......@@ -942,19 +957,22 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
if (key->tun_flags & TUNNEL_GENEVE_OPT)
opts = ip_tunnel_info_opts(info);
udp_csum = !!(key->tun_flags & TUNNEL_CSUM);
if (key->tun_flags & TUNNEL_CSUM)
flags |= GENEVE_F_UDP_CSUM;
else
flags &= ~GENEVE_F_UDP_CSUM;
err = geneve6_build_skb(dst, skb, key->tun_flags, vni,
info->options_len, opts,
udp_csum, xnet);
flags, xnet);
if (unlikely(err))
goto err;
prio = ip_tunnel_ecn_encap(key->tos, iip, skb);
ttl = key->ttl;
} else {
udp_csum = false;
err = geneve6_build_skb(dst, skb, 0, geneve->vni,
0, NULL, udp_csum, xnet);
0, NULL, flags, xnet);
if (unlikely(err))
goto err;
......@@ -966,7 +984,8 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
}
err = udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev,
&fl6.saddr, &fl6.daddr, prio, ttl,
sport, geneve->dst_port, !udp_csum);
sport, geneve->dst_port,
!!(flags & GENEVE_F_UDP_ZERO_CSUM6_TX));
iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
return NETDEV_TX_OK;
......@@ -1099,6 +1118,9 @@ static const struct nla_policy geneve_policy[IFLA_GENEVE_MAX + 1] = {
[IFLA_GENEVE_TOS] = { .type = NLA_U8 },
[IFLA_GENEVE_PORT] = { .type = NLA_U16 },
[IFLA_GENEVE_COLLECT_METADATA] = { .type = NLA_FLAG },
[IFLA_GENEVE_UDP_CSUM] = { .type = NLA_U8 },
[IFLA_GENEVE_UDP_ZERO_CSUM6_TX] = { .type = NLA_U8 },
[IFLA_GENEVE_UDP_ZERO_CSUM6_RX] = { .type = NLA_U8 },
};
static int geneve_validate(struct nlattr *tb[], struct nlattr *data[])
......@@ -1152,7 +1174,7 @@ static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
static int geneve_configure(struct net *net, struct net_device *dev,
union geneve_addr *remote,
__u32 vni, __u8 ttl, __u8 tos, __be16 dst_port,
bool metadata)
bool metadata, u32 flags)
{
struct geneve_net *gn = net_generic(net, geneve_net_id);
struct geneve_dev *t, *geneve = netdev_priv(dev);
......@@ -1183,6 +1205,7 @@ static int geneve_configure(struct net *net, struct net_device *dev,
geneve->tos = tos;
geneve->dst_port = dst_port;
geneve->collect_md = metadata;
geneve->flags = flags;
t = geneve_find_dev(gn, dst_port, remote, geneve->vni,
&tun_on_same_port, &tun_collect_md);
......@@ -1213,6 +1236,7 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
bool metadata = false;
union geneve_addr remote = geneve_remote_unspec;
__u32 vni = 0;
u32 flags = 0;
if (data[IFLA_GENEVE_REMOTE] && data[IFLA_GENEVE_REMOTE6])
return -EINVAL;
......@@ -1253,8 +1277,20 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
if (data[IFLA_GENEVE_COLLECT_METADATA])
metadata = true;
if (data[IFLA_GENEVE_UDP_CSUM] &&
nla_get_u8(data[IFLA_GENEVE_UDP_CSUM]))
flags |= GENEVE_F_UDP_CSUM;
if (data[IFLA_GENEVE_UDP_ZERO_CSUM6_TX] &&
nla_get_u8(data[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]))
flags |= GENEVE_F_UDP_ZERO_CSUM6_TX;
if (data[IFLA_GENEVE_UDP_ZERO_CSUM6_RX] &&
nla_get_u8(data[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]))
flags |= GENEVE_F_UDP_ZERO_CSUM6_RX;
return geneve_configure(net, dev, &remote, vni, ttl, tos, dst_port,
metadata);
metadata, flags);
}
static void geneve_dellink(struct net_device *dev, struct list_head *head)
......@@ -1273,6 +1309,9 @@ static size_t geneve_get_size(const struct net_device *dev)
nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_TOS */
nla_total_size(sizeof(__be16)) + /* IFLA_GENEVE_PORT */
nla_total_size(0) + /* IFLA_GENEVE_COLLECT_METADATA */
nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_UDP_CSUM */
nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_UDP_ZERO_CSUM6_TX */
nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_UDP_ZERO_CSUM6_RX */
0;
}
......@@ -1309,6 +1348,14 @@ static int geneve_fill_info(struct sk_buff *skb, const struct net_device *dev)
goto nla_put_failure;
}
if (nla_put_u8(skb, IFLA_GENEVE_UDP_CSUM,
!!(geneve->flags & GENEVE_F_UDP_CSUM)) ||
nla_put_u8(skb, IFLA_GENEVE_UDP_ZERO_CSUM6_TX,
!!(geneve->flags & GENEVE_F_UDP_ZERO_CSUM6_TX)) ||
nla_put_u8(skb, IFLA_GENEVE_UDP_ZERO_CSUM6_RX,
!!(geneve->flags & GENEVE_F_UDP_ZERO_CSUM6_RX)))
goto nla_put_failure;
return 0;
nla_put_failure:
......@@ -1342,7 +1389,7 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name,
return dev;
err = geneve_configure(net, dev, &geneve_remote_unspec,
0, 0, 0, htons(dst_port), true);
0, 0, 0, htons(dst_port), true, 0);
if (err) {
free_netdev(dev);
return ERR_PTR(err);
......
......@@ -462,6 +462,9 @@ enum {
IFLA_GENEVE_PORT, /* destination port */
IFLA_GENEVE_COLLECT_METADATA,
IFLA_GENEVE_REMOTE6,
IFLA_GENEVE_UDP_CSUM,
IFLA_GENEVE_UDP_ZERO_CSUM6_TX,
IFLA_GENEVE_UDP_ZERO_CSUM6_RX,
__IFLA_GENEVE_MAX
};
#define IFLA_GENEVE_MAX (__IFLA_GENEVE_MAX - 1)
......
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