|
@@ -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 @@ error:
|
|
|
}
|
|
|
|
|
|
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 @@ free_rt:
|
|
|
#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);
|