log syslog all;

/* Definitionen fuer die Bitarithmetik der Schleifenverhinderungsmechanik */

define OSPF_tag_sourcebits = 5; /* ld(32) */
define OSPF_tag_pathbits = 32 - OSPF_tag_sourcebits;
define OSPF_tag_pathvalue_div = 32 ; /* 2 ^ OSPF_tag_sourcebits */

function fn_tag_to_source (int tagbits) {
	return tagbits - ((tagbits / OSPF_tag_pathvalue_div) * OSPF_tag_pathvalue_div);
}

function fn_tag_to_path (int tagbits) {
	return tagbits / OSPF_tag_pathvalue_div;
}

function fn_power_of_2 (int n) {
	# awk 'BEGIN { for ( i=0 ; i < 32 ; i++ ) print "\t\t" i ": return " 2^i ";" }'
	case n {
		0: return 1;
		1: return 2;
		2: return 4;
		3: return 8;
		4: return 16;
		5: return 32;
		6: return 64;
		7: return 128;
		8: return 256;
		9: return 512;
		10: return 1024;
		11: return 2048;
		12: return 4096;
		13: return 8192;
		14: return 16384;
		15: return 32768;
		16: return 65536;
		17: return 131072;
		18: return 262144;
		19: return 524288;
		20: return 1048576;
		21: return 2097152;
		22: return 4194304;
		23: return 8388608;
		24: return 16777216;
		25: return 33554432;
		26: return 67108864;
		27: return 134217728;
		28: return 268435456;
		29: return 536870912;
		30: return 1073741824;
		31: return 2147483648;
		else: return 0;
		}
}

function fn_instance_to_tagbit (int inst) {
	return	fn_power_of_2(OSPF_tag_sourcebits - 1 + inst);
}

function fn_instancebit_is_set_in_ospf_tag (int inst)
int instpathbits;
{
	instpathbits = fn_tag_to_path(ospf_tag) / fn_power_of_2(inst-1);
	return	(instpathbits / 2) * 2 != instpathbits;
}

function fn_reentrybit_is_set_in_ospf_tag() {
	return	fn_instancebit_is_set_in_ospf_tag(fn_tag_to_source(ospf_tag));
}

function fn_set_reentrybit_in_ospf_tag() {
	ospf_tag = ospf_tag + fn_instance_to_tagbit(fn_tag_to_source(ospf_tag));
}

include "/usr/local/etc/bird.local.conf";

/* Dort muss mindestens Folgendes definiert werden (Beispiel):

router id 91.204.4.242;

function fn_is_local_ospf_instance (int inst) {
 	case inst {
		1: return true;
		2: return true;
		else: return false;
	}
}

function fn_any_local_instancebit_is_set_in_ospf_tag () {
	return	fn_instancebit_is_set_in_ospf_tag(1) ||
		fn_instancebit_is_set_in_ospf_tag(2);
}

define Unrouted_local = [ 192.168.1.0/24+, 192.168.84.0/24+ ];
*/

define Babel_WLAN_metric_base = 1000;
define Babel_WLAN_metric_inactive = 999;
define OSPF_metric2_max = 16777215;
define OSPF_preference_local	= 160;	/* > OSPF default = 150 */

define HOT  = 0;
define BEST = 1;
define COLD = 2;

##### Definitionen fuer alle bekannten IGP Instanzen:

include "/usr/local/etc/bird.inst.conf";

# Dort muss mindestens Folgendes definiert werden:
#define potato_ospf1 = BEST; [... et al. ...]
#function fn_instance_to_potato (int inst) -> int (HOT, ...)
#function fn_proto_to_instance (string from_proto) -> int (1, 2, ...)
#function fn_instance_to_bgpospf (int inst) -> string ("bgpXospfX")

/* XXX zwecks Einrechnung der Babel Metrik, gehoert hier aber nicht hin: */
function fn_a_Babel_WLAN_routes () {
	return	ifname = "tap9";
	};

/* Bausteine fuer Filter. Namenskonventionen:
   f_...	Filter (accept/reject)
   fn_...	Funktion
   ..._a_...	true => accept
   ..._r_...	true => reject
   ..._am_...	true => accept + modify
*/

function fn_a_Routed () {
	return net ~ Routed;
	};

filter f_a_Routed {
	if fn_a_Routed()
	then accept;
	else reject "Routed";
	};

function fn_r_Unrouted () {
	return ! (net ~ Unrouted_global || net ~ Unrouted_local);
	};

filter f_r_Unrouted {
	if fn_r_Unrouted()
	then accept;
	else reject "Unrouted";
	};

function fn_a_acceptable_routes () {
	return fn_r_Unrouted() && fn_a_Routed();
}
 
/* Einrechnung der Babel Metrik: */
function fn_include_Babel_WLAN_routes () {
	case gw {
		include "/var/run/bird.babeld.conf";
		else: ospf_metric1 = ospf_metric1 + Babel_WLAN_metric_base + Babel_WLAN_metric_inactive;
	}
}

function fn_debug_ospf_route (string str)
{
	print	str, " ", net, " ^", scope, " *", source,
		" !", from, " :", gw, " (", preference, ") ",
		igp_metric, "/", ospf_metric1, "/", ospf_metric2,
 		" [", ospf_tag, "]";
		# Fuehrt zu:
		# bird: filters, line 169: Can't operate with values of incompatible types
		#" [", fn_tag_to_source(ospf_tag), "|", fn_tag_to_path(ospf_tag), "]";
}

function fn_debug_ospf_route_before (string str)
{
	printn str; fn_debug_ospf_route("?");
}

function fn_debug_ospf_route_after (string str)
{
	printn str; fn_debug_ospf_route("!");
}

function fn_am_local_to_ospf_table () {
	if ! (proto ~ "ospf*") &&
	   fn_a_acceptable_routes()
	then if ifname ~ "lo0"
	     then return false;	
	     else { if preference < OSPF_preference_local
		    then preference = OSPF_preference_local;
		    return true; }
	else return false;
}

function fn_am_local_to_ospf_export () {
	if ! (proto ~ "*ospf*") &&
	   fn_a_acceptable_routes()
	then {	if ifname ~ "lo0"
	      	then return false;
	      	else # geroutete loopback Adressen auch per OSPF stubnet!
		     if ifname ~ "lo*"
		     then ospf_metric1 = 1;
		     else ospf_metric1 = 10;
		ospf_metric2 = 0;
	       return true; }
	else return false;
}

function fn_import_ospf_master (int from_instance) {
	if ! fn_a_acceptable_routes()
	then return false;
	# Schleifenvermeidung: routes mit tags von OSPF-Instanzen,
	# an denen dieser Router teilnimmt, sind schonmal hier
	# durchgelaufen, und zwar mit tag = 0, d.h. ohne Uebergang
	# zwischen OSPF-Instanzen. (Evtl. Ausnahme: Partitionierung
	# der source Instanz, sodass die urspruengliche route nicht
	# mit source = 0 hier ankam, sondern nur per Transit.)
	else if ospf_tag != 0
	then {
		if fn_instance_to_potato(from_instance) = HOT
		   # TBD || fn_instance_to_transit(from_instance) = false
		then	# kein Transit durch hot potato Instanzen
			return false;
		else if	fn_is_local_ospf_instance(fn_tag_to_source(ospf_tag)) ||
			fn_any_local_instancebit_is_set_in_ospf_tag()
		then	# re-entry
			if ! fn_reentrybit_is_set_in_ospf_tag()
			then	# wg. counting-to-infinity
				fn_set_reentrybit_in_ospf_tag();
	     }
	case from_instance {
		/* XXX zwecks Einrechnung der Babel Metrik, gehoert hier aber nicht hin: */
		# metric mittels /var/run/bird.babeld.conf (hoch)setzen:
		2: fn_include_Babel_WLAN_routes();
	     	}
	return true;
}


function fn_import_master_ospf (string from_proto) {
	return proto = from_proto;
}

/* Funktioniert nicht als Funktion, aber als Filter,
   s. protocol pipe master2ospfX in bird.instX.conf.
   Grumpf:-(bird 1.5.0)
function fn_export_master_ospf () {
	if proto ~ "ospf*"
	then return false;
	else return fn_am_local_to_ospf_table();
}
*/

function fn_am_master_to_bgpospf (int dest_instance)
int from_instance;
{
	if fn_a_acceptable_routes()
	then {	from_instance = fn_proto_to_instance(proto);
		if ospf_tag = 0		# (lokale Route, keine Transitroute)
		then {	# route entstammt einer IGP Instanz, an der dieser
			# router teilnimmt. Beim Uebergang in eine andere
			# IGP Instanz muss diese route, die gerade ein
			# lokales IGP verlaesst, mit dem tag dieser Instanz
			# versehen werden. Kommt die route dann spaeter
			# per transit wieder zur Urprungs-Instanz zurueck,
			# kann sie dank des tags als Rundlauefer erkannt
			# und entsprechend schleifenbehandelt werden.
			ospf_tag = from_instance;
			return true;
		     }
		else	if fn_instance_to_potato(from_instance) = HOT
			   # TBD || fn_instance_to_transit(from_instance) = false
			then	# kein Transit durch HOT Instanzen wg. Verlust der ursprünglichen Metrik
				return false;
			else {	if fn_reentrybit_is_set_in_ospf_tag() &&
				   fn_instancebit_is_set_in_ospf_tag(dest_instance)
				then	# kein counting-to-infinity
					return false;
				# Transit-IGP, aus dem die Route gerade kommt, in die Route "einkerben"
				if ! fn_instancebit_is_set_in_ospf_tag(from_instance)
				then	ospf_tag = ospf_tag + fn_instance_to_tagbit(from_instance);
				return true;
			     }
	     }
	else return false;
}

function fn_export_master_bgpospf (string dest_proto)
int dest_instance;
{
	dest_instance = fn_proto_to_instance(dest_proto);
	printn "master2bgp"; fn_debug_ospf_route_before(dest_proto);
	if proto ~ "ospf*" &&
	   proto != dest_proto &&
	   fn_am_master_to_bgpospf(dest_instance)
	then {	printn "master2bgp"; fn_debug_ospf_route_after(dest_proto);
		bgp_local_pref = preference;
		bgp_path.prepend(ospf_tag);
		# optimal:
		# -> E1 mit metric1 = Summe
		# hot potato ("closest exit"):
		# -> E1 mit metric1 = 0, # geht nicht: metric2 = Summe
		# retour: automatisch mit mit metric1 = Summe, metric2 = 0
		# cold potato ("best exit", RFC4451):
		# -> E2 mit metric2 = Summe
		# ?: wie geht retour E2 -> E1, d.h. wie funktioniert
		# eine Unterscheidung von ursprünglichen E2 routes?
		# flag im ospf_tag?
		if # XXX geht nicht: ospf_metric2 = 16777235
		     ospf_metric2 = OSPF_metric2_max
		then
			bgp_med = ospf_metric1;
		else
			bgp_med = ospf_metric2 + ospf_metric1;
		# zZ unbenutzt, erscheint im show all als [ASXi]/[ASXe]
		if ospf_tag != 0
		then
			if fn_reentrybit_is_set_in_ospf_tag()
			then
				bgp_origin = 2;
			else
				bgp_origin = 1;
		else
			bgp_origin = 0;
		accept;
	     }
	else reject;
}

function fn_import_bgpospf ()
{
	preference = bgp_local_pref;
	return true;
}

function fn_export_ospf (int dest_instance)
string proto_bgpospf;
int potato_ospf;
{
	proto_bgpospf = fn_instance_to_bgpospf(dest_instance);
	potato_ospf = fn_instance_to_potato(dest_instance);
	printn "export-", dest_instance; fn_debug_ospf_route_before("");
	if fn_am_local_to_ospf_export()
	then return true;
	else if proto = proto_bgpospf
	then {	ospf_tag = bgp_path.first;
		# optimal:
		# -> E1 mit metric1 = Summe
		# hot potato ("closest exit"):
		# -> E1 mit metric1 = 0, metric2 = Summe
		# retour: automatisch mit mit metric1 = Summe, metric2 = 0
		# cold potato ("best exit", RFC4451):
		# -> E2 mit metric2 = Summe
		# ?: wie geht retour E2 -> E1, d.h. wie funktioniert
		#    eine Unterscheidung von ursprünglichen E2 routes?
		#    also flag im ospf_tag kodieren?
		# !: nein, keine E2 routen als E2 durchlassen,
		#    weil das jede Instanz selbst entscheiden soll,
		#    also nur bei cold potato auf E2 setzen (lassen)
		#
		if potato_ospf = BEST
			then {
				# igp_metric = bgp_med;
				# bgp_med = 0;
				# scope = SCOPE_ORGANIZATION;
				ospf_metric1 = bgp_med;
			     }
			else if potato_ospf = HOT
			then {
				# geht nicht, wird beim Setzen von
				# von ospf_metric1 genichtet:
				# ospf_metric2 = bgp_med;
				ospf_metric1 = 0;
				# XXX instance mit hot potato
				# XXX darf kein Transit sein!
				# XXX => keine tag routes exportieren
			     }
			else if potato_ospf = COLD
			     then ospf_metric2 = bgp_med;
			printn "export-", dest_instance; fn_debug_ospf_route_after("");
			return true;
	     }
	else return false;
}

protocol device {
	scan time 10;
}

/* wg. BSD: */
protocol direct {
}

protocol kernel kernel0 {
	learn on;
	scan time 5;
	import filter f_r_Unrouted;
	export filter f_r_Unrouted;
}

/* XXX zwecks Einrechnung der Babel Metrik, gehoert hier aber nicht hin: */
table fib4table;
protocol kernel kernel4 {
	description "babel FIB 4";
	kernel table 4;
	table fib4table;
	learn on;
	scan time 10;	
}

##### Definition der IGP Instanzen, an denen dieser Router teilnimmt:

include "/usr/local/etc/bird.inst.local.conf";