From: Willem de Bruijn <will...@google.com> Extension headers in ipv6 are pulled without calling a callback function. An inet6_offload signals this feature with flag INET6_PROTO_GSO_EXTHDR.
Add net_has_flag helper to hide implementation details and in prepartion for configurable gro. Convert NEXTHDR_HOP from a special case branch to a standard extension header offload. Signed-off-by: Willem de Bruijn <will...@google.com> --- include/linux/netdevice.h | 9 +++++++++ net/ipv6/exthdrs_offload.c | 17 ++++++++++++++--- net/ipv6/ip6_offload.c | 36 +++++++++++++----------------------- 3 files changed, 36 insertions(+), 26 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 0be594f8d1ce..1c97a048506f 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3567,6 +3567,15 @@ static inline u8 net_offload_from_type(u16 type) return type & 0xFF; } +static inline bool net_offload_has_flag(const struct net_offload __rcu **offs, + u16 type, u16 flag) +{ + const struct net_offload *off; + + off = offs ? rcu_dereference(offs[net_offload_from_type(type)]) : NULL; + return off && off->flags & flag; +} + static inline const struct net_offload * net_gro_receive(const struct net_offload __rcu **offs, u16 type) { diff --git a/net/ipv6/exthdrs_offload.c b/net/ipv6/exthdrs_offload.c index f5e2ba1c18bf..2230331c6012 100644 --- a/net/ipv6/exthdrs_offload.c +++ b/net/ipv6/exthdrs_offload.c @@ -12,11 +12,15 @@ #include <net/protocol.h> #include "ip6_offload.h" -static const struct net_offload rthdr_offload = { +static struct net_offload hophdr_offload = { .flags = INET6_PROTO_GSO_EXTHDR, }; -static const struct net_offload dstopt_offload = { +static struct net_offload rthdr_offload = { + .flags = INET6_PROTO_GSO_EXTHDR, +}; + +static struct net_offload dstopt_offload = { .flags = INET6_PROTO_GSO_EXTHDR, }; @@ -24,10 +28,14 @@ int __init ipv6_exthdrs_offload_init(void) { int ret; - ret = inet6_add_offload(&rthdr_offload, IPPROTO_ROUTING); + ret = inet6_add_offload(&hophdr_offload, IPPROTO_HOPOPTS); if (ret) goto out; + ret = inet6_add_offload(&rthdr_offload, IPPROTO_ROUTING); + if (ret) + goto out_hop; + ret = inet6_add_offload(&dstopt_offload, IPPROTO_DSTOPTS); if (ret) goto out_rt; @@ -37,5 +45,8 @@ int __init ipv6_exthdrs_offload_init(void) out_rt: inet6_del_offload(&rthdr_offload, IPPROTO_ROUTING); + +out_hop: + inet6_del_offload(&rthdr_offload, IPPROTO_HOPOPTS); goto out; } diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c index 9d301bef0e23..4854509a2c5d 100644 --- a/net/ipv6/ip6_offload.c +++ b/net/ipv6/ip6_offload.c @@ -22,21 +22,13 @@ static int ipv6_gso_pull_exthdrs(struct sk_buff *skb, int proto) { - const struct net_offload *ops = NULL; - for (;;) { struct ipv6_opt_hdr *opth; int len; - if (proto != NEXTHDR_HOP) { - ops = rcu_dereference(inet6_offloads[proto]); - - if (unlikely(!ops)) - break; - - if (!(ops->flags & INET6_PROTO_GSO_EXTHDR)) - break; - } + if (!net_offload_has_flag(inet6_offloads, proto, + INET6_PROTO_GSO_EXTHDR)) + break; if (unlikely(!pskb_may_pull(skb, 8))) break; @@ -141,26 +133,24 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, /* Return the total length of all the extension hdrs, following the same * logic in ipv6_gso_pull_exthdrs() when parsing ext-hdrs. */ -static int ipv6_exthdrs_len(struct ipv6hdr *iph, - const struct net_offload **opps) +static int ipv6_exthdrs_len(struct ipv6hdr *iph, u8 *pproto) { struct ipv6_opt_hdr *opth = (void *)iph; int len = 0, proto, optlen = sizeof(*iph); proto = iph->nexthdr; for (;;) { - if (proto != NEXTHDR_HOP) { - *opps = rcu_dereference(inet6_offloads[proto]); - if (unlikely(!(*opps))) - break; - if (!((*opps)->flags & INET6_PROTO_GSO_EXTHDR)) - break; - } + if (!net_offload_has_flag(inet6_offloads, proto, + INET6_PROTO_GSO_EXTHDR)) + break; + opth = (void *)opth + optlen; optlen = ipv6_optlen(opth); len += optlen; proto = opth->nexthdr; } + + *pproto = proto; return len; } @@ -296,8 +286,8 @@ static struct sk_buff *ip4ip6_gro_receive(struct list_head *head, static int ipv6_gro_complete(struct sk_buff *skb, int nhoff) { - const struct net_offload *ops; struct ipv6hdr *iph = (struct ipv6hdr *)(skb->data + nhoff); + u8 proto; if (skb->encapsulation) { skb_set_inner_protocol(skb, cpu_to_be16(ETH_P_IPV6)); @@ -306,8 +296,8 @@ static int ipv6_gro_complete(struct sk_buff *skb, int nhoff) iph->payload_len = htons(skb->len - nhoff - sizeof(*iph)); - nhoff += sizeof(*iph) + ipv6_exthdrs_len(iph, &ops); - return net_gro_complete(inet6_offloads, ops->type, skb, nhoff); + nhoff += sizeof(*iph) + ipv6_exthdrs_len(iph, &proto); + return net_gro_complete(inet6_offloads, proto, skb, nhoff); } static int sit_gro_complete(struct sk_buff *skb, int nhoff) -- 2.19.0.397.gdd90340f6a-goog