Print this page
7819 IPv6 Packet and MTU bug


   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright (c) 1990 Mentat Inc.

  24  */
  25 
  26 #include <sys/types.h>
  27 #include <sys/stream.h>
  28 #include <sys/dlpi.h>
  29 #include <sys/stropts.h>
  30 #include <sys/sysmacros.h>
  31 #include <sys/strsun.h>
  32 #include <sys/strlog.h>
  33 #include <sys/strsubr.h>
  34 #define _SUN_TPI_VERSION        2
  35 #include <sys/tihdr.h>
  36 #include <sys/ddi.h>
  37 #include <sys/sunddi.h>
  38 #include <sys/cmn_err.h>
  39 #include <sys/debug.h>
  40 #include <sys/sdt.h>
  41 #include <sys/kobj.h>
  42 #include <sys/zone.h>
  43 #include <sys/neti.h>


 665  * which generated error is in the send window.
 666  *
 667  * In some cases modified this MTU in the ICMP header packet; the caller
 668  * should pass to the matching ULP after this returns.
 669  */
 670 static void
 671 icmp_inbound_too_big_v6(icmp6_t *icmp6, ip_recv_attr_t *ira)
 672 {
 673         uint32_t        mtu;
 674         dce_t           *dce;
 675         ill_t           *ill = ira->ira_ill; /* Upper ill if IPMP */
 676         ip_stack_t      *ipst = ill->ill_ipst;
 677         int             old_max_frag;
 678         in6_addr_t      final_dst;
 679         ip6_t           *ip6h;  /* Inner IP header */
 680 
 681         /* Caller has already pulled up everything. */
 682         ip6h = (ip6_t *)&icmp6[1];
 683         final_dst = ip_get_dst_v6(ip6h, NULL, NULL);
 684 


 685         /*










 686          * For link local destinations matching simply on address is not
 687          * sufficient. Same link local addresses for different ILL's is
 688          * possible.
 689          */
 690         if (IN6_IS_ADDR_LINKSCOPE(&final_dst)) {
 691                 dce = dce_lookup_and_add_v6(&final_dst,
 692                     ill->ill_phyint->phyint_ifindex, ipst);
 693         } else {
 694                 dce = dce_lookup_and_add_v6(&final_dst, 0, ipst);
 695         }
 696         if (dce == NULL) {
 697                 /* Couldn't add a unique one - ENOMEM */
 698                 if (ip_debug > 2) {
 699                         /* ip1dbg */
 700                         pr_addr_dbg("icmp_inbound_too_big_v6:"
 701                             "no dce for dst %s\n", AF_INET6,
 702                             &final_dst);
 703                 }
 704                 return;
 705         }
 706 
 707         mtu = ntohl(icmp6->icmp6_mtu);
 708 
 709         mutex_enter(&dce->dce_lock);
 710         if (dce->dce_flags & DCEF_PMTU)
 711                 old_max_frag = dce->dce_pmtu;
 712         else if (IN6_IS_ADDR_MULTICAST(&final_dst))
 713                 old_max_frag = ill->ill_mc_mtu;
 714         else
 715                 old_max_frag = ill->ill_mtu;
 716 
 717         if (mtu < IPV6_MIN_MTU) {
 718                 ip1dbg(("Received mtu less than IPv6 "
 719                     "min mtu %d: %d\n", IPV6_MIN_MTU, mtu));
 720                 mtu = IPV6_MIN_MTU;
 721                 /*
 722                  * If an mtu less than IPv6 min mtu is received,
 723                  * we must include a fragment header in
 724                  * subsequent packets.
 725                  */
 726                 dce->dce_flags |= DCEF_TOO_SMALL_PMTU;
 727         } else {
 728                 dce->dce_flags &= ~DCEF_TOO_SMALL_PMTU;
 729         }
 730         ip1dbg(("Received mtu from router: %d\n", mtu));

 731         dce->dce_pmtu = MIN(old_max_frag, mtu);
 732 
 733         /* Prepare to send the new max frag size for the ULP. */
 734         if (dce->dce_flags & DCEF_TOO_SMALL_PMTU) {
 735                 /*
 736                  * If we need a fragment header in every packet
 737                  * (above case or multirouting), make sure the
 738                  * ULP takes it into account when computing the
 739                  * payload size.
 740                  */
 741                 icmp6->icmp6_mtu = htonl(dce->dce_pmtu - sizeof (ip6_frag_t));
 742         } else {
 743                 icmp6->icmp6_mtu = htonl(dce->dce_pmtu);
 744         }
 745         /* We now have a PMTU for sure */
 746         dce->dce_flags |= DCEF_PMTU;
 747         dce->dce_last_change_time = TICK_TO_SEC(ddi_get_lbolt64());

 748         mutex_exit(&dce->dce_lock);
 749         /*
 750          * After dropping the lock the new value is visible to everyone.
 751          * Then we bump the generation number so any cached values reinspect
 752          * the dce_t.
 753          */
 754         dce_increment_generation(dce);
 755         dce_refrele(dce);
 756 }
 757 
 758 /*
 759  * Fanout received ICMPv6 error packets to the transports.
 760  * Assumes the IPv6 plus ICMPv6 headers have been pulled up but nothing else.
 761  *
 762  * The caller must have called icmp_inbound_verify_v6.
 763  */
 764 void
 765 icmp_inbound_error_fanout_v6(mblk_t *mp, icmp6_t *icmp6, ip_recv_attr_t *ira)
 766 {
 767         uint16_t        *up;    /* Pointer to ports in ULP header */




   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright (c) 1990 Mentat Inc.
  24  * Copyright 2017 OmniTI Computer Consulting, Inc. All rights reserved.
  25  */
  26 
  27 #include <sys/types.h>
  28 #include <sys/stream.h>
  29 #include <sys/dlpi.h>
  30 #include <sys/stropts.h>
  31 #include <sys/sysmacros.h>
  32 #include <sys/strsun.h>
  33 #include <sys/strlog.h>
  34 #include <sys/strsubr.h>
  35 #define _SUN_TPI_VERSION        2
  36 #include <sys/tihdr.h>
  37 #include <sys/ddi.h>
  38 #include <sys/sunddi.h>
  39 #include <sys/cmn_err.h>
  40 #include <sys/debug.h>
  41 #include <sys/sdt.h>
  42 #include <sys/kobj.h>
  43 #include <sys/zone.h>
  44 #include <sys/neti.h>


 666  * which generated error is in the send window.
 667  *
 668  * In some cases modified this MTU in the ICMP header packet; the caller
 669  * should pass to the matching ULP after this returns.
 670  */
 671 static void
 672 icmp_inbound_too_big_v6(icmp6_t *icmp6, ip_recv_attr_t *ira)
 673 {
 674         uint32_t        mtu;
 675         dce_t           *dce;
 676         ill_t           *ill = ira->ira_ill; /* Upper ill if IPMP */
 677         ip_stack_t      *ipst = ill->ill_ipst;
 678         int             old_max_frag;
 679         in6_addr_t      final_dst;
 680         ip6_t           *ip6h;  /* Inner IP header */
 681 
 682         /* Caller has already pulled up everything. */
 683         ip6h = (ip6_t *)&icmp6[1];
 684         final_dst = ip_get_dst_v6(ip6h, NULL, NULL);
 685 
 686         mtu = ntohl(icmp6->icmp6_mtu);
 687         if (mtu < IPV6_MIN_MTU) {
 688                 /*
 689                  * RFC 8021 suggests to ignore messages where mtu is
 690                  * less than the IPv6 minimum.
 691                  */
 692                 ip1dbg(("Received mtu less than IPv6 "
 693                     "min mtu %d: %d\n", IPV6_MIN_MTU, mtu));
 694                 DTRACE_PROBE1(icmp6__too__small__mtu, uint32_t, mtu);
 695                 return;
 696         }
 697 
 698         /*
 699          * For link local destinations matching simply on address is not
 700          * sufficient. Same link local addresses for different ILL's is
 701          * possible.
 702          */
 703         if (IN6_IS_ADDR_LINKSCOPE(&final_dst)) {
 704                 dce = dce_lookup_and_add_v6(&final_dst,
 705                     ill->ill_phyint->phyint_ifindex, ipst);
 706         } else {
 707                 dce = dce_lookup_and_add_v6(&final_dst, 0, ipst);
 708         }
 709         if (dce == NULL) {
 710                 /* Couldn't add a unique one - ENOMEM */
 711                 if (ip_debug > 2) {
 712                         /* ip1dbg */
 713                         pr_addr_dbg("icmp_inbound_too_big_v6:"
 714                             "no dce for dst %s\n", AF_INET6,
 715                             &final_dst);
 716                 }
 717                 return;
 718         }
 719 


 720         mutex_enter(&dce->dce_lock);
 721         if (dce->dce_flags & DCEF_PMTU)
 722                 old_max_frag = dce->dce_pmtu;
 723         else if (IN6_IS_ADDR_MULTICAST(&final_dst))
 724                 old_max_frag = ill->ill_mc_mtu;
 725         else
 726                 old_max_frag = ill->ill_mtu;
 727 













 728         ip1dbg(("Received mtu from router: %d\n", mtu));
 729         DTRACE_PROBE1(icmp6__received__mtu, uint32_t, mtu);
 730         dce->dce_pmtu = MIN(old_max_frag, mtu);











 731         icmp6->icmp6_mtu = htonl(dce->dce_pmtu);
 732 
 733         /* We now have a PMTU for sure */
 734         dce->dce_flags |= DCEF_PMTU;
 735         dce->dce_last_change_time = TICK_TO_SEC(ddi_get_lbolt64());
 736 
 737         mutex_exit(&dce->dce_lock);
 738         /*
 739          * After dropping the lock the new value is visible to everyone.
 740          * Then we bump the generation number so any cached values reinspect
 741          * the dce_t.
 742          */
 743         dce_increment_generation(dce);
 744         dce_refrele(dce);
 745 }
 746 
 747 /*
 748  * Fanout received ICMPv6 error packets to the transports.
 749  * Assumes the IPv6 plus ICMPv6 headers have been pulled up but nothing else.
 750  *
 751  * The caller must have called icmp_inbound_verify_v6.
 752  */
 753 void
 754 icmp_inbound_error_fanout_v6(mblk_t *mp, icmp6_t *icmp6, ip_recv_attr_t *ira)
 755 {
 756         uint16_t        *up;    /* Pointer to ports in ULP header */