LTP GCOV extension - code coverage report
Current view: directory - trunk/libteredo - packets.c
Test: lcov.info
Date: 2007-05-09 Instrumented lines: 141
Code covered: 0.0 % Executed lines: 0

       1                 : /*
       2                 :  * packets.c - helpers to send Teredo packet from relay/client
       3                 :  * $Id: packets.c 1938 2007-02-22 20:55:28Z remi $
       4                 :  *
       5                 :  * See "Teredo: Tunneling IPv6 over UDP through NATs"
       6                 :  * for more information
       7                 :  */
       8                 : 
       9                 : /***********************************************************************
      10                 :  *  Copyright © 2004-2007 Rémi Denis-Courmont.                         *
      11                 :  *  This program is free software; you can redistribute and/or modify  *
      12                 :  *  it under the terms of the GNU General Public License as published  *
      13                 :  *  by the Free Software Foundation; version 2 of the license.         *
      14                 :  *                                                                     *
      15                 :  *  This program is distributed in the hope that it will be useful,    *
      16                 :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of     *
      17                 :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.               *
      18                 :  *  See the GNU General Public License for more details.               *
      19                 :  *                                                                     *
      20                 :  *  You should have received a copy of the GNU General Public License  *
      21                 :  *  along with this program; if not, you can get it from:              *
      22                 :  *  http://www.gnu.org/copyleft/gpl.html                               *
      23                 :  ***********************************************************************/
      24                 : 
      25                 : #ifdef HAVE_CONFIG_H
      26                 : # include <config.h>
      27                 : #endif
      28                 : 
      29                 : #include <gettext.h>
      30                 : 
      31                 : #include <string.h>
      32                 : #include <stdbool.h>
      33                 : #include <inttypes.h>
      34                 : 
      35                 : #include <sys/types.h>
      36                 : #include <netinet/in.h>
      37                 : #include <netinet/ip6.h> /* struct ip6_hdr */
      38                 : #include <netinet/icmp6.h> /* router solicication */
      39                 : #include <syslog.h>
      40                 : #include <sys/uio.h>
      41                 : 
      42                 : #include "teredo.h"
      43                 : #include "v4global.h" // is_ipv4_global_unicast()
      44                 : #include "teredo-udp.h"
      45                 : 
      46                 : #include <time.h>
      47                 : #include "security.h"
      48                 : 
      49                 : #include "packets.h"
      50                 : #include "checksum.h"
      51                 : 
      52                 : 
      53                 : /**
      54                 :  * Sends a Teredo Bubble.
      55                 :  *
      56                 :  * @param ip destination IPv4
      57                 :  * @param port destination UDP port
      58                 :  * @param src unaligned pointer to source IPv6 address
      59                 :  * @param dst unaligned pointer to destination IPv6 address
      60                 :  *
      61                 :  * @return 0 on success, -1 on error.
      62                 :  */
      63                 : int
      64                 : teredo_send_bubble (int fd, uint32_t ip, uint16_t port,
      65                 :                     const uint8_t *src, const uint8_t *dst)
      66               0 : {
      67               0 :         if (is_ipv4_global_unicast (ip))
      68                 :         {
      69                 :                 static const uint8_t head[] =
      70                 :                         "\x60\x00\x00\x00" /* flow */
      71                 :                         "\x00\x00" /* plen = 0 */
      72                 :                         "\x3b" /* nxt = IPPROTO_NONE */
      73                 :                         "\x00" /* hlim = 0 */;
      74                 :                 struct iovec iov[3] =
      75                 :                 {
      76                 :                         { (void *)head, 8 },
      77                 :                         { (void *)src, 16 },
      78                 :                         { (void *)dst, 16 }
      79               0 :                 };
      80                 : 
      81               0 :                 return teredo_sendv (fd, iov, 3, ip, port) == 40 ? 0 : -1;
      82                 :         }
      83                 : 
      84               0 :         return 0;
      85                 : }
      86                 : 
      87                 : 
      88                 : /**
      89                 :  * Sends a Teredo Bubble.
      90                 :  *
      91                 :  * @param dst Teredo destination address.
      92                 :  * @param indirect determines whether the bubble is sent to the server (true)
      93                 :  * or the client (if indirect is false) - as determined from dst.
      94                 :  *
      95                 :  * @return 0 on success, -1 on error.
      96                 :  */
      97                 : int
      98                 : SendBubbleFromDst (int fd, const struct in6_addr *dst, bool indirect)
      99               0 : {
     100               0 :         uint32_t ip = IN6_TEREDO_IPV4 (dst);
     101               0 :         uint16_t port = IN6_TEREDO_PORT (dst);
     102                 : 
     103                 :         struct in6_addr src;
     104               0 :         memcpy (src.s6_addr, "\xfe\x80\x00\x00\x00\x00\x00\x00", 8);
     105                 :         /* TODO: use some time information */
     106               0 :         teredo_get_nonce (0, ip, port, src.s6_addr + 8);
     107               0 :         src.s6_addr[8] &= 0xfc; /* Modified EUI-64 */
     108                 : 
     109               0 :         if (indirect)
     110                 :         {
     111               0 :                 ip = IN6_TEREDO_SERVER (dst);
     112               0 :                 port = htons (IPPORT_TEREDO);
     113                 :         }
     114                 : 
     115               0 :         return teredo_send_bubble (fd, ip, port, src.s6_addr, dst->s6_addr);
     116                 : }
     117                 : 
     118                 : 
     119                 : int CheckBubble (const teredo_packet *packet)
     120               0 : {
     121               0 :         const struct ip6_hdr *ip6 = (const struct ip6_hdr *)packet->ip6;
     122               0 :         const struct in6_addr *me = &ip6->ip6_dst, *it = &ip6->ip6_src;
     123                 : 
     124                 :         uint8_t hash[8];
     125                 :         /* TODO: use some time information */
     126               0 :         teredo_get_nonce (0, IN6_TEREDO_IPV4 (it), IN6_TEREDO_PORT (it), hash);
     127               0 :         hash[0] &= 0xfc; /* Modified EUI-64: non-global, non-group address */
     128                 : 
     129               0 :         return memcmp (hash, me->s6_addr + 8, 8) ? -1 : 0;
     130                 : }
     131                 : 
     132                 : 
     133                 : #ifdef MIREDO_TEREDO_CLIENT
     134                 : static const struct in6_addr in6addr_allrouters =
     135                 : { { { 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2 } } };
     136                 : 
     137                 : /**
     138                 :  * Sends a router solication with an Authentication header.
     139                 :  *
     140                 :  * @param fd socket through which the RS will be sent
     141                 :  * @param server_ip server IPv4 address toward which the solicitation should
     142                 :  * be encapsulated (network byte order)
     143                 :  * @param nonce pointer to the 8-bytes authentication nonce
     144                 :  * @param cone whether to send a Teredo “cone” solicitation
     145                 :  *
     146                 :  * @return 0 on success, -1 on error.
     147                 :  */
     148                 : int
     149                 : teredo_send_rs (int fd, uint32_t server_ip,
     150                 :                 const unsigned char *nonce, bool cone)
     151               0 : {
     152                 :         struct teredo_simple_auth auth;
     153                 :         struct
     154                 :         {
     155                 :                 struct ip6_hdr ip6;
     156                 :                 struct nd_router_solicit rs;
     157                 :         } rs;
     158               0 :         struct iovec iov[2] = { { &auth, 13 }, { &rs, sizeof (rs) } };
     159                 : 
     160                 :         // Authentication header
     161                 :         // TODO: secure qualification
     162                 : 
     163               0 :         auth.hdr.hdr.zero = 0;
     164               0 :         auth.hdr.hdr.code = teredo_auth_hdr;
     165               0 :         auth.hdr.id_len = auth.hdr.au_len = 0;
     166               0 :         memcpy (auth.nonce, nonce, 8);
     167               0 :         auth.confirmation = 0;
     168                 : 
     169               0 :         rs.ip6.ip6_flow = htonl (0x60000000);
     170               0 :         rs.ip6.ip6_plen = htons (sizeof (rs) - sizeof (rs.ip6));
     171               0 :         rs.ip6.ip6_nxt = IPPROTO_ICMPV6;
     172               0 :         rs.ip6.ip6_hlim = 255;
     173               0 :         memcpy (&rs.ip6.ip6_src, cone ? &teredo_cone : &teredo_restrict,
     174                 :                 sizeof (rs.ip6.ip6_src));
     175               0 :         memcpy (&rs.ip6.ip6_dst, &in6addr_allrouters, sizeof (rs.ip6.ip6_dst));
     176                 : 
     177               0 :         rs.rs.nd_rs_type = ND_ROUTER_SOLICIT;
     178               0 :         rs.rs.nd_rs_code = 0;
     179                 :         // Checksums are pre-computed
     180               0 :         rs.rs.nd_rs_cksum = cone ? htons (0x125d) : htons (0x7d37);
     181               0 :         rs.rs.nd_rs_reserved = 0;
     182                 : 
     183               0 :         return teredo_sendv (fd, iov, 2, server_ip, htons (IPPORT_TEREDO)) > 0
     184                 :                         ? 0 : -1;
     185                 : }
     186                 : 
     187                 : 
     188                 : /**
     189                 :  * Validates a router advertisement from the Teredo server.
     190                 :  * The RA must be of type cone if and only if cone is true.
     191                 :  * Prefix, flags, mapped port and IP are returned through newaddr.
     192                 :  * If there is a MTU option in the packet, the specified MTU value will
     193                 :  * be returned at mtu. If not, the value pointed to by mtu will not be
     194                 :  * modified.
     195                 :  *
     196                 :  * Assumptions:
     197                 :  * - newaddr must be 4-bytes aligned (NOT necessarily the packet).
     198                 :  * - newaddr->teredo.server_ip must be set to the server's expected IP by the
     199                 :  *   caller.
     200                 :  * - IPv6 header is valid (ie. version 6, plen matches packet's length, and
     201                 :  *   the full packet is at least 40 bytes long).
     202                 :  *
     203                 :  * @param packet Teredo packet to be checked
     204                 :  * @param newaddr upon entry, see assumptions, upon succesful return, the
     205                 :  * infered Teredo client address. In other words, the caller must set the
     206                 :  * server IPv4 part, this function will set the 32 bits Teredo prefix, the
     207                 :  * Teredo flags, mapped port and mapped IPv4 parts.
     208                 :  * @param cone whether the RA should be a reply to “cone” RS
     209                 :  * @param mtu [out] MTU parameter found in the RA, not modified if the RA
     210                 :  * had no MTU option. Undefined on error.
     211                 :  *
     212                 :  * @return 0 on success, -1 on error.
     213                 :  */
     214                 : int
     215                 : teredo_parse_ra (const teredo_packet *restrict packet,
     216                 :                  union teredo_addr *restrict newaddr, bool cone,
     217                 :                  uint16_t *restrict mtu)
     218               0 : {
     219               0 :         if (packet->orig_ipv4 == 0)
     220               0 :                 return -1;
     221                 : 
     222                 :         // Only read ip6_next (1 byte), so no need to align
     223               0 :         const struct ip6_hdr *ip6 = (const struct ip6_hdr *)packet->ip6;
     224               0 :         size_t length = packet->ip6_len;
     225                 : 
     226               0 :         length -= sizeof (*ip6);
     227                 : 
     228               0 :         if (memcmp (&ip6->ip6_dst, cone ? &teredo_cone : &teredo_restrict,
     229                 :                         sizeof (ip6->ip6_dst))
     230                 :          || (ip6->ip6_nxt != IPPROTO_ICMPV6)
     231                 :          || (length < sizeof (struct nd_router_advert)))
     232               0 :                 return -1;
     233                 : 
     234                 :         // Only read bytes, so no need to align
     235                 :         const struct nd_router_advert *ra =
     236                 :                 (const struct nd_router_advert *)
     237               0 :                         (((const uint8_t *)ip6) + sizeof (struct ip6_hdr));
     238               0 :         length -= sizeof (struct nd_router_advert);
     239                 : 
     240               0 :         if ((ra->nd_ra_type != ND_ROUTER_ADVERT)
     241                 :          || (ra->nd_ra_code != 0)
     242                 :          || (length < sizeof (struct nd_opt_prefix_info)))
     243                 :         /*
     244                 :          * We don't check checksum, because it is rather useless.
     245                 :          * There were already (at least) two lower-level checksums.
     246                 :          */
     247               0 :                 return -1;
     248                 : 
     249               0 :         uint32_t net_mtu = 0;
     250               0 :         newaddr->teredo.server_ip = 0;
     251                 : 
     252                 :         // Looks for a prefix information option
     253                 :         const struct nd_opt_hdr *hdr;
     254               0 :         for (hdr = (const struct nd_opt_hdr *)(ra + 1); length >= 8;
     255                 :              hdr = (const struct nd_opt_hdr *)
     256               0 :                                 (((const uint8_t *)hdr) + (hdr->nd_opt_len << 3)))
     257                 :         {
     258               0 :                 size_t optlen = (size_t)(hdr->nd_opt_len << 3);
     259                 : 
     260               0 :                 if ((length < optlen) /* too short */
     261                 :                  || (optlen == 0) /* invalid */)
     262               0 :                         return -1;
     263                 : 
     264               0 :                 switch (hdr->nd_opt_type)
     265                 :                 {
     266                 :                 /* Prefix information option */
     267                 :                  case ND_OPT_PREFIX_INFORMATION:
     268                 :                  {
     269                 :                         const struct nd_opt_prefix_info *pi = 
     270               0 :                                 (const struct nd_opt_prefix_info *)hdr;
     271                 : 
     272               0 :                         if ((optlen < sizeof (*pi)) /* option too short */
     273                 :                          || (pi->nd_opt_pi_prefix_len != 64) /* unsupp. prefix size */)
     274               0 :                                 return -1;
     275                 : 
     276               0 :                         if (newaddr->teredo.server_ip != 0)
     277                 :                         {
     278                 :                                 /* The Teredo specification excludes multiple prefixes */
     279               0 :                                 syslog (LOG_ERR, _("Multiple Teredo prefixes received"));
     280               0 :                                 return -1;
     281                 :                         }
     282                 : 
     283               0 :                         memcpy (newaddr, &pi->nd_opt_pi_prefix, 8);
     284               0 :                         break;
     285                 :                  }
     286                 : 
     287                 :                 /* MTU option */
     288                 :                  case ND_OPT_MTU:
     289                 :                  {
     290               0 :                         const struct nd_opt_mtu *mo = (const struct nd_opt_mtu *)hdr;
     291                 : 
     292                 :                         /*if (optlen < sizeof (*mo)) -- not possible (optlen >= 8)
     293                 :                                 return -1;*/
     294                 : 
     295               0 :                         memcpy (&net_mtu, &mo->nd_opt_mtu_mtu, sizeof (net_mtu));
     296               0 :                         net_mtu = ntohl (net_mtu);
     297                 : 
     298               0 :                         if ((net_mtu < 1280) || (net_mtu > 65535))
     299               0 :                                 return -1; // invalid IPv6 MTU
     300                 : 
     301                 :                         break;
     302                 :                  }
     303                 :                 }
     304                 : 
     305               0 :                 length -= optlen;
     306                 :         }
     307                 : 
     308                 :         /*
     309                 :          * NOTE: bug-to-bug-to-bug (sic!) compatibility with Microsoft servers.
     310                 :          * These severs still advertise the obsolete broken experimental Teredo
     311                 :          * prefix. That's obviously for backward (but kinda useless) compatibility
     312                 :          * with Windows XP SP1/SP2 clients, that only accept that broken prefix,
     313                 :          * unless a dedicated registry key is merged.
     314                 :          *
     315                 :          * This work-around works because Microsoft servers handles both the
     316                 :          * standard and the experimental prefixes.
     317                 :          */
     318               0 :         if (newaddr->teredo.prefix == htonl (TEREDO_PREFIX_OBSOLETE))
     319               0 :                 newaddr->teredo.prefix = htonl (TEREDO_PREFIX);
     320                 : 
     321               0 :         if (!is_valid_teredo_prefix (newaddr->teredo.prefix))
     322               0 :                 return -1;
     323                 : 
     324                 :         // only accept the cone flag:
     325               0 :         newaddr->teredo.flags = cone ? htons (TEREDO_FLAG_CONE) : 0;
     326                 : 
     327               0 :         newaddr->teredo.client_port = ~packet->orig_port;
     328               0 :         newaddr->teredo.client_ip = ~packet->orig_ipv4;
     329                 : 
     330               0 :         if (net_mtu != 0)
     331               0 :                 *mtu = (uint16_t)net_mtu;
     332                 : 
     333               0 :         return 0;
     334                 : }
     335                 : 
     336                 : #define PING_PAYLOAD (LIBTEREDO_HMAC_LEN - 4)
     337                 : /**
     338                 :  * Sends an ICMPv6 Echo request toward an IPv6 node through the Teredo server.
     339                 :  */
     340                 : int
     341                 : SendPing (int fd, const union teredo_addr *src, const struct in6_addr *dst)
     342               0 : {
     343                 :         struct
     344                 :         {
     345                 :                 struct ip6_hdr ip6;
     346                 :                 struct icmp6_hdr icmp6;
     347                 :                 uint8_t payload[PING_PAYLOAD];
     348                 :         } ping;
     349                 : 
     350               0 :         ping.ip6.ip6_flow = htonl (0x60000000);
     351               0 :         ping.ip6.ip6_plen = htons (sizeof (ping.icmp6) + PING_PAYLOAD);
     352               0 :         ping.ip6.ip6_nxt = IPPROTO_ICMPV6;
     353               0 :         ping.ip6.ip6_hlim = 128;
     354               0 :         memcpy (&ping.ip6.ip6_src, src, sizeof (ping.ip6.ip6_src));
     355               0 :         memcpy (&ping.ip6.ip6_dst, dst, sizeof (ping.ip6.ip6_dst));
     356                 :         
     357               0 :         ping.icmp6.icmp6_type = ICMP6_ECHO_REQUEST;
     358               0 :         ping.icmp6.icmp6_code = 0;
     359               0 :         ping.icmp6.icmp6_cksum = 0;
     360                 :         /*
     361                 :         ping.icmp6.icmp6_id = 0;
     362                 :         ping.icmp6.icmp6_seq = 0;
     363                 :          */
     364               0 :         teredo_get_pinghash ((uint32_t)time (NULL), &ping.ip6.ip6_src,
     365                 :                              &ping.ip6.ip6_dst, (uint8_t *)&ping.icmp6.icmp6_id);
     366                 : 
     367               0 :         ping.icmp6.icmp6_cksum = icmp6_checksum (&ping.ip6, &ping.icmp6);
     368                 : 
     369               0 :         return teredo_send (fd, &ping, sizeof (ping.ip6) + sizeof (ping.icmp6)
     370                 :                             + PING_PAYLOAD, IN6_TEREDO_SERVER (src),
     371                 :                             htons (IPPORT_TEREDO)) > 0 ? 0 : -1;
     372                 : }
     373                 : 
     374                 : 
     375                 : /**
     376                 :  * Checks that the packet is an ICMPv6 Echo reply and authenticates it.
     377                 :  *
     378                 :  * @return 0 if that is the case, -1 otherwise.
     379                 :  */
     380                 : int CheckPing (const teredo_packet *packet)
     381               0 : {
     382               0 :         const struct ip6_hdr *ip6 = (const struct ip6_hdr *)packet->ip6;
     383               0 :         size_t length = packet->ip6_len;
     384                 : 
     385                 :         // Only read bytes, so no need to align
     386               0 :         if ((ip6->ip6_nxt != IPPROTO_ICMPV6)
     387                 :          || (length < (sizeof (*ip6) + sizeof (struct icmp6_hdr) + PING_PAYLOAD)))
     388               0 :                 return -1;
     389                 : 
     390               0 :         const struct icmp6_hdr *icmp6 = (const struct icmp6_hdr *)(ip6 + 1);
     391               0 :         const struct in6_addr *me = &ip6->ip6_dst, *it = &ip6->ip6_src;
     392                 : 
     393               0 :         if (icmp6->icmp6_type == ICMP6_DST_UNREACH)
     394                 :         {
     395                 :                 /*
     396                 :                  * NOTE:
     397                 :                  * Some brain-dead IPv6 nodes/firewalls don't reply to pings (which is
     398                 :                  * as explicit breakage of the IPv6/ICMPv6 specifications, btw). Some
     399                 :                  * of these brain-dead hosts reply with ICMPv6 unreachable messages.
     400                 :                  * We can authenticate them by looking at the payload of the message
     401                 :                  * and see if it is an ICMPv6 Echo request with the matching nonce in
     402                 :                  * it (Yes, this is a nasty kludge).
     403                 :                  *
     404                 :                  * NOTE 2:
     405                 :                  * We don't check source and destination addresses there...
     406                 :                  */
     407               0 :                 length -= sizeof (*ip6) + sizeof (*icmp6);
     408               0 :                 ip6 = (const struct ip6_hdr *)(icmp6 + 1);
     409                 : 
     410               0 :                 if ((length < (sizeof (*ip6) + sizeof (*icmp6) + PING_PAYLOAD))
     411                 :                  || (ip6->ip6_nxt != IPPROTO_ICMPV6))
     412               0 :                         return -1;
     413                 : 
     414                 :                 uint16_t plen;
     415               0 :                 memcpy (&plen, &ip6->ip6_plen, sizeof (plen));
     416               0 :                 if (ntohs (plen) != (sizeof (*icmp6) + PING_PAYLOAD))
     417               0 :                         return -1; // not a ping from us
     418                 : 
     419               0 :                 icmp6 = (const struct icmp6_hdr *)(ip6 + 1);
     420                 : 
     421               0 :                 if (memcmp (&ip6->ip6_src, me, sizeof (*me))
     422                 :                  || (icmp6->icmp6_type != ICMP6_ECHO_REQUEST))
     423               0 :                         return -1;
     424                 : 
     425                 :                 /*
     426                 :                  * The callers wants to check the ping as regards the Unreachable
     427                 :                  * error packet's source, not as regards the inner Echo Request
     428                 :                  * packet destination. Sometimes, firewalls send error with their
     429                 :                  * own address (as a router) as source. In that case, we must not
     430                 :                  * accept the packet. If we did, the core relaying logic would
     431                 :                  * believe that we have authenticated the firewall address, while
     432                 :                  * we really authenticated the inner packet destination address.
     433                 :                  * In practice, this would potentially be a way to bypass the HMAC
     434                 :                  * authentication of Teredo relays: once a relays catches one valid
     435                 :                  * HMAC authentified Echo reply from an IPv6 node, it could fake
     436                 :                  * error message and spoof any other non-Teredo IPv6 node, by setting
     437                 :                  * the ICMPv6 Destination Unreachable packet IPv6 source address to
     438                 :                  * that of a host to be spoofed.
     439                 :                  *
     440                 :                  * An alternive to simply dropping these packets would be to implement
     441                 :                  * a complex interaction between the caller and CheckPing(), so that
     442                 :                  * CheckPing() could notify the caller that the authenticated IPv6
     443                 :                  * node is not the packet's source. However, this is probably
     444                 :                  * overkill, and might not even work properly depending on the cuplrit
     445                 :                  * firewall rules.
     446                 :                  */
     447               0 :                 if (memcmp (&ip6->ip6_dst, it, sizeof (*it)))
     448               0 :                         return -1;
     449                 : 
     450               0 :                 me = &ip6->ip6_src;
     451               0 :                 it = &ip6->ip6_dst;
     452                 :         }
     453                 :         else
     454               0 :         if (icmp6->icmp6_type != ICMP6_ECHO_REPLY)
     455               0 :                 return -1;
     456                 : 
     457               0 :         if (icmp6->icmp6_code != 0)
     458               0 :                 return -1;
     459                 : 
     460               0 :         return teredo_verify_pinghash ((uint32_t)time (NULL), me, it,
     461                 :                                        (const uint8_t *)&icmp6->icmp6_id);
     462                 :         /* TODO: check the sum(?) */
     463                 : }
     464                 : #endif
     465                 : 
     466                 : 
     467                 : /**
     468                 :  * Builds an ICMPv6 error message with specified type and code from an IPv6
     469                 :  * packet. The output buffer must be at least 1240 bytes long and have
     470                 :  * adequate IPv6 packet alignment. The ICMPv6 checksum is not set as they are
     471                 :  * not enough information for its computation.
     472                 :  *
     473                 :  * It is assumed that the output buffer is properly aligned. The input
     474                 :  * buffer does not need to be aligned.
     475                 :  *
     476                 :  * @param out output buffer
     477                 :  * @param type ICMPv6 error type
     478                 :  * @param code ICMPv6 error code
     479                 :  * @param in original IPv6 packet
     480                 :  * @param inlen original IPv6 packet length (including IPv6 header)
     481                 :  *
     482                 :  * @return the actual size of the generated error message, or zero if no
     483                 :  * ICMPv6 packet should be generated. Never fails.
     484                 :  */
     485                 : int
     486                 : BuildICMPv6Error (struct icmp6_hdr *restrict out, uint8_t type, uint8_t code,
     487                 :                   const void *restrict in, size_t inlen)
     488               0 : {
     489                 :         const struct in6_addr *p;
     490                 : 
     491                 :         /* don't reply if the packet is too small */
     492               0 :         if (inlen < sizeof (struct ip6_hdr))
     493               0 :                 return 0;
     494                 : 
     495                 :         /* don't reply to ICMPv6 error */
     496               0 :         if ((((const struct ip6_hdr *)in)->ip6_nxt == IPPROTO_ICMPV6)
     497                 :           && ((((const struct icmp6_hdr *)(((const struct ip6_hdr *)in) + 1))
     498                 :                                                 ->icmp6_type & 0x80) == 0))
     499               0 :                 return 0;
     500                 : 
     501                 :         /* don't reply to multicast */
     502               0 :         if (((const struct ip6_hdr *)in)->ip6_dst.s6_addr[0] == 0xff)
     503               0 :                 return 0;
     504                 : 
     505               0 :         p = &((const struct ip6_hdr *)in)->ip6_src;
     506                 :         /* don't reply to incorrect source address (multicast, undefined) */
     507               0 :         if ((p->s6_addr[0] == 0xff) /* multicast */
     508                 :          || (memcmp (p, &in6addr_any, sizeof (*p)) == 0))
     509               0 :                 return 0;
     510                 : 
     511               0 :         out->icmp6_type = type;
     512               0 :         out->icmp6_code = code;
     513               0 :         out->icmp6_cksum = 0;
     514               0 :         out->icmp6_data32[0] = 0;
     515                 : 
     516               0 :         if (inlen > 1280 - (sizeof (struct ip6_hdr) + sizeof (struct icmp6_hdr)))
     517               0 :                 inlen = 1280 - (sizeof (struct ip6_hdr) + sizeof (struct icmp6_hdr));
     518                 : 
     519               0 :         memcpy (out + 1, in, inlen);
     520                 : 
     521               0 :         return sizeof (struct icmp6_hdr) + inlen;
     522                 : }
     523                 : 
     524                 : 
     525                 : #if 0
     526                 : /**
     527                 :  * Builds an ICMPv6/IPv6 error message with specified type and code from an
     528                 :  * IPv6 packet. The output buffer must be at least 1280 bytes long and have
     529                 :  * adequate IPv6 packet alignment.
     530                 :  *
     531                 :  * It is assumed that the output buffer is properly aligned. The input
     532                 :  * buffer does not need to be aligned.
     533                 :  *
     534                 :  * @param out output buffer
     535                 :  * @param type ICMPv6 error type
     536                 :  * @param code ICMPv6 error code
     537                 :  * @param src source IPv6 address for ICMPv6 message
     538                 :  * @param in original IPv6 packet
     539                 :  * @param len original IPv6 packet length (including IPv6 header)
     540                 :  *
     541                 :  * @return the actual size of the generated error message, or zero if no
     542                 :  * ICMPv6/IPv6 packet should be sent. Never fails.
     543                 :  */
     544                 : int
     545                 : BuildIPv6Error (struct ip6_hdr *out, const struct in6_addr *src,
     546                 :                 uint8_t type, uint8_t code, const void *in, uint16_t len)
     547                 : {
     548                 :         struct icmp6_hdr *h;
     549                 : 
     550                 :         h = (struct icmp6_hdr *)(out + 1);
     551                 :         len = BuildICMPv6Error (h, type, code, in, len);
     552                 :         if (len == 0)
     553                 :                 return 0;
     554                 : 
     555                 :         out->ip6_flow = htonl (0x60000000);
     556                 :         out->ip6_plen = htons (len);
     557                 :         out->ip6_nxt = IPPROTO_ICMPV6;
     558                 :         out->ip6_hlim = 255;
     559                 :         memcpy (&out->ip6_src, src, sizeof (out->ip6_src));
     560                 :         memcpy (&out->ip6_dst, &((const struct ip6_hdr *)in)->ip6_src,
     561                 :                 sizeof (out->ip6_dst));
     562                 :         
     563                 :         len += sizeof (struct ip6_hdr);
     564                 : 
     565                 :         h->icmp6_cksum = icmp6_checksum (out, h);
     566                 :         return len;
     567                 : }
     568                 : #endif

Generated by: LTP GCOV extension version 1.5