// 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,
								 IPV4GetAddress(),
								 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(IPV4GetAddress());
	//

	// 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);