/* GTPv1-U Templates in TTCN-3
 * (C) 2018 Harald Welte <laforge@gnumonks.org>
 * contributions by sysmocom - s.f.m.c. GmbH
 * All rights reserved.
 *
 * Released under the terms of GNU General Public License, Version 2 or
 * (at your option) any later version.
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

module GTPv1U_Templates {

	import from General_Types all;
	import from Osmocom_Types all;
	import from GTPU_Types all;
	import from GTPv1U_CodecPort all;

	template (present) PDU_GTPU tr_GTP1U_PDU(template (present) OCT1 msg_type,
						 template (present) OCT4 teid,
						 template (present) GTPU_IEs ies := ?) := {
		pn_bit := ?,
		s_bit := ?,
		e_bit := ?,
		spare := ?,
		/* Protocol Type flag (PT) shall be set to '1' in GTP */
		pt := '1'B,
		/* Version shall be set to decimal 1 ('001'). */
		version := '001'B,
		messageType := msg_type,
		lengthf := ?,
		teid := teid,
		opt_part := *,
		gtpu_IEs := ies
	}

	function f_GTPU_s_bit(template (omit) GTPU_Header_optional_part opt_part) return BIT1 {
		if (istemplatekind(opt_part, "omit")) {
			return '0'B;
		}
		return '1'B;
	}

	function f_GTPU_e_bit(template (omit) GTPU_Header_optional_part opt_part) return BIT1 {
		if (istemplatekind(opt_part, "omit")) {
			return '0'B;
		}
		if (istemplatekind(opt_part.gTPU_extensionHeader_List, "omit")) {
			return '0'B;
		}
		return '1'B;
	}

	function f_GTPU_opt_part_from_seq(template (omit) uint16_t seq) return template (omit) GTPU_Header_optional_part {
		if (istemplatekind(seq, "omit")) {
			return omit;
		}
		var GTPU_Header_optional_part ret := {
			sequenceNumber := int2oct(valueof(seq), 2),
			npduNumber := '00'O,
			nextExtHeader := '00'O,
			gTPU_extensionHeader_List := omit
		};
		return ret;
	}

	/* generalized GTP-U send template */
	template (value) PDU_GTPU ts_GTP1U_PDU(template (value) OCT1 msg_type,
					       template (omit) GTPU_Header_optional_part opt_part,
					       template (value) OCT4 teid,
					       template (value) GTPU_IEs ies) := {
		/* N-PDU Number flag (PN): the GTP-U header contains a meaningful N-PDU Number field if the PN
		 * flag is set to 1. */
		pn_bit := '0'B,	/* we assume the encoder overwrites this if an optional part is given */
		/* If the Sequence Number flag (S) is set to '1' the sequence number field is present and
		 * meaningful otherwise it is set to '0'. For GTP-U messages Echo Request, Echo Response,
		 * Error Indication and Supported Extension Headers Notification, the S flag shall be set to '1'.
		 *
		 * Note that the caller must ensure that these conditions hold.
		 * The caller can either pass a sequence number (we set s_bit to '1'B) when appropriate,
		 * or may omit the sequence number (we set s_bit to '0'B). */
		s_bit := f_GTPU_s_bit(opt_part),
		/* Extension header presence */
		e_bit := f_GTPU_e_bit(opt_part),
		spare := '0'B,
		/* Protocol Type flag (PT) shall be set to '1' in GTP */
		pt := '1'B,
		/* Version shall be set to decimal 1 ('001'). */
		version := '001'B,
		messageType := msg_type,
		lengthf := 0,	/* we assume encoder overwrites this */
		teid := teid,
		opt_part := opt_part,
		gtpu_IEs := ies
	}

	template (present) Gtp1uUnitdata tr_GTPU_MsgType(template (present) Gtp1uPeer peer,
							 template (present) OCT1 msg_type,
							 template (present) OCT4 teid) := {
		peer := peer,
		gtpu := tr_GTP1U_PDU(msg_type, teid)
	}


	/* template matching reception of GTP-U echo-request/response */
	template (present) Gtp1uUnitdata tr_GTPU_PING(template (present) Gtp1uPeer peer) := tr_GTPU_MsgType(peer, '01'O, '00000000'O);
	template (present) Gtp1uUnitdata tr_GTPU_PONG(template (present) Gtp1uPeer peer) := tr_GTPU_MsgType(peer, '02'O, '00000000'O);

	/* template matching reception of GTP-U GPDU */
	template GTPU_IEs t_GPDU(template (present) octetstring data) := {
		g_PDU_IEs := {
			data := data
		}
	}
	template (present) Gtp1uUnitdata tr_GTPU_GPDU(template (present) Gtp1uPeer peer,
						      template (present) OCT4 teid,
						      template (present) octetstring data := ?) := {
		peer := peer,
		gtpu := tr_GTP1U_PDU('FF'O, teid, t_GPDU(data))
	}

	template (present) GTPU_IEs ts_UEchoReqPDU := {
		echoRequest_IEs := {
			private_extension_gtpu := omit
		}
	}

	/* master template for sending a GTP-C echo request */
	template (value) Gtp1uUnitdata ts_GTPU_PING(template (value) Gtp1uPeer peer,
						    template (value) uint16_t seq) := {
		peer := peer,
		gtpu := ts_GTP1U_PDU('01'O, f_GTPU_opt_part_from_seq(seq), '00000000'O, ts_UEchoReqPDU)
	}

	template GTPU_IEs ts_UEchoRespPDU(template (value) OCT1 restart_counter) := {
		echoResponse_IEs := {
			recovery_gtpu := {
				type_gtpu := '00'O, /* we assume encoder fixes? */
				restartCounter := restart_counter
			},
			private_extension_gtpu := omit
		}
	}

	/* master template for sending a GTP-U echo response */
	template (present) Gtp1uUnitdata ts_GTPU_PONG(template (value) Gtp1uPeer peer,
						      template (value) uint16_t seq,
						      template (value) OCT1 rest_ctr) := {
		peer := peer,
		gtpu := ts_GTP1U_PDU('02'O, f_GTPU_opt_part_from_seq(seq), '00000000'O, ts_UEchoRespPDU(rest_ctr))
	}

	template (present) GSNAddress_gtpu tr_UGsnAddr(template (present) octetstring ip_addr := ?) := {
		type_gtpu := '85'O,
		lengthf := ?,
		gSNAddressValue := ip_addr
	}
	template (value) GSNAddress_gtpu ts_UGsnAddr(template (value) octetstring ip_addr) := {
		type_gtpu := '85'O,
		lengthf := lengthof(valueof(ip_addr)),
		gSNAddressValue := ip_addr
	}

	template (present) TeidDataI_gtpu tr_UteidDataI(template (present) OCT4 teid := ?) := {
		type_gtpu := '10'O,
		teidDataI := teid
	}
	template (value) TeidDataI_gtpu ts_UteidDataI(template (value) OCT4 teid) := {
		type_gtpu := '10'O,
		teidDataI := teid
	}

	template (present)  GTPU_IEs tr_UErrorIndication(template (present) OCT4 teid := ?,
							 template (present) octetstring gsn_addr := ?) := {
		errorIndication_IEs := {
			teidDataI_gtpu := tr_UteidDataI(teid),
			gSNAddress_gtpu := tr_UGsnAddr(gsn_addr),
			private_extension_gtpu := *
		}
	}
	template (present) Gtp1uUnitdata tr_GTPU_ErrorIndication(template (present) Gtp1uPeer peer := ?,
								 template (present) OCT4 teid := ?,
								 template (present) octetstring gsn_addr := ?) := {
		peer := peer,
		gtpu := tr_GTP1U_PDU('1A'O, '00000000'O, tr_UErrorIndication(teid, gsn_addr))
	}

	template (value)  GTPU_IEs ts_UErrorIndication(template (value) OCT4 teid,
						       template (value) octetstring gsn_addr) := {
		errorIndication_IEs := {
			teidDataI_gtpu := ts_UteidDataI(teid),
			gSNAddress_gtpu := ts_UGsnAddr(gsn_addr),
			private_extension_gtpu := omit
		}
	}
	/* master template for sending a GTP-U Error indication */
	template (value) Gtp1uUnitdata ts_GTPU_ErrorIndication(template (value) Gtp1uPeer peer,
							       template (value) uint16_t seq,
							       template (value) OCT4 teid,
							       template (value) octetstring gsn_addr) := {
		peer := peer,
		gtpu := ts_GTP1U_PDU('1A'O, f_GTPU_opt_part_from_seq(seq), '00000000'O, ts_UErrorIndication(teid, gsn_addr))
	}

	/* master template for sending a GTP-U user plane data */
	template (value) Gtp1uUnitdata ts_GTP1U_GPDU(template (value) Gtp1uPeer peer,
						     template (omit) GTPU_Header_optional_part opt_part,
						     template (value) OCT4 teid,
						     template (value) octetstring data) := {
		peer := peer,
		gtpu := ts_GTP1U_PDU('FF'O, opt_part, teid, { g_PDU_IEs := { data := data }})
	}
}