[ovs-dev] [PATCH v2] openvswitch: Trim off padding before L3+ netfilter processing

Ed Swierk eswierk at skyportsystems.com
Fri Jan 5 23:20:36 UTC 2018


On Fri, Jan 5, 2018 at 10:14 AM, Ed Swierk <eswierk at skyportsystems.com>
wrote:
> On Thu, Jan 4, 2018 at 7:36 PM, Pravin Shelar <pshelar at ovn.org> wrote:
>> OVS already pull all required headers in skb linear data, so no need
>> to redo all of it. only check required is the ip-checksum validation.
>> I think we could avoid it in most of cases by checking skb length to
>> ipheader length before verifying the ip header-checksum.
>
> Shouldn't the IP header checksum be verified even earlier, like in
> key_extract(), before actually using any of the fields in the IP
> header?

Something like this for verifying the IP header checksum (not tested):

--- a/net/openvswitch/flow.c
+++ b/net/openvswitch/flow.c
@@ -203,6 +203,7 @@ static int check_iphdr(struct sk_buff *skb)
 {
  unsigned int nh_ofs = skb_network_offset(skb);
  unsigned int ip_len;
+ const struct iphdr *nh;
  int err;

  err = check_header(skb, nh_ofs + sizeof(struct iphdr));
@@ -214,6 +215,13 @@ static int check_iphdr(struct sk_buff *skb)
       skb->len < nh_ofs + ip_len))
  return -EINVAL;

+ if (unlikely(!pskb_may_pull(skb, nh_ofs + ip_len)))
+ return -ENOMEM;
+
+ nh = ip_hdr(skb);
+ if (unlikely(ip_fast_csum((u8 *)nh, nh->ihl)))
+ return -EINVAL;
+
  skb_set_transport_header(skb, nh_ofs + ip_len);
  return 0;
 }

> And since key_extract() is already inspecting the IP/IPv6 header, it
> would be a convenient spot to check whether the skb->len matches. If
> there's a difference, it could record the number of bytes to trim in
> an ovs_skb_cb field. Then ovs_ct_execute() would look at this field
> and trim the skb only if necessary.

And something like this for trimming the padding (also not tested):

--- a/net/openvswitch/conntrack.c
+++ b/net/openvswitch/conntrack.c
@@ -1112,6 +1112,14 @@ int ovs_ct_execute(struct net *net, struct sk_buff
*skb,
  nh_ofs = skb_network_offset(skb);
  skb_pull_rcsum(skb, nh_ofs);

+ if (unlikely(OVS_CB(skb)->padlen)) {
+ err = pskb_trim_rcsum(skb, skb->len - OVS_CB(skb)->padlen);
+ if (err) {
+ kfree_skb(skb);
+ return err;
+ }
+ }
+
  if (key->ip.frag != OVS_FRAG_TYPE_NONE) {
  err = handle_fragments(net, key, info->zone.id, skb);
  if (err)
diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h
index 523d655..eab29fa 100644
--- a/net/openvswitch/datapath.h
+++ b/net/openvswitch/datapath.h
@@ -106,12 +106,14 @@ struct datapath {
  * fragmented.
  * @acts_origlen: The netlink size of the flow actions applied to this skb.
  * @cutlen: The number of bytes from the packet end to be removed.
+ * @padlen: The number of padding bytes to be ignored in L3+ processing.
  */
 struct ovs_skb_cb {
  struct vport *input_vport;
  u16 mru;
  u16 acts_origlen;
  u32 cutlen;
+ u32                     padlen;
 };
 #define OVS_CB(skb) ((struct ovs_skb_cb *)(skb)->cb)

diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c
index 987a97f..c749dfd 100644
--- a/net/openvswitch/flow.c
+++ b/net/openvswitch/flow.c
@@ -223,6 +223,7 @@ static int check_iphdr(struct sk_buff *skb)
  return -EINVAL;

  skb_set_transport_header(skb, nh_ofs + ip_len);
+ OVS_CB(skb)->padlen = skb->len - nh_ofs - ntohs(nh->tot_len);
  return 0;
 }

@@ -305,6 +306,7 @@ static int parse_ipv6hdr(struct sk_buff *skb, struct
sw_flow_key *key)

  nh_len = payload_ofs - nh_ofs;
  skb_set_transport_header(skb, nh_ofs + nh_len);
+ OVS_CB(skb)->padlen = skb->len - payload_ofs - ntohs(nh->payload_len);
  key->ip.proto = nexthdr;
  return nh_len;
 }


More information about the dev mailing list