Skip to content

Commit

Permalink
ipv6: exthdrs: get rid of indirect calls in ip6_parse_tlv()
Browse files Browse the repository at this point in the history
As presented last month in our "BIG TCP" talk at netdev 0x15,
we plan using IPv6 jumbograms.

One of the minor problem we talked about is the fact that
ip6_parse_tlv() is currently using tables to list known tlvs,
thus using potentially expensive indirect calls.

While we could mitigate this cost using macros from
indirect_call_wrapper.h, we also can get rid of the tables
and let the compiler emit optimized code.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Justin Iurman <justin.iurman@uliege.be>
Cc: Coco Li <lixiaoyan@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Eric Dumazet authored and David S. Miller committed Aug 4, 2021
1 parent d851798 commit 51b8f81
Showing 1 changed file with 46 additions and 59 deletions.
105 changes: 46 additions & 59 deletions net/ipv6/exthdrs.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,19 +55,6 @@

#include <linux/uaccess.h>

/*
* Parsing tlv encoded headers.
*
* Parsing function "func" returns true, if parsing succeed
* and false, if it failed.
* It MUST NOT touch skb->h.
*/

struct tlvtype_proc {
int type;
bool (*func)(struct sk_buff *skb, int offset);
};

/*********************
Generic functions
*********************/
Expand Down Expand Up @@ -112,16 +99,23 @@ static bool ip6_tlvopt_unknown(struct sk_buff *skb, int optoff,
return false;
}

static bool ipv6_hop_ra(struct sk_buff *skb, int optoff);
static bool ipv6_hop_ioam(struct sk_buff *skb, int optoff);
static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff);
static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff);
#if IS_ENABLED(CONFIG_IPV6_MIP6)
static bool ipv6_dest_hao(struct sk_buff *skb, int optoff);
#endif

/* Parse tlv encoded option header (hop-by-hop or destination) */

static bool ip6_parse_tlv(const struct tlvtype_proc *procs,
static bool ip6_parse_tlv(bool hopbyhop,
struct sk_buff *skb,
int max_count)
{
int len = (skb_transport_header(skb)[1] + 1) << 3;
const unsigned char *nh = skb_network_header(skb);
int off = skb_network_header_len(skb);
const struct tlvtype_proc *curr;
bool disallow_unknowns = false;
int tlv_count = 0;
int padlen = 0;
Expand Down Expand Up @@ -176,20 +170,45 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs,
if (tlv_count > max_count)
goto bad;

for (curr = procs; curr->type >= 0; curr++) {
if (curr->type == nh[off]) {
/* type specific length/alignment
checks will be performed in the
func(). */
if (curr->func(skb, off) == false)
if (hopbyhop) {
switch (nh[off]) {
case IPV6_TLV_ROUTERALERT:
if (!ipv6_hop_ra(skb, off))
return false;
break;
case IPV6_TLV_IOAM:
if (!ipv6_hop_ioam(skb, off))
return false;
break;
case IPV6_TLV_JUMBO:
if (!ipv6_hop_jumbo(skb, off))
return false;
break;
case IPV6_TLV_CALIPSO:
if (!ipv6_hop_calipso(skb, off))
return false;
break;
default:
if (!ip6_tlvopt_unknown(skb, off,
disallow_unknowns))
return false;
break;
}
} else {
switch (nh[off]) {
#if IS_ENABLED(CONFIG_IPV6_MIP6)
case IPV6_TLV_HAO:
if (!ipv6_dest_hao(skb, off))
return false;
break;
#endif
default:
if (!ip6_tlvopt_unknown(skb, off,
disallow_unknowns))
return false;
break;
}
}
if (curr->type < 0 &&
!ip6_tlvopt_unknown(skb, off, disallow_unknowns))
return false;

padlen = 0;
}
off += optlen;
Expand Down Expand Up @@ -267,16 +286,6 @@ static bool ipv6_dest_hao(struct sk_buff *skb, int optoff)
}
#endif

static const struct tlvtype_proc tlvprocdestopt_lst[] = {
#if IS_ENABLED(CONFIG_IPV6_MIP6)
{
.type = IPV6_TLV_HAO,
.func = ipv6_dest_hao,
},
#endif
{-1, NULL}
};

static int ipv6_destopt_rcv(struct sk_buff *skb)
{
struct inet6_dev *idev = __in6_dev_get(skb->dev);
Expand Down Expand Up @@ -307,8 +316,7 @@ static int ipv6_destopt_rcv(struct sk_buff *skb)
dstbuf = opt->dst1;
#endif

if (ip6_parse_tlv(tlvprocdestopt_lst, skb,
net->ipv6.sysctl.max_dst_opts_cnt)) {
if (ip6_parse_tlv(false, skb, net->ipv6.sysctl.max_dst_opts_cnt)) {
skb->transport_header += extlen;
opt = IP6CB(skb);
#if IS_ENABLED(CONFIG_IPV6_MIP6)
Expand Down Expand Up @@ -1051,26 +1059,6 @@ static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff)
return false;
}

static const struct tlvtype_proc tlvprochopopt_lst[] = {
{
.type = IPV6_TLV_ROUTERALERT,
.func = ipv6_hop_ra,
},
{
.type = IPV6_TLV_IOAM,
.func = ipv6_hop_ioam,
},
{
.type = IPV6_TLV_JUMBO,
.func = ipv6_hop_jumbo,
},
{
.type = IPV6_TLV_CALIPSO,
.func = ipv6_hop_calipso,
},
{ -1, }
};

int ipv6_parse_hopopts(struct sk_buff *skb)
{
struct inet6_skb_parm *opt = IP6CB(skb);
Expand All @@ -1096,8 +1084,7 @@ int ipv6_parse_hopopts(struct sk_buff *skb)
goto fail_and_free;

opt->flags |= IP6SKB_HOPBYHOP;
if (ip6_parse_tlv(tlvprochopopt_lst, skb,
net->ipv6.sysctl.max_hbh_opts_cnt)) {
if (ip6_parse_tlv(true, skb, net->ipv6.sysctl.max_hbh_opts_cnt)) {
skb->transport_header += extlen;
opt = IP6CB(skb);
opt->nhoff = sizeof(struct ipv6hdr);
Expand Down

0 comments on commit 51b8f81

Please sign in to comment.