On Mon, Jan 22, 2018 at 6:42 PM, Ed Swierk <eswi...@skyportsystems.com> wrote: > IPv4 and IPv6 packets may arrive with lower-layer padding that is not > included in the L3 length. For example, a short IPv4 packet may have > up to 6 bytes of padding following the IP payload when received on an > Ethernet device with a minimum packet length of 64 bytes. > > Higher-layer processing functions in netfilter (e.g. nf_ip_checksum(), > and help() in nf_conntrack_ftp) assume skb->len reflects the length of > the L3 header and payload, rather than referring back to > ip_hdr->tot_len or ipv6_hdr->payload_len, and get confused by > lower-layer padding. > > In the normal IPv4 receive path, ip_rcv() trims the packet to > ip_hdr->tot_len before invoking netfilter hooks. In the IPv6 receive > path, ip6_rcv() does the same using ipv6_hdr->payload_len. Similarly > in the br_netfilter receive path, br_validate_ipv4() and > br_validate_ipv6() trim the packet to the L3 length before invoking > netfilter hooks. > > Currently the openvswitch receive path does not perform such trimming, > which will be fixed by the next patch. In preparation, this patch adds > a generic skb_network_trim() function. > > Signed-off-by: Ed Swierk <eswi...@skyportsystems.com> > --- > include/linux/skbuff.h | 2 ++ > net/core/skbuff.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 46 insertions(+) > > diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h > index 051e093..0478645 100644 > --- a/include/linux/skbuff.h > +++ b/include/linux/skbuff.h > @@ -4018,6 +4018,8 @@ struct sk_buff *skb_checksum_trimmed(struct sk_buff > *skb, > unsigned int transport_len, > __sum16(*skb_chkf)(struct sk_buff *skb)); > > +int skb_network_trim(struct sk_buff *skb); > + > /** > * skb_head_is_locked - Determine if the skb->head is locked down > * @skb: skb to check > diff --git a/net/core/skbuff.c b/net/core/skbuff.c > index 15fa5ba..cef3d1e 100644 > --- a/net/core/skbuff.c > +++ b/net/core/skbuff.c > @@ -4743,6 +4743,50 @@ struct sk_buff *skb_checksum_trimmed(struct sk_buff > *skb, > } > EXPORT_SYMBOL(skb_checksum_trimmed); > > +/** > + * skb_network_trim - trim skb to length specified by the network header > + * @skb: the skb to trim > + * > + * Trims the skb to the length specified by the IP/IPv6 header, > + * removing any trailing lower-layer padding. This prepares the skb > + * for higher-layer processing that assumes skb->len excludes padding. > + * > + * Leaves the skb alone if the protocol is not IP or IPv6. Frees the > + * skb on error. > + * > + * Caller needs to pull the skb to the network header. > + */ > +int skb_network_trim(struct sk_buff *skb) > +{ > + unsigned int len; > + int err = -ENOMEM; > + > + switch (skb->protocol) { > + case htons(ETH_P_IP): > + if (!pskb_may_pull(skb, sizeof(struct iphdr))) > + goto out; Since you are going to move this to OVS specific code, can you remove this skb-pull, which is not required in OVS code path.
> + len = ntohs(ip_hdr(skb)->tot_len); > + break; > + case htons(ETH_P_IPV6): > + if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) > + goto out; > + len = sizeof(struct ipv6hdr) > + + ntohs(ipv6_hdr(skb)->payload_len);