// Port Control Protocol prototyping.
// RFC 6887

class CPCPRequestHeader
{
        U8      version;
        U8      opcode;                         // includes R bit.
        U16     reserved;                       // must be all 0 on TX
        U32     req_lifetime;           // seconds. 0 interpreted as 'delete'.
        U8      pcp_client_ip[16];      // source ipv4 addr converted to ipv4-mapped ipv6 as per spec. 128 bits
};

class CPCPMapRequest
{
        CPCPRequestHeader       header;
        U8                                      nonce[12];              // "Mapping Nonce" 96 bits of random vals.
        U8                                      protocol;               // 6 TCP. 17 UDP. 0 'all protocols'.
        U8                                      reserved[3];    // 24 bits, TX must be 0, ignore on RX.

        U16                                     internal_port;
        /*      internal_port. 0 'all ports' legal only on lifetime 0 (delete) or client req 'all ports'.
                must be ignored on RX. */

        U16                                     external_port;
        /*      external_port. 'suggested'. if client doesnt know it or has no preference it must be 0.*/

        U8                                      external_ip[16];
        /*      external_ip. 'suggested'. if client doesnt know it or no preference,
                                                                  it must use 'address-family-specific all-zero address. */

};

I64 PCPSendMapRequest(U16 internal_port, U16 external_port=NULL, U32 external_ip=NULL)
{
        U8                              *frame;
        CPCPMapRequest  *request;
        I64                              de_index;
        I64                              i;

        de_index = UDPPacketAllocate(&frame,
                                                                 IPV4AddressGet,
                                                                 5351,
                                                                 ipv4_globals.ipv4_router_address,
                                                                 5351,
                                                                 sizeof(CPCPMapRequest));
        if (de_index < 0)
        {
                NetErr("PCP SEND MAP REQ: Failed, UDPPacketAllocate returned error.");
                return de_index;
        }

        request = frame;

        request->header.version                 = 2;
        request->header.opcode                  = 1; // MAP
        request->header.req_lifetime    = 60; // live for 1 min.

        // force local IPV4 addr into IPV4-mapped-IPV6
        for (i = 0; i < 10; i++) // first 80 bits 0
                request->header.pcp_client_ip[i] = 0;

        for (; i < 10 + 2; i++) // next 16 bits 1
                request->header.pcp_client_ip[i] = 0xFF;

        *(&request->header.pcp_client_ip[i](U32 *)) = EndianU32(IPV4AddressGet);
        //

        // set random 'nonce'
        for (i = 0; i < 12; i++)
                request->nonce[i] = RandU8;

        request->protocol = IP_PROTOCOL_UDP;

        request->internal_port = internal_port;
        request->external_port = external_port;

        if (!external_ip)
        { // if no specified ext ip, use specific ipv4-mapped-ipv6 all-zeroes address.
                for (i = 0; i < 10; i++)
                        request->external_ip[i] = 0;
                for (; i < 10 + 2; i++)
                        request->external_ip[i] = 0xFF;
                *(&request->external_ip[i](U32 *)) = 0;
        }
        else
        { // else, map to param ip
                for (i = 0; i < 10; i++)
                        request->external_ip[i] = 0;
                for (; i < 10 + 2; i++)
                        request->external_ip[i] = 0xFF;
                *(&request->external_ip[i](U32 *)) = EndianU32(external_ip);
        }

        UDPPacketFinish(de_index);
        return 0;

}

PCPSendMapRequest(0xDEAD, 0xBEEF);