GGSN IPv6: Transmit Router Solicit, receive RouterAdv, transmit NeightSolicit
diff --git a/ggsn_tests/GGSN_Tests.ttcn b/ggsn_tests/GGSN_Tests.ttcn
index d485fd8..43e754b 100644
--- a/ggsn_tests/GGSN_Tests.ttcn
+++ b/ggsn_tests/GGSN_Tests.ttcn
@@ -8,6 +8,8 @@
import from GTP_CodecPort_CtrlFunct all;
import from GTPC_Types all;
import from GTPU_Types all;
+ import from IP_Types all;
+ import from ICMPv6_Types all;
const integer GTP0_PORT := 3386;
const integer GTP1C_PORT := 2123;
@@ -20,6 +22,7 @@
octetstring msisdn optional,
octetstring apn,
EndUserAddress eua,
+ OCT16 ip6_prefix optional,
BIT4 nsapi,
/* TEI (Data) local side */
OCT4 teid,
@@ -336,7 +339,7 @@
/* GTP-U */
- template PDU_GTPU tr_GTP1U_PDU(template OCT1 msg_type, template OCT4 teid) := {
+ template PDU_GTPU tr_GTP1U_PDU(template OCT1 msg_type, template OCT4 teid, template GTPU_IEs ies := ?) := {
pn_bit := ?,
s_bit := ?,
e_bit := ?,
@@ -349,7 +352,7 @@
lengthf := ?,
teid := teid,
opt_part := *,
- gtpu_IEs := ?
+ gtpu_IEs := ies
}
/* generalized GTP-U send template */
@@ -386,9 +389,20 @@
}
- /* template matching reception of GTP-C echo-request */
+ /* template matching reception of GTP-U echo-request */
template Gtp1uUnitdata tr_GTPU_PING(template GtpPeer peer) := tr_GTPU_MsgType(peer, echoRequest, '00000000'O);
+ /* template matching reception of GTP-U GPDU */
+ template GTPU_IEs t_GPDU(template octetstring data) := {
+ g_PDU_IEs := {
+ data := data
+ }
+ }
+ template Gtp1uUnitdata tr_GTPU_GPDU(template GtpPeer peer, template OCT4 teid, template octetstring data := ?) := {
+ peer := peer,
+ gtpu := tr_GTP1U_PDU('FF'O, teid, t_GPDU(data))
+ }
+
template GTPU_IEs ts_UEchoRespPDU(OCT1 restart_counter) := {
echoResponse_IEs := {
recovery_gtpu := {
@@ -466,8 +480,8 @@
}
/* send GTP-U for a given context and increment sequence number */
- function f_send_gtpu(inout PdpContext ctx, in template Gtp1uUnitdata data) runs on GT_CT {
- GTPU.send(data);
+ function f_send_gtpu(inout PdpContext ctx, in octetstring data) runs on GT_CT {
+ GTPU.send(ts_GTP1U_GPDU(g_peer_u, ctx.d_seq_nr, ctx.teid_remote, data));
ctx.d_seq_nr := ctx.d_seq_nr + 1;
}
@@ -488,6 +502,7 @@
if (cpr.cause.causevalue == '80'O) {
ctx.teid_remote := cpr.teidDataI.teidDataI;
ctx.teic_remote := cpr.teidControlPlane.teidControlPlane;
+ ctx.eua := cpr.endUserAddress;
setverdict(pass);
} else {
setverdict(fail);
@@ -528,12 +543,200 @@
f_pdp_ctx_act(ctx);
}
+ /* template to generate a 'Prefix Information' ICMPv6 option */
+ template OptionField ts_ICMP6_OptPrefix(OCT16 prefix, INT1 prefix_len) := {
+ prefixInformation := {
+ typeField := 3,
+ lengthIndicator := 8,
+ prefixLength := prefix_len,
+ reserved1 := '000000'B,
+ a_Bit := '0'B,
+ l_Bit := '0'B,
+ validLifetime := oct2int('FFFFFFFF'O),
+ preferredLifetime := oct2int('FFFFFFFF'O),
+ reserved2 := '00000000'O,
+ prefix := prefix
+ }
+ }
+
+ /* template for an ICMPv6 router solicitation */
+ template PDU_ICMPv6 ts_ICMPv6_RS := {
+ routerSolicitation := {
+ typeField := 133,
+ code := 0,
+ checksum := '0000'O,
+ reserved := '00000000'O,
+ /* TODO: do we need 'Source link-layer address' ? */
+ options := omit
+ }
+ }
+
+ /* template for an ICMPv6 router advertisement */
+ template PDU_ICMPv6 ts_ICMPv6_RA(OCT16 prefix, INT1 prefix_len) := {
+ routerAdvertisement := {
+ typeField := 134,
+ code := 0,
+ checksum := '0000'O,
+ curHopLimit := ?,
+ reserved := '000000'B,
+ o_Bit := '0'B,
+ m_Bit := '0'B,
+ routerLifetime := oct2int('FFFF'O),
+ reachableTime := oct2int('FFFFFFFF'O),
+ retransTimer := oct2int('FFFFFFFF'O),
+ options := {
+ ts_ICMP6_OptPrefix(prefix, prefix_len)
+ }
+ }
+ }
+
+ template PDU_ICMPv6 ts_ICMPv6_NS(OCT16 target_addr) := {
+ neighborSolicitation := {
+ typeField := 135,
+ code := 0,
+ checksum := '0000'O,
+ reserved := '00000000'O,
+ targetAddress := target_addr,
+ /* TODO: do we need 'Source link-layer address' ? */
+ options := omit
+ }
+ }
+
+ /* derive ICMPv6 link-local address from lower 64bit of link_id */
+ /* template for receiving/matching an ICMPv6 'Prefix Information' option */
+ template OptionField tr_ICMP6_OptPrefix(template OCT16 prefix, template INT1 prefix_len) := {
+ prefixInformation := {
+ typeField := 3,
+ lengthIndicator := 4,
+ prefixLength := prefix_len,
+ reserved1 := ?,
+ a_Bit := ?,
+ l_Bit := ?,
+ validLifetime := ?,
+ preferredLifetime := ?,
+ reserved2 := ?,
+ prefix := prefix
+ }
+ }
+
+ /* template for receiving/matching an ICMPv6 router advertisement */
+ template PDU_ICMPv6 tr_ICMPv6_RA(template OCT16 prefix, template INT1 prefix_len) := {
+ routerAdvertisement := {
+ typeField := 134,
+ code := 0,
+ checksum := ?,
+ curHopLimit := ?,
+ reserved := ?,
+ o_Bit := '0'B,
+ m_Bit := '0'B,
+ routerLifetime := ?,
+ reachableTime := ?,
+ retransTimer := ?,
+ options := {
+ tr_ICMP6_OptPrefix(prefix, prefix_len)
+ }
+ }
+ }
+
+ /* template to construct IPv6_packet from input arguments, ready for use in f_IPv6_enc() */
+ template IPv6_packet ts_IP6(OCT16 srcaddr, OCT16 dstaddr, LIN1 nexthead, octetstring payload, LIN1 hlim := 255) := {
+ header := {
+ ver := 6,
+ trclass := 0,
+ flabel := 0,
+ plen := 0,
+ nexthead := nexthead,
+ hlim := hlim,
+ srcaddr := srcaddr,
+ dstaddr := dstaddr
+ },
+ ext_headers := omit,
+ payload := payload
+ }
+
+ function f_ipv6_link_local(in OCT16 link_id) return OCT16 {
+ return 'FE80000000000000'O & substr(link_id, 8, 8);
+ }
+
+ /* Compute solicited-node multicast address as per RFC4291 2.7.1 */
+ function f_ipv6_sol_node_mcast(in OCT16 addr) return OCT16 {
+ return 'FF0200000000000000000001FF'O & substr(addr, 13, 3);
+ }
+
+ /* generate and encode ICMPv6 router solicitation */
+ function f_gen_icmpv6_router_solicitation(in OCT16 link_id) return octetstring {
+ const OCT16 c_ip6_all_router_mcast := 'FF020000000000000000000000000002'O;
+ var OCT16 saddr := f_ipv6_link_local(link_id);
+
+ var octetstring tmp;
+ tmp := f_enc_PDU_ICMPv6(valueof(ts_ICMPv6_RS), saddr, c_ip6_all_router_mcast);
+ var IPv6_packet ip6 := valueof(ts_IP6(saddr, c_ip6_all_router_mcast, 58, tmp));
+
+ return f_IPv6_enc(ip6);
+ }
+
+ /* create ICMPv6 router solicitation deriving link-id from PDP Context EUA */
+ function f_icmpv6_rs_for_pdp(in PdpContext ctx) return octetstring {
+ var OCT16 interface_id := ctx.eua.endUserAddress.endUserAddressIPv6.ipv6_address;
+ return f_gen_icmpv6_router_solicitation(interface_id);
+ }
+
+ /* generate and encode ICMPv6 neighbor solicitation */
+ function f_gen_icmpv6_neigh_solicit(in OCT16 saddr, in OCT16 daddr, in OCT16 tgt_addr) return octetstring {
+ var octetstring tmp;
+ tmp := f_enc_PDU_ICMPv6(valueof(ts_ICMPv6_NS(tgt_addr)), saddr, daddr);
+ var IPv6_packet ip6 := valueof(ts_IP6(saddr, daddr, 58, tmp));
+ return f_IPv6_enc(ip6);
+ }
+
+ /* generate and encode ICMPv6 neighbor solicitation for PDP Context */
+ function f_gen_icmpv6_neigh_solicit_for_pdp(in PdpContext ctx) return octetstring {
+ var OCT16 interface_id := ctx.eua.endUserAddress.endUserAddressIPv6.ipv6_address;
+ var OCT16 link_local := f_ipv6_link_local(interface_id);
+ var OCT16 daddr := f_ipv6_sol_node_mcast(link_local);
+
+ return f_gen_icmpv6_neigh_solicit(link_local, daddr, link_local);
+ }
+
+ /* wait for GGSN to send us an ICMPv6 router advertisement */
+ function f_wait_rtr_adv(PdpContext ctx) runs on GT_CT {
+ var Gtp1uUnitdata ud;
+ T_default.start;
+ alt {
+ //'6???????????3aff'O
+ [] GTPU.receive(tr_GTPU_GPDU(g_peer_u, ?)) -> value ud {
+ var octetstring gpdu := ud.gtpu.gtpu_IEs.g_PDU_IEs.data;
+ var IPv6_packet ip6 := f_IPv6_dec(gpdu);
+ if (ip6.header.ver != 6 or ip6.header.nexthead != 58 or ip6.header.hlim != 255) {
+ repeat;
+ }
+ var PDU_ICMPv6 icmp6 := f_dec_PDU_ICMPv6(ip6.payload);
+ if (not match(icmp6, tr_ICMPv6_RA(?, 64))) {
+ repeat;
+ }
+ ctx.ip6_prefix := icmp6.routerAdvertisement.options[0].prefixInformation.prefix;
+ log("RA with /64 prefix ", ctx.ip6_prefix);
+ }
+ [] GTPU.receive(tr_GTPU_GPDU(?, ?)) { repeat; }
+ [] GTPU.receive { setverdict(fail); }
+ [] T_default.timeout { setverdict(fail); }
+ }
+ T_default.stop;
+ }
+
testcase TC_activate_pdp6() runs on GT_CT {
f_init();
+
var PdpContext ctx := valueof(t_DefinePDP('262420123456789'H, '1234'O, valueof(t_ApnInternet), valueof(t_EuaIPv6Dyn)));
f_pdp_ctx_act(ctx);
- f_send_gtpu(ctx, ts_GTP1U_GPDU(g_peer_u, ctx.d_seq_nr, ctx.teid_remote, c_router_solicit));
- f_send_gtpu(ctx, ts_GTP1U_GPDU(g_peer_u, ctx.d_seq_nr, ctx.teid_remote, c_neigh_solicit));
+
+ //f_send_gtpu(ctx, c_router_solicit);
+ //f_send_gtpu(ctx, c_neigh_solicit);
+
+ f_send_gtpu(ctx, f_icmpv6_rs_for_pdp(ctx));
+ f_wait_rtr_adv(ctx);
+ f_send_gtpu(ctx, f_gen_icmpv6_neigh_solicit_for_pdp(ctx));
+
f_pdp_ctx_del(ctx, '1'B);
}