Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/netwerk/sctp/src/netinet/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 405 kB image not shown  

Quelle  sctp_output.c   Sprache: C

 
/*-
 * SPDX-License-Identifier: BSD-3-Clause
 *
 * Copyright (c) 2001-2008, by Cisco Systems, Inc. All rights reserved.
 * Copyright (c) 2008-2012, by Randall Stewart. All rights reserved.
 * Copyright (c) 2008-2012, by Michael Tuexen. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * a) Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * b) Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the distribution.
 *
 * c) Neither the name of Cisco Systems, Inc. nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */


#include <netinet/sctp_os.h>
#if defined(__FreeBSD__) && !defined(__Userspace__)
#include <sys/proc.h>
#endif
#include <netinet/sctp_var.h>
#include <netinet/sctp_sysctl.h>
#include <netinet/sctp_header.h>
#include <netinet/sctp_pcb.h>
#include <netinet/sctputil.h>
#include <netinet/sctp_output.h>
#include <netinet/sctp_uio.h>
#include <netinet/sctputil.h>
#include <netinet/sctp_auth.h>
#include <netinet/sctp_timer.h>
#include <netinet/sctp_asconf.h>
#include <netinet/sctp_indata.h>
#include <netinet/sctp_bsd_addr.h>
#include <netinet/sctp_input.h>
#include <netinet/sctp_crc32.h>
#if defined(__FreeBSD__) && !defined(__Userspace__)
#include <netinet/sctp_kdtrace.h>
#endif
#if defined(__linux__)
#define __FAVOR_BSD    /* (on Ubuntu at least) enables UDP header field names like BSD in RFC 768 */
#endif
#if defined(INET) || defined(INET6)
#if !defined(_WIN32)
#include <netinet/udp.h>
#endif
#endif
#if !defined(__Userspace__)
#if defined(__APPLE__)
#include <netinet/in.h>
#endif
#if defined(__FreeBSD__) && !defined(__Userspace__)
#include <netinet/udp_var.h>
#include <machine/in_cksum.h>
#endif
#endif
#if defined(__Userspace__) && defined(INET6)
#include <netinet6/sctp6_var.h>
#endif
#if defined(__APPLE__) && !defined(__Userspace__)
#if !(defined(APPLE_LEOPARD) || defined(APPLE_SNOWLEOPARD))
#define SCTP_MAX_LINKHDR 16
#endif
#endif

#define SCTP_MAX_GAPS_INARRAY 4
struct sack_track {
 uint8_t right_edge; /* mergable on the right edge */
 uint8_t left_edge; /* mergable on the left edge */
 uint8_t num_entries;
 uint8_t spare;
 struct sctp_gap_ack_block gaps[SCTP_MAX_GAPS_INARRAY];
};

const struct sack_track sack_array[256] = {
 {0, 0, 0, 0,  /* 0x00 */
  {{0, 0},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 1, 0,  /* 0x01 */
  {{0, 0},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 1, 0,  /* 0x02 */
  {{1, 1},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 1, 0,  /* 0x03 */
  {{0, 1},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 1, 0,  /* 0x04 */
  {{2, 2},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x05 */
  {{0, 0},
  {2, 2},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 1, 0,  /* 0x06 */
  {{1, 2},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 1, 0,  /* 0x07 */
  {{0, 2},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 1, 0,  /* 0x08 */
  {{3, 3},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x09 */
  {{0, 0},
  {3, 3},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x0a */
  {{1, 1},
  {3, 3},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x0b */
  {{0, 1},
  {3, 3},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 1, 0,  /* 0x0c */
  {{2, 3},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x0d */
  {{0, 0},
  {2, 3},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 1, 0,  /* 0x0e */
  {{1, 3},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 1, 0,  /* 0x0f */
  {{0, 3},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 1, 0,  /* 0x10 */
  {{4, 4},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x11 */
  {{0, 0},
  {4, 4},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x12 */
  {{1, 1},
  {4, 4},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x13 */
  {{0, 1},
  {4, 4},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x14 */
  {{2, 2},
  {4, 4},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 3, 0,  /* 0x15 */
  {{0, 0},
  {2, 2},
  {4, 4},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x16 */
  {{1, 2},
  {4, 4},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x17 */
  {{0, 2},
  {4, 4},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 1, 0,  /* 0x18 */
  {{3, 4},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x19 */
  {{0, 0},
  {3, 4},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x1a */
  {{1, 1},
  {3, 4},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x1b */
  {{0, 1},
  {3, 4},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 1, 0,  /* 0x1c */
  {{2, 4},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x1d */
  {{0, 0},
  {2, 4},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 1, 0,  /* 0x1e */
  {{1, 4},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 1, 0,  /* 0x1f */
  {{0, 4},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 1, 0,  /* 0x20 */
  {{5, 5},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x21 */
  {{0, 0},
  {5, 5},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x22 */
  {{1, 1},
  {5, 5},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x23 */
  {{0, 1},
  {5, 5},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x24 */
  {{2, 2},
  {5, 5},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 3, 0,  /* 0x25 */
  {{0, 0},
  {2, 2},
  {5, 5},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x26 */
  {{1, 2},
  {5, 5},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x27 */
  {{0, 2},
  {5, 5},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x28 */
  {{3, 3},
  {5, 5},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 3, 0,  /* 0x29 */
  {{0, 0},
  {3, 3},
  {5, 5},
  {0, 0}
  }
 },
 {0, 0, 3, 0,  /* 0x2a */
  {{1, 1},
  {3, 3},
  {5, 5},
  {0, 0}
  }
 },
 {1, 0, 3, 0,  /* 0x2b */
  {{0, 1},
  {3, 3},
  {5, 5},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x2c */
  {{2, 3},
  {5, 5},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 3, 0,  /* 0x2d */
  {{0, 0},
  {2, 3},
  {5, 5},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x2e */
  {{1, 3},
  {5, 5},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x2f */
  {{0, 3},
  {5, 5},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 1, 0,  /* 0x30 */
  {{4, 5},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x31 */
  {{0, 0},
  {4, 5},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x32 */
  {{1, 1},
  {4, 5},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x33 */
  {{0, 1},
  {4, 5},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x34 */
  {{2, 2},
  {4, 5},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 3, 0,  /* 0x35 */
  {{0, 0},
  {2, 2},
  {4, 5},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x36 */
  {{1, 2},
  {4, 5},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x37 */
  {{0, 2},
  {4, 5},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 1, 0,  /* 0x38 */
  {{3, 5},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x39 */
  {{0, 0},
  {3, 5},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x3a */
  {{1, 1},
  {3, 5},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x3b */
  {{0, 1},
  {3, 5},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 1, 0,  /* 0x3c */
  {{2, 5},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x3d */
  {{0, 0},
  {2, 5},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 1, 0,  /* 0x3e */
  {{1, 5},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 1, 0,  /* 0x3f */
  {{0, 5},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 1, 0,  /* 0x40 */
  {{6, 6},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x41 */
  {{0, 0},
  {6, 6},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x42 */
  {{1, 1},
  {6, 6},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x43 */
  {{0, 1},
  {6, 6},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x44 */
  {{2, 2},
  {6, 6},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 3, 0,  /* 0x45 */
  {{0, 0},
  {2, 2},
  {6, 6},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x46 */
  {{1, 2},
  {6, 6},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x47 */
  {{0, 2},
  {6, 6},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x48 */
  {{3, 3},
  {6, 6},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 3, 0,  /* 0x49 */
  {{0, 0},
  {3, 3},
  {6, 6},
  {0, 0}
  }
 },
 {0, 0, 3, 0,  /* 0x4a */
  {{1, 1},
  {3, 3},
  {6, 6},
  {0, 0}
  }
 },
 {1, 0, 3, 0,  /* 0x4b */
  {{0, 1},
  {3, 3},
  {6, 6},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x4c */
  {{2, 3},
  {6, 6},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 3, 0,  /* 0x4d */
  {{0, 0},
  {2, 3},
  {6, 6},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x4e */
  {{1, 3},
  {6, 6},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x4f */
  {{0, 3},
  {6, 6},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x50 */
  {{4, 4},
  {6, 6},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 3, 0,  /* 0x51 */
  {{0, 0},
  {4, 4},
  {6, 6},
  {0, 0}
  }
 },
 {0, 0, 3, 0,  /* 0x52 */
  {{1, 1},
  {4, 4},
  {6, 6},
  {0, 0}
  }
 },
 {1, 0, 3, 0,  /* 0x53 */
  {{0, 1},
  {4, 4},
  {6, 6},
  {0, 0}
  }
 },
 {0, 0, 3, 0,  /* 0x54 */
  {{2, 2},
  {4, 4},
  {6, 6},
  {0, 0}
  }
 },
 {1, 0, 4, 0,  /* 0x55 */
  {{0, 0},
  {2, 2},
  {4, 4},
  {6, 6}
  }
 },
 {0, 0, 3, 0,  /* 0x56 */
  {{1, 2},
  {4, 4},
  {6, 6},
  {0, 0}
  }
 },
 {1, 0, 3, 0,  /* 0x57 */
  {{0, 2},
  {4, 4},
  {6, 6},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x58 */
  {{3, 4},
  {6, 6},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 3, 0,  /* 0x59 */
  {{0, 0},
  {3, 4},
  {6, 6},
  {0, 0}
  }
 },
 {0, 0, 3, 0,  /* 0x5a */
  {{1, 1},
  {3, 4},
  {6, 6},
  {0, 0}
  }
 },
 {1, 0, 3, 0,  /* 0x5b */
  {{0, 1},
  {3, 4},
  {6, 6},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x5c */
  {{2, 4},
  {6, 6},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 3, 0,  /* 0x5d */
  {{0, 0},
  {2, 4},
  {6, 6},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x5e */
  {{1, 4},
  {6, 6},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x5f */
  {{0, 4},
  {6, 6},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 1, 0,  /* 0x60 */
  {{5, 6},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x61 */
  {{0, 0},
  {5, 6},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x62 */
  {{1, 1},
  {5, 6},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x63 */
  {{0, 1},
  {5, 6},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x64 */
  {{2, 2},
  {5, 6},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 3, 0,  /* 0x65 */
  {{0, 0},
  {2, 2},
  {5, 6},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x66 */
  {{1, 2},
  {5, 6},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x67 */
  {{0, 2},
  {5, 6},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x68 */
  {{3, 3},
  {5, 6},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 3, 0,  /* 0x69 */
  {{0, 0},
  {3, 3},
  {5, 6},
  {0, 0}
  }
 },
 {0, 0, 3, 0,  /* 0x6a */
  {{1, 1},
  {3, 3},
  {5, 6},
  {0, 0}
  }
 },
 {1, 0, 3, 0,  /* 0x6b */
  {{0, 1},
  {3, 3},
  {5, 6},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x6c */
  {{2, 3},
  {5, 6},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 3, 0,  /* 0x6d */
  {{0, 0},
  {2, 3},
  {5, 6},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x6e */
  {{1, 3},
  {5, 6},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x6f */
  {{0, 3},
  {5, 6},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 1, 0,  /* 0x70 */
  {{4, 6},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x71 */
  {{0, 0},
  {4, 6},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x72 */
  {{1, 1},
  {4, 6},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x73 */
  {{0, 1},
  {4, 6},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x74 */
  {{2, 2},
  {4, 6},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 3, 0,  /* 0x75 */
  {{0, 0},
  {2, 2},
  {4, 6},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x76 */
  {{1, 2},
  {4, 6},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x77 */
  {{0, 2},
  {4, 6},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 1, 0,  /* 0x78 */
  {{3, 6},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x79 */
  {{0, 0},
  {3, 6},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 2, 0,  /* 0x7a */
  {{1, 1},
  {3, 6},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x7b */
  {{0, 1},
  {3, 6},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 1, 0,  /* 0x7c */
  {{2, 6},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 2, 0,  /* 0x7d */
  {{0, 0},
  {2, 6},
  {0, 0},
  {0, 0}
  }
 },
 {0, 0, 1, 0,  /* 0x7e */
  {{1, 6},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 0, 1, 0,  /* 0x7f */
  {{0, 6},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {0, 1, 1, 0,  /* 0x80 */
  {{7, 7},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 2, 0,  /* 0x81 */
  {{0, 0},
  {7, 7},
  {0, 0},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0x82 */
  {{1, 1},
  {7, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 2, 0,  /* 0x83 */
  {{0, 1},
  {7, 7},
  {0, 0},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0x84 */
  {{2, 2},
  {7, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0x85 */
  {{0, 0},
  {2, 2},
  {7, 7},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0x86 */
  {{1, 2},
  {7, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 2, 0,  /* 0x87 */
  {{0, 2},
  {7, 7},
  {0, 0},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0x88 */
  {{3, 3},
  {7, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0x89 */
  {{0, 0},
  {3, 3},
  {7, 7},
  {0, 0}
  }
 },
 {0, 1, 3, 0,  /* 0x8a */
  {{1, 1},
  {3, 3},
  {7, 7},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0x8b */
  {{0, 1},
  {3, 3},
  {7, 7},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0x8c */
  {{2, 3},
  {7, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0x8d */
  {{0, 0},
  {2, 3},
  {7, 7},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0x8e */
  {{1, 3},
  {7, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 2, 0,  /* 0x8f */
  {{0, 3},
  {7, 7},
  {0, 0},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0x90 */
  {{4, 4},
  {7, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0x91 */
  {{0, 0},
  {4, 4},
  {7, 7},
  {0, 0}
  }
 },
 {0, 1, 3, 0,  /* 0x92 */
  {{1, 1},
  {4, 4},
  {7, 7},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0x93 */
  {{0, 1},
  {4, 4},
  {7, 7},
  {0, 0}
  }
 },
 {0, 1, 3, 0,  /* 0x94 */
  {{2, 2},
  {4, 4},
  {7, 7},
  {0, 0}
  }
 },
 {1, 1, 4, 0,  /* 0x95 */
  {{0, 0},
  {2, 2},
  {4, 4},
  {7, 7}
  }
 },
 {0, 1, 3, 0,  /* 0x96 */
  {{1, 2},
  {4, 4},
  {7, 7},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0x97 */
  {{0, 2},
  {4, 4},
  {7, 7},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0x98 */
  {{3, 4},
  {7, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0x99 */
  {{0, 0},
  {3, 4},
  {7, 7},
  {0, 0}
  }
 },
 {0, 1, 3, 0,  /* 0x9a */
  {{1, 1},
  {3, 4},
  {7, 7},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0x9b */
  {{0, 1},
  {3, 4},
  {7, 7},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0x9c */
  {{2, 4},
  {7, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0x9d */
  {{0, 0},
  {2, 4},
  {7, 7},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0x9e */
  {{1, 4},
  {7, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 2, 0,  /* 0x9f */
  {{0, 4},
  {7, 7},
  {0, 0},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0xa0 */
  {{5, 5},
  {7, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0xa1 */
  {{0, 0},
  {5, 5},
  {7, 7},
  {0, 0}
  }
 },
 {0, 1, 3, 0,  /* 0xa2 */
  {{1, 1},
  {5, 5},
  {7, 7},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0xa3 */
  {{0, 1},
  {5, 5},
  {7, 7},
  {0, 0}
  }
 },
 {0, 1, 3, 0,  /* 0xa4 */
  {{2, 2},
  {5, 5},
  {7, 7},
  {0, 0}
  }
 },
 {1, 1, 4, 0,  /* 0xa5 */
  {{0, 0},
  {2, 2},
  {5, 5},
  {7, 7}
  }
 },
 {0, 1, 3, 0,  /* 0xa6 */
  {{1, 2},
  {5, 5},
  {7, 7},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0xa7 */
  {{0, 2},
  {5, 5},
  {7, 7},
  {0, 0}
  }
 },
 {0, 1, 3, 0,  /* 0xa8 */
  {{3, 3},
  {5, 5},
  {7, 7},
  {0, 0}
  }
 },
 {1, 1, 4, 0,  /* 0xa9 */
  {{0, 0},
  {3, 3},
  {5, 5},
  {7, 7}
  }
 },
 {0, 1, 4, 0,  /* 0xaa */
  {{1, 1},
  {3, 3},
  {5, 5},
  {7, 7}
  }
 },
 {1, 1, 4, 0,  /* 0xab */
  {{0, 1},
  {3, 3},
  {5, 5},
  {7, 7}
  }
 },
 {0, 1, 3, 0,  /* 0xac */
  {{2, 3},
  {5, 5},
  {7, 7},
  {0, 0}
  }
 },
 {1, 1, 4, 0,  /* 0xad */
  {{0, 0},
  {2, 3},
  {5, 5},
  {7, 7}
  }
 },
 {0, 1, 3, 0,  /* 0xae */
  {{1, 3},
  {5, 5},
  {7, 7},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0xaf */
  {{0, 3},
  {5, 5},
  {7, 7},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0xb0 */
  {{4, 5},
  {7, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0xb1 */
  {{0, 0},
  {4, 5},
  {7, 7},
  {0, 0}
  }
 },
 {0, 1, 3, 0,  /* 0xb2 */
  {{1, 1},
  {4, 5},
  {7, 7},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0xb3 */
  {{0, 1},
  {4, 5},
  {7, 7},
  {0, 0}
  }
 },
 {0, 1, 3, 0,  /* 0xb4 */
  {{2, 2},
  {4, 5},
  {7, 7},
  {0, 0}
  }
 },
 {1, 1, 4, 0,  /* 0xb5 */
  {{0, 0},
  {2, 2},
  {4, 5},
  {7, 7}
  }
 },
 {0, 1, 3, 0,  /* 0xb6 */
  {{1, 2},
  {4, 5},
  {7, 7},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0xb7 */
  {{0, 2},
  {4, 5},
  {7, 7},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0xb8 */
  {{3, 5},
  {7, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0xb9 */
  {{0, 0},
  {3, 5},
  {7, 7},
  {0, 0}
  }
 },
 {0, 1, 3, 0,  /* 0xba */
  {{1, 1},
  {3, 5},
  {7, 7},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0xbb */
  {{0, 1},
  {3, 5},
  {7, 7},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0xbc */
  {{2, 5},
  {7, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0xbd */
  {{0, 0},
  {2, 5},
  {7, 7},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0xbe */
  {{1, 5},
  {7, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 2, 0,  /* 0xbf */
  {{0, 5},
  {7, 7},
  {0, 0},
  {0, 0}
  }
 },
 {0, 1, 1, 0,  /* 0xc0 */
  {{6, 7},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 2, 0,  /* 0xc1 */
  {{0, 0},
  {6, 7},
  {0, 0},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0xc2 */
  {{1, 1},
  {6, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 2, 0,  /* 0xc3 */
  {{0, 1},
  {6, 7},
  {0, 0},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0xc4 */
  {{2, 2},
  {6, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0xc5 */
  {{0, 0},
  {2, 2},
  {6, 7},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0xc6 */
  {{1, 2},
  {6, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 2, 0,  /* 0xc7 */
  {{0, 2},
  {6, 7},
  {0, 0},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0xc8 */
  {{3, 3},
  {6, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0xc9 */
  {{0, 0},
  {3, 3},
  {6, 7},
  {0, 0}
  }
 },
 {0, 1, 3, 0,  /* 0xca */
  {{1, 1},
  {3, 3},
  {6, 7},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0xcb */
  {{0, 1},
  {3, 3},
  {6, 7},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0xcc */
  {{2, 3},
  {6, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0xcd */
  {{0, 0},
  {2, 3},
  {6, 7},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0xce */
  {{1, 3},
  {6, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 2, 0,  /* 0xcf */
  {{0, 3},
  {6, 7},
  {0, 0},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0xd0 */
  {{4, 4},
  {6, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0xd1 */
  {{0, 0},
  {4, 4},
  {6, 7},
  {0, 0}
  }
 },
 {0, 1, 3, 0,  /* 0xd2 */
  {{1, 1},
  {4, 4},
  {6, 7},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0xd3 */
  {{0, 1},
  {4, 4},
  {6, 7},
  {0, 0}
  }
 },
 {0, 1, 3, 0,  /* 0xd4 */
  {{2, 2},
  {4, 4},
  {6, 7},
  {0, 0}
  }
 },
 {1, 1, 4, 0,  /* 0xd5 */
  {{0, 0},
  {2, 2},
  {4, 4},
  {6, 7}
  }
 },
 {0, 1, 3, 0,  /* 0xd6 */
  {{1, 2},
  {4, 4},
  {6, 7},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0xd7 */
  {{0, 2},
  {4, 4},
  {6, 7},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0xd8 */
  {{3, 4},
  {6, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0xd9 */
  {{0, 0},
  {3, 4},
  {6, 7},
  {0, 0}
  }
 },
 {0, 1, 3, 0,  /* 0xda */
  {{1, 1},
  {3, 4},
  {6, 7},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0xdb */
  {{0, 1},
  {3, 4},
  {6, 7},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0xdc */
  {{2, 4},
  {6, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0xdd */
  {{0, 0},
  {2, 4},
  {6, 7},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0xde */
  {{1, 4},
  {6, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 2, 0,  /* 0xdf */
  {{0, 4},
  {6, 7},
  {0, 0},
  {0, 0}
  }
 },
 {0, 1, 1, 0,  /* 0xe0 */
  {{5, 7},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 2, 0,  /* 0xe1 */
  {{0, 0},
  {5, 7},
  {0, 0},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0xe2 */
  {{1, 1},
  {5, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 2, 0,  /* 0xe3 */
  {{0, 1},
  {5, 7},
  {0, 0},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0xe4 */
  {{2, 2},
  {5, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0xe5 */
  {{0, 0},
  {2, 2},
  {5, 7},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0xe6 */
  {{1, 2},
  {5, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 2, 0,  /* 0xe7 */
  {{0, 2},
  {5, 7},
  {0, 0},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0xe8 */
  {{3, 3},
  {5, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0xe9 */
  {{0, 0},
  {3, 3},
  {5, 7},
  {0, 0}
  }
 },
 {0, 1, 3, 0,  /* 0xea */
  {{1, 1},
  {3, 3},
  {5, 7},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0xeb */
  {{0, 1},
  {3, 3},
  {5, 7},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0xec */
  {{2, 3},
  {5, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0xed */
  {{0, 0},
  {2, 3},
  {5, 7},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0xee */
  {{1, 3},
  {5, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 2, 0,  /* 0xef */
  {{0, 3},
  {5, 7},
  {0, 0},
  {0, 0}
  }
 },
 {0, 1, 1, 0,  /* 0xf0 */
  {{4, 7},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 2, 0,  /* 0xf1 */
  {{0, 0},
  {4, 7},
  {0, 0},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0xf2 */
  {{1, 1},
  {4, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 2, 0,  /* 0xf3 */
  {{0, 1},
  {4, 7},
  {0, 0},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0xf4 */
  {{2, 2},
  {4, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 3, 0,  /* 0xf5 */
  {{0, 0},
  {2, 2},
  {4, 7},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0xf6 */
  {{1, 2},
  {4, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 2, 0,  /* 0xf7 */
  {{0, 2},
  {4, 7},
  {0, 0},
  {0, 0}
  }
 },
 {0, 1, 1, 0,  /* 0xf8 */
  {{3, 7},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 2, 0,  /* 0xf9 */
  {{0, 0},
  {3, 7},
  {0, 0},
  {0, 0}
  }
 },
 {0, 1, 2, 0,  /* 0xfa */
  {{1, 1},
  {3, 7},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 2, 0,  /* 0xfb */
  {{0, 1},
  {3, 7},
  {0, 0},
  {0, 0}
  }
 },
 {0, 1, 1, 0,  /* 0xfc */
  {{2, 7},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 2, 0,  /* 0xfd */
  {{0, 0},
  {2, 7},
  {0, 0},
  {0, 0}
  }
 },
 {0, 1, 1, 0,  /* 0xfe */
  {{1, 7},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 },
 {1, 1, 1, 0,  /* 0xff */
  {{0, 7},
  {0, 0},
  {0, 0},
  {0, 0}
  }
 }
};

int
sctp_is_address_in_scope(struct sctp_ifa *ifa,
                         struct sctp_scoping *scope,
                         int do_update)
{
 if ((scope->loopback_scope == 0) &&
     (ifa->ifn_p) && SCTP_IFN_IS_IFT_LOOP(ifa->ifn_p)) {
  /*
 * skip loopback if not in scope *
 */

  return (0);
 }
 switch (ifa->address.sa.sa_family) {
#ifdef INET
 case AF_INET:
  if (scope->ipv4_addr_legal) {
   struct sockaddr_in *sin;

   sin = &ifa->address.sin;
   if (sin->sin_addr.s_addr == 0) {
    /* not in scope , unspecified */
    return (0);
   }
   if ((scope->ipv4_local_scope == 0) &&
       (IN4_ISPRIVATE_ADDRESS(&sin->sin_addr))) {
    /* private address not in scope */
    return (0);
   }
  } else {
   return (0);
  }
  break;
#endif
#ifdef INET6
 case AF_INET6:
  if (scope->ipv6_addr_legal) {
   struct sockaddr_in6 *sin6;

   /* Must update the flags,  bummer, which
 * means any IFA locks must now be applied HERE <->
 */

   if (do_update) {
    sctp_gather_internal_ifa_flags(ifa);
   }
   if (ifa->localifa_flags & SCTP_ADDR_IFA_UNUSEABLE) {
    return (0);
   }
   /* ok to use deprecated addresses? */
   sin6 = &ifa->address.sin6;
   if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
    /* skip unspecified addresses */
    return (0);
   }
   if (  /* (local_scope == 0) && */
       (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))) {
    return (0);
   }
   if ((scope->site_scope == 0) &&
       (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr))) {
    return (0);
   }
  } else {
   return (0);
  }
  break;
#endif
#if defined(__Userspace__)
 case AF_CONN:
  if (!scope->conn_addr_legal) {
   return (0);
  }
  break;
#endif
 default:
  return (0);
 }
 return (1);
}

static struct mbuf *
sctp_add_addr_to_mbuf(struct mbuf *m, struct sctp_ifa *ifa, uint16_t *len)
{
#if defined(INET) || defined(INET6)
 struct sctp_paramhdr *paramh;
 struct mbuf *mret;
 uint16_t plen;
#endif

 switch (ifa->address.sa.sa_family) {
#ifdef INET
 case AF_INET:
  plen = (uint16_t)sizeof(struct sctp_ipv4addr_param);
  break;
#endif
#ifdef INET6
 case AF_INET6:
  plen = (uint16_t)sizeof(struct sctp_ipv6addr_param);
  break;
#endif
 default:
  return (m);
 }
#if defined(INET) || defined(INET6)
 if (M_TRAILINGSPACE(m) >= plen) {
  /* easy side we just drop it on the end */
  paramh = (struct sctp_paramhdr *)(SCTP_BUF_AT(m, SCTP_BUF_LEN(m)));
  mret = m;
 } else {
  /* Need more space */
  mret = m;
  while (SCTP_BUF_NEXT(mret) != NULL) {
   mret = SCTP_BUF_NEXT(mret);
  }
  SCTP_BUF_NEXT(mret) = sctp_get_mbuf_for_msg(plen, 0, M_NOWAIT, 1, MT_DATA);
  if (SCTP_BUF_NEXT(mret) == NULL) {
   /* We are hosed, can't add more addresses */
   return (m);
  }
  mret = SCTP_BUF_NEXT(mret);
  paramh = mtod(mret, struct sctp_paramhdr *);
 }
 /* now add the parameter */
 switch (ifa->address.sa.sa_family) {
#ifdef INET
 case AF_INET:
 {
  struct sctp_ipv4addr_param *ipv4p;
  struct sockaddr_in *sin;

  sin = &ifa->address.sin;
  ipv4p = (struct sctp_ipv4addr_param *)paramh;
  paramh->param_type = htons(SCTP_IPV4_ADDRESS);
  paramh->param_length = htons(plen);
  ipv4p->addr = sin->sin_addr.s_addr;
  SCTP_BUF_LEN(mret) += plen;
  break;
 }
#endif
#ifdef INET6
 case AF_INET6:
 {
  struct sctp_ipv6addr_param *ipv6p;
  struct sockaddr_in6 *sin6;

  sin6 = &ifa->address.sin6;
  ipv6p = (struct sctp_ipv6addr_param *)paramh;
  paramh->param_type = htons(SCTP_IPV6_ADDRESS);
  paramh->param_length = htons(plen);
  memcpy(ipv6p->addr, &sin6->sin6_addr,
      sizeof(ipv6p->addr));
#if defined(SCTP_EMBEDDED_V6_SCOPE)
  /* clear embedded scope in the address */
  in6_clearscope((struct in6_addr *)ipv6p->addr);
#endif
  SCTP_BUF_LEN(mret) += plen;
  break;
 }
#endif
 default:
  return (m);
 }
 if (len != NULL) {
  *len += plen;
 }
 return (mret);
#endif
}

struct mbuf *
sctp_add_addresses_to_i_ia(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
                           struct sctp_scoping *scope,
      struct mbuf *m_at, int cnt_inits_to,
      uint16_t *padding_len, uint16_t *chunk_len)
{
 struct sctp_vrf *vrf = NULL;
 int cnt, limit_out = 0, total_count;
 uint32_t vrf_id;

 vrf_id = inp->def_vrf_id;
 SCTP_IPI_ADDR_RLOCK();
 vrf = sctp_find_vrf(vrf_id);
 if (vrf == NULL) {
  SCTP_IPI_ADDR_RUNLOCK();
  return (m_at);
 }
 if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
  struct sctp_ifa *sctp_ifap;
  struct sctp_ifn *sctp_ifnp;

  cnt = cnt_inits_to;
  if (vrf->total_ifa_count > SCTP_COUNT_LIMIT) {
   limit_out = 1;
   cnt = SCTP_ADDRESS_LIMIT;
   goto skip_count;
  }
  LIST_FOREACH(sctp_ifnp, &vrf->ifnlist, next_ifn) {
   if ((scope->loopback_scope == 0) &&
       SCTP_IFN_IS_IFT_LOOP(sctp_ifnp)) {
    /*
 * Skip loopback devices if loopback_scope
 * not set
 */

    continue;
   }
   LIST_FOREACH(sctp_ifap, &sctp_ifnp->ifalist, next_ifa) {
#if defined(__FreeBSD__) && !defined(__Userspace__)
#ifdef INET
    if ((sctp_ifap->address.sa.sa_family == AF_INET) &&
        (prison_check_ip4(inp->ip_inp.inp.inp_cred,
                          &sctp_ifap->address.sin.sin_addr) != 0)) {
     continue;
    }
#endif
#ifdef INET6
    if ((sctp_ifap->address.sa.sa_family == AF_INET6) &&
        (prison_check_ip6(inp->ip_inp.inp.inp_cred,
                          &sctp_ifap->address.sin6.sin6_addr) != 0)) {
     continue;
    }
#endif
#endif
    if (sctp_is_addr_restricted(stcb, sctp_ifap)) {
     continue;
    }
#if defined(__Userspace__)
    if (sctp_ifap->address.sa.sa_family == AF_CONN) {
     continue;
    }
#endif
    if (sctp_is_address_in_scope(sctp_ifap, scope, 1) == 0) {
     continue;
    }
    cnt++;
    if (cnt > SCTP_ADDRESS_LIMIT) {
     break;
    }
   }
   if (cnt > SCTP_ADDRESS_LIMIT) {
    break;
   }
  }
 skip_count:
  if (cnt > 1) {
   total_count = 0;
   LIST_FOREACH(sctp_ifnp, &vrf->ifnlist, next_ifn) {
    cnt = 0;
    if ((scope->loopback_scope == 0) &&
        SCTP_IFN_IS_IFT_LOOP(sctp_ifnp)) {
     /*
 * Skip loopback devices if
 * loopback_scope not set
 */

     continue;
    }
    LIST_FOREACH(sctp_ifap, &sctp_ifnp->ifalist, next_ifa) {
#if defined(__FreeBSD__) && !defined(__Userspace__)
#ifdef INET
     if ((sctp_ifap->address.sa.sa_family == AF_INET) &&
         (prison_check_ip4(inp->ip_inp.inp.inp_cred,
                           &sctp_ifap->address.sin.sin_addr) != 0)) {
      continue;
     }
#endif
#ifdef INET6
     if ((sctp_ifap->address.sa.sa_family == AF_INET6) &&
         (prison_check_ip6(inp->ip_inp.inp.inp_cred,
                           &sctp_ifap->address.sin6.sin6_addr) != 0)) {
      continue;
     }
#endif
#endif
     if (sctp_is_addr_restricted(stcb, sctp_ifap)) {
      continue;
     }
#if defined(__Userspace__)
     if (sctp_ifap->address.sa.sa_family == AF_CONN) {
      continue;
     }
#endif
     if (sctp_is_address_in_scope(sctp_ifap,
             scope, 0) == 0) {
      continue;
     }
     if ((chunk_len != NULL) &&
         (padding_len != NULL) &&
         (*padding_len > 0)) {
      memset(mtod(m_at, caddr_t) + *chunk_len, 0, *padding_len);
      SCTP_BUF_LEN(m_at) += *padding_len;
      *chunk_len += *padding_len;
      *padding_len = 0;
     }
     m_at = sctp_add_addr_to_mbuf(m_at, sctp_ifap, chunk_len);
     if (limit_out) {
      cnt++;
      total_count++;
      if (cnt >= 2) {
       /* two from each address */
       break;
      }
      if (total_count > SCTP_ADDRESS_LIMIT) {
       /* No more addresses */
       break;
      }
     }
    }
   }
  }
 } else {
  struct sctp_laddr *laddr;

  cnt = cnt_inits_to;
  /* First, how many ? */
  LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
   if (laddr->ifa == NULL) {
    continue;
   }
   if (laddr->ifa->localifa_flags & SCTP_BEING_DELETED)
    /* Address being deleted by the system, dont
 * list.
 */

    continue;
   if (laddr->action == SCTP_DEL_IP_ADDRESS) {
    /* Address being deleted on this ep
 * don't list.
 */

    continue;
   }
#if defined(__Userspace__)
   if (laddr->ifa->address.sa.sa_family == AF_CONN) {
    continue;
   }
#endif
   if (sctp_is_address_in_scope(laddr->ifa,
           scope, 1) == 0) {
    continue;
   }
   cnt++;
  }
  /*
 * To get through a NAT we only list addresses if we have
 * more than one. That way if you just bind a single address
 * we let the source of the init dictate our address.
 */

  if (cnt > 1) {
   cnt = cnt_inits_to;
   LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
    if (laddr->ifa == NULL) {
     continue;
    }
    if (laddr->ifa->localifa_flags & SCTP_BEING_DELETED) {
     continue;
    }
#if defined(__Userspace__)
    if (laddr->ifa->address.sa.sa_family == AF_CONN) {
     continue;
    }
#endif
    if (sctp_is_address_in_scope(laddr->ifa,
            scope, 0) == 0) {
     continue;
    }
    if ((chunk_len != NULL) &&
        (padding_len != NULL) &&
        (*padding_len > 0)) {
     memset(mtod(m_at, caddr_t) + *chunk_len, 0, *padding_len);
     SCTP_BUF_LEN(m_at) += *padding_len;
     *chunk_len += *padding_len;
     *padding_len = 0;
    }
    m_at = sctp_add_addr_to_mbuf(m_at, laddr->ifa, chunk_len);
    cnt++;
    if (cnt >= SCTP_ADDRESS_LIMIT) {
     break;
    }
   }
  }
 }
 SCTP_IPI_ADDR_RUNLOCK();
 return (m_at);
}

static struct sctp_ifa *
sctp_is_ifa_addr_preferred(struct sctp_ifa *ifa,
      uint8_t dest_is_loop,
      uint8_t dest_is_priv,
      sa_family_t fam)
{
 uint8_t dest_is_global = 0;
 /* dest_is_priv is true if destination is a private address */
 /* dest_is_loop is true if destination is a loopback addresses */

 /**
 * Here we determine if its a preferred address. A preferred address
 * means it is the same scope or higher scope then the destination.
 * L = loopback, P = private, G = global
 * -----------------------------------------
 *    src    |  dest | result
 *  ----------------------------------------
 *     L     |    L  |    yes
 *  -----------------------------------------
 *     P     |    L  |    yes-v4 no-v6
 *  -----------------------------------------
 *     G     |    L  |    yes-v4 no-v6
 *  -----------------------------------------
 *     L     |    P  |    no
 *  -----------------------------------------
 *     P     |    P  |    yes
 *  -----------------------------------------
 *     G     |    P  |    no
 *   -----------------------------------------
 *     L     |    G  |    no
 *   -----------------------------------------
 *     P     |    G  |    no
 *    -----------------------------------------
 *     G     |    G  |    yes
 *    -----------------------------------------
 */


 if (ifa->address.sa.sa_family != fam) {
  /* forget mis-matched family */
  return (NULL);
 }
 if ((dest_is_priv == 0) && (dest_is_loop == 0)) {
  dest_is_global = 1;
 }
 SCTPDBG(SCTP_DEBUG_OUTPUT2, "Is destination preferred:");
 SCTPDBG_ADDR(SCTP_DEBUG_OUTPUT2, &ifa->address.sa);
 /* Ok the address may be ok */
#ifdef INET6
 if (fam == AF_INET6) {
  /* ok to use deprecated addresses? no lets not! */
  if (ifa->localifa_flags & SCTP_ADDR_IFA_UNUSEABLE) {
   SCTPDBG(SCTP_DEBUG_OUTPUT3, "NO:1\n");
   return (NULL);
  }
  if (ifa->src_is_priv && !ifa->src_is_loop) {
   if (dest_is_loop) {
    SCTPDBG(SCTP_DEBUG_OUTPUT3, "NO:2\n");
    return (NULL);
   }
  }
  if (ifa->src_is_glob) {
   if (dest_is_loop) {
    SCTPDBG(SCTP_DEBUG_OUTPUT3, "NO:3\n");
    return (NULL);
   }
  }
 }
#endif
 /* Now that we know what is what, implement or table
 * this could in theory be done slicker (it used to be), but this
 * is straightforward and easier to validate :-)
 */

 SCTPDBG(SCTP_DEBUG_OUTPUT3, "src_loop:%d src_priv:%d src_glob:%d\n",
  ifa->src_is_loop, ifa->src_is_priv, ifa->src_is_glob);
 SCTPDBG(SCTP_DEBUG_OUTPUT3, "dest_loop:%d dest_priv:%d dest_glob:%d\n",
  dest_is_loop, dest_is_priv, dest_is_global);

 if ((ifa->src_is_loop) && (dest_is_priv)) {
  SCTPDBG(SCTP_DEBUG_OUTPUT3, "NO:4\n");
  return (NULL);
 }
 if ((ifa->src_is_glob) && (dest_is_priv)) {
  SCTPDBG(SCTP_DEBUG_OUTPUT3, "NO:5\n");
  return (NULL);
 }
 if ((ifa->src_is_loop) && (dest_is_global)) {
  SCTPDBG(SCTP_DEBUG_OUTPUT3, "NO:6\n");
  return (NULL);
 }
 if ((ifa->src_is_priv) && (dest_is_global)) {
  SCTPDBG(SCTP_DEBUG_OUTPUT3, "NO:7\n");
  return (NULL);
 }
 SCTPDBG(SCTP_DEBUG_OUTPUT3, "YES\n");
 /* its a preferred address */
 return (ifa);
}

static struct sctp_ifa *
sctp_is_ifa_addr_acceptable(struct sctp_ifa *ifa,
       uint8_t dest_is_loop,
       uint8_t dest_is_priv,
       sa_family_t fam)
{
 uint8_t dest_is_global = 0;

 /**
 * Here we determine if its a acceptable address. A acceptable
 * address means it is the same scope or higher scope but we can
 * allow for NAT which means its ok to have a global dest and a
 * private src.
 *
 * L = loopback, P = private, G = global
 * -----------------------------------------
 *  src    |  dest | result
 * -----------------------------------------
 *   L     |   L   |    yes
 *  -----------------------------------------
 *   P     |   L   |    yes-v4 no-v6
 *  -----------------------------------------
 *   G     |   L   |    yes
 * -----------------------------------------
 *   L     |   P   |    no
 * -----------------------------------------
 *   P     |   P   |    yes
 * -----------------------------------------
 *   G     |   P   |    yes - May not work
 * -----------------------------------------
 *   L     |   G   |    no
 * -----------------------------------------
 *   P     |   G   |    yes - May not work
 * -----------------------------------------
 *   G     |   G   |    yes
 * -----------------------------------------
 */


 if (ifa->address.sa.sa_family != fam) {
  /* forget non matching family */
  SCTPDBG(SCTP_DEBUG_OUTPUT3, "ifa_fam:%d fam:%d\n",
   ifa->address.sa.sa_family, fam);
  return (NULL);
 }
 /* Ok the address may be ok */
 SCTPDBG_ADDR(SCTP_DEBUG_OUTPUT3, &ifa->address.sa);
 SCTPDBG(SCTP_DEBUG_OUTPUT3, "dst_is_loop:%d dest_is_priv:%d\n",
  dest_is_loop, dest_is_priv);
 if ((dest_is_loop == 0) && (dest_is_priv == 0)) {
  dest_is_global = 1;
 }
#ifdef INET6
 if (fam == AF_INET6) {
  /* ok to use deprecated addresses? */
  if (ifa->localifa_flags & SCTP_ADDR_IFA_UNUSEABLE) {
   return (NULL);
  }
  if (ifa->src_is_priv) {
   /* Special case, linklocal to loop */
   if (dest_is_loop)
    return (NULL);
  }
 }
#endif
 /*
 * Now that we know what is what, implement our table.
 * This could in theory be done slicker (it used to be), but this
 * is straightforward and easier to validate :-)
 */

 SCTPDBG(SCTP_DEBUG_OUTPUT3, "ifa->src_is_loop:%d dest_is_priv:%d\n",
  ifa->src_is_loop,
  dest_is_priv);
 if ((ifa->src_is_loop == 1) && (dest_is_priv)) {
  return (NULL);
 }
 SCTPDBG(SCTP_DEBUG_OUTPUT3, "ifa->src_is_loop:%d dest_is_glob:%d\n",
  ifa->src_is_loop,
  dest_is_global);
 if ((ifa->src_is_loop == 1) && (dest_is_global)) {
  return (NULL);
 }
 SCTPDBG(SCTP_DEBUG_OUTPUT3, "address is acceptable\n");
 /* its an acceptable address */
 return (ifa);
}

int
sctp_is_addr_restricted(struct sctp_tcb *stcb, struct sctp_ifa *ifa)
{
 struct sctp_laddr *laddr;

 if (stcb == NULL) {
  /* There are no restrictions, no TCB :-) */
  return (0);
 }
 LIST_FOREACH(laddr, &stcb->asoc.sctp_restricted_addrs, sctp_nxt_addr) {
  if (laddr->ifa == NULL) {
   SCTPDBG(SCTP_DEBUG_OUTPUT1, "%s: NULL ifa\n",
    __func__);
   continue;
  }
  if (laddr->ifa == ifa) {
   /* Yes it is on the list */
   return (1);
  }
 }
 return (0);
}

int
sctp_is_addr_in_ep(struct sctp_inpcb *inp, struct sctp_ifa *ifa)
{
 struct sctp_laddr *laddr;

 if (ifa == NULL)
  return (0);
 LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
  if (laddr->ifa == NULL) {
   SCTPDBG(SCTP_DEBUG_OUTPUT1, "%s: NULL ifa\n",
    __func__);
   continue;
  }
  if ((laddr->ifa == ifa) && laddr->action == 0)
   /* same pointer */
   return (1);
 }
 return (0);
}

static struct sctp_ifa *
sctp_choose_boundspecific_inp(struct sctp_inpcb *inp,
         sctp_route_t *ro,
         uint32_t vrf_id,
         int non_asoc_addr_ok,
         uint8_t dest_is_priv,
         uint8_t dest_is_loop,
         sa_family_t fam)
{
 struct sctp_laddr *laddr, *starting_point;
 void *ifn;
 int resettotop = 0;
 struct sctp_ifn *sctp_ifn;
 struct sctp_ifa *sctp_ifa, *sifa;
 struct sctp_vrf *vrf;
 uint32_t ifn_index;

 vrf = sctp_find_vrf(vrf_id);
 if (vrf == NULL)
  return (NULL);

 ifn = SCTP_GET_IFN_VOID_FROM_ROUTE(ro);
 ifn_index = SCTP_GET_IF_INDEX_FROM_ROUTE(ro);
 sctp_ifn = sctp_find_ifn(ifn, ifn_index);
 /*
 * first question, is the ifn we will emit on in our list, if so, we
 * want such an address. Note that we first looked for a
 * preferred address.
 */

 if (sctp_ifn) {
  /* is a preferred one on the interface we route out? */
  LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) {
#if defined(__FreeBSD__) && !defined(__Userspace__)
#ifdef INET
   if ((sctp_ifa->address.sa.sa_family == AF_INET) &&
       (prison_check_ip4(inp->ip_inp.inp.inp_cred,
                         &sctp_ifa->address.sin.sin_addr) != 0)) {
    continue;
   }
#endif
#ifdef INET6
   if ((sctp_ifa->address.sa.sa_family == AF_INET6) &&
       (prison_check_ip6(inp->ip_inp.inp.inp_cred,
                         &sctp_ifa->address.sin6.sin6_addr) != 0)) {
    continue;
   }
#endif
#endif
   if ((sctp_ifa->localifa_flags & SCTP_ADDR_DEFER_USE) &&
       (non_asoc_addr_ok == 0))
    continue;
   sifa = sctp_is_ifa_addr_preferred(sctp_ifa,
         dest_is_loop,
         dest_is_priv, fam);
   if (sifa == NULL)
    continue;
   if (sctp_is_addr_in_ep(inp, sifa)) {
    atomic_add_int(&sifa->refcount, 1);
    return (sifa);
   }
  }
 }
 /*
 * ok, now we now need to find one on the list of the addresses.
 * We can't get one on the emitting interface so let's find first
 * a preferred one. If not that an acceptable one otherwise...
 * we return NULL.
 */

 starting_point = inp->next_addr_touse;
 once_again:
 if (inp->next_addr_touse == NULL) {
  inp->next_addr_touse = LIST_FIRST(&inp->sctp_addr_list);
  resettotop = 1;
 }
 for (laddr = inp->next_addr_touse; laddr;
      laddr = LIST_NEXT(laddr, sctp_nxt_addr)) {
  if (laddr->ifa == NULL) {
   /* address has been removed */
   continue;
  }
  if (laddr->action == SCTP_DEL_IP_ADDRESS) {
   /* address is being deleted */
   continue;
  }
  sifa = sctp_is_ifa_addr_preferred(laddr->ifa, dest_is_loop,
        dest_is_priv, fam);
  if (sifa == NULL)
   continue;
  atomic_add_int(&sifa->refcount, 1);
  return (sifa);
 }
 if (resettotop == 0) {
  inp->next_addr_touse = NULL;
  goto once_again;
 }

 inp->next_addr_touse = starting_point;
 resettotop = 0;
 once_again_too:
 if (inp->next_addr_touse == NULL) {
  inp->next_addr_touse = LIST_FIRST(&inp->sctp_addr_list);
  resettotop = 1;
 }

 /* ok, what about an acceptable address in the inp */
 for (laddr = inp->next_addr_touse; laddr;
      laddr = LIST_NEXT(laddr, sctp_nxt_addr)) {
  if (laddr->ifa == NULL) {
   /* address has been removed */
   continue;
  }
  if (laddr->action == SCTP_DEL_IP_ADDRESS) {
   /* address is being deleted */
   continue;
  }
  sifa = sctp_is_ifa_addr_acceptable(laddr->ifa, dest_is_loop,
         dest_is_priv, fam);
  if (sifa == NULL)
   continue;
  atomic_add_int(&sifa->refcount, 1);
  return (sifa);
 }
 if (resettotop == 0) {
  inp->next_addr_touse = NULL;
  goto once_again_too;
 }

 /*
 * no address bound can be a source for the destination we are in
 * trouble
 */

 return (NULL);
}

static struct sctp_ifa *
sctp_choose_boundspecific_stcb(struct sctp_inpcb *inp,
          struct sctp_tcb *stcb,
          sctp_route_t *ro,
          uint32_t vrf_id,
          uint8_t dest_is_priv,
          uint8_t dest_is_loop,
          int non_asoc_addr_ok,
          sa_family_t fam)
{
 struct sctp_laddr *laddr, *starting_point;
 void *ifn;
 struct sctp_ifn *sctp_ifn;
 struct sctp_ifa *sctp_ifa, *sifa;
 uint8_t start_at_beginning = 0;
 struct sctp_vrf *vrf;
 uint32_t ifn_index;

 /*
 * first question, is the ifn we will emit on in our list, if so, we
 * want that one.
 */

 vrf = sctp_find_vrf(vrf_id);
 if (vrf == NULL)
  return (NULL);

 ifn = SCTP_GET_IFN_VOID_FROM_ROUTE(ro);
 ifn_index = SCTP_GET_IF_INDEX_FROM_ROUTE(ro);
 sctp_ifn = sctp_find_ifn(ifn, ifn_index);

 /*
 * first question, is the ifn we will emit on in our list?  If so,
 * we want that one. First we look for a preferred. Second, we go
 * for an acceptable.
 */

 if (sctp_ifn) {
  /* first try for a preferred address on the ep */
  LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) {
#if defined(__FreeBSD__) && !defined(__Userspace__)
#ifdef INET
   if ((sctp_ifa->address.sa.sa_family == AF_INET) &&
       (prison_check_ip4(inp->ip_inp.inp.inp_cred,
                         &sctp_ifa->address.sin.sin_addr) != 0)) {
    continue;
   }
#endif
#ifdef INET6
   if ((sctp_ifa->address.sa.sa_family == AF_INET6) &&
       (prison_check_ip6(inp->ip_inp.inp.inp_cred,
                         &sctp_ifa->address.sin6.sin6_addr) != 0)) {
    continue;
   }
#endif
#endif
   if ((sctp_ifa->localifa_flags & SCTP_ADDR_DEFER_USE) && (non_asoc_addr_ok == 0))
    continue;
   if (sctp_is_addr_in_ep(inp, sctp_ifa)) {
    sifa = sctp_is_ifa_addr_preferred(sctp_ifa, dest_is_loop, dest_is_priv, fam);
    if (sifa == NULL)
     continue;
    if (((non_asoc_addr_ok == 0) &&
         (sctp_is_addr_restricted(stcb, sifa))) ||
        (non_asoc_addr_ok &&
         (sctp_is_addr_restricted(stcb, sifa)) &&
         (!sctp_is_addr_pending(stcb, sifa)))) {
     /* on the no-no list */
     continue;
    }
    atomic_add_int(&sifa->refcount, 1);
    return (sifa);
   }
  }
  /* next try for an acceptable address on the ep */
  LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) {
#if defined(__FreeBSD__) && !defined(__Userspace__)
#ifdef INET
   if ((sctp_ifa->address.sa.sa_family == AF_INET) &&
       (prison_check_ip4(inp->ip_inp.inp.inp_cred,
                         &sctp_ifa->address.sin.sin_addr) != 0)) {
    continue;
   }
#endif
#ifdef INET6
   if ((sctp_ifa->address.sa.sa_family == AF_INET6) &&
       (prison_check_ip6(inp->ip_inp.inp.inp_cred,
                         &sctp_ifa->address.sin6.sin6_addr) != 0)) {
    continue;
   }
#endif
#endif
   if ((sctp_ifa->localifa_flags & SCTP_ADDR_DEFER_USE) && (non_asoc_addr_ok == 0))
    continue;
   if (sctp_is_addr_in_ep(inp, sctp_ifa)) {
    sifa= sctp_is_ifa_addr_acceptable(sctp_ifa, dest_is_loop, dest_is_priv,fam);
    if (sifa == NULL)
     continue;
    if (((non_asoc_addr_ok == 0) &&
         (sctp_is_addr_restricted(stcb, sifa))) ||
        (non_asoc_addr_ok &&
         (sctp_is_addr_restricted(stcb, sifa)) &&
         (!sctp_is_addr_pending(stcb, sifa)))) {
     /* on the no-no list */
     continue;
    }
    atomic_add_int(&sifa->refcount, 1);
    return (sifa);
   }
  }
 }
 /*
 * if we can't find one like that then we must look at all
 * addresses bound to pick one at first preferable then
 * secondly acceptable.
 */

 starting_point = stcb->asoc.last_used_address;
 sctp_from_the_top:
 if (stcb->asoc.last_used_address == NULL) {
  start_at_beginning = 1;
  stcb->asoc.last_used_address = LIST_FIRST(&inp->sctp_addr_list);
 }
 /* search beginning with the last used address */
 for (laddr = stcb->asoc.last_used_address; laddr;
      laddr = LIST_NEXT(laddr, sctp_nxt_addr)) {
  if (laddr->ifa == NULL) {
   /* address has been removed */
   continue;
  }
  if (laddr->action == SCTP_DEL_IP_ADDRESS) {
   /* address is being deleted */
   continue;
  }
  sifa = sctp_is_ifa_addr_preferred(laddr->ifa, dest_is_loop, dest_is_priv, fam);
  if (sifa == NULL)
   continue;
  if (((non_asoc_addr_ok == 0) &&
       (sctp_is_addr_restricted(stcb, sifa))) ||
      (non_asoc_addr_ok &&
       (sctp_is_addr_restricted(stcb, sifa)) &&
       (!sctp_is_addr_pending(stcb, sifa)))) {
   /* on the no-no list */
   continue;
  }
  stcb->asoc.last_used_address = laddr;
  atomic_add_int(&sifa->refcount, 1);
  return (sifa);
 }
 if (start_at_beginning == 0) {
  stcb->asoc.last_used_address = NULL;
  goto sctp_from_the_top;
 }
 /* now try for any higher scope than the destination */
 stcb->asoc.last_used_address = starting_point;
 start_at_beginning = 0;
 sctp_from_the_top2:
 if (stcb->asoc.last_used_address == NULL) {
  start_at_beginning = 1;
  stcb->asoc.last_used_address = LIST_FIRST(&inp->sctp_addr_list);
 }
 /* search beginning with the last used address */
 for (laddr = stcb->asoc.last_used_address; laddr;
      laddr = LIST_NEXT(laddr, sctp_nxt_addr)) {
  if (laddr->ifa == NULL) {
   /* address has been removed */
   continue;
  }
  if (laddr->action == SCTP_DEL_IP_ADDRESS) {
   /* address is being deleted */
   continue;
  }
  sifa = sctp_is_ifa_addr_acceptable(laddr->ifa, dest_is_loop,
         dest_is_priv, fam);
  if (sifa == NULL)
   continue;
  if (((non_asoc_addr_ok == 0) &&
       (sctp_is_addr_restricted(stcb, sifa))) ||
      (non_asoc_addr_ok &&
       (sctp_is_addr_restricted(stcb, sifa)) &&
       (!sctp_is_addr_pending(stcb, sifa)))) {
   /* on the no-no list */
   continue;
  }
  stcb->asoc.last_used_address = laddr;
  atomic_add_int(&sifa->refcount, 1);
  return (sifa);
 }
 if (start_at_beginning == 0) {
  stcb->asoc.last_used_address = NULL;
  goto sctp_from_the_top2;
 }
 return (NULL);
}

static struct sctp_ifa *
sctp_select_nth_preferred_addr_from_ifn_boundall(struct sctp_ifn *ifn,
#if defined(__FreeBSD__) && !defined(__Userspace__)
                                                 struct sctp_inpcb *inp,
#else
                                                 struct sctp_inpcb *inp SCTP_UNUSED,
#endif
                                                 struct sctp_tcb *stcb,
                                                 int non_asoc_addr_ok,
                                                 uint8_t dest_is_loop,
                                                 uint8_t dest_is_priv,
                                                 int addr_wanted,
                                                 sa_family_t fam,
                                                 sctp_route_t *ro)
{
 struct sctp_ifa *ifa, *sifa;
 int num_eligible_addr = 0;
#ifdef INET6
#ifdef SCTP_EMBEDDED_V6_SCOPE
 struct sockaddr_in6 sin6, lsa6;

 if (fam == AF_INET6) {
  memcpy(&sin6, &ro->ro_dst, sizeof(struct sockaddr_in6));
#ifdef SCTP_KAME
  (void)sa6_recoverscope(&sin6);
#else
  (void)in6_recoverscope(&sin6, &sin6.sin6_addr, NULL);
#endif  /* SCTP_KAME */
 }
#endif  /* SCTP_EMBEDDED_V6_SCOPE */
#endif /* INET6 */
 LIST_FOREACH(ifa, &ifn->ifalist, next_ifa) {
#if defined(__FreeBSD__) && !defined(__Userspace__)
#ifdef INET
  if ((ifa->address.sa.sa_family == AF_INET) &&
      (prison_check_ip4(inp->ip_inp.inp.inp_cred,
                        &ifa->address.sin.sin_addr) != 0)) {
   continue;
  }
#endif
#ifdef INET6
  if ((ifa->address.sa.sa_family == AF_INET6) &&
      (prison_check_ip6(inp->ip_inp.inp.inp_cred,
                        &ifa->address.sin6.sin6_addr) != 0)) {
   continue;
  }
#endif
#endif
  if ((ifa->localifa_flags & SCTP_ADDR_DEFER_USE) &&
      (non_asoc_addr_ok == 0))
   continue;
  sifa = sctp_is_ifa_addr_preferred(ifa, dest_is_loop,
        dest_is_priv, fam);
  if (sifa == NULL)
   continue;
#ifdef INET6
  if (fam == AF_INET6 &&
      dest_is_loop &&
      sifa->src_is_loop && sifa->src_is_priv) {
   /* don't allow fe80::1 to be a src on loop ::1, we don't list it
 * to the peer so we will get an abort.
 */

   continue;
  }
#ifdef SCTP_EMBEDDED_V6_SCOPE
  if (fam == AF_INET6 &&
      IN6_IS_ADDR_LINKLOCAL(&sifa->address.sin6.sin6_addr) &&
      IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr)) {
   /* link-local <-> link-local must belong to the same scope. */
   memcpy(&lsa6, &sifa->address.sin6, sizeof(struct sockaddr_in6));
#ifdef SCTP_KAME
   (void)sa6_recoverscope(&lsa6);
#else
   (void)in6_recoverscope(&lsa6, &lsa6.sin6_addr, NULL);
#endif  /* SCTP_KAME */
   if (sin6.sin6_scope_id != lsa6.sin6_scope_id) {
    continue;
   }
  }
#endif  /* SCTP_EMBEDDED_V6_SCOPE */
#endif /* INET6 */

#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__Userspace__)
  /* Check if the IPv6 address matches to next-hop.
   In the mobile case, old IPv6 address may be not deleted
   from the interface. Then, the interface has previous and
   new addresses.  We should use one corresponding to the
   next-hop.  (by micchie)
 */

#ifdef INET6
  if (stcb && fam == AF_INET6 &&
      sctp_is_mobility_feature_on(stcb->sctp_ep, SCTP_MOBILITY_BASE)) {
   if (sctp_v6src_match_nexthop(&sifa->address.sin6, ro) == 0) {
    continue;
   }
  }
#endif
#ifdef INET
  /* Avoid topologically incorrect IPv4 address */
  if (stcb && fam == AF_INET &&
      sctp_is_mobility_feature_on(stcb->sctp_ep, SCTP_MOBILITY_BASE)) {
   if (sctp_v4src_match_nexthop(sifa, ro) == 0) {
    continue;
   }
  }
#endif
#endif
  if (stcb) {
   if (sctp_is_address_in_scope(ifa, &stcb->asoc.scope, 0) == 0) {
    continue;
   }
   if (((non_asoc_addr_ok == 0) &&
        (sctp_is_addr_restricted(stcb, sifa))) ||
       (non_asoc_addr_ok &&
        (sctp_is_addr_restricted(stcb, sifa)) &&
        (!sctp_is_addr_pending(stcb, sifa)))) {
    /*
 * It is restricted for some reason..
 * probably not yet added.
 */

    continue;
   }
  }
  if (num_eligible_addr >= addr_wanted) {
   return (sifa);
  }
  num_eligible_addr++;
 }
 return (NULL);
}

static int
sctp_count_num_preferred_boundall(struct sctp_ifn *ifn,
#if defined(__FreeBSD__) && !defined(__Userspace__)
                                  struct sctp_inpcb *inp,
#else
                                  struct sctp_inpcb *inp SCTP_UNUSED,
#endif
      struct sctp_tcb *stcb,
      int non_asoc_addr_ok,
      uint8_t dest_is_loop,
      uint8_t dest_is_priv,
      sa_family_t fam)
{
 struct sctp_ifa *ifa, *sifa;
 int num_eligible_addr = 0;

 LIST_FOREACH(ifa, &ifn->ifalist, next_ifa) {
#if defined(__FreeBSD__) && !defined(__Userspace__)
#ifdef INET
  if ((ifa->address.sa.sa_family == AF_INET) &&
      (prison_check_ip4(inp->ip_inp.inp.inp_cred,
                        &ifa->address.sin.sin_addr) != 0)) {
   continue;
  }
#endif
#ifdef INET6
  if ((ifa->address.sa.sa_family == AF_INET6) &&
      (stcb != NULL) &&
      (prison_check_ip6(inp->ip_inp.inp.inp_cred,
                        &ifa->address.sin6.sin6_addr) != 0)) {
   continue;
  }
#endif
#endif
  if ((ifa->localifa_flags & SCTP_ADDR_DEFER_USE) &&
      (non_asoc_addr_ok == 0)) {
   continue;
  }
  sifa = sctp_is_ifa_addr_preferred(ifa, dest_is_loop,
        dest_is_priv, fam);
  if (sifa == NULL) {
   continue;
  }
  if (stcb) {
   if (sctp_is_address_in_scope(ifa, &stcb->asoc.scope, 0) == 0) {
    continue;
   }
   if (((non_asoc_addr_ok == 0) &&
        (sctp_is_addr_restricted(stcb, sifa))) ||
       (non_asoc_addr_ok &&
        (sctp_is_addr_restricted(stcb, sifa)) &&
        (!sctp_is_addr_pending(stcb, sifa)))) {
    /*
 * It is restricted for some reason..
 * probably not yet added.
 */

    continue;
   }
  }
  num_eligible_addr++;
 }
 return (num_eligible_addr);
}

static struct sctp_ifa *
sctp_choose_boundall(struct sctp_inpcb *inp,
                     struct sctp_tcb *stcb,
       struct sctp_nets *net,
       sctp_route_t *ro,
       uint32_t vrf_id,
       uint8_t dest_is_priv,
       uint8_t dest_is_loop,
       int non_asoc_addr_ok,
       sa_family_t fam)
{
 int cur_addr_num = 0, num_preferred = 0;
 void *ifn;
 struct sctp_ifn *sctp_ifn, *looked_at = NULL, *emit_ifn;
 struct sctp_ifa *sctp_ifa, *sifa;
 uint32_t ifn_index;
 struct sctp_vrf *vrf;
#ifdef INET
 int retried = 0;
#endif

 /*-
 * For boundall we can use any address in the association.
 * If non_asoc_addr_ok is set we can use any address (at least in
 * theory). So we look for preferred addresses first. If we find one,
 * we use it. Otherwise we next try to get an address on the
 * interface, which we should be able to do (unless non_asoc_addr_ok
 * is false and we are routed out that way). In these cases where we
 * can't use the address of the interface we go through all the
 * ifn's looking for an address we can use and fill that in. Punting
 * means we send back address 0, which will probably cause problems
 * actually since then IP will fill in the address of the route ifn,
 * which means we probably already rejected it.. i.e. here comes an
 * abort :-<.
 */

 vrf = sctp_find_vrf(vrf_id);
 if (vrf == NULL)
  return (NULL);

 ifn = SCTP_GET_IFN_VOID_FROM_ROUTE(ro);
 ifn_index = SCTP_GET_IF_INDEX_FROM_ROUTE(ro);
 SCTPDBG(SCTP_DEBUG_OUTPUT2,"ifn from route:%p ifn_index:%d\n", ifn, ifn_index);
 emit_ifn = looked_at = sctp_ifn = sctp_find_ifn(ifn, ifn_index);
 if (sctp_ifn == NULL) {
  /* ?? We don't have this guy ?? */
  SCTPDBG(SCTP_DEBUG_OUTPUT2,"No ifn emit interface?\n");
  goto bound_all_plan_b;
 }
 SCTPDBG(SCTP_DEBUG_OUTPUT2,"ifn_index:%d name:%s is emit interface\n",
  ifn_index, sctp_ifn->ifn_name);

 if (net) {
  cur_addr_num = net->indx_of_eligible_next_to_use;
 }
 num_preferred = sctp_count_num_preferred_boundall(sctp_ifn,
         inp, stcb,
         non_asoc_addr_ok,
         dest_is_loop,
         dest_is_priv, fam);
 SCTPDBG(SCTP_DEBUG_OUTPUT2, "Found %d preferred source addresses for intf:%s\n",
  num_preferred, sctp_ifn->ifn_name);
 if (num_preferred == 0) {
  /*
 * no eligible addresses, we must use some other interface
 * address if we can find one.
 */

  goto bound_all_plan_b;
 }
 /*
 * Ok we have num_eligible_addr set with how many we can use, this
 * may vary from call to call due to addresses being deprecated
 * etc..
 */

 if (cur_addr_num >= num_preferred) {
  cur_addr_num = 0;
 }
 /*
 * select the nth address from the list (where cur_addr_num is the
 * nth) and 0 is the first one, 1 is the second one etc...
 */

 SCTPDBG(SCTP_DEBUG_OUTPUT2, "cur_addr_num:%d\n", cur_addr_num);

 sctp_ifa = sctp_select_nth_preferred_addr_from_ifn_boundall(sctp_ifn, inp, stcb, non_asoc_addr_ok, dest_is_loop,
                                                                    dest_is_priv, cur_addr_num, fam, ro);

 /* if sctp_ifa is NULL something changed??, fall to plan b. */
 if (sctp_ifa) {
  atomic_add_int(&sctp_ifa->refcount, 1);
  if (net) {
   /* save off where the next one we will want */
   net->indx_of_eligible_next_to_use = cur_addr_num + 1;
  }
  return (sctp_ifa);
 }
 /*
 * plan_b: Look at all interfaces and find a preferred address. If
 * no preferred fall through to plan_c.
 */

 bound_all_plan_b:
 SCTPDBG(SCTP_DEBUG_OUTPUT2, "Trying Plan B\n");
 LIST_FOREACH(sctp_ifn, &vrf->ifnlist, next_ifn) {
  SCTPDBG(SCTP_DEBUG_OUTPUT2, "Examine interface %s\n",
   sctp_ifn->ifn_name);
  if (dest_is_loop == 0 && SCTP_IFN_IS_IFT_LOOP(sctp_ifn)) {
   /* wrong base scope */
   SCTPDBG(SCTP_DEBUG_OUTPUT2, "skip\n");
   continue;
  }
  if ((sctp_ifn == looked_at) && looked_at) {
   /* already looked at this guy */
   SCTPDBG(SCTP_DEBUG_OUTPUT2, "already seen\n");
   continue;
  }
  num_preferred = sctp_count_num_preferred_boundall(sctp_ifn, inp, stcb, non_asoc_addr_ok,
                                                                  dest_is_loop, dest_is_priv, fam);
  SCTPDBG(SCTP_DEBUG_OUTPUT2,
   "Found ifn:%p %d preferred source addresses\n",
   ifn, num_preferred);
  if (num_preferred == 0) {
   /* None on this interface. */
   SCTPDBG(SCTP_DEBUG_OUTPUT2, "No preferred -- skipping to next\n");
   continue;
  }
  SCTPDBG(SCTP_DEBUG_OUTPUT2,
   "num preferred:%d on interface:%p cur_addr_num:%d\n",
   num_preferred, (void *)sctp_ifn, cur_addr_num);

  /*
 * Ok we have num_eligible_addr set with how many we can
 * use, this may vary from call to call due to addresses
 * being deprecated etc..
 */

  if (cur_addr_num >= num_preferred) {
   cur_addr_num = 0;
  }
  sifa = sctp_select_nth_preferred_addr_from_ifn_boundall(sctp_ifn, inp, stcb, non_asoc_addr_ok, dest_is_loop,
                                                                        dest_is_priv, cur_addr_num, fam, ro);
  if (sifa == NULL)
   continue;
  if (net) {
   net->indx_of_eligible_next_to_use = cur_addr_num + 1;
   SCTPDBG(SCTP_DEBUG_OUTPUT2, "we selected %d\n",
    cur_addr_num);
   SCTPDBG(SCTP_DEBUG_OUTPUT2, "Source:");
   SCTPDBG_ADDR(SCTP_DEBUG_OUTPUT2, &sifa->address.sa);
   SCTPDBG(SCTP_DEBUG_OUTPUT2, "Dest:");
   SCTPDBG_ADDR(SCTP_DEBUG_OUTPUT2, &net->ro._l_addr.sa);
  }
  atomic_add_int(&sifa->refcount, 1);
  return (sifa);
 }
#ifdef INET
again_with_private_addresses_allowed:
#endif
 /* plan_c: do we have an acceptable address on the emit interface */
 sifa = NULL;
 SCTPDBG(SCTP_DEBUG_OUTPUT2,"Trying Plan C: find acceptable on interface\n");
 if (emit_ifn == NULL) {
  SCTPDBG(SCTP_DEBUG_OUTPUT2,"Jump to Plan D - no emit_ifn\n");
  goto plan_d;
 }
 LIST_FOREACH(sctp_ifa, &emit_ifn->ifalist, next_ifa) {
  SCTPDBG(SCTP_DEBUG_OUTPUT2, "ifa:%p\n", (void *)sctp_ifa);
#if defined(__FreeBSD__) && !defined(__Userspace__)
#ifdef INET
  if ((sctp_ifa->address.sa.sa_family == AF_INET) &&
      (prison_check_ip4(inp->ip_inp.inp.inp_cred,
                        &sctp_ifa->address.sin.sin_addr) != 0)) {
   SCTPDBG(SCTP_DEBUG_OUTPUT2,"Jailed\n");
   continue;
  }
#endif
#ifdef INET6
  if ((sctp_ifa->address.sa.sa_family == AF_INET6) &&
      (prison_check_ip6(inp->ip_inp.inp.inp_cred,
                        &sctp_ifa->address.sin6.sin6_addr) != 0)) {
   SCTPDBG(SCTP_DEBUG_OUTPUT2,"Jailed\n");
   continue;
  }
#endif
#endif
  if ((sctp_ifa->localifa_flags & SCTP_ADDR_DEFER_USE) &&
      (non_asoc_addr_ok == 0)) {
   SCTPDBG(SCTP_DEBUG_OUTPUT2,"Defer\n");
   continue;
  }
  sifa = sctp_is_ifa_addr_acceptable(sctp_ifa, dest_is_loop,
         dest_is_priv, fam);
  if (sifa == NULL) {
   SCTPDBG(SCTP_DEBUG_OUTPUT2, "IFA not acceptable\n");
   continue;
  }
  if (stcb) {
   if (sctp_is_address_in_scope(sifa, &stcb->asoc.scope, 0) == 0) {
    SCTPDBG(SCTP_DEBUG_OUTPUT2, "NOT in scope\n");
    sifa = NULL;
    continue;
   }
   if (((non_asoc_addr_ok == 0) &&
        (sctp_is_addr_restricted(stcb, sifa))) ||
       (non_asoc_addr_ok &&
        (sctp_is_addr_restricted(stcb, sifa)) &&
        (!sctp_is_addr_pending(stcb, sifa)))) {
    /*
 * It is restricted for some
 * reason.. probably not yet added.
 */

    SCTPDBG(SCTP_DEBUG_OUTPUT2, "Its restricted\n");
    sifa = NULL;
    continue;
   }
  }
  atomic_add_int(&sifa->refcount, 1);
  goto out;
 }
 plan_d:
 /*
 * plan_d: We are in trouble. No preferred address on the emit
 * interface. And not even a preferred address on all interfaces.
 * Go out and see if we can find an acceptable address somewhere
 * amongst all interfaces.
 */

 SCTPDBG(SCTP_DEBUG_OUTPUT2, "Trying Plan D looked_at is %p\n", (void *)looked_at);
 LIST_FOREACH(sctp_ifn, &vrf->ifnlist, next_ifn) {
  if (dest_is_loop == 0 && SCTP_IFN_IS_IFT_LOOP(sctp_ifn)) {
   /* wrong base scope */
   continue;
  }
  LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) {
#if defined(__FreeBSD__) && !defined(__Userspace__)
#ifdef INET
   if ((sctp_ifa->address.sa.sa_family == AF_INET) &&
       (prison_check_ip4(inp->ip_inp.inp.inp_cred,
                         &sctp_ifa->address.sin.sin_addr) != 0)) {
    continue;
   }
#endif
#ifdef INET6
   if ((sctp_ifa->address.sa.sa_family == AF_INET6) &&
       (prison_check_ip6(inp->ip_inp.inp.inp_cred,
                         &sctp_ifa->address.sin6.sin6_addr) != 0)) {
    continue;
   }
#endif
#endif
   if ((sctp_ifa->localifa_flags & SCTP_ADDR_DEFER_USE) &&
       (non_asoc_addr_ok == 0))
    continue;
   sifa = sctp_is_ifa_addr_acceptable(sctp_ifa,
          dest_is_loop,
          dest_is_priv, fam);
   if (sifa == NULL)
    continue;
   if (stcb) {
    if (sctp_is_address_in_scope(sifa, &stcb->asoc.scope, 0) == 0) {
     sifa = NULL;
     continue;
    }
    if (((non_asoc_addr_ok == 0) &&
         (sctp_is_addr_restricted(stcb, sifa))) ||
        (non_asoc_addr_ok &&
         (sctp_is_addr_restricted(stcb, sifa)) &&
         (!sctp_is_addr_pending(stcb, sifa)))) {
     /*
 * It is restricted for some
 * reason.. probably not yet added.
 */

     sifa = NULL;
     continue;
    }
   }
   goto out;
  }
 }
#ifdef INET
 if (stcb) {
  if ((retried == 0) && (stcb->asoc.scope.ipv4_local_scope == 0)) {
   stcb->asoc.scope.ipv4_local_scope = 1;
   retried = 1;
   goto again_with_private_addresses_allowed;
  } else if (retried == 1) {
   stcb->asoc.scope.ipv4_local_scope = 0;
  }
 }
#endif
out:
#ifdef INET
 if (sifa) {
  if (retried == 1) {
   LIST_FOREACH(sctp_ifn, &vrf->ifnlist, next_ifn) {
    if (dest_is_loop == 0 && SCTP_IFN_IS_IFT_LOOP(sctp_ifn)) {
     /* wrong base scope */
     continue;
    }
    LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) {
     struct sctp_ifa *tmp_sifa;

#if defined(__FreeBSD__) && !defined(__Userspace__)
#ifdef INET
     if ((sctp_ifa->address.sa.sa_family == AF_INET) &&
         (prison_check_ip4(inp->ip_inp.inp.inp_cred,
                           &sctp_ifa->address.sin.sin_addr) != 0)) {
      continue;
     }
#endif
#ifdef INET6
     if ((sctp_ifa->address.sa.sa_family == AF_INET6) &&
         (prison_check_ip6(inp->ip_inp.inp.inp_cred,
                           &sctp_ifa->address.sin6.sin6_addr) != 0)) {
      continue;
     }
#endif
#endif
     if ((sctp_ifa->localifa_flags & SCTP_ADDR_DEFER_USE) &&
         (non_asoc_addr_ok == 0))
      continue;
     tmp_sifa = sctp_is_ifa_addr_acceptable(sctp_ifa,
                                            dest_is_loop,
                                            dest_is_priv, fam);
     if (tmp_sifa == NULL) {
      continue;
     }
     if (tmp_sifa == sifa) {
      continue;
     }
     if (stcb) {
      if (sctp_is_address_in_scope(tmp_sifa,
                                   &stcb->asoc.scope, 0) == 0) {
       continue;
      }
      if (((non_asoc_addr_ok == 0) &&
           (sctp_is_addr_restricted(stcb, tmp_sifa))) ||
          (non_asoc_addr_ok &&
           (sctp_is_addr_restricted(stcb, tmp_sifa)) &&
           (!sctp_is_addr_pending(stcb, tmp_sifa)))) {
       /*
 * It is restricted for some
 * reason.. probably not yet added.
 */

       continue;
      }
     }
     if ((tmp_sifa->address.sin.sin_family == AF_INET) &&
         (IN4_ISPRIVATE_ADDRESS(&(tmp_sifa->address.sin.sin_addr)))) {
      sctp_add_local_addr_restricted(stcb, tmp_sifa);
     }
    }
   }
  }
  atomic_add_int(&sifa->refcount, 1);
 }
#endif
 return (sifa);
}

/* tcb may be NULL */
struct sctp_ifa *
sctp_source_address_selection(struct sctp_inpcb *inp,
         struct sctp_tcb *stcb,
         sctp_route_t *ro,
         struct sctp_nets *net,
         int non_asoc_addr_ok, uint32_t vrf_id)
{
 struct sctp_ifa *answer;
 uint8_t dest_is_priv, dest_is_loop;
 sa_family_t fam;
#ifdef INET
 struct sockaddr_in *to = (struct sockaddr_in *)&ro->ro_dst;
#endif
#ifdef INET6
 struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&ro->ro_dst;
#endif

 /**
 * Rules:
 * - Find the route if needed, cache if I can.
 * - Look at interface address in route, Is it in the bound list. If so we
 *   have the best source.
 * - If not we must rotate amongst the addresses.
 *
 * Caveats and issues
 *
 * Do we need to pay attention to scope. We can have a private address
 * or a global address we are sourcing or sending to. So if we draw
 * it out
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=90 H=96 G=93

¤ Dauer der Verarbeitung: 0.27 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.