#
# For running this script on Linux in your private network there is some
# preparation required. Let's assume 'eth0' is your physical network device
# (run 'ifconfig' for that), 'shrimp' is your Linux user name, and
# 192.168.178.1 is the IP of a device in your local network you want to ping.
# You would do the following:
#
# 1) Establish a bridge between a TAP device and a physical network device in
#    your Linux:
#    ! sudo brctl addbr br0
#    ! sudo ip addr flush dev eth0
#    ! sudo brctl addif br0 eth0
#    ! sudo ip tuntap add tap0 mode tap user shrimp
#    ! sudo brctl addif br0 tap0
#    ! sudo ifconfig eth0 up
#    ! sudo ifconfig tap0 up
#    ! sudo ifconfig br0 up
#    ! brctl show
#    ! sudo dhclient -v br0
#
# 2) Now you can do testing with the script:
#    ! cd <GENODE_DIR>/build/x86_64
#    ! make run/ping KERNEL=linux BOARD=linux ON_LINUX_WITH_DST_IP=192.168.178.1
#
# 3) Clean up your Linux when done testing:
#    ! sudo brctl delif br0 tap0
#    ! sudo ip tuntap delete tap0 mode tap
#    ! sudo brctl delif br0 eth0
#    ! sudo ifconfig br0 down
#    ! sudo brctl delbr br0
#    ! sudo ifconfig eth0 up
#    ! sudo dhclient -v eth0
#

if {[have_spec foc] || [have_board rpi3] || [have_board imx53_qsb_tz]} {
	puts "Run script is not supported on this platform."
	exit 0
}

if {[get_cmd_switch --autopilot] && ([have_spec linux] ||
                                     [have_board virt_qemu_riscv])} {
	puts "Autopilot mode is not supported on this platform."
	exit 0
}

set on_linux_with_dst_ip ""
catch {set on_linux_with_dst_ip $::env(ON_LINUX_WITH_DST_IP)}
set on_linux [expr ![string equal $on_linux_with_dst_ip ""]]

if {[expr [have_spec linux] && !$on_linux]} {

	puts "You must set 'on_linux_with_dst_ip' in the run script if you want to run on Linux."
	exit 0
}

if {[expr ![have_spec linux] && $on_linux]} {

	puts "You must run on Linux if 'on_linux_with_dst_ip' in the run script is set."
	exit 0
}

set on_hardware [expr ![have_include power_on/qemu]]
set second_ping_via_udp [expr $on_hardware && !$on_linux]

create_boot_directory

import_from_depot [depot_user]/src/[base_src] \
                  [depot_user]/pkg/[drivers_nic_pkg] \
                  [depot_user]/src/init \
                  [depot_user]/src/nic_router

build { app/ping }

proc dst_ip { } {

	global on_linux_with_dst_ip
	global on_linux
	if {$on_linux} {
		return $on_linux_with_dst_ip
	}

	global on_hardware
	if {$on_hardware} {
		return "10.0.0.2"
	} else {
		return "10.0.2.2"
	}
}

append config {

<config>

	<parent-provides>
		<service name="ROM"/>
		<service name="IRQ"/>
		<service name="IO_MEM"/>
		<service name="IO_PORT"/>
		<service name="PD"/>
		<service name="RM"/>
		<service name="CPU"/>
		<service name="LOG"/>
	</parent-provides>

	<default-route>
		<any-service> <parent/> <any-child/> </any-service>
	</default-route>

	<default caps="100"/>

	<start name="timer">
		<resource name="RAM" quantum="1M"/>
		<provides><service name="Timer"/></provides>
	</start>

	<start name="drivers" caps="1000" managing_system="yes">
		<resource name="RAM" quantum="32M"/>
		<binary name="init"/>
		<route>
			<service name="ROM" label="config"> <parent label="drivers.config"/> </service>
			<service name="Uplink"> <child name="nic_router"/> </service>
			<any-service> <parent/> <any-child/> </any-service>
		</route>
	</start>

	<start name="nic_router" caps="200">
		<resource name="RAM" quantum="10M"/>
		<provides>
			<service name="Nic"/>
			<service name="Uplink"/>
		</provides>
		<config dhcp_discover_timeout_sec="1">

			<policy label_prefix="drivers" domain="uplink"/>
			<policy label_prefix="ping"    domain="downlink"/>

			<domain name="uplink">

				<nat domain="downlink" icmp-ids="100" udp-ports="100"/>

			</domain>

			<domain name="downlink" interface="10.0.3.1/24">

				<dhcp-server ip_first="10.0.3.100" ip_last="10.0.3.200"/>
				<icmp dst="} [dst_ip] {/24" domain="uplink"/>
				<udp dst="} [dst_ip] {/24">
					<permit port="12345" domain="uplink"/>
				</udp>

			</domain>

		</config>
	</start> }

append_if $second_ping_via_udp config {

	<start name="ping_2">
		<binary name="ping"/>
		<resource name="RAM" quantum="8M"/>
		<config period_sec="1"
		        verbose="no"
		        count="1"
		        protocol="udp"
		        dst_ip="} [dst_ip] {"
		        dst_port="12345"/>
		<route>
			<service name="Nic"> <child name="nic_router"/> </service>
			<any-service> <parent/> <any-child/> </any-service>
		</route>
	</start> }

append config {

	<start name="ping_1">
		<binary name="ping"/>
		<resource name="RAM" quantum="8M"/>
		<config dst_ip="} [dst_ip] {"
		        period_sec="1"
		        verbose="no"
		        count="3"/>
		<route>
			<service name="Nic"> <child name="nic_router"/> </service>
			<any-service> <parent/> <any-child/> </any-service>
		</route>
	</start>

</config> }

install_config $config
build_boot_image { ping }

append qemu_args " -nographic "
append_qemu_nic_args

set done_string ".*\"ping_1\" exited with exit value 0.*\n"
append_if $second_ping_via_udp done_string ".*ping_2\] From [dst_ip] Destination Unreachable.*\n"

run_genode_until $done_string 30
