@@ -3,6 +3,8 @@
#define _LINUX_VIRTIO_NET_H
#include <linux/if_vlan.h>
+#include <linux/jump_label.h>
+#include <net/sock.h>
#include <uapi/linux/tcp.h>
#include <uapi/linux/udp.h>
#include <uapi/linux/virtio_net.h>
@@ -25,10 +27,13 @@ static inline int virtio_net_hdr_set_proto(struct sk_buff *skb,
return 0;
}
+DECLARE_STATIC_KEY_FALSE(sysctl_flow_dissect_vnet_hdr_key);
+
static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
const struct virtio_net_hdr *hdr,
bool little_endian)
{
+ struct flow_keys_basic keys;
unsigned int gso_type = 0;
unsigned int thlen = 0;
unsigned int p_off = 0;
@@ -78,13 +83,24 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
p_off = skb_transport_offset(skb) + thlen;
if (!pskb_may_pull(skb, p_off))
return -EINVAL;
- } else {
+ }
+
+ /* BPF flow dissection for optional strict validation.
+ *
+ * Admins can define permitted packets more strictly, such as dropping
+ * deprecated UDP_UFO packets and requiring skb->protocol to be non-zero
+ * and matching packet headers.
+ */
+ if (static_branch_unlikely(&sysctl_flow_dissect_vnet_hdr_key) &&
+ !__skb_flow_dissect_flow_keys_basic(NULL, skb, &keys, NULL, 0, 0, 0,
+ 0, hdr))
+ return -EINVAL;
+
+ if (!(hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM)) {
/* gso packets without NEEDS_CSUM do not set transport_offset.
* probe and drop if does not match one of the above types.
*/
if (gso_type && skb->network_header) {
- struct flow_keys_basic keys;
-
if (!skb->protocol) {
__be16 protocol = dev_parse_header_protocol(skb);
@@ -92,8 +108,11 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
if (protocol && protocol != skb->protocol)
return -EINVAL;
}
+
retry:
- if (!skb_flow_dissect_flow_keys_basic(NULL, skb, &keys,
+ /* only if flow dissection not already done */
+ if (!static_branch_unlikely(&sysctl_flow_dissect_vnet_hdr_key) &&
+ !skb_flow_dissect_flow_keys_basic(NULL, skb, &keys,
NULL, 0, 0, 0,
0)) {
/* UFO does not specify ipv4 or 6: try both */
@@ -36,6 +36,9 @@ static int net_msg_warn; /* Unused, but still a sysctl */
int sysctl_fb_tunnels_only_for_init_net __read_mostly = 0;
EXPORT_SYMBOL(sysctl_fb_tunnels_only_for_init_net);
+DEFINE_STATIC_KEY_FALSE(sysctl_flow_dissect_vnet_hdr_key);
+EXPORT_SYMBOL(sysctl_flow_dissect_vnet_hdr_key);
+
/* 0 - Keep current behavior:
* IPv4: inherit all current settings from init_net
* IPv6: reset all settings to default
@@ -580,6 +583,13 @@ static struct ctl_table net_core_table[] = {
.extra1 = SYSCTL_ONE,
.extra2 = &int_3600,
},
+ {
+ .procname = "flow_dissect_vnet_hdr",
+ .data = &sysctl_flow_dissect_vnet_hdr_key.key,
+ .maxlen = sizeof(sysctl_flow_dissect_vnet_hdr_key),
+ .mode = 0644,
+ .proc_handler = proc_do_static_key,
+ },
{ }
};