diff mbox series

[net-next,1/7] ipv6: Add ipv6_skip_exthdr_no_rthdr

Message ID 20240701012101.182784-2-tom@herbertland.com (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series drivers: Fix drivers doing TX csum offload with EH | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 857 this patch: 857
netdev/build_tools success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers warning 3 maintainers not CCed: edumazet@google.com pabeni@redhat.com dsahern@kernel.org
netdev/build_clang fail Errors and warnings before: 875 this patch: 878
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 2821 this patch: 2821
netdev/checkpatch warning WARNING: From:/Signed-off-by: email address mismatch: 'From: Tom Herbert <tom@sipanda.io>' != 'Signed-off-by: Tom Herbert <tom@herbertland.com>'
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Tom Herbert July 1, 2024, 1:20 a.m. UTC
From: Tom Herbert <tom@sipanda.io>

ipv6_skip_exthdr_no_rthdr will be called by drivers that support
protocol specific transmit checksum offload with extension headers.
Protocol specific checksum offload doesn't work with routing headers
since the destination address in the IPv6 header is not the one used
in the pseduo checksum for TCP or UDP. This is not a problem with
protocol agnostic checksum offload.

If a routing header is present then ipv6_skip_exthdr_no_rthdr returns
a value less than zero, this is an indication that the driver should
call skb_checksum_help instead of offloading the checksum which  would
be doomed to cause a packet drop at the receiver due to a bad checksum.

Signed-off-by: Tom Herbert <tom@herbertland.com>
---
 include/net/ipv6.h      | 17 +++++++++++++++--
 net/ipv6/exthdrs_core.c | 22 ++++++++++++++++------
 2 files changed, 31 insertions(+), 8 deletions(-)

Comments

Dan Carpenter July 1, 2024, 5:28 p.m. UTC | #1
Hi Tom,

kernel test robot noticed the following build warnings:

url:    https://github.com/intel-lab-lkp/linux/commits/Tom-Herbert/ipv6-Add-ipv6_skip_exthdr_no_rthdr/20240701-110252
base:   net-next/main
patch link:    https://lore.kernel.org/r/20240701012101.182784-2-tom%40herbertland.com
patch subject: [PATCH net-next 1/7] ipv6: Add ipv6_skip_exthdr_no_rthdr
config: csky-randconfig-r071-20240701
compiler: csky-linux-gcc (GCC) 13.2.0

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
| Closes: https://lore.kernel.org/r/202407020050.bNOF4wiE-lkp@intel.com/

smatch warnings:
net/ipv6/exthdrs_core.c:118 __ipv6_skip_exthdr() error: uninitialized symbol 'hdrlen'.

vim +/hdrlen +118 net/ipv6/exthdrs_core.c

9b96802d744289 Tom Herbert    2024-06-30   72  int __ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp,
9b96802d744289 Tom Herbert    2024-06-30   73  		       __be16 *frag_offp, bool no_rthdr)
^1da177e4c3f41 Linus Torvalds 2005-04-16   74  {
^1da177e4c3f41 Linus Torvalds 2005-04-16   75  	u8 nexthdr = *nexthdrp;
^1da177e4c3f41 Linus Torvalds 2005-04-16   76  
75f2811c6460cc Jesse Gross    2011-11-30   77  	*frag_offp = 0;
75f2811c6460cc Jesse Gross    2011-11-30   78  
^1da177e4c3f41 Linus Torvalds 2005-04-16   79  	while (ipv6_ext_hdr(nexthdr)) {
^1da177e4c3f41 Linus Torvalds 2005-04-16   80  		struct ipv6_opt_hdr _hdr, *hp;
^1da177e4c3f41 Linus Torvalds 2005-04-16   81  		int hdrlen;
^1da177e4c3f41 Linus Torvalds 2005-04-16   82  
^1da177e4c3f41 Linus Torvalds 2005-04-16   83  		if (nexthdr == NEXTHDR_NONE)
^1da177e4c3f41 Linus Torvalds 2005-04-16   84  			return -1;
^1da177e4c3f41 Linus Torvalds 2005-04-16   85  		hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
63159f29be1df7 Ian Morris     2015-03-29   86  		if (!hp)
0d3d077cd4f115 Herbert Xu     2005-04-24   87  			return -1;
9b96802d744289 Tom Herbert    2024-06-30   88  		switch (nexthdr) {
9b96802d744289 Tom Herbert    2024-06-30   89  		case NEXTHDR_FRAGMENT: {
e69a4adc669fe2 Al Viro        2006-11-14   90  			__be16 _frag_off, *fp;
^1da177e4c3f41 Linus Torvalds 2005-04-16   91  			fp = skb_header_pointer(skb,
^1da177e4c3f41 Linus Torvalds 2005-04-16   92  						start+offsetof(struct frag_hdr,
^1da177e4c3f41 Linus Torvalds 2005-04-16   93  							       frag_off),
^1da177e4c3f41 Linus Torvalds 2005-04-16   94  						sizeof(_frag_off),
^1da177e4c3f41 Linus Torvalds 2005-04-16   95  						&_frag_off);
63159f29be1df7 Ian Morris     2015-03-29   96  			if (!fp)
^1da177e4c3f41 Linus Torvalds 2005-04-16   97  				return -1;
^1da177e4c3f41 Linus Torvalds 2005-04-16   98  
75f2811c6460cc Jesse Gross    2011-11-30   99  			*frag_offp = *fp;
75f2811c6460cc Jesse Gross    2011-11-30  100  			if (ntohs(*frag_offp) & ~0x7)
^1da177e4c3f41 Linus Torvalds 2005-04-16  101  				break;

hdrlen uninitialized on this break

^1da177e4c3f41 Linus Torvalds 2005-04-16  102  			hdrlen = 8;
9b96802d744289 Tom Herbert    2024-06-30  103  			break;
9b96802d744289 Tom Herbert    2024-06-30  104  		}
9b96802d744289 Tom Herbert    2024-06-30  105  		case NEXTHDR_AUTH:
d4e1b299ec2853 Xiang Gao      2017-09-20  106  			hdrlen = ipv6_authlen(hp);
9b96802d744289 Tom Herbert    2024-06-30  107  			break;
9b96802d744289 Tom Herbert    2024-06-30  108  		case NEXTHDR_ROUTING:
9b96802d744289 Tom Herbert    2024-06-30  109  			if (no_rthdr)
9b96802d744289 Tom Herbert    2024-06-30  110  				return -1;
9b96802d744289 Tom Herbert    2024-06-30  111  			fallthrough;
9b96802d744289 Tom Herbert    2024-06-30  112  		default:
^1da177e4c3f41 Linus Torvalds 2005-04-16  113  			hdrlen = ipv6_optlen(hp);
9b96802d744289 Tom Herbert    2024-06-30  114  			break;
9b96802d744289 Tom Herbert    2024-06-30  115  		}
^1da177e4c3f41 Linus Torvalds 2005-04-16  116  
^1da177e4c3f41 Linus Torvalds 2005-04-16  117  		nexthdr = hp->nexthdr;
^1da177e4c3f41 Linus Torvalds 2005-04-16 @118  		start += hdrlen;
^1da177e4c3f41 Linus Torvalds 2005-04-16  119  	}
^1da177e4c3f41 Linus Torvalds 2005-04-16  120  
^1da177e4c3f41 Linus Torvalds 2005-04-16  121  	*nexthdrp = nexthdr;
^1da177e4c3f41 Linus Torvalds 2005-04-16  122  	return start;
^1da177e4c3f41 Linus Torvalds 2005-04-16  123  }
kernel test robot July 1, 2024, 5:34 p.m. UTC | #2
Hi Tom,

kernel test robot noticed the following build warnings:

[auto build test WARNING on net-next/main]

url:    https://github.com/intel-lab-lkp/linux/commits/Tom-Herbert/ipv6-Add-ipv6_skip_exthdr_no_rthdr/20240701-110252
base:   net-next/main
patch link:    https://lore.kernel.org/r/20240701012101.182784-2-tom%40herbertland.com
patch subject: [PATCH net-next 1/7] ipv6: Add ipv6_skip_exthdr_no_rthdr
config: riscv-defconfig
compiler: clang version 19.0.0git (https://github.com/llvm/llvm-project 326ba38a991250a8587a399a260b0f7af2c9166a)
reproduce (this is a W=1 build):

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202407020142.6Vmptiya-lkp@intel.com/

All warnings (new ones prefixed by >>):

   In file included from net/ipv6/exthdrs_core.c:7:
   In file included from include/net/ipv6.h:12:
   In file included from include/linux/ipv6.h:101:
   In file included from include/linux/tcp.h:17:
   In file included from include/linux/skbuff.h:17:
   In file included from include/linux/bvec.h:10:
   In file included from include/linux/highmem.h:8:
   In file included from include/linux/cacheflush.h:5:
   In file included from arch/riscv/include/asm/cacheflush.h:9:
   In file included from include/linux/mm.h:2258:
   include/linux/vmstat.h:514:36: warning: arithmetic between different enumeration types ('enum node_stat_item' and 'enum lru_list') [-Wenum-enum-conversion]
     514 |         return node_stat_name(NR_LRU_BASE + lru) + 3; // skip "nr_"
         |                               ~~~~~~~~~~~ ^ ~~~
>> net/ipv6/exthdrs_core.c:100:8: warning: variable 'hdrlen' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
     100 |                         if (ntohs(*frag_offp) & ~0x7)
         |                             ^~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/byteorder/generic.h:142:18: note: expanded from macro 'ntohs'
     142 | #define ntohs(x) ___ntohs(x)
         |                  ^
   include/linux/byteorder/generic.h:137:21: note: expanded from macro '___ntohs'
     137 | #define ___ntohs(x) __be16_to_cpu(x)
         |                     ^
   include/uapi/linux/byteorder/little_endian.h:43:26: note: expanded from macro '__be16_to_cpu'
      43 | #define __be16_to_cpu(x) __swab16((__force __u16)(__be16)(x))
         |                          ^
   include/uapi/linux/swab.h:105:2: note: expanded from macro '__swab16'
     105 |         (__u16)(__builtin_constant_p(x) ?       \
         |         ^
   net/ipv6/exthdrs_core.c:118:12: note: uninitialized use occurs here
     118 |                 start += hdrlen;
         |                          ^~~~~~
   net/ipv6/exthdrs_core.c:100:4: note: remove the 'if' if its condition is always false
     100 |                         if (ntohs(*frag_offp) & ~0x7)
         |                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     101 |                                 break;
         |                                 ~~~~~
   net/ipv6/exthdrs_core.c:81:13: note: initialize the variable 'hdrlen' to silence this warning
      81 |                 int hdrlen;
         |                           ^
         |                            = 0
   2 warnings generated.


vim +100 net/ipv6/exthdrs_core.c

^1da177e4c3f41 Linus Torvalds 2005-04-16   26  
^1da177e4c3f41 Linus Torvalds 2005-04-16   27  /*
^1da177e4c3f41 Linus Torvalds 2005-04-16   28   * Skip any extension headers. This is used by the ICMP module.
^1da177e4c3f41 Linus Torvalds 2005-04-16   29   *
^1da177e4c3f41 Linus Torvalds 2005-04-16   30   * Note that strictly speaking this conflicts with RFC 2460 4.0:
^1da177e4c3f41 Linus Torvalds 2005-04-16   31   * ...The contents and semantics of each extension header determine whether
^1da177e4c3f41 Linus Torvalds 2005-04-16   32   * or not to proceed to the next header.  Therefore, extension headers must
^1da177e4c3f41 Linus Torvalds 2005-04-16   33   * be processed strictly in the order they appear in the packet; a
^1da177e4c3f41 Linus Torvalds 2005-04-16   34   * receiver must not, for example, scan through a packet looking for a
^1da177e4c3f41 Linus Torvalds 2005-04-16   35   * particular kind of extension header and process that header prior to
^1da177e4c3f41 Linus Torvalds 2005-04-16   36   * processing all preceding ones.
^1da177e4c3f41 Linus Torvalds 2005-04-16   37   *
^1da177e4c3f41 Linus Torvalds 2005-04-16   38   * We do exactly this. This is a protocol bug. We can't decide after a
^1da177e4c3f41 Linus Torvalds 2005-04-16   39   * seeing an unknown discard-with-error flavour TLV option if it's a
^1da177e4c3f41 Linus Torvalds 2005-04-16   40   * ICMP error message or not (errors should never be send in reply to
^1da177e4c3f41 Linus Torvalds 2005-04-16   41   * ICMP error messages).
^1da177e4c3f41 Linus Torvalds 2005-04-16   42   *
^1da177e4c3f41 Linus Torvalds 2005-04-16   43   * But I see no other way to do this. This might need to be reexamined
^1da177e4c3f41 Linus Torvalds 2005-04-16   44   * when Linux implements ESP (and maybe AUTH) headers.
^1da177e4c3f41 Linus Torvalds 2005-04-16   45   * --AK
^1da177e4c3f41 Linus Torvalds 2005-04-16   46   *
0d3d077cd4f115 Herbert Xu     2005-04-24   47   * This function parses (probably truncated) exthdr set "hdr".
0d3d077cd4f115 Herbert Xu     2005-04-24   48   * "nexthdrp" initially points to some place,
^1da177e4c3f41 Linus Torvalds 2005-04-16   49   * where type of the first header can be found.
^1da177e4c3f41 Linus Torvalds 2005-04-16   50   *
^1da177e4c3f41 Linus Torvalds 2005-04-16   51   * It skips all well-known exthdrs, and returns pointer to the start
^1da177e4c3f41 Linus Torvalds 2005-04-16   52   * of unparsable area i.e. the first header with unknown type.
^1da177e4c3f41 Linus Torvalds 2005-04-16   53   * If it is not NULL *nexthdr is updated by type/protocol of this header.
^1da177e4c3f41 Linus Torvalds 2005-04-16   54   *
^1da177e4c3f41 Linus Torvalds 2005-04-16   55   * NOTES: - if packet terminated with NEXTHDR_NONE it returns NULL.
^1da177e4c3f41 Linus Torvalds 2005-04-16   56   *        - it may return pointer pointing beyond end of packet,
^1da177e4c3f41 Linus Torvalds 2005-04-16   57   *	    if the last recognized header is truncated in the middle.
^1da177e4c3f41 Linus Torvalds 2005-04-16   58   *        - if packet is truncated, so that all parsed headers are skipped,
^1da177e4c3f41 Linus Torvalds 2005-04-16   59   *	    it returns NULL.
^1da177e4c3f41 Linus Torvalds 2005-04-16   60   *	  - First fragment header is skipped, not-first ones
^1da177e4c3f41 Linus Torvalds 2005-04-16   61   *	    are considered as unparsable.
75f2811c6460cc Jesse Gross    2011-11-30   62   *	  - Reports the offset field of the final fragment header so it is
75f2811c6460cc Jesse Gross    2011-11-30   63   *	    possible to tell whether this is a first fragment, later fragment,
75f2811c6460cc Jesse Gross    2011-11-30   64   *	    or not fragmented.
^1da177e4c3f41 Linus Torvalds 2005-04-16   65   *	  - ESP is unparsable for now and considered like
^1da177e4c3f41 Linus Torvalds 2005-04-16   66   *	    normal payload protocol.
^1da177e4c3f41 Linus Torvalds 2005-04-16   67   *	  - Note also special handling of AUTH header. Thanks to IPsec wizards.
^1da177e4c3f41 Linus Torvalds 2005-04-16   68   *
^1da177e4c3f41 Linus Torvalds 2005-04-16   69   * --ANK (980726)
^1da177e4c3f41 Linus Torvalds 2005-04-16   70   */
^1da177e4c3f41 Linus Torvalds 2005-04-16   71  
9b96802d744289 Tom Herbert    2024-06-30   72  int __ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp,
9b96802d744289 Tom Herbert    2024-06-30   73  		       __be16 *frag_offp, bool no_rthdr)
^1da177e4c3f41 Linus Torvalds 2005-04-16   74  {
^1da177e4c3f41 Linus Torvalds 2005-04-16   75  	u8 nexthdr = *nexthdrp;
^1da177e4c3f41 Linus Torvalds 2005-04-16   76  
75f2811c6460cc Jesse Gross    2011-11-30   77  	*frag_offp = 0;
75f2811c6460cc Jesse Gross    2011-11-30   78  
^1da177e4c3f41 Linus Torvalds 2005-04-16   79  	while (ipv6_ext_hdr(nexthdr)) {
^1da177e4c3f41 Linus Torvalds 2005-04-16   80  		struct ipv6_opt_hdr _hdr, *hp;
^1da177e4c3f41 Linus Torvalds 2005-04-16   81  		int hdrlen;
^1da177e4c3f41 Linus Torvalds 2005-04-16   82  
^1da177e4c3f41 Linus Torvalds 2005-04-16   83  		if (nexthdr == NEXTHDR_NONE)
^1da177e4c3f41 Linus Torvalds 2005-04-16   84  			return -1;
^1da177e4c3f41 Linus Torvalds 2005-04-16   85  		hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
63159f29be1df7 Ian Morris     2015-03-29   86  		if (!hp)
0d3d077cd4f115 Herbert Xu     2005-04-24   87  			return -1;
9b96802d744289 Tom Herbert    2024-06-30   88  		switch (nexthdr) {
9b96802d744289 Tom Herbert    2024-06-30   89  		case NEXTHDR_FRAGMENT: {
e69a4adc669fe2 Al Viro        2006-11-14   90  			__be16 _frag_off, *fp;
^1da177e4c3f41 Linus Torvalds 2005-04-16   91  			fp = skb_header_pointer(skb,
^1da177e4c3f41 Linus Torvalds 2005-04-16   92  						start+offsetof(struct frag_hdr,
^1da177e4c3f41 Linus Torvalds 2005-04-16   93  							       frag_off),
^1da177e4c3f41 Linus Torvalds 2005-04-16   94  						sizeof(_frag_off),
^1da177e4c3f41 Linus Torvalds 2005-04-16   95  						&_frag_off);
63159f29be1df7 Ian Morris     2015-03-29   96  			if (!fp)
^1da177e4c3f41 Linus Torvalds 2005-04-16   97  				return -1;
^1da177e4c3f41 Linus Torvalds 2005-04-16   98  
75f2811c6460cc Jesse Gross    2011-11-30   99  			*frag_offp = *fp;
75f2811c6460cc Jesse Gross    2011-11-30 @100  			if (ntohs(*frag_offp) & ~0x7)
^1da177e4c3f41 Linus Torvalds 2005-04-16  101  				break;
^1da177e4c3f41 Linus Torvalds 2005-04-16  102  			hdrlen = 8;
9b96802d744289 Tom Herbert    2024-06-30  103  			break;
9b96802d744289 Tom Herbert    2024-06-30  104  		}
9b96802d744289 Tom Herbert    2024-06-30  105  		case NEXTHDR_AUTH:
d4e1b299ec2853 Xiang Gao      2017-09-20  106  			hdrlen = ipv6_authlen(hp);
9b96802d744289 Tom Herbert    2024-06-30  107  			break;
9b96802d744289 Tom Herbert    2024-06-30  108  		case NEXTHDR_ROUTING:
9b96802d744289 Tom Herbert    2024-06-30  109  			if (no_rthdr)
9b96802d744289 Tom Herbert    2024-06-30  110  				return -1;
9b96802d744289 Tom Herbert    2024-06-30  111  			fallthrough;
9b96802d744289 Tom Herbert    2024-06-30  112  		default:
^1da177e4c3f41 Linus Torvalds 2005-04-16  113  			hdrlen = ipv6_optlen(hp);
9b96802d744289 Tom Herbert    2024-06-30  114  			break;
9b96802d744289 Tom Herbert    2024-06-30  115  		}
^1da177e4c3f41 Linus Torvalds 2005-04-16  116  
^1da177e4c3f41 Linus Torvalds 2005-04-16  117  		nexthdr = hp->nexthdr;
^1da177e4c3f41 Linus Torvalds 2005-04-16  118  		start += hdrlen;
^1da177e4c3f41 Linus Torvalds 2005-04-16  119  	}
^1da177e4c3f41 Linus Torvalds 2005-04-16  120  
^1da177e4c3f41 Linus Torvalds 2005-04-16  121  	*nexthdrp = nexthdr;
^1da177e4c3f41 Linus Torvalds 2005-04-16  122  	return start;
^1da177e4c3f41 Linus Torvalds 2005-04-16  123  }
9b96802d744289 Tom Herbert    2024-06-30  124  EXPORT_SYMBOL(__ipv6_skip_exthdr);
3c73a0368e995f Vlad Yasevich  2012-11-15  125
diff mbox series

Patch

diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 88a8e554f7a1..6581fabd1e1e 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -1157,8 +1157,21 @@  void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
 void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
 			 u8 *proto);
 
-int ipv6_skip_exthdr(const struct sk_buff *, int start, u8 *nexthdrp,
-		     __be16 *frag_offp);
+int __ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp,
+		       __be16 *frag_offp, bool no_rthdr);
+
+static inline int ipv6_skip_exthdr(const struct sk_buff *skb, int start,
+				   u8 *nexthdrp, __be16 *frag_offp)
+{
+	return __ipv6_skip_exthdr(skb, start, nexthdrp, frag_offp, false);
+}
+
+static inline int ipv6_skip_exthdr_no_rthdr(const struct sk_buff *skb,
+					    int start, u8 *nexthdrp,
+					    __be16 *frag_offp)
+{
+	return __ipv6_skip_exthdr(skb, start, nexthdrp, frag_offp, true);
+}
 
 bool ipv6_ext_hdr(u8 nexthdr);
 
diff --git a/net/ipv6/exthdrs_core.c b/net/ipv6/exthdrs_core.c
index 49e31e4ae7b7..e08f9fb7c0ec 100644
--- a/net/ipv6/exthdrs_core.c
+++ b/net/ipv6/exthdrs_core.c
@@ -69,8 +69,8 @@  EXPORT_SYMBOL(ipv6_ext_hdr);
  * --ANK (980726)
  */
 
-int ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp,
-		     __be16 *frag_offp)
+int __ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp,
+		       __be16 *frag_offp, bool no_rthdr)
 {
 	u8 nexthdr = *nexthdrp;
 
@@ -85,7 +85,8 @@  int ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp,
 		hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
 		if (!hp)
 			return -1;
-		if (nexthdr == NEXTHDR_FRAGMENT) {
+		switch (nexthdr) {
+		case NEXTHDR_FRAGMENT: {
 			__be16 _frag_off, *fp;
 			fp = skb_header_pointer(skb,
 						start+offsetof(struct frag_hdr,
@@ -99,10 +100,19 @@  int ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp,
 			if (ntohs(*frag_offp) & ~0x7)
 				break;
 			hdrlen = 8;
-		} else if (nexthdr == NEXTHDR_AUTH)
+			break;
+		}
+		case NEXTHDR_AUTH:
 			hdrlen = ipv6_authlen(hp);
-		else
+			break;
+		case NEXTHDR_ROUTING:
+			if (no_rthdr)
+				return -1;
+			fallthrough;
+		default:
 			hdrlen = ipv6_optlen(hp);
+			break;
+		}
 
 		nexthdr = hp->nexthdr;
 		start += hdrlen;
@@ -111,7 +121,7 @@  int ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp,
 	*nexthdrp = nexthdr;
 	return start;
 }
-EXPORT_SYMBOL(ipv6_skip_exthdr);
+EXPORT_SYMBOL(__ipv6_skip_exthdr);
 
 int ipv6_find_tlv(const struct sk_buff *skb, int offset, int type)
 {