netfilter-titan/testproject/IPL4_example.ttcn

575 lines
17 KiB
Plaintext

module IPL4_example {
import from General_Types all;
import from IP_Types all;
import from UDP_Types all;
import from CommonDefs all;
import from TunDevice_PortType all;
import from TunDevice_Types all;
import from NetfilterConntrack_Functions all;
import from http_www_netfilter_org_xml_libnetfilter_conntrack all;
import from XSD all;
external function enc_Flow(in Flow pdu) return octetstring
with { extension "prototype (convert) encode(XER:XER_EXTENDED)" }
external function dec_Flow(in octetstring stream) return Flow
with { extension "prototype (convert) decode(XER:XER_EXTENDED)" }
external function enc_Flows(in Flows pdu) return octetstring
with { extension "prototype (convert) encode(XER:XER_EXTENDED)" }
external function dec_Flows(in octetstring stream) return Flows
with { extension "prototype (convert) decode(XER:XER_EXTENDED)" }
type component dummy_CT {
port TunDevice_PT TUN;
port TunDevice_PT TUN2;
var boolean initialized := false;
timer T := 3.0;
}
template (value) Tun_send tunmsg(octetstring p_data) := { msg := p_data };
template (value) Tun_recv tunrcv(octetstring p_data) := { msg := p_data };
const integer AF_INET := 2;
const integer AF_INET6 := 23;
import from UsefulTtcn3Types all;
type enumerated nfct_direction { DIR_ORIG, DIR_REPLY };
type record flow_info {
unsignedbyte l3_protocol,
charstring src_ip,
charstring dst_ip,
unsignedbyte l4_protocol,
unsignedshort src_port,
unsignedshort dst_port
}
type record pkt_info {
nfct_direction direction,
octetstring payload,
NonNegativeInteger trunc_len,
boolean exp_pass
}
/* generate a flow_info using pre-defined default addresses + * incremented port */
private function flow_gen(integer port_delta, unsignedbyte l3_prot := 4, unsignedbyte l4_prot := c_ip_proto_udp) return flow_info {
var flow_info flowi := {
l3_protocol := c_ip_proto_ipv4,
src_ip := "1.1.1.200",
dst_ip := "2.2.2.200",
l4_protocol := l4_prot,
src_port := 1000 + port_delta,
dst_port := 2000 + port_delta
}
if (l3_prot == 6) {
flowi.l3_protocol := c_ip_proto_ipv6;
flowi.src_ip := "";
flowi.dst_ip := "";
}
return flowi
}
private function get_random_port_offset() return unsignedshort {
var float r := rnd();
return float2int(r * (65535.0 - 2000.0));
}
private function get_random_id() return unsignedshort {
var float r := rnd();
return float2int(r * 65535.0);
}
template IPv4_header t_ipv4h_normal(LIN1 l4_proto, OCT4 saddr, OCT4 daddr) := {
ver := c_ip_version_ipv4,
hlen := 5,
tos := 0,
tlen := 0,
id := get_random_id(),
res := '0'B,
dfrag := '0'B,
mfrag := '0'B,
foffset := 0,
ttl := 128,
proto := l4_proto,
cksum := 0,
srcaddr := saddr,
dstaddr := daddr
}
template IPv6_header t_ipv6h_normal(LIN1 l4_proto, OCT16 saddr, OCT16 daddr, NonNegativeInteger plen) := {
ver := c_ip_version_ipv6,
trclass := 0,
flabel := 0,
plen := plen,
nexthead := l4_proto,
hlim := 128,
srcaddr := saddr,
dstaddr := daddr
}
/* generate a payload (L5+) */
private function flow_gen_payload(flow_info flowi, pkt_info pkti) return octetstring {
var integer random_offset := float2int(rnd()*256.0);
return substr(tsc_RandomPRBS, random_offset, 232);
}
/* generate a UDP pseudo-header */
private function flow_gen_udp_pseudo_hdr4(flow_info flowi, pkt_info pkti, integer plen) return UDP_pseudo_header {
var UDP_pseudo_header ret := {
ipv4 := {
zero := 0,
proto := c_ip_proto_udp,
plen := plen
}
};
if (pkti.direction == DIR_ORIG) {
ret.ipv4.srcaddr := f_IPv4_addr_enc(flowi.src_ip);
ret.ipv4.dstaddr := f_IPv4_addr_enc(flowi.dst_ip);
} else {
ret.ipv4.srcaddr := f_IPv4_addr_enc(flowi.dst_ip);
ret.ipv4.dstaddr := f_IPv4_addr_enc(flowi.src_ip);
}
return ret;
}
private function flow_gen_udp_pseudo_hdr6(flow_info flowi, pkt_info pkti, integer plen) return UDP_pseudo_header {
var UDP_pseudo_header ret := {
ipv6 := {
plen := plen,
zero := 0,
nextheader := c_ip_proto_udp
}
};
if (pkti.direction == DIR_ORIG) {
ret.ipv6.srcaddr := f_IPv6_addr_enc(flowi.src_ip);
ret.ipv6.dstaddr := f_IPv6_addr_enc(flowi.dst_ip);
} else {
ret.ipv6.srcaddr := f_IPv6_addr_enc(flowi.dst_ip);
ret.ipv6.dstaddr := f_IPv6_addr_enc(flowi.src_ip);
}
return ret;
}
private function flow_gen_udp_pseudo_hdr(flow_info flowi, pkt_info pkti, integer plen) return UDP_pseudo_header {
if (flowi.l3_protocol == c_ip_proto_ipv4) {
return flow_gen_udp_pseudo_hdr4(flowi, pkti, plen);
} else if (flowi.l3_protocol == c_ip_proto_ipv6) {
return flow_gen_udp_pseudo_hdr6(flowi, pkti, plen);
} else {
testcase.stop("unknown/unsupported L3 Protocol");
}
}
/* generate a L4 packet according to flowi + pkti */
private function flow_gen_l4_pkt(flow_info flowi, pkt_info pkti) return octetstring {
var octetstring payload := flow_gen_payload(flowi, pkti);
var octetstring data;
var OCT2 csum;
if (flowi.l4_protocol == c_ip_proto_udp) {
var UDP_packet udp := {
header := {
len := lengthof(payload),
cksum := 0
},
payload := payload
};
if (pkti.direction == DIR_ORIG) {
udp.header.srcport := flowi.src_port;
udp.header.dstport := flowi.dst_port;
} else {
udp.header.srcport := flowi.dst_port;
udp.header.dstport := flowi.src_port;
}
data := f_UDP_enc(udp);
csum := f_UDP_checksum(f_UDP_pseudo_header_enc(flow_gen_udp_pseudo_hdr(flowi, pkti, lengthof(data))) & data);
data[6] := csum[0];
data[7] := csum[1];
} else {
}
return data;
}
/* generate an IPv4 packet according to flowi + pkti */
function flow_gen_ipv4_pkt(flow_info flowi, pkt_info pkti) return octetstring {
var octetstring data;
var OCT2 csum;
var charstring src_ip, dst_ip;
if (pkti.direction == DIR_ORIG) {
src_ip := flowi.src_ip;
dst_ip := flowi.dst_ip;
} else {
src_ip := flowi.dst_ip;
dst_ip := flowi.src_ip;
}
var IPv4_packet ip := {
header := valueof(t_ipv4h_normal(flowi.l4_protocol,
f_IPv4_addr_enc(src_ip),
f_IPv4_addr_enc(dst_ip))),
ext_headers := omit,
payload := flow_gen_l4_pkt(flowi, pkti)
}
data := f_IPv4_enc(ip);
csum := f_IPv4_checksum(data);
data[10] := csum[0];
data[11] := csum[1];
return data;
}
/* generate an IPv6 packet according to flowi + pkti */
function flow_gen_ipv6_pkt(flow_info flowi, pkt_info pkti) return octetstring {
var octetstring payload := flow_gen_l4_pkt(flowi, pkti);
var octetstring data;
var OCT2 csum;
var charstring src_ip, dst_ip;
if (pkti.direction == DIR_ORIG) {
src_ip := flowi.src_ip;
dst_ip := flowi.dst_ip;
} else {
src_ip := flowi.dst_ip;
dst_ip := flowi.src_ip;
}
var IPv6_packet ip := {
header := valueof(t_ipv6h_normal(flowi.l4_protocol, f_IPv4_addr_enc(src_ip), f_IPv4_addr_enc(dst_ip), lengthof(payload))),
payload := payload
}
data := f_IPv6_enc(ip);
csum := f_IPv4_checksum(data);
data[10] := csum[0];
data[11] := csum[1];
return data;
}
/* generate a packet according to the input flow + pkt information */
function flow_gen_pkt(flow_info flowi, pkt_info pkti) return octetstring {
var octetstring data;
if (flowi.l3_protocol == c_ip_proto_ipv4) {
data := flow_gen_ipv4_pkt(flowi, pkti);
} else if (flowi.l3_protocol == c_ip_proto_ipv6) {
data := flow_gen_ipv6_pkt(flowi, pkti);
} else {
data := ''O;
}
if (pkti.trunc_len > 0 and pkti.trunc_len < lengthof(data)) {
data := substr(data, 0, pkti.trunc_len);
}
return data;
}
/* generate a packet according to the input flow + pkt information */
/*
function flow_gen_pkt_(flow_info flowi, pkt_info pkti) return octetstring {
var octetstring ret;
var unsignedshort src_port, dst_port;
var charstring src_ip, dst_ip;
if (pkti.direction == DIR_ORIG) {
src_ip := flowi.src_ip
src_port := flowi.src_port
dst_ip := flowi.dst_ip
dst_port := flowi.dst_port
} else {
src_ip := flowi.dst_ip
src_port := flowi.dst_port
dst_ip := flowi.src_ip
dst_port := flowi.src_port
}
if (flowi.l4_protocol == c_ip_proto_udp) {
ret := f_IPv4IPv6_AnyUdpPacket(src_ip, dst_ip, src_port, dst_port);
}
if (pkti.trunc_len > 0 and pkti.trunc_len < lengthof(ret)) {
ret := substr(ret, 0, pkti.trunc_len);
}
return ret
}
*/
/* reverse the L3 portion */
private function f_nfct_l3_reverse(template Layer3_type input) return template Layer3_type {
return {
protoname := input.protoname,
protonum := input.protonum,
src := input.dst,
dst := input.src
}
}
/* reverse the L4 portion */
private function f_nfct_l4_reverse(template Layer4_type input) return template Layer4_type {
return { protoname := input.protoname,
protonum := input.protonum,
sport := input.dport,
dport := input.sport }
}
/* reverse an Orig_repl_group template */
private function f_nfct_orig_repl_reverse(template Orig_repl_group input) return template Orig_repl_group {
var template Orig_repl_group output
output.layer3 := f_nfct_l3_reverse(input.layer3)
output.layer4 := f_nfct_l4_reverse(input.layer4)
output.zone := input.zone
/* we cannot assume inverse direction counters have any relation to the forward direction */
output.counters := *
return output
}
function f_proto_to_af(integer proto) return integer {
if (proto == c_ip_proto_ipv4) {
return AF_INET;
} else if (proto == c_ip_proto_ipv6) {
return AF_INET6;
} else {
return 0;
}
}
/* construct a template that can be used to match nf-conntrack XML */
function f_nfct_templ_from_flow(flow_info flowi) return template Flow {
/* construct original tuple from flow */
var template Orig_repl_group orig := {
layer3 := {
protoname := *,
protonum := int2str(f_proto_to_af(flowi.l3_protocol)),
src := flowi.src_ip,
dst := flowi.dst_ip
},
layer4 := {
protoname := *,
protonum := int2str(flowi.l4_protocol),
sport := flowi.src_port,
dport := flowi.dst_port
},
zone := *,
counters := *
}
/* create the inverse of the original tuple */
var template Orig_repl_group repl := f_nfct_orig_repl_reverse(orig)
return {
meta := { direction := "original", choice := { orig_repl_group := orig } },
meta_1 := { direction := "reply", choice := { orig_repl_group := repl } },
meta_2 := ?,
when := * }
}
/* get a single conntrack entry derived from the specified flow_info */
//{ meta := { direction := "original", choice := { orig_repl_group := { layer3 := { protoname := "ipv4", protonum := "2", src := "1.1.1.200", dst := "2.2.2.200" }, layer4 := { protoname := "udp", protonum := "17", sport := 1001, dport := 2001 }, zone := omit, counters := omit } } }, meta_1 := { direction := "reply", choice := { orig_repl_group := { layer3 := { protoname := "ipv4", protonum := "2", src := "2.2.2.200", dst := "1.1.1.200" }, layer4 := { protoname := "udp", protonum := "17", sport := 2001, dport := 1001 }, zone := omit, counters := omit } } }, meta_2 := { direction := "independent", choice := { indep_group := { state := omit, timeout_ := 30, mark := 0, secmark := omit, zone := omit, use := 2, id := 2741869312, assured := omit, unreplied := { }, timestamp := omit, deltatime := omit } } }, when := omit }
function f_get_conntracks(flow_info flowi) return Flows {
var charstring xml := f_get_conntrack_xml(flowi.src_ip, flowi.dst_ip, flowi.l4_protocol, flowi.src_port, flowi.dst_port)
return dec_Flows(unichar2oct(xml));
}
function f_get_conntrack(flow_info flowi) return Flow {
var Flows flows := f_get_conntracks(flowi);
return flows.flow_list[0];
}
/* generate + send packet for given flow through TUN */
function flow_send_pkt_tun1(flow_info flowi, pkt_info pkti) runs on dummy_CT {
var octetstring pkt := flow_gen_pkt(flowi, pkti);
TUN.send(tunmsg(pkt));
if (pkti.exp_pass) {
T.start;
alt {
//[] TUN2.receive(tunrcv(pkt)) { }
//[] TUN2.receive { log("unexpected receive"); repeat };
[] TUN2.receive {}
[] T.timeout { setverdict(inconc) }
}
T.stop;
}
}
/* generate + send packet for given flow through TUN2 */
function flow_send_pkt_tun2(flow_info flowi, pkt_info pkti) runs on dummy_CT {
var octetstring pkt := flow_gen_pkt(flowi, pkti);
TUN2.send(tunmsg(pkt));
if (pkti.exp_pass) {
T.start;
alt {
//[] TUN.receive(tunrcv(pkt)) { }
//[] TUN.receive { log("unexpected"); repeat }
[] TUN.receive {}
[] T.timeout { setverdict(inconc) }
}
T.stop;
}
}
/* get nf_conntrack from kernel for given flow and match against template */
function get_nfct_and_match(flow_info flowi, template Flow t_flow) return boolean {
var Flow ct := f_get_conntrack(flowi);
log("conntrack found: ", ct);
var boolean ret := match(ct, t_flow);
if (not ret) {
setverdict(fail);
log("conntrack as read from kernel:", ct);
log("template that didn't match:", t_flow);
}
return ret;
}
/* get nf_conntrack from kernel for given flow and ensure there is none */
function get_nfct_ensure_none(flow_info flowi) return boolean {
var Flows cts := f_get_conntracks(flowi);
var boolean ret := false;
if (lengthof(cts.flow_list) == 0) {
ret := true;
}
if (not ret) {
log("conntrack found but expected none:", cts);
setverdict(fail);
}
return ret;
}
/* apply a certain tolerance of up to 1 second in the timeout * retrieved after the packet traversed conntrack */
function timeout_range(NonNegativeInteger secs) return template NonNegativeInteger {
return (secs-1 .. secs);
}
/* update Flow template tiemout with timeout range of (tout-1 .. tout) */
function tflow_set_timeout(inout template Flow tflow, NonNegativeInteger tout) {
tflow.meta_2.choice.indep_group.timeout_ := timeout_range(tout);
}
/* shared init function for all test cases */
function init() runs on dummy_CT {
if (initialized) {
return;
}
map(self:TUN, system:TUN);
map(self:TUN2, system:TUN);
initialized := true;
}
/* generate pkt_info based on arguments */
function pkti_gen(in nfct_direction direction, in octetstring payload := ''O,
in NonNegativeInteger trunc_len := 0, in boolean exp_pass := true) return pkt_info {
var pkt_info pkti := {
direction := direction,
payload := payload,
trunc_len := trunc_len,
exp_pass := exp_pass
};
return pkti;
}
/* just verify that we can get conntrack XML and parse it */
testcase TC_xml() runs on dummy_CT {
var charstring xml;
init();
xml := f_get_conntracks_xml()
log(xml)
log(dec_Flows(unichar2oct(xml)))
}
/* test for classic netfilter UDP '3way handshake' ORIG, REPL, ORIG */
testcase TC_udp_3way() runs on dummy_CT {
var flow_info flowi := flow_gen(get_random_port_offset())
var Flow ct;
var template Flow t_flow;
init();
log("First packet (ORIG): We expect to create conntrack: unreplied, 30s");
flow_send_pkt_tun1(flowi, pkti_gen(DIR_ORIG))
t_flow := f_nfct_templ_from_flow(flowi);
t_flow.meta_2.choice.indep_group.unreplied := {};
tflow_set_timeout(t_flow, 30);
get_nfct_and_match(flowi, t_flow);
log("Second packet (REPLY): Unreplied should go, still 30s");
flow_send_pkt_tun2(flowi, pkti_gen(DIR_REPLY))
t_flow := f_nfct_templ_from_flow(flowi);
tflow_set_timeout(t_flow, 30);
get_nfct_and_match(flowi, t_flow);
log("Third packet (ORIG): Assured, 180s");
flow_send_pkt_tun1(flowi, pkti_gen(DIR_ORIG))
t_flow := f_nfct_templ_from_flow(flowi);
t_flow.meta_2.choice.indep_group.assured := {};
tflow_set_timeout(t_flow, 180);
get_nfct_and_match(flowi, t_flow);
setverdict(pass);
}
/* test for inverse netfilter UDP '3way handshake' ORIG, REPL, REPL */
testcase TC_udp_uni2() runs on dummy_CT {
var flow_info flowi := flow_gen(get_random_port_offset());
var template Flow t_flow;
var integer i;
init();
for (i := 1; i <= 2; i := i+1) {
log("Packet (ORIG): We expect to create conntrack: unreplied, 30s");
flow_send_pkt_tun1(flowi, pkti_gen(DIR_ORIG))
t_flow := f_nfct_templ_from_flow(flowi);
t_flow.meta_2.choice.indep_group.unreplied := {};
tflow_set_timeout(t_flow, 30);
get_nfct_and_match(flowi, t_flow);
}
log("First (REPLY): Unreplied should go, still 30s");
flow_send_pkt_tun2(flowi, pkti_gen(DIR_REPLY))
t_flow := f_nfct_templ_from_flow(flowi);
tflow_set_timeout(t_flow, 30);
get_nfct_and_match(flowi, t_flow);
log("Second (REPLY): now 180s");
flow_send_pkt_tun2(flowi, pkti_gen(DIR_REPLY))
t_flow := f_nfct_templ_from_flow(flowi);
t_flow.meta_2.choice.indep_group.assured := {};
tflow_set_timeout(t_flow, 180);
get_nfct_and_match(flowi, t_flow);
setverdict(pass);
}
/* truncated UDP header should neither pass nor create conntrack */
testcase TC_udp_shorthdr() runs on dummy_CT {
var flow_info flowi := flow_gen(get_random_port_offset());
var template Flow t_flow;
init();
log("First packet (ORIG): We expect to create no conntrack");
flow_send_pkt_tun1(flowi, pkti_gen(DIR_ORIG, -, 20+5, false))
t_flow := f_nfct_templ_from_flow(flowi);
get_nfct_ensure_none(flowi);
setverdict(pass);
}
/* truncated UDP packet (full hdr, short data) should neither pass nor create conntrack */
testcase TC_udp_shortdata() runs on dummy_CT {
var flow_info flowi := flow_gen(get_random_port_offset());
var template Flow t_flow;
init();
log("First packet (ORIG): We expect to create no conntrack");
flow_send_pkt_tun1(flowi, pkti_gen(DIR_ORIG, -, 20+8+5, false))
t_flow := f_nfct_templ_from_flow(flowi);
get_nfct_ensure_none(flowi);
setverdict(pass);
}
control {
//execute(TC_xml());
execute(TC_udp_3way());
execute(TC_udp_uni2());
execute(TC_udp_shorthdr());
execute(TC_udp_shortdata());
}
}